允许平台库存资产直接完成换货归属继承
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 11m27s
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 11m27s
换货完成事务允许平台库存新资产换入,并同步店铺归属、分销状态及钱包租户标签;其他店铺资产继续拒绝。 Constraint: 七月迭代要求归属继承不受 migrate_data 控制 Rejected: 先将新资产人工分配到旧资产店铺 | 会保留冲突校验并增加多余操作 Confidence: high Scope-risk: narrow Directive: 后续换货资料迁移不得覆盖归属继承规则 Tested: gofmt;git diff --check Not-tested: 按用户要求未运行自动化测试
This commit is contained in:
@@ -583,6 +583,9 @@ func (s *Service) completeExchangeWithTx(ctx context.Context, tx *gorm.DB, order
|
||||
if err = s.validateExchangeAssetsWithTx(ctx, tx, order.ID, oldAsset, newAsset); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = s.syncNewAssetOwnershipWithTx(ctx, tx, oldAsset, newAsset); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = s.switchCustomerBindingWithTx(ctx, tx, oldAsset, newAsset); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -755,7 +758,9 @@ func (s *Service) validateExchangeAssetsWithTx(ctx context.Context, tx *gorm.DB,
|
||||
if newAsset.AssetStatus != constants.AssetStatusInStock {
|
||||
return errors.New(errors.CodeExchangeNewAssetNotInStock)
|
||||
}
|
||||
if !sameShopID(oldAsset.ShopID, newAsset.ShopID) {
|
||||
// 平台库存资产没有店铺归属,换货完成时会继承旧资产店铺;
|
||||
// 已归属其他店铺的资产仍必须拒绝,避免跨店铺覆盖资产归属。
|
||||
if newAsset.ShopID != nil && !sameShopID(oldAsset.ShopID, newAsset.ShopID) {
|
||||
return errors.New(errors.CodeForbidden, "新旧资产归属不一致")
|
||||
}
|
||||
if err := s.ensureNewAssetBindingAvailableWithTx(ctx, tx, oldAsset, newAsset); err != nil {
|
||||
@@ -793,6 +798,59 @@ func (s *Service) switchCustomerBindingWithTx(ctx context.Context, tx *gorm.DB,
|
||||
return s.customerBinding.Migrate(ctx, tx, oldAsset.AssetType, oldAsset.AssetID, newAsset.AssetType, newAsset.AssetID)
|
||||
}
|
||||
|
||||
// syncNewAssetOwnershipWithTx 将新资产归属同步为旧资产当前归属。
|
||||
// 归属继承不受 migrate_data 控制,避免平台库存资产换入店铺后仍处于平台租户范围。
|
||||
func (s *Service) syncNewAssetOwnershipWithTx(ctx context.Context, tx *gorm.DB, oldAsset, newAsset *resolvedExchangeAsset) error {
|
||||
ownershipStatus := constants.IotCardStatusInStock
|
||||
if oldAsset.ShopID != nil {
|
||||
ownershipStatus = constants.IotCardStatusDistributed
|
||||
}
|
||||
|
||||
modelValue := any(&model.IotCard{})
|
||||
if newAsset.AssetType == constants.ExchangeAssetTypeDevice {
|
||||
modelValue = &model.Device{}
|
||||
ownershipStatus = constants.DeviceStatusInStock
|
||||
if oldAsset.ShopID != nil {
|
||||
ownershipStatus = constants.DeviceStatusDistributed
|
||||
}
|
||||
}
|
||||
|
||||
result := tx.WithContext(ctx).Model(modelValue).
|
||||
Where("id = ? AND asset_status = ?", newAsset.AssetID, constants.AssetStatusInStock).
|
||||
Updates(map[string]any{
|
||||
"shop_id": oldAsset.ShopID,
|
||||
"status": ownershipStatus,
|
||||
"updated_at": time.Now(),
|
||||
})
|
||||
if result.Error != nil {
|
||||
return errors.Wrap(errors.CodeDatabaseError, result.Error, "同步新资产店铺归属失败")
|
||||
}
|
||||
if result.RowsAffected == 0 {
|
||||
return errors.New(errors.CodeExchangeNewAssetNotInStock)
|
||||
}
|
||||
|
||||
shopIDTag := uint(0)
|
||||
if oldAsset.ShopID != nil {
|
||||
shopIDTag = *oldAsset.ShopID
|
||||
}
|
||||
if err := tx.WithContext(ctx).Model(&model.AssetWallet{}).
|
||||
Where("resource_type = ? AND resource_id = ?", newAsset.AssetType, newAsset.AssetID).
|
||||
Updates(map[string]any{"shop_id_tag": shopIDTag, "updated_at": time.Now()}).Error; err != nil {
|
||||
return errors.Wrap(errors.CodeDatabaseError, err, "同步新资产钱包店铺标签失败")
|
||||
}
|
||||
|
||||
newAsset.ShopID = cloneShopID(oldAsset.ShopID)
|
||||
return nil
|
||||
}
|
||||
|
||||
func cloneShopID(shopID *uint) *uint {
|
||||
if shopID == nil {
|
||||
return nil
|
||||
}
|
||||
value := *shopID
|
||||
return &value
|
||||
}
|
||||
|
||||
func (s *Service) updateAssetStatusesForCompletion(ctx context.Context, tx *gorm.DB, oldAsset, newAsset *resolvedExchangeAsset) error {
|
||||
now := time.Now()
|
||||
if oldAsset.AssetType == constants.ExchangeAssetTypeIotCard {
|
||||
|
||||
Reference in New Issue
Block a user