diff --git a/backend/internal/service/delivery.go b/backend/internal/service/delivery.go index a8c498c..a4ae2f7 100644 --- a/backend/internal/service/delivery.go +++ b/backend/internal/service/delivery.go @@ -357,7 +357,16 @@ func (s *DeliveryService) submit(orderNo, gameAccount, bindUUID string, apiClien s.markDeliverySubmissionFailed(claimed, apiClientID, deliveryStageBindMismatch, err.Error()) return nil, err } - queueOrderID, err := s.createOrderQueue(goodID, order.OrderNo) + goodsNo := "" + goodsName := "" + if info != nil && info.Product != nil { + goodsNo = info.Product.SKU + goodsName = info.Product.Name + } else if openOrder != nil && openOrder.Product != nil { + goodsNo = openOrder.Product.SKU + goodsName = openOrder.Product.Name + } + queueOrderID, err := s.createOrderQueue(goodID, order.OrderNo, goodsNo) if err != nil { s.markDeliverySubmissionFailed(claimed, apiClientID, deliveryStageCreateQueue, err.Error()) return nil, err @@ -369,7 +378,7 @@ func (s *DeliveryService) submit(orderNo, gameAccount, bindUUID string, apiClien }); err != nil { return nil, err } - if err := s.patchOrderQueue(queueOrderID, gameAccount, bindUUID); err != nil { + if err := s.patchOrderQueue(queueOrderID, gameAccount, bindUUID, goodsNo, goodsName); err != nil { s.markDeliverySubmissionFailed(claimed, apiClientID, deliveryStagePatchQueue, err.Error()) return nil, err } diff --git a/backend/internal/service/delivery_upstream.go b/backend/internal/service/delivery_upstream.go index d8fa482..4faa72d 100644 --- a/backend/internal/service/delivery_upstream.go +++ b/backend/internal/service/delivery_upstream.go @@ -44,15 +44,20 @@ func (s *DeliveryService) accountBound(bindUUID, goodID string) (map[string]inte return out.GameAccount, nil } -func (s *DeliveryService) createOrderQueue(goodID, orderNo string) (string, error) { +func (s *DeliveryService) createOrderQueue(goodID, orderNo, goodsNo string) (string, error) { var out struct { Orders []map[string]interface{} `json:"orders"` } - if err := s.signProxy("/public/users/orders-queue", "POST", map[string]interface{}{ - "good_id": goodID, - "quantity": 1, - "order_sn": orderNo, - }, &out); err != nil { + payload := map[string]interface{}{ + "good_id": goodID, + "quantity": 1, + "order_sn": orderNo, + "h5_prefix": s.channel, + } + if goodsNo != "" { + payload["goods_no"] = goodsNo + } + if err := s.signProxy("/public/users/orders-queue", "POST", payload, &out); err != nil { return "", err } if len(out.Orders) == 0 { @@ -65,13 +70,21 @@ func (s *DeliveryService) createOrderQueue(goodID, orderNo string) (string, erro return orderID, nil } -func (s *DeliveryService) patchOrderQueue(orderID, gameAccount, bindUUID string) error { +func (s *DeliveryService) patchOrderQueue(orderID, gameAccount, bindUUID, goodsNo, goodsName string) error { var out map[string]interface{} - return s.signProxy("/public/users/orders-queue", "PATCH", map[string]interface{}{ + payload := map[string]interface{}{ "order_id": orderID, "game_account": gameAccount, "bind_uuid": bindUUID, - }, &out) + "h5_prefix": s.channel, + } + if goodsNo != "" { + payload["goods_no"] = goodsNo + } + if goodsName != "" { + payload["goods_name"] = goodsName + } + return s.signProxy("/public/users/orders-queue", "PATCH", payload, &out) } func (s *DeliveryService) createUpstreamOrder(orderID, gameAccount, goodID, orderNo string) (map[string]interface{}, error) { diff --git a/backend/internal/service/merchant_test.go b/backend/internal/service/merchant_test.go index d2822b2..5819bf0 100644 --- a/backend/internal/service/merchant_test.go +++ b/backend/internal/service/merchant_test.go @@ -531,3 +531,42 @@ func TestUsernameCanBeReusedAfterMerchantAccountSoftDelete(t *testing.T) { t.Fatalf("expected a new account record, got id=%d", recreated.ID) } } + +// TestRemoveMemberDoesNotReleaseUsername 固化行为:商户侧"移除成员"只删除成员关系, +// 登录账号(含用户名)保留,同名用户名仍被占用,无法新建。 +func TestRemoveMemberDoesNotReleaseUsername(t *testing.T) { + db := newServiceTestDB(t) + merchant := model.Merchant{Code: "remove-member-keep", Name: "移除成员保留账号", Status: model.MerchantStatusActive} + actor := model.User{Username: "remove-member-actor", PasswordHash: "hash", Role: model.RoleMerchant, Status: 1} + staff := model.User{Username: "kf-2", PasswordHash: "hash", Role: model.RoleMerchant, Status: 1} + for _, entity := range []interface{}{&merchant, &actor, &staff} { + if err := db.Create(entity).Error; err != nil { + t.Fatalf("create fixture: %v", err) + } + } + if err := createDefaultMerchantRoles(db, merchant.ID); err != nil { + t.Fatalf("create default roles: %v", err) + } + + merchantSvc := NewMerchantService(db, nil, NewTenantService(db)) + member, err := merchantSvc.AddMember(merchant.ID, AddMemberInput{ + UserID: staff.ID, + Role: model.MemberRoleOperator, + }, actor.ID) + if err != nil { + t.Fatalf("add member: %v", err) + } + if err := merchantSvc.RemoveMember(merchant.ID, member.ID, actor.ID); err != nil { + t.Fatalf("remove member: %v", err) + } + + // 登录账号仍在(未被删除),用户名继续占用。 + var kept model.User + if err := db.First(&kept, staff.ID).Error; err != nil { + t.Fatalf("login account should be retained after member removal: %v", err) + } + userSvc := NewUserService(db, nil) + if _, err := userSvc.Create("kf-2", "password123", "新员工", model.RoleMerchant, 0); err == nil { + t.Fatalf("username should still be occupied after member removal") + } +}