feat(运营报表): AUG26-015 设备激活与套餐续费日报快照、查询趋势与受控导出
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 15m33s

- 新增成对迁移 000231 与三张快照表:日级头行、设备粒度激活行、到期事件粒度续费行,以快照日期为唯一键
- 新增每日 03:30(Asia/Shanghai)日报快照生成任务与幂等整日替换,失败重试沿用同一目标日
- 新增六条受控入口:两张报表的汇总、日/月趋势与受控导出,配套查询层只读快照事实
- 新增 operations_activation 与 operations_renewal 两个导出场景,创建期冻结筛选与可见店铺范围、派发期冻结表头、执行期只按冻结值复核资格
- 采购数量口径按系统内未删除设备数实施并在 PRD 标注,附实测差额依据
- 同步证据链 requirement-evidence.json 与入口能力矩阵、ARCHITECTURE 与验证记录
- 归档 change add-operations-reports 并新建主 Spec openspec/specs/operations-report/spec.md
This commit is contained in:
2026-09-18 09:42:28 +08:00
parent 6333f4ad13
commit 5e78809b93
46 changed files with 5580 additions and 101 deletions

View File

@@ -0,0 +1,43 @@
package operationsreport
import (
"time"
"github.com/break/junhong_cmp_fiber/pkg/constants"
)
// ExpiredMainUsage 是一条到期事实:快照日(上海自然日)等于其到期日的主套餐使用记录。
type ExpiredMainUsage struct {
UsageID uint
AssetType string
AssetID uint
ExpiresAt time.Time
}
// MainUsageCandidate 是同资产上参与续费判定的主套餐记录投影。
// 调用方必须只传入未退款refund_id IS NULL的主套餐master_usage_id IS NULL记录。
type MainUsageCandidate struct {
UsageID uint
Status int
ActivatedAt *time.Time
}
// IsRenewed 判定该到期事实是否已续费(设计 D9
// 存在**另一条**未退款主套餐记录,其生效时间晚于本条到期时间,或处于待生效状态。
//
// 候选中属于本条记录自身的项不参与判定;判定的结果挂在到期行上,
// 使续费资产集合恒为到期资产集合的子集,续费率不超过 100% 由构造保证,不做任何截断或钳制。
func IsRenewed(expired ExpiredMainUsage, candidates []MainUsageCandidate) bool {
for _, candidate := range candidates {
if candidate.UsageID == expired.UsageID {
continue
}
if candidate.Status == constants.PackageUsageStatusPending {
return true
}
if candidate.ActivatedAt != nil && candidate.ActivatedAt.After(expired.ExpiresAt) {
return true
}
}
return false
}