feat(资产钱包自动续费): 新增全局配置、每日扫描续购与可靠复机
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 10m15s
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 10m15s
- 新增单行配置表 tb_asset_auto_renewal_config 与尝试记录表 tb_asset_auto_renewal_attempt(迁移 000229/000230) - 每日按上海自然日扫描,窗口内以同一资产钱包可用余额续购当前主套餐,资金/订单/套餐/审计同一事务闭合 - 唯一键保证每资产每日至多一次尝试,占位中断由后续扫描收敛,当日不重试 - 四类失败原因向客户与店铺各投递每日至多一条站内通知,并注册通知类型与个人客户白名单 - 续费成功后按条件经 Outbox 可靠投递复机,新增恢复扫描只查询回填,不使用即发即弃调用 - 配置读写仅超级管理员与平台账号,保存记录操作者、前后值快照并登记统一审计 - tasks 7.1–7.15 全部验证通过(本机隔离 PostgreSQL/Redis,零外部渠道调用)
This commit is contained in:
167
pkg/constants/asset_auto_renewal.go
Normal file
167
pkg/constants/asset_auto_renewal.go
Normal file
@@ -0,0 +1,167 @@
|
||||
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 "未知"
|
||||
}
|
||||
}
|
||||
@@ -556,6 +556,12 @@ const (
|
||||
AuditActionPackageTrafficAlertRuleDisabled = "package_traffic_alert_rule.disable"
|
||||
// AuditActionPackageTrafficAlertTriggered 表示扫描命中阈值时创建套餐真流量达量预警事实。
|
||||
AuditActionPackageTrafficAlertTriggered = "package_traffic_alert.trigger"
|
||||
// AuditActionAssetAutoRenewalConfigUpdated 表示保存受控的资产钱包自动续费配置。
|
||||
AuditActionAssetAutoRenewalConfigUpdated = "asset_auto_renewal.config.updated"
|
||||
// AuditActionAssetAutoRenewalRenewed 表示自动续费在同一事务内完成续购并置尝试成功。
|
||||
AuditActionAssetAutoRenewalRenewed = "asset_auto_renewal.renewed"
|
||||
// AuditActionAssetAutoRenewalFailed 表示自动续费以失败或跳过终态结束。
|
||||
AuditActionAssetAutoRenewalFailed = "asset_auto_renewal.failed"
|
||||
// AuditActionNotificationDelivered 表示 Outbox 消费后实际生成站内通知。
|
||||
AuditActionNotificationDelivered = "notification.deliver"
|
||||
// AuditActionNotificationRead 表示单条通知首次标记已读。
|
||||
@@ -634,6 +640,8 @@ const (
|
||||
AuditActionPermissionDeleted = "permission.delete"
|
||||
// AuditOperationSystemConfigUpdate 表示系统配置旧接缝传入的操作类型。
|
||||
AuditOperationSystemConfigUpdate = "system_config_update"
|
||||
// AuditOperationAssetAutoRenewalConfigUpdate 表示资产钱包自动续费配置旧接缝传入的操作类型。
|
||||
AuditOperationAssetAutoRenewalConfigUpdate = "asset_auto_renewal_config_update"
|
||||
// AuditOperationPaymentConfigCreate 表示创建支付连接配置。
|
||||
AuditOperationPaymentConfigCreate = "payment_config_create"
|
||||
// AuditOperationPaymentConfigUpdate 表示更新支付连接配置。
|
||||
@@ -731,6 +739,10 @@ const (
|
||||
AuditResourcePackageTrafficAlertRule = "package_traffic_alert_rule"
|
||||
// AuditResourcePackageTrafficAlert 表示套餐真流量达量预警事实资源。
|
||||
AuditResourcePackageTrafficAlert = "package_traffic_alert"
|
||||
// AuditResourceAssetAutoRenewalConfig 表示资产钱包自动续费全局配置资源。
|
||||
AuditResourceAssetAutoRenewalConfig = "asset_auto_renewal_config"
|
||||
// AuditResourceAssetAutoRenewalAttempt 表示资产钱包自动续费尝试记录资源。
|
||||
AuditResourceAssetAutoRenewalAttempt = "asset_auto_renewal_attempt"
|
||||
// AuditResourceNotification 表示站内通知资源。
|
||||
AuditResourceNotification = "notification"
|
||||
// AuditResourceNotificationReadBatch 表示通知批量已读资源。
|
||||
@@ -879,6 +891,16 @@ const (
|
||||
AuditResourceRolePackageTrafficAlertRuleTarget = "package_traffic_alert_rule_target"
|
||||
// AuditResourceRolePackageTrafficAlertTarget 表示套餐真流量达量预警事实资源。
|
||||
AuditResourceRolePackageTrafficAlertTarget = "package_traffic_alert_target"
|
||||
// AuditResourceRoleAssetAutoRenewalConfigTarget 表示资产钱包自动续费配置主资源。
|
||||
AuditResourceRoleAssetAutoRenewalConfigTarget = "asset_auto_renewal_config_target"
|
||||
// AuditResourceRoleAssetAutoRenewalAttemptTarget 表示本次自动续费尝试记录主资源。
|
||||
AuditResourceRoleAssetAutoRenewalAttemptTarget = "asset_auto_renewal_attempt_target"
|
||||
// AuditResourceRoleAssetAutoRenewalWallet 表示本次自动续费实际扣款的资产钱包。
|
||||
AuditResourceRoleAssetAutoRenewalWallet = "asset_auto_renewal_wallet"
|
||||
// AuditResourceRoleAssetAutoRenewalWalletTransaction 表示本次自动续费产生的资产钱包流水。
|
||||
AuditResourceRoleAssetAutoRenewalWalletTransaction = "asset_auto_renewal_wallet_transaction"
|
||||
// AuditResourceRoleAssetAutoRenewalOrder 表示本次自动续费产生的续费订单。
|
||||
AuditResourceRoleAssetAutoRenewalOrder = "asset_auto_renewal_order"
|
||||
// AuditResourceRoleRetentionMonth 表示留存清理目标自然月。
|
||||
AuditResourceRoleRetentionMonth = "retention_month"
|
||||
// AuditResourceRoleNotificationTarget 表示本次写操作的通知资源。
|
||||
|
||||
@@ -157,12 +157,14 @@ const (
|
||||
CardObservationSceneBusinessResume = "business_resume" // 业务复机成功
|
||||
// CardObservationSceneCarrierThresholdRecovery 是通道阈值恢复扫描的只读状态查询场景。
|
||||
CardObservationSceneCarrierThresholdRecovery = "carrier_threshold_recovery"
|
||||
CardObservationScenePackageChanged = "package_changed" // 购包或套餐激活成功
|
||||
CardObservationSceneDeviceSwitchCard = "device_switch_card" // 设备切卡成功
|
||||
CardObservationSceneDeviceSwitchMode = "device_switch_mode" // 设备切卡模式成功
|
||||
CardObservationSceneDeviceReboot = "device_reboot" // 设备重启成功
|
||||
CardObservationSceneDeviceReset = "device_reset" // 设备恢复出厂成功
|
||||
CardObservationSceneDeviceSetWiFi = "device_set_wifi" // 设备 WiFi 设置成功
|
||||
// CardObservationSceneAssetAutoRenewalResumeRecovery 是资产钱包自动续费复机结果恢复扫描的只读状态查询场景。
|
||||
CardObservationSceneAssetAutoRenewalResumeRecovery = "asset_auto_renewal_resume_recovery"
|
||||
CardObservationScenePackageChanged = "package_changed" // 购包或套餐激活成功
|
||||
CardObservationSceneDeviceSwitchCard = "device_switch_card" // 设备切卡成功
|
||||
CardObservationSceneDeviceSwitchMode = "device_switch_mode" // 设备切卡模式成功
|
||||
CardObservationSceneDeviceReboot = "device_reboot" // 设备重启成功
|
||||
CardObservationSceneDeviceReset = "device_reset" // 设备恢复出厂成功
|
||||
CardObservationSceneDeviceSetWiFi = "device_set_wifi" // 设备 WiFi 设置成功
|
||||
)
|
||||
|
||||
// 卡实名状态变更 Outbox 事件
|
||||
|
||||
@@ -101,6 +101,10 @@ const (
|
||||
// 运营商通道流量阈值任务类型(由 Asynq Scheduler 调度)
|
||||
TaskTypeCarrierThresholdCycle = "carrier_threshold:cycle" // 通道阈值周期处理:跨期解锁与条件复机
|
||||
TaskTypeCarrierThresholdRecovery = "carrier_threshold:recovery" // 通道阈值停复机结果恢复扫描
|
||||
|
||||
// 资产钱包自动续费任务类型(由 Asynq Scheduler 调度)
|
||||
TaskTypeAssetAutoRenewalScan = "asset_auto_renewal:scan" // 每日自动续费扫描:终态收敛、候选判定与续购执行
|
||||
TaskTypeAssetAutoRenewalRecovery = "asset_auto_renewal:recovery" // 自动续费复机结果恢复扫描
|
||||
)
|
||||
|
||||
// 用户状态常量
|
||||
@@ -330,6 +334,9 @@ func QueueForTaskType(taskType string) string {
|
||||
case TaskTypeCarrierThresholdCycle, TaskTypeCarrierThresholdRecovery:
|
||||
// 与退款恢复同队列:每分钟轻量扫描,复用既有已监听队列,不新增队列与权重。
|
||||
return QueueDefault
|
||||
case TaskTypeAssetAutoRenewalScan, TaskTypeAssetAutoRenewalRecovery:
|
||||
// 与套餐临期扫描同队列:轻量只读扫描与收敛,复用既有已监听队列,不新增队列与权重。
|
||||
return QueueDataCleanup
|
||||
default:
|
||||
return QueueDefault
|
||||
}
|
||||
|
||||
@@ -48,6 +48,8 @@ const (
|
||||
NotificationTypeH5PopupOperation = "h5.popup.operation"
|
||||
// NotificationTypePackageTrafficAlert 表示套餐真流量达量预警。
|
||||
NotificationTypePackageTrafficAlert = "package.traffic.alert"
|
||||
// NotificationTypeAssetAutoRenewalFailed 表示资产钱包自动续费失败(含复机失败)。
|
||||
NotificationTypeAssetAutoRenewalFailed = "asset.auto_renewal.failed"
|
||||
|
||||
// NotificationRefTypeSystemConfig 表示系统配置资源引用。
|
||||
NotificationRefTypeSystemConfig = "system_config"
|
||||
|
||||
Reference in New Issue
Block a user