快照订单号以及退款订单号
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 7m27s
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 7m27s
This commit is contained in:
@@ -98,6 +98,10 @@ type AssetPackageResponse struct {
|
|||||||
PackageID uint `json:"package_id" description:"套餐ID"`
|
PackageID uint `json:"package_id" description:"套餐ID"`
|
||||||
PackageName string `json:"package_name" description:"套餐名称"`
|
PackageName string `json:"package_name" description:"套餐名称"`
|
||||||
PackageType string `json:"package_type" description:"套餐类型:formal/addon"`
|
PackageType string `json:"package_type" description:"套餐类型:formal/addon"`
|
||||||
|
OrderID uint `json:"order_id" description:"关联订单ID(无订单分配时为0)"`
|
||||||
|
OrderNo string `json:"order_no,omitempty" description:"订单号快照(无订单分配时为空)"`
|
||||||
|
RefundID *uint `json:"refund_id,omitempty" description:"退款主键ID快照(未退款时为空)"`
|
||||||
|
RefundNo string `json:"refund_no,omitempty" description:"退款单号快照(未退款时为空)"`
|
||||||
UsageType string `json:"usage_type" description:"使用类型:single_card/device"`
|
UsageType string `json:"usage_type" description:"使用类型:single_card/device"`
|
||||||
Status int `json:"status" description:"状态:0待生效 1生效中 2已用完 3已过期 4已失效"`
|
Status int `json:"status" description:"状态:0待生效 1生效中 2已用完 3已过期 4已失效"`
|
||||||
StatusName string `json:"status_name" description:"状态名称"`
|
StatusName string `json:"status_name" description:"状态名称"`
|
||||||
|
|||||||
@@ -61,6 +61,9 @@ type PackageUsage struct {
|
|||||||
gorm.Model
|
gorm.Model
|
||||||
BaseModel `gorm:"embedded"`
|
BaseModel `gorm:"embedded"`
|
||||||
OrderID uint `gorm:"column:order_id;index;not null;comment:订单ID" json:"order_id"`
|
OrderID uint `gorm:"column:order_id;index;not null;comment:订单ID" json:"order_id"`
|
||||||
|
OrderNo string `gorm:"column:order_no;type:varchar(30);not null;default:'';comment:订单号快照(从订单复制)" json:"order_no"`
|
||||||
|
RefundID *uint `gorm:"column:refund_id;index;comment:退款单ID快照(退款后写入)" json:"refund_id,omitempty"`
|
||||||
|
RefundNo string `gorm:"column:refund_no;type:varchar(50);not null;default:'';comment:退款单号快照(退款后写入)" json:"refund_no"`
|
||||||
PackageID uint `gorm:"column:package_id;index;not null;comment:套餐ID" json:"package_id"`
|
PackageID uint `gorm:"column:package_id;index;not null;comment:套餐ID" json:"package_id"`
|
||||||
UsageType string `gorm:"column:usage_type;type:varchar(20);not null;comment:使用类型 single_card-单卡套餐 device-设备级套餐" json:"usage_type"`
|
UsageType string `gorm:"column:usage_type;type:varchar(20);not null;comment:使用类型 single_card-单卡套餐 device-设备级套餐" json:"usage_type"`
|
||||||
IotCardID uint `gorm:"column:iot_card_id;index;comment:IoT卡ID(单卡套餐时有值)" json:"iot_card_id"`
|
IotCardID uint `gorm:"column:iot_card_id;index;comment:IoT卡ID(单卡套餐时有值)" json:"iot_card_id"`
|
||||||
|
|||||||
@@ -714,6 +714,10 @@ func (s *Service) GetPackages(ctx context.Context, assetType string, id uint, pa
|
|||||||
PackageID: u.PackageID,
|
PackageID: u.PackageID,
|
||||||
PackageName: pkgName,
|
PackageName: pkgName,
|
||||||
PackageType: pkgType,
|
PackageType: pkgType,
|
||||||
|
OrderID: u.OrderID,
|
||||||
|
OrderNo: u.OrderNo,
|
||||||
|
RefundID: u.RefundID,
|
||||||
|
RefundNo: u.RefundNo,
|
||||||
UsageType: u.UsageType,
|
UsageType: u.UsageType,
|
||||||
Status: u.Status,
|
Status: u.Status,
|
||||||
StatusName: packageStatusName(u.Status),
|
StatusName: packageStatusName(u.Status),
|
||||||
@@ -791,6 +795,10 @@ func (s *Service) GetCurrentPackage(ctx context.Context, assetType string, id ui
|
|||||||
PackageID: usage.PackageID,
|
PackageID: usage.PackageID,
|
||||||
PackageName: pkgName,
|
PackageName: pkgName,
|
||||||
PackageType: pkgType,
|
PackageType: pkgType,
|
||||||
|
OrderID: usage.OrderID,
|
||||||
|
OrderNo: usage.OrderNo,
|
||||||
|
RefundID: usage.RefundID,
|
||||||
|
RefundNo: usage.RefundNo,
|
||||||
UsageType: usage.UsageType,
|
UsageType: usage.UsageType,
|
||||||
Status: usage.Status,
|
Status: usage.Status,
|
||||||
StatusName: packageStatusName(usage.Status),
|
StatusName: packageStatusName(usage.Status),
|
||||||
|
|||||||
@@ -2011,6 +2011,7 @@ func (s *Service) activateMainPackage(ctx context.Context, tx *gorm.DB, order *m
|
|||||||
Updater: order.Creator,
|
Updater: order.Creator,
|
||||||
},
|
},
|
||||||
OrderID: order.ID,
|
OrderID: order.ID,
|
||||||
|
OrderNo: order.OrderNo,
|
||||||
PackageID: pkg.ID,
|
PackageID: pkg.ID,
|
||||||
PackageName: pkg.PackageName,
|
PackageName: pkg.PackageName,
|
||||||
UsageType: order.OrderType,
|
UsageType: order.OrderType,
|
||||||
@@ -2095,6 +2096,7 @@ func (s *Service) activateAddonPackage(ctx context.Context, tx *gorm.DB, order *
|
|||||||
Updater: order.Creator,
|
Updater: order.Creator,
|
||||||
},
|
},
|
||||||
OrderID: order.ID,
|
OrderID: order.ID,
|
||||||
|
OrderNo: order.OrderNo,
|
||||||
PackageID: pkg.ID,
|
PackageID: pkg.ID,
|
||||||
PackageName: pkg.PackageName,
|
PackageName: pkg.PackageName,
|
||||||
UsageType: order.OrderType,
|
UsageType: order.OrderType,
|
||||||
|
|||||||
@@ -491,12 +491,12 @@ func (s *ActivationService) syncCarrierStatusActivated(ctx context.Context, tx *
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// InvalidatePackagesForRefund 退款精准失效套餐
|
// InvalidatePackagesForRefund 退款精准失效套餐并写入退款快照
|
||||||
// 规则:
|
// 规则:
|
||||||
// 1. 优先按 packageUsageID 精准失效单条套餐(且必须属于当前退款订单)
|
// 1. 优先按 packageUsageID 精准失效单条套餐(且必须属于当前退款订单)
|
||||||
// 2. 未传 packageUsageID 时,按 orderID 失效该订单生成的套餐
|
// 2. 未传 packageUsageID 时,按 orderID 失效该订单生成的套餐
|
||||||
// 3. 若命中主套餐,级联失效其加油包,避免残留挂靠关系
|
// 3. 若命中主套餐,级联失效其加油包,避免残留挂靠关系
|
||||||
func (s *ActivationService) InvalidatePackagesForRefund(ctx context.Context, assetType string, assetID uint, orderID uint, packageUsageID *uint) error {
|
func (s *ActivationService) InvalidatePackagesForRefund(ctx context.Context, assetType string, assetID uint, orderID uint, refundID uint, refundNo string, packageUsageID *uint) error {
|
||||||
if orderID == 0 {
|
if orderID == 0 {
|
||||||
return errors.New(errors.CodeInvalidParam, "无效的订单ID")
|
return errors.New(errors.CodeInvalidParam, "无效的订单ID")
|
||||||
}
|
}
|
||||||
@@ -584,10 +584,20 @@ func (s *ActivationService) InvalidatePackagesForRefund(ctx context.Context, ass
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updates := map[string]any{
|
||||||
|
"status": constants.PackageUsageStatusInvalidated,
|
||||||
|
}
|
||||||
|
if refundID > 0 {
|
||||||
|
updates["refund_id"] = refundID
|
||||||
|
}
|
||||||
|
if refundNo != "" {
|
||||||
|
updates["refund_no"] = refundNo
|
||||||
|
}
|
||||||
|
|
||||||
result := s.db.WithContext(ctx).
|
result := s.db.WithContext(ctx).
|
||||||
Model(&model.PackageUsage{}).
|
Model(&model.PackageUsage{}).
|
||||||
Where("id IN ?", targetIDs).
|
Where("id IN ?", targetIDs).
|
||||||
Update("status", constants.PackageUsageStatusInvalidated)
|
Updates(updates)
|
||||||
if result.Error != nil {
|
if result.Error != nil {
|
||||||
return errors.Wrap(errors.CodeDatabaseError, result.Error, "退款失效套餐失败")
|
return errors.Wrap(errors.CodeDatabaseError, result.Error, "退款失效套餐失败")
|
||||||
}
|
}
|
||||||
@@ -596,6 +606,8 @@ func (s *ActivationService) InvalidatePackagesForRefund(ctx context.Context, ass
|
|||||||
zap.String("asset_type", assetType),
|
zap.String("asset_type", assetType),
|
||||||
zap.Uint("asset_id", assetID),
|
zap.Uint("asset_id", assetID),
|
||||||
zap.Uint("order_id", orderID),
|
zap.Uint("order_id", orderID),
|
||||||
|
zap.Uint("refund_id", refundID),
|
||||||
|
zap.String("refund_no", refundNo),
|
||||||
zap.Int("target_count", len(targetIDs)),
|
zap.Int("target_count", len(targetIDs)),
|
||||||
zap.Int64("affected", result.RowsAffected),
|
zap.Int64("affected", result.RowsAffected),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -516,11 +516,13 @@ func (s *Service) handleAssetReset(ctx context.Context, refundID uint) {
|
|||||||
|
|
||||||
// 1. 按退款单精准失效套餐(仅处理本次退款订单关联套餐)
|
// 1. 按退款单精准失效套餐(仅处理本次退款订单关联套餐)
|
||||||
if s.packageActivationService != nil {
|
if s.packageActivationService != nil {
|
||||||
if err := s.packageActivationService.InvalidatePackagesForRefund(ctx, assetType, assetID, order.ID, refund.PackageUsageID); err != nil {
|
if err := s.packageActivationService.InvalidatePackagesForRefund(ctx, assetType, assetID, order.ID, refund.ID, refund.RefundNo, refund.PackageUsageID); err != nil {
|
||||||
fields := []zap.Field{
|
fields := []zap.Field{
|
||||||
zap.String("asset_type", assetType),
|
zap.String("asset_type", assetType),
|
||||||
zap.Uint("asset_id", assetID),
|
zap.Uint("asset_id", assetID),
|
||||||
zap.Uint("order_id", order.ID),
|
zap.Uint("order_id", order.ID),
|
||||||
|
zap.Uint("refund_id", refund.ID),
|
||||||
|
zap.String("refund_no", refund.RefundNo),
|
||||||
zap.Error(err),
|
zap.Error(err),
|
||||||
}
|
}
|
||||||
if refund.PackageUsageID != nil {
|
if refund.PackageUsageID != nil {
|
||||||
|
|||||||
@@ -510,6 +510,7 @@ func (h *AutoPurchaseHandler) activateMainPackage(
|
|||||||
Updater: order.Creator,
|
Updater: order.Creator,
|
||||||
},
|
},
|
||||||
OrderID: order.ID,
|
OrderID: order.ID,
|
||||||
|
OrderNo: order.OrderNo,
|
||||||
PackageID: pkg.ID,
|
PackageID: pkg.ID,
|
||||||
PackageName: pkg.PackageName,
|
PackageName: pkg.PackageName,
|
||||||
UsageType: order.OrderType,
|
UsageType: order.OrderType,
|
||||||
@@ -582,6 +583,7 @@ func (h *AutoPurchaseHandler) activateAddonPackage(
|
|||||||
Updater: order.Creator,
|
Updater: order.Creator,
|
||||||
},
|
},
|
||||||
OrderID: order.ID,
|
OrderID: order.ID,
|
||||||
|
OrderNo: order.OrderNo,
|
||||||
PackageID: pkg.ID,
|
PackageID: pkg.ID,
|
||||||
PackageName: pkg.PackageName,
|
PackageName: pkg.PackageName,
|
||||||
UsageType: order.OrderType,
|
UsageType: order.OrderType,
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
DROP INDEX IF EXISTS idx_package_usage_refund_id;
|
||||||
|
|
||||||
|
ALTER TABLE tb_package_usage
|
||||||
|
DROP COLUMN IF EXISTS refund_no,
|
||||||
|
DROP COLUMN IF EXISTS refund_id,
|
||||||
|
DROP COLUMN IF EXISTS order_no;
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
-- 为 tb_package_usage 新增订单号与退款快照字段
|
||||||
|
-- 目标:资产套餐接口直接读快照,不联表 tb_order/tb_refund_request
|
||||||
|
ALTER TABLE tb_package_usage
|
||||||
|
ADD COLUMN IF NOT EXISTS order_no VARCHAR(30) NOT NULL DEFAULT '',
|
||||||
|
ADD COLUMN IF NOT EXISTS refund_id BIGINT,
|
||||||
|
ADD COLUMN IF NOT EXISTS refund_no VARCHAR(50) NOT NULL DEFAULT '';
|
||||||
|
|
||||||
|
COMMENT ON COLUMN tb_package_usage.order_no IS '订单号快照(从订单复制)';
|
||||||
|
COMMENT ON COLUMN tb_package_usage.refund_id IS '退款单ID快照(退款后写入)';
|
||||||
|
COMMENT ON COLUMN tb_package_usage.refund_no IS '退款单号快照(退款后写入)';
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_package_usage_refund_id ON tb_package_usage(refund_id);
|
||||||
|
|
||||||
|
-- 存量数据回填订单号:通过 order_id 关联订单表
|
||||||
|
UPDATE tb_package_usage pu
|
||||||
|
SET order_no = o.order_no
|
||||||
|
FROM tb_order o
|
||||||
|
WHERE pu.order_id = o.id
|
||||||
|
AND pu.order_id != 0
|
||||||
|
AND (pu.order_no = '' OR pu.order_no IS NULL);
|
||||||
|
|
||||||
|
-- 存量数据回填退款快照(优先 package_usage_id 精准映射)
|
||||||
|
UPDATE tb_package_usage pu
|
||||||
|
SET refund_id = r.id,
|
||||||
|
refund_no = r.refund_no
|
||||||
|
FROM tb_refund_request r
|
||||||
|
WHERE r.package_usage_id IS NOT NULL
|
||||||
|
AND pu.id = r.package_usage_id
|
||||||
|
AND (
|
||||||
|
pu.refund_id IS NULL
|
||||||
|
OR pu.refund_no = ''
|
||||||
|
OR pu.refund_no IS NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
-- 兜底回填:按 order_id 取最新退款单(兼容历史未填 package_usage_id 的场景)
|
||||||
|
WITH latest_refund AS (
|
||||||
|
SELECT DISTINCT ON (order_id) id, order_id, refund_no
|
||||||
|
FROM tb_refund_request
|
||||||
|
ORDER BY order_id, id DESC
|
||||||
|
)
|
||||||
|
UPDATE tb_package_usage pu
|
||||||
|
SET refund_id = lr.id,
|
||||||
|
refund_no = lr.refund_no
|
||||||
|
FROM latest_refund lr
|
||||||
|
WHERE pu.order_id = lr.order_id
|
||||||
|
AND pu.order_id != 0
|
||||||
|
AND (
|
||||||
|
pu.refund_id IS NULL
|
||||||
|
OR pu.refund_no = ''
|
||||||
|
OR pu.refund_no IS NULL
|
||||||
|
);
|
||||||
Reference in New Issue
Block a user