Files
junhong_cmp_fiber/pkg/constants/asset_auto_renewal.go
break d52be16802
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 10m15s
feat(资产钱包自动续费): 新增全局配置、每日扫描续购与可靠复机
- 新增单行配置表 tb_asset_auto_renewal_config 与尝试记录表 tb_asset_auto_renewal_attempt(迁移 000229/000230)
- 每日按上海自然日扫描,窗口内以同一资产钱包可用余额续购当前主套餐,资金/订单/套餐/审计同一事务闭合
- 唯一键保证每资产每日至多一次尝试,占位中断由后续扫描收敛,当日不重试
- 四类失败原因向客户与店铺各投递每日至多一条站内通知,并注册通知类型与个人客户白名单
- 续费成功后按条件经 Outbox 可靠投递复机,新增恢复扫描只查询回填,不使用即发即弃调用
- 配置读写仅超级管理员与平台账号,保存记录操作者、前后值快照并登记统一审计
- tasks 7.1–7.15 全部验证通过(本机隔离 PostgreSQL/Redis,零外部渠道调用)
2026-09-17 16:39:26 +08:00

168 lines
7.1 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 constants
import "time"
// 资产钱包自动续费配置适用范围。
const (
// AssetAutoRenewalScopeAll 表示配置适用于全部主套餐。
AssetAutoRenewalScopeAll = "all"
// AssetAutoRenewalScopeSpecified 表示配置只适用于指定主套餐集合。
AssetAutoRenewalScopeSpecified = "specified"
)
// 资产钱包自动续费配置的结构约束。
const (
// AssetAutoRenewalConfigSingletonID 是单行配置表的主键,恒为 1。
AssetAutoRenewalConfigSingletonID = 1
// AssetAutoRenewalConfigEnabledOff 表示总开关关闭。
AssetAutoRenewalConfigEnabledOff = 0
// AssetAutoRenewalConfigEnabledOn 表示总开关开启。
AssetAutoRenewalConfigEnabledOn = 1
// AssetAutoRenewalMinDaysBeforeExpiry 是到期前天数的最小值。
AssetAutoRenewalMinDaysBeforeExpiry = 1
// AssetAutoRenewalMaxDaysBeforeExpiry 是到期前天数的上限。
AssetAutoRenewalMaxDaysBeforeExpiry = 90
)
// 资产钱包自动续费尝试状态ENG-STATE-001生命周期状态使用整数编码
const (
// AssetAutoRenewalAttemptStatusProcessing 表示尝试已占位、执行尚未得出终态。
AssetAutoRenewalAttemptStatusProcessing = 1
// AssetAutoRenewalAttemptStatusSucceeded 表示本次尝试已完成续费。
AssetAutoRenewalAttemptStatusSucceeded = 2
// AssetAutoRenewalAttemptStatusFailed 表示本次尝试以失败终态结束。
AssetAutoRenewalAttemptStatusFailed = 3
// AssetAutoRenewalAttemptStatusSkipped 表示本次尝试因人工优先而跳过。
AssetAutoRenewalAttemptStatusSkipped = 4
)
// 资产钱包自动续费通知原因,固定为四类。
//
// 前三类由尝试失败产生通知(写入尝试记录的 failure_reason
// 第四类复机失败只由复机子结果resume_status=失败)产生通知,
// 绝不回写尝试记录的 failure_reason——复机结果只在复机字段表达同一事实不两处表达。
const (
// AssetAutoRenewalFailureInsufficientBalance 表示可用余额不支持续购。
AssetAutoRenewalFailureInsufficientBalance = "insufficient_balance"
// AssetAutoRenewalFailureNotRenewable 表示当前条件不允许自动续购。
AssetAutoRenewalFailureNotRenewable = "not_renewable"
// AssetAutoRenewalFailureOrderFailed 表示续购事务本身失败并整体回滚。
AssetAutoRenewalFailureOrderFailed = "order_failed"
// AssetAutoRenewalFailureResumeFailed 表示复机最终确认失败(只作为通知原因,不写 failure_reason
AssetAutoRenewalFailureResumeFailed = "resume_failed"
)
// 资产钱包自动续费尝试记录中允许出现的失败归类。
//
// 取值域是通知原因中的前三类加上中断收敛值:失败归类只描述「实际进入执行后得出的失败」,
// 因此不含复机失败。interrupted 是占位后进程中断的收敛表示,不属于四类通知原因,
// 由 IsAssetAutoRenewalNotifiableFailureReason 显式排除,收敛过程不发送任何通知。
const (
// AssetAutoRenewalFailureInterrupted 表示占位写入后进程中断,由后续扫描收敛。
AssetAutoRenewalFailureInterrupted = "interrupted"
)
// IsAssetAutoRenewalNotifiableFailureReason 判断该失败原因是否触发站内通知。
// 未登记的原因一律不通知fail-closed避免把收敛产生的 interrupted 误当执行失败通知出去。
func IsAssetAutoRenewalNotifiableFailureReason(reason string) bool {
switch reason {
case AssetAutoRenewalFailureInsufficientBalance, AssetAutoRenewalFailureNotRenewable,
AssetAutoRenewalFailureOrderFailed, AssetAutoRenewalFailureResumeFailed:
return true
default:
return false
}
}
// 资产钱包自动续费跳过原因。
const (
// AssetAutoRenewalSkipManualRenewed 表示人工已完成续购(已存在待生效主套餐)。
AssetAutoRenewalSkipManualRenewed = "manual_renewed"
// AssetAutoRenewalSkipManualOrderPending 表示该资产存在未关闭的人工主套餐订单。
AssetAutoRenewalSkipManualOrderPending = "manual_order_pending"
)
// 资产钱包自动续费尝试的复机状态。
const (
// AssetAutoRenewalResumeStatusSkipped 表示可复机条件不成立,未投递复机。
AssetAutoRenewalResumeStatusSkipped = 1
// AssetAutoRenewalResumeStatusRequested 表示已可靠投递复机请求,等待结果。
AssetAutoRenewalResumeStatusRequested = 2
// AssetAutoRenewalResumeStatusSucceeded 表示复机已确认成功。
AssetAutoRenewalResumeStatusSucceeded = 3
// AssetAutoRenewalResumeStatusFailed 表示复机已确认失败。
AssetAutoRenewalResumeStatusFailed = 4
// AssetAutoRenewalResumeStatusUnknown 表示复机结果未知,等待恢复扫描确认。
AssetAutoRenewalResumeStatusUnknown = 5
)
// 资产钱包自动续费尝试的操作者类型:本能力无人工处理入口,恒为系统任务。
const (
// AssetAutoRenewalOperatorTypeSystemTask 表示系统任务操作者。
AssetAutoRenewalOperatorTypeSystemTask = "system_task"
)
// 资产钱包自动续费尝试的批次与查询约束。
const (
// AssetAutoRenewalRecoveryBatchSize 是恢复扫描单批处理条数上限。
AssetAutoRenewalRecoveryBatchSize = 200
// AssetAutoRenewalConfigDisplayName 是配置资源的展示名。
AssetAutoRenewalConfigDisplayName = "资产钱包自动续费配置"
)
// 资产钱包自动续费的可靠事件类型。
const (
// OutboxEventTypeAssetAutoRenewalResumeRequested 表示续费成功后按条件触发的可靠复机请求。
OutboxEventTypeAssetAutoRenewalResumeRequested = "asset_auto_renewal.resume.requested"
)
// 资产钱包自动续费复机子任务的查询窗口。
const (
// AssetAutoRenewalResumeQueryWindow 是复机子任务自提交起允许自动查询确认的窗口。
// 语义:复机事件进入已投递/已提交后,恢复扫描只查询运营商状态回填;超过该窗口仍无法确认的
// 尝试会标记异常并转人工核对,不再重复查询同一笔无法收敛的结果,也不回滚任何续费事实。
AssetAutoRenewalResumeQueryWindow = 30 * time.Minute
)
// GetAssetAutoRenewalScopeName 返回自动续费适用范围的中文名称。
func GetAssetAutoRenewalScopeName(scope string) string {
switch scope {
case AssetAutoRenewalScopeAll:
return "全部主套餐"
case AssetAutoRenewalScopeSpecified:
return "指定主套餐"
default:
return "未知"
}
}
// GetAssetAutoRenewalFailureReasonName 返回自动续费失败原因的中文名称。
func GetAssetAutoRenewalFailureReasonName(reason string) string {
switch reason {
case AssetAutoRenewalFailureInsufficientBalance:
return "余额不足"
case AssetAutoRenewalFailureNotRenewable:
return "不可续费"
case AssetAutoRenewalFailureOrderFailed:
return "订单失败"
case AssetAutoRenewalFailureResumeFailed:
return "复机失败"
case AssetAutoRenewalFailureInterrupted:
return "中断收敛"
default:
return "未知"
}
}
// GetAssetAutoRenewalSkipReasonName 返回自动续费跳过原因的中文名称。
func GetAssetAutoRenewalSkipReasonName(reason string) string {
switch reason {
case AssetAutoRenewalSkipManualRenewed:
return "人工已完成续购"
case AssetAutoRenewalSkipManualOrderPending:
return "人工订单在途"
default:
return "未知"
}
}