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,零外部渠道调用)
71 lines
2.7 KiB
Go
71 lines
2.7 KiB
Go
package assetautorenewal
|
|
|
|
import (
|
|
"context"
|
|
|
|
"github.com/hibiken/asynq"
|
|
|
|
assetAutoRenewalApp "github.com/break/junhong_cmp_fiber/internal/application/assetautorenewal"
|
|
"github.com/break/junhong_cmp_fiber/pkg/auditcontext"
|
|
"github.com/break/junhong_cmp_fiber/pkg/constants"
|
|
"github.com/break/junhong_cmp_fiber/pkg/errors"
|
|
)
|
|
|
|
// DailyScanTaskHandler 执行资产钱包自动续费的每日扫描任务。
|
|
//
|
|
// 审计上下文固定为计划任务来源,使终态收敛、续费成功与失败事实都可按计划任务维度追溯。
|
|
type DailyScanTaskHandler struct {
|
|
service *assetAutoRenewalApp.Service
|
|
}
|
|
|
|
// NewDailyScanTaskHandler 创建自动续费每日扫描任务处理器。
|
|
func NewDailyScanTaskHandler(service *assetAutoRenewalApp.Service) *DailyScanTaskHandler {
|
|
return &DailyScanTaskHandler{service: service}
|
|
}
|
|
|
|
// Handle 执行一次每日扫描;只有扫描级失败才返回错误交既有任务重试。
|
|
func (h *DailyScanTaskHandler) Handle(ctx context.Context, task *asynq.Task) error {
|
|
if h == nil || h.service == nil {
|
|
return errors.New(errors.CodeServiceUnavailable, "自动续费扫描任务未配置")
|
|
}
|
|
taskType := constants.TaskTypeAssetAutoRenewalScan
|
|
if task != nil && task.Type() != "" {
|
|
taskType = task.Type()
|
|
}
|
|
ctx = auditcontext.With(ctx, auditcontext.Context{
|
|
ActorKind: constants.AuditActorScheduledJob, ActorID: taskType,
|
|
ActorName: "资产钱包自动续费每日扫描计划任务", Source: constants.AuditSourceScheduler,
|
|
})
|
|
_, err := h.service.RunDailyScan(ctx)
|
|
return err
|
|
}
|
|
|
|
// RecoveryTaskHandler 执行资产钱包自动续费的复机结果恢复扫描任务。
|
|
//
|
|
// 只查询运营商状态回填,绝不重复发起复机调用;只有最终确认失败才按通知契约投递通知。
|
|
type RecoveryTaskHandler struct {
|
|
service *assetAutoRenewalApp.Service
|
|
}
|
|
|
|
// NewRecoveryTaskHandler 创建自动续费复机结果恢复任务处理器。
|
|
func NewRecoveryTaskHandler(service *assetAutoRenewalApp.Service) *RecoveryTaskHandler {
|
|
return &RecoveryTaskHandler{service: service}
|
|
}
|
|
|
|
// Handle 扫描未收敛的复机子结果并只查询状态回填。
|
|
func (h *RecoveryTaskHandler) Handle(ctx context.Context, task *asynq.Task) error {
|
|
if h == nil || h.service == nil {
|
|
return errors.New(errors.CodeServiceUnavailable, "自动续费复机结果恢复任务未配置")
|
|
}
|
|
taskType := constants.TaskTypeAssetAutoRenewalRecovery
|
|
if task != nil && task.Type() != "" {
|
|
taskType = task.Type()
|
|
}
|
|
ctx = auditcontext.With(ctx, auditcontext.Context{
|
|
ActorKind: constants.AuditActorScheduledJob, ActorID: taskType,
|
|
ActorName: "资产钱包自动续费结果恢复计划任务", Source: constants.AuditSourceScheduler,
|
|
})
|
|
_, err := h.service.RecoverResumeResults(ctx)
|
|
return err
|
|
}
|