Skip to content

Commit ecb0668

Browse files
authored
fix(errand): 需求并发校验时间戳改为详情接口现取 (#96)
* fix(errand): 需求并发校验时间戳改为详情接口现取 UpdateErrandDemand 首次编辑 500 的根因:前端拿不到需求的最新 updated_at(创建响应无时间戳、订单列表/详情也无),快照存 null 导致乐观锁校验失败。 采用无状态方案(回退 Create 响应改动): - 回退 CreateErrandDemandResponse.updated_at(时间戳不存前端快照) - BuyerErrandOrderDetail 新增 updated_at = 18 - service BuyerOrderDetail 填充 demand.UpdatedAt - handler 透传 编辑页进入时调 GetBuyerErrandOrderDetail 现取数据库最新 updated_at,提交撤回/修改时使用,不依赖 localStorage 快照。 * buf skip breaking
1 parent 0cef8f6 commit ecb0668

6 files changed

Lines changed: 14 additions & 14 deletions

File tree

internal/service/errandservice/internal/handler/v1/buyer_errand_order.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@ func (s *BuyerErrandOrderServiceServer) GetBuyerErrandOrderDetail(
142142
TotalServiceFeeCents: detail.TotalServiceFeeCents,
143143
CaptainInfo: detail.CaptainInfo,
144144
Deadline: timestamppb.New(detail.Deadline),
145+
UpdatedAt: timestamppb.New(detail.UpdatedAt),
145146
}
146147

147148
if detail.ShoppingStartAt != nil {

internal/service/errandservice/internal/handler/v1/errand_demand.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ func (s *ErrandDemandServiceServer) CreateErrandDemand(
4848
}
4949

5050
// 4. 调用 service 层
51-
demandID, updatedAt, err := service.CreateErrandDemand(ctx, requesterID, msg.StoreId, deadline, items)
51+
demandID, err := service.CreateErrandDemand(ctx, requesterID, msg.StoreId, deadline, items)
5252
if err != nil {
5353
// service 层已经记过日志了,这里只翻译成 connect.Error
5454
if errors.Is(err, service.ErrProductInvalid) {
@@ -61,10 +61,9 @@ func (s *ErrandDemandServiceServer) CreateErrandDemand(
6161
return nil, errandError()
6262
}
6363

64-
// 5. 返回响应(updated_at 供前端保存,用于后续撤回/修改的并发校验)
64+
// 5. 返回响应
6565
return connect.NewResponse(&errandv1.CreateErrandDemandResponse{
6666
ErrandDemandId: demandID,
67-
UpdatedAt: timestamppb.New(*updatedAt),
6867
}), nil
6968
}
7069

internal/service/errandservice/internal/service/buyer_order.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ type BuyerOrderDetail struct {
5656
DistributionCompletedAt *time.Time
5757
PaymentCompletedAt *time.Time
5858
CancelledAt *time.Time
59+
UpdatedAt time.Time
5960
}
6061

6162
func GetBuyerOrderBriefList(
@@ -222,6 +223,7 @@ func GetBuyerOrderDetail(ctx context.Context, requesterID, demandID int64) (*Buy
222223
DistributionCompletedAt: demand.DistributionCompletedAt,
223224
PaymentCompletedAt: demand.PaymentCompletedAt,
224225
CancelledAt: demand.CancelledAt,
226+
UpdatedAt: demand.UpdatedAt,
225227
}, nil
226228
}
227229

internal/service/errandservice/internal/service/demand.go

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -39,36 +39,35 @@ type DemandItemDraft struct {
3939

4040
// CreateErrandDemand — 买家创建跑腿需求
4141
// CreateErrandDemand 买家创建跑腿需求订单。
42-
// 返回新创建的 errand_demand ID 和创建后的 updated_at(用于后续并发校验)
42+
// 返回新创建的 errand_demand ID。
4343
func CreateErrandDemand(
4444
ctx context.Context,
4545
requesterID int64,
4646
storeID int64,
4747
deadline time.Time,
4848
items []DemandItemDraft,
49-
) (int64, *time.Time, error) {
49+
) (int64, error) {
5050
// 1. 参数校验
5151

5252
// 检查重复商品(同一个 product_template_id 不能出现两次)
5353
productSet := make(map[int64]struct{})
5454
for _, item := range items {
5555
if item.Quantity <= 0 {
56-
return 0, nil, ErrInvalidQuantity
56+
return 0, ErrInvalidQuantity
5757
}
5858
if _, exists := productSet[item.ProductTemplateID]; exists {
59-
return 0, nil, ErrDuplicateProduct
59+
return 0, ErrDuplicateProduct
6060
}
6161
productSet[item.ProductTemplateID] = struct{}{}
6262
}
6363

6464
// 2. 逐个调用 catalog 服务校验商品:存在性、归属、版本
6565
productMap, err := validateProducts(ctx, storeID, items)
6666
if err != nil {
67-
return 0, nil, err
67+
return 0, err
6868
}
6969

7070
var demandID int64
71-
var updatedAt time.Time
7271
err = repository.RunInTx(ctx, func(ctx context.Context, tx bun.Tx) error {
7372
// 3. 插入 errand_demand 主记录
7473
demand := &model.ErrandDemand{
@@ -81,7 +80,6 @@ func CreateErrandDemand(
8180
return ErrInternal
8281
}
8382
demandID = demand.ID
84-
updatedAt = demand.UpdatedAt.UTC()
8583

8684
// 4. 批量插入 errand_demand_item 明细
8785
demandItems := make([]*model.ErrandDemandItem, 0, len(items))
@@ -105,10 +103,10 @@ func CreateErrandDemand(
105103
return nil
106104
})
107105
if err != nil {
108-
return 0, nil, err
106+
return 0, err
109107
}
110108

111-
return demandID, &updatedAt, nil
109+
return demandID, nil
112110
}
113111

114112
// validateProducts 逐个校验商品:存在、归属店铺、版本号匹配。

proto/sast/sastshopv2/errand/v1/buyer_errand_order.proto

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,4 +82,6 @@ message BuyerErrandOrderDetail {
8282
google.protobuf.Timestamp payment_completed_at = 16;
8383
// 订单取消时间
8484
google.protobuf.Timestamp cancelled_at = 17;
85+
// 需求最近更新时间,用于撤回/修改的并发校验
86+
google.protobuf.Timestamp updated_at = 18;
8587
}

proto/sast/sastshopv2/errand/v1/errand_demand_service.proto

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,6 @@ message CreateErrandDemandRequest {
2323
message CreateErrandDemandResponse {
2424
// 买家视角跑腿订单 ID
2525
int64 errand_demand_id = 1;
26-
// 创建后的 updated_at,用于后续撤回/修改的并发校验
27-
google.protobuf.Timestamp updated_at = 2;
2826
}
2927

3028
message GetDemandListRequest {

0 commit comments

Comments
 (0)