Some checks failed
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Failing after 8m39s
- 迁移 000259~000265:套餐记录沉默期字段与扫描索引、套餐默认沉默期、延期政策/流水/订单/批次/明细 5 张表、默认政策与系统配置(总开关默认关闭)、企微审批白名单、卡与设备出库时间及出库记录 - 沉默期设置(订单发放、自动购包、钱包续费)、后台购买时调整(超默认值按政策免审或企微审批)、统一判定与激活闸门、排队与孤儿扫描跳过过期套餐、过期套餐禁止退款、每 10 分钟扫描 - 总开关专用接口与打开前 4 项检查;延期政策;延期资格与计费;H5 自助与后台二维码在线支付(商户池路由)、支付回调与查单恢复、线下登记、特殊/批量延期、异常重试与原路退款;沉默期审批场景与终态处理 - 换货沉默期流水与出库时间;卡/设备列表、详情、导出与出库记录接口;延期订单与即将过期套餐列表及导出;财务流水纳入延期支付 - 沉默期与出库时间回填工具 cmd/silence-backfill;前端接口变更说明 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
116 lines
4.5 KiB
Go
116 lines
4.5 KiB
Go
// silence-backfill 是沉默期与出库时间历史回填工具(add-package-silence-period,技术方案 §6、§17.4)。
|
||
//
|
||
// 用法(数据库连接只读取显式传入的 DB_* 环境变量):
|
||
//
|
||
// DB_HOST=... DB_PORT=... DB_USER=... DB_PASSWORD=... DB_NAME=... DB_SSLMODE=disable \
|
||
// go run ./cmd/silence-backfill -target silence # 只输出影响清单(默认 dry-run)
|
||
// go run ./cmd/silence-backfill -target silence -execute # 业务确认清单后执行
|
||
// go run ./cmd/silence-backfill -target outbound # 出库时间:只输出可回填数与无订单数
|
||
// go run ./cmd/silence-backfill -target outbound -execute
|
||
//
|
||
// 两种回填都只处理为空的记录、分批执行、可重复执行;可在沉默期总开关关闭时运行。
|
||
package main
|
||
|
||
import (
|
||
"context"
|
||
"encoding/json"
|
||
"flag"
|
||
"fmt"
|
||
"os"
|
||
"strings"
|
||
"time"
|
||
|
||
"gorm.io/driver/postgres"
|
||
"gorm.io/gorm"
|
||
"gorm.io/gorm/logger"
|
||
|
||
packagesilenceapp "github.com/break/junhong_cmp_fiber/internal/application/packagesilence"
|
||
auditInfra "github.com/break/junhong_cmp_fiber/internal/infrastructure/audit"
|
||
"github.com/break/junhong_cmp_fiber/pkg/auditcontext"
|
||
"github.com/break/junhong_cmp_fiber/pkg/constants"
|
||
)
|
||
|
||
func main() {
|
||
target := flag.String("target", "", "回填对象:silence(套餐沉默期)/ outbound(资产出库时间)")
|
||
execute := flag.Bool("execute", false, "执行写入;不传时只输出影响清单")
|
||
flag.Parse()
|
||
if *target != "silence" && *target != "outbound" {
|
||
fail("必须指定 -target silence 或 -target outbound")
|
||
}
|
||
db, err := openDB()
|
||
if err != nil {
|
||
fail(err.Error())
|
||
}
|
||
ctx := auditcontext.With(context.Background(), auditcontext.Context{
|
||
ActorKind: constants.AuditActorSystemTask, ActorID: "silence-backfill", ActorName: "沉默期历史回填工具",
|
||
Source: constants.AuditSourceWorker,
|
||
})
|
||
backfill := packagesilenceapp.NewBackfill(db, auditInfra.NewWriter(auditInfra.NewRegistry(), nil))
|
||
started := time.Now()
|
||
mode := "dry-run(只出清单)"
|
||
if *execute {
|
||
mode = "execute(执行写入)"
|
||
}
|
||
fmt.Printf("目标库:%s,对象:%s,模式:%s\n", os.Getenv("DB_NAME"), *target, mode)
|
||
var report any
|
||
switch *target {
|
||
case "silence":
|
||
silence, err := backfill.SilenceBackfill(ctx, *execute)
|
||
if err != nil {
|
||
fail(err.Error())
|
||
}
|
||
printSilence(silence)
|
||
report = silence
|
||
case "outbound":
|
||
outbound, err := backfill.OutboundBackfill(ctx, *execute)
|
||
if err != nil {
|
||
fail(err.Error())
|
||
}
|
||
report = outbound
|
||
}
|
||
encoded, _ := json.MarshalIndent(report, "", " ")
|
||
fmt.Println(string(encoded))
|
||
fmt.Printf("耗时:%s\n", time.Since(started).Round(time.Millisecond))
|
||
}
|
||
|
||
func printSilence(report *packagesilenceapp.SilenceBackfillReport) {
|
||
fmt.Printf("待回填记录 %d,可回填 %d,未配置默认沉默期 %d(不回填)\n", report.Candidates, report.Backfillable, report.UnconfiguredCount)
|
||
fmt.Printf("期内已实名(记为结束)%d,其中实名时间为空 %d;仍在期内 %d;打开总开关后将过期 %d,其中无法付费延期 %d\n",
|
||
report.EndedInPeriod, report.RealnamedWithoutTime, report.Waiting, report.WillExpire, report.WillExpireNoPaidExtend)
|
||
fmt.Println("按代理分布:")
|
||
for _, item := range packagesilenceapp.SortedCounts(report.ByShop) {
|
||
fmt.Printf(" %s:%d\n", item.Name, item.Count)
|
||
}
|
||
fmt.Println("按套餐分布:")
|
||
for _, item := range packagesilenceapp.SortedCounts(report.ByPackage) {
|
||
fmt.Printf(" %s:%d\n", item.Name, item.Count)
|
||
}
|
||
if len(report.UnconfiguredPackages) > 0 {
|
||
fmt.Println("未配置默认沉默期的套餐:")
|
||
for _, item := range packagesilenceapp.SortedCounts(report.UnconfiguredPackages) {
|
||
fmt.Printf(" %s:%d\n", item.Name, item.Count)
|
||
}
|
||
}
|
||
}
|
||
|
||
func openDB() (*gorm.DB, error) {
|
||
required := []string{"DB_HOST", "DB_PORT", "DB_USER", "DB_PASSWORD", "DB_NAME"}
|
||
for _, key := range required {
|
||
if strings.TrimSpace(os.Getenv(key)) == "" {
|
||
return nil, fmt.Errorf("缺少环境变量 %s(只使用显式传入的数据库连接)", key)
|
||
}
|
||
}
|
||
sslMode := os.Getenv("DB_SSLMODE")
|
||
if sslMode == "" {
|
||
sslMode = "disable"
|
||
}
|
||
dsn := fmt.Sprintf("host=%s port=%s user=%s password=%s dbname=%s sslmode=%s TimeZone=Asia/Shanghai",
|
||
os.Getenv("DB_HOST"), os.Getenv("DB_PORT"), os.Getenv("DB_USER"), os.Getenv("DB_PASSWORD"), os.Getenv("DB_NAME"), sslMode)
|
||
return gorm.Open(postgres.Open(dsn), &gorm.Config{Logger: logger.Default.LogMode(logger.Warn)})
|
||
}
|
||
|
||
func fail(message string) {
|
||
fmt.Fprintln(os.Stderr, "回填失败:"+message)
|
||
os.Exit(1)
|
||
}
|