Files
junhong_cmp_fiber/internal/application/packagesilence/exchange.go
邹进 909f4d0ec2
Some checks failed
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Failing after 8m39s
feat: 实名即生效套餐沉默期、延期办理与资产出库时间
- 迁移 000259~000265:套餐记录沉默期字段与扫描索引、套餐默认沉默期、延期政策/流水/订单/批次/明细 5 张表、默认政策与系统配置(总开关默认关闭)、企微审批白名单、卡与设备出库时间及出库记录
- 沉默期设置(订单发放、自动购包、钱包续费)、后台购买时调整(超默认值按政策免审或企微审批)、统一判定与激活闸门、排队与孤儿扫描跳过过期套餐、过期套餐禁止退款、每 10 分钟扫描
- 总开关专用接口与打开前 4 项检查;延期政策;延期资格与计费;H5 自助与后台二维码在线支付(商户池路由)、支付回调与查单恢复、线下登记、特殊/批量延期、异常重试与原路退款;沉默期审批场景与终态处理
- 换货沉默期流水与出库时间;卡/设备列表、详情、导出与出库记录接口;延期订单与即将过期套餐列表及导出;财务流水纳入延期支付
- 沉默期与出库时间回填工具 cmd/silence-backfill;前端接口变更说明

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 16:32:58 +09:00

79 lines
3.2 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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
outbound *OutboundRecorder
}
// NewExchangeHook 创建换货接缝。
func NewExchangeHook(setter *Setter, outbound *OutboundRecorder) *ExchangeHook {
return &ExchangeHook{setter: setter, outbound: outbound}
}
// 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
}
// OnNewAssetInTx 换货完成时为新资产写出库时间(换货完成时间)与 exchange 出库记录;已有出库时间的不覆盖。
func (h *ExchangeHook) OnNewAssetInTx(ctx context.Context, tx *gorm.DB, assetType string, assetID uint, order *model.ExchangeOrder, completedAt time.Time) error {
if h == nil || h.outbound == nil {
return nil
}
return h.outbound.RecordForExchangeInTx(ctx, tx, normalizeOutboundAssetType(assetType), assetID, order.ID, order.ExchangeNo, completedAt)
}
// 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.outbound == nil {
return nil
}
return h.outbound.EndGenerationInTx(ctx, tx, normalizeOutboundAssetType(assetType), assetID, oldGeneration, at)
}
func normalizeOutboundAssetType(assetType string) string {
switch assetType {
case "card", constants.OutboundAssetTypeIotCard:
return constants.OutboundAssetTypeIotCard
default:
return constants.OutboundAssetTypeDevice
}
}