Some checks failed
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Failing after 38s
- 字段 outbound_at → first_purchase_at,记录表 tb_asset_outbound_record → tb_asset_first_purchase_record,接口 /outbound-records → /first-purchase-records,筛选参数与导出列同步更名 - 换货后的新资产不因换货写入,在当前世代真正支付套餐订单时才记录;记录来源只保留 order / backfill,去掉换货单字段 - 迁移 000266 重命名列、表、索引与约束;同步 Spec、设计、任务与前端接口变更说明 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
71 lines
2.8 KiB
Go
71 lines
2.8 KiB
Go
package packagesilence
|
||
|
||
import (
|
||
"context"
|
||
"time"
|
||
|
||
"gorm.io/gorm"
|
||
|
||
"github.com/break/junhong_cmp_fiber/internal/model"
|
||
"github.com/break/junhong_cmp_fiber/pkg/constants"
|
||
"github.com/break/junhong_cmp_fiber/pkg/errors"
|
||
)
|
||
|
||
// ExchangeHook 是换货流程中的沉默期与首次购买套餐时间接缝(技术方案 §9、§17.3.2)。
|
||
type ExchangeHook struct {
|
||
setter *Setter
|
||
firstPurchase *FirstPurchaseRecorder
|
||
}
|
||
|
||
// NewExchangeHook 创建换货接缝。
|
||
func NewExchangeHook(setter *Setter, firstPurchase *FirstPurchaseRecorder) *ExchangeHook {
|
||
return &ExchangeHook{setter: setter, firstPurchase: firstPurchase}
|
||
}
|
||
|
||
// OnUsagesMigratedInTx 换货迁移套餐记录后写沉默期流水(exchange):沉默期字段随记录迁移,不重新起算,截止时间不变;
|
||
// 过期记录仍为过期,可在新资产上延期。
|
||
func (h *ExchangeHook) OnUsagesMigratedInTx(ctx context.Context, tx *gorm.DB, order *model.ExchangeOrder, usageIDs []uint) error {
|
||
if h == nil || len(usageIDs) == 0 {
|
||
return nil
|
||
}
|
||
var usages []model.PackageUsage
|
||
if err := tx.WithContext(ctx).Where("id IN ? AND silence_status IS NOT NULL", usageIDs).Find(&usages).Error; err != nil {
|
||
return errors.Wrap(errors.CodeDatabaseError, err, "查询迁移的沉默期套餐记录失败")
|
||
}
|
||
var writer = h.setter.audit
|
||
for index := range usages {
|
||
usage := &usages[index]
|
||
exchangeID := order.ID
|
||
if err := writeRecord(ctx, tx, writer, recordInput{
|
||
Usage: usage, EventType: constants.SilenceRecordEventExchange, Trigger: constants.SilenceTriggerExchange,
|
||
BeforeStatus: copyStringPtr(usage.SilenceStatus), BeforeDeadline: copyTimePtr(usage.SilenceDeadlineAt),
|
||
ExchangeID: &exchangeID, ExchangeNo: order.ExchangeNo, Reason: "换货迁移套餐记录,沉默期随套餐迁移、不重新起算",
|
||
Extra: map[string]any{
|
||
"old_asset_type": order.OldAssetType, "old_asset_id": order.OldAssetID,
|
||
"new_asset_type": order.NewAssetType, "new_asset_id": order.NewAssetID,
|
||
},
|
||
AuditAction: constants.AuditActionUsageSilenceMigratedOnExchange, AuditSummary: "换货迁移套餐沉默期",
|
||
}); err != nil {
|
||
return err
|
||
}
|
||
}
|
||
return nil
|
||
}
|
||
|
||
// OnOldAssetRenewedInTx 旧资产换货回库、世代 +1 的同一事务内清空首次购买套餐时间并结束旧世代首次购买套餐记录。
|
||
func (h *ExchangeHook) OnOldAssetRenewedInTx(ctx context.Context, tx *gorm.DB, assetType string, assetID uint, oldGeneration int, at time.Time) error {
|
||
if h == nil || h.firstPurchase == nil {
|
||
return nil
|
||
}
|
||
return h.firstPurchase.EndGenerationInTx(ctx, tx, normalizeFirstPurchaseAssetType(assetType), assetID, oldGeneration, at)
|
||
}
|
||
|
||
func normalizeFirstPurchaseAssetType(assetType string) string {
|
||
switch assetType {
|
||
case "card", constants.FirstPurchaseAssetTypeIotCard:
|
||
return constants.FirstPurchaseAssetTypeIotCard
|
||
default:
|
||
return constants.FirstPurchaseAssetTypeDevice
|
||
}
|
||
}
|