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

@@ -5019,5 +5019,220 @@
],
"exit_status": 0
}
},
{
"capability": "operations-report",
"requirement": "每日报表快照与不回填历史",
"spec": "openspec/specs/operations-report/spec.md",
"entries": [
"constants.TaskTypeOperationsReportSnapshot"
],
"handler_consumer_job": [
"internal/task/operations_report_snapshot.go",
"pkg/queue/handler.go",
"cmd/worker/main.go"
],
"application_service_query": [
"internal/application/operationsreport/generate.go"
],
"domain_state_amount": [
"internal/domain/operationsreport/metrics.go"
],
"store_migration_config": [
"migrations/000231_create_operations_report_snapshot.up.sql",
"migrations/000231_create_operations_report_snapshot.down.sql",
"internal/infrastructure/operationsreport/snapshot_store.go"
],
"verification": {
"command": "rg -n \"TaskTypeOperationsReportSnapshot\" pkg/constants/constants.go pkg/queue/handler.go cmd/worker/main.go internal/task/operations_report_snapshot.go",
"literal_output": [
"pkg/constants/constants.go:100:\tTaskTypeOperationsReportSnapshot = \"operations:report:snapshot\" // 每日运营报表日报快照生成",
"pkg/constants/constants.go:323:\tcase TaskTypeOperationsReportSnapshot:",
"pkg/queue/handler.go:404:\th.mux.HandleFunc(constants.TaskTypeOperationsReportSnapshot, handler.Handle)",
"pkg/queue/handler.go:405:\th.logger.Info(\"注册每日运营报表日报快照生成任务处理器\", zap.String(\"task_type\", constants.TaskTypeOperationsReportSnapshot))",
"cmd/worker/main.go:1018:\t\tconstants.TaskTypeOperationsReportSnapshot,",
"cmd/worker/main.go:1023:\t\tasynq.Queue(constants.QueueForTaskType(constants.TaskTypeOperationsReportSnapshot)),"
],
"exit_status": 0
}
},
{
"capability": "operations-report",
"requirement": "激活情况指标口径",
"spec": "openspec/specs/operations-report/spec.md",
"entries": [
"/api/admin/operations-reports/activation-summary",
"/api/admin/operations-reports/activation-summary/export"
],
"handler_consumer_job": [
"internal/handler/admin/operations_report.go",
"internal/exporter/operations_report_scene.go"
],
"application_service_query": [
"internal/application/operationsreport/generate.go",
"internal/query/operationsreport/aggregate.go"
],
"domain_state_amount": [
"internal/domain/operationsreport/metrics.go",
"internal/domain/operationsreport/purchase.go"
],
"store_migration_config": [
"internal/infrastructure/operationsreport/source.go",
"migrations/000231_create_operations_report_snapshot.up.sql"
],
"verification": {
"command": "rg -n \"MBPerGB|func Ratio|func CardAverageGB|func ForecastCardAverageGB|func PurchaseCountAsOf|ValidMainPackageStatuses\" internal/domain/operationsreport/metrics.go internal/domain/operationsreport/purchase.go",
"literal_output": [
"internal/domain/operationsreport/metrics.go:15:const MBPerGB = 1024",
"internal/domain/operationsreport/metrics.go:100:func Ratio(numerator, denominator int64) (float64, bool) {",
"internal/domain/operationsreport/metrics.go:109:func CardAverageGB(totalRealTrafficMB float64, denominator int64) (float64, bool) {",
"internal/domain/operationsreport/metrics.go:113:\taverage := totalRealTrafficMB / MBPerGB / float64(denominator)",
"internal/domain/operationsreport/metrics.go:120:func ForecastCardAverageGB(totalRealTrafficMB float64, denominator int64, endDate time.Time) (float64, bool) {",
"internal/domain/operationsreport/purchase.go:26:func PurchaseCountAsOf(ctx context.Context, source PurchaseCountSource, snapshotDate time.Time) (int64, error) {",
"internal/domain/operationsreport/purchase.go:34:var ValidMainPackageStatuses = []int{"
],
"exit_status": 0
}
},
{
"capability": "operations-report",
"requirement": "套餐续费指标口径",
"spec": "openspec/specs/operations-report/spec.md",
"entries": [
"/api/admin/operations-reports/package-renewal-summary",
"/api/admin/operations-reports/package-renewal-summary/export"
],
"handler_consumer_job": [
"internal/handler/admin/operations_report.go",
"internal/exporter/operations_report_scene.go"
],
"application_service_query": [
"internal/application/operationsreport/generate.go",
"internal/query/operationsreport/aggregate.go"
],
"domain_state_amount": [
"internal/domain/operationsreport/renewal.go",
"internal/domain/operationsreport/metrics.go"
],
"store_migration_config": [
"internal/infrastructure/operationsreport/source.go",
"migrations/000231_create_operations_report_snapshot.up.sql"
],
"verification": {
"command": "rg -n \"func IsRenewed|RenewalRate|RenewalDueAssetCount|RenewalRenewedAssetCount|due_assets\" internal/domain/operationsreport/renewal.go internal/domain/operationsreport/metrics.go internal/application/operationsreport/generate.go internal/query/operationsreport/aggregate.go",
"literal_output": [
"internal/domain/operationsreport/renewal.go:30:func IsRenewed(expired ExpiredMainUsage, candidates []MainUsageCandidate) bool {",
"internal/domain/operationsreport/metrics.go:148:func RenewalRate(renewed, due int64) (float64, bool) {",
"internal/application/operationsreport/generate.go:140:\t\t\tzap.Int64(\"renewal_due_asset_count\", snapshot.RenewalDueAssetCount),",
"internal/application/operationsreport/generate.go:236:\tsnapshot.RenewalDueAssetCount = int64(len(dueAssets))",
"internal/query/operationsreport/aggregate.go:304:\t\t\"COUNT(DISTINCT asset_type || ':' || asset_id) AS due_assets\","
],
"exit_status": 0
}
},
{
"capability": "operations-report",
"requirement": "报表维度分组与归属冻结",
"spec": "openspec/specs/operations-report/spec.md",
"entries": [
"/api/admin/operations-reports/activation-summary",
"/api/admin/operations-reports/activation-trend",
"/api/admin/operations-reports/package-renewal-summary",
"/api/admin/operations-reports/package-renewal-trend"
],
"handler_consumer_job": [
"internal/handler/admin/operations_report.go"
],
"application_service_query": [
"internal/query/operationsreport/aggregate.go"
],
"domain_state_amount": [
"internal/domain/operationsreport/dimension.go"
],
"store_migration_config": [
"internal/infrastructure/operationsreport/source.go",
"migrations/000231_create_operations_report_snapshot.up.sql"
],
"verification": {
"command": "rg -n \"DimensionAll =|PlaceholderUnset =|RootShopName|AgentAccountID\" internal/domain/operationsreport/dimension.go internal/infrastructure/operationsreport/source.go",
"literal_output": [
"internal/domain/operationsreport/dimension.go:27:const DimensionAll = \"全部\"",
"internal/domain/operationsreport/dimension.go:50:const PlaceholderUnset = \"未设置\"",
"internal/infrastructure/operationsreport/source.go:344:\tRootShopName string",
"internal/infrastructure/operationsreport/source.go:345:\tAgentAccountID *uint",
"internal/infrastructure/operationsreport/source.go:365:\t\tattribute.RootShopName = o.shops[rootID].ShopName",
"internal/infrastructure/operationsreport/source.go:369:\t\t\tattribute.AgentAccountID = &agentID"
],
"exit_status": 0
}
},
{
"capability": "operations-report",
"requirement": "报表查询与趋势",
"spec": "openspec/specs/operations-report/spec.md",
"entries": [
"/api/admin/operations-reports/activation-summary",
"/api/admin/operations-reports/activation-trend",
"/api/admin/operations-reports/package-renewal-summary",
"/api/admin/operations-reports/package-renewal-trend"
],
"handler_consumer_job": [
"internal/handler/admin/operations_report.go"
],
"application_service_query": [
"internal/query/operationsreport/query.go",
"internal/query/operationsreport/periods.go"
],
"domain_state_amount": [
"internal/domain/operationsreport/metrics.go"
],
"store_migration_config": [
"internal/model/dto/operations_report_dto.go"
],
"verification": {
"command": "rg -n \"utils.ParseTimeRange|granularity 只能为 day 或 month|has_snapshot|snapshot_dates\" internal/query/operationsreport/query.go internal/model/dto/operations_report_dto.go",
"literal_output": [
"internal/query/operationsreport/query.go:43:\tstart, end, err := utils.ParseTimeRange(request.StartTime, request.EndTime)",
"internal/query/operationsreport/query.go:134:\t\treturn nil, errors.New(errors.CodeInvalidParam, \"granularity 只能为 day 或 month\")",
"internal/model/dto/operations_report_dto.go:54:\tHasSnapshot bool `json:\"has_snapshot\" description:\"结束日是否存在快照;为 false 时累计类与派生指标为空且分组行为空集\"`",
"internal/model/dto/operations_report_dto.go:55:\tSnapshotDates []string `json:\"snapshot_dates\" description:\"区间内实际命中的快照日期yyyy-MM-dd\"`"
],
"exit_status": 0
}
},
{
"capability": "operations-report",
"requirement": "报表权限、数据范围与导出冻结",
"spec": "openspec/specs/operations-report/spec.md",
"entries": [
"/api/admin/operations-reports/activation-summary/export",
"/api/admin/operations-reports/package-renewal-summary/export"
],
"handler_consumer_job": [
"internal/routes/operations_report.go",
"internal/handler/admin/operations_report.go"
],
"application_service_query": [
"internal/service/export_task/service.go"
],
"domain_state_amount": [
"internal/exporter/time_filters.go"
],
"store_migration_config": [
"internal/exporter/registry.go",
"internal/exporter/operations_report_scene.go"
],
"verification": {
"command": "rg -n \"ensureOperationsReportExportAllowed|frozenQueryContext|PlatformManagementForbiddenMessage\" internal/exporter/operations_report_scene.go internal/routes/operations_report.go internal/query/operationsreport/query.go",
"literal_output": [
"internal/exporter/operations_report_scene.go:72:\tif err := ensureOperationsReportExportAllowed(params); err != nil {",
"internal/exporter/operations_report_scene.go:79:\tresponse, err := s.query.ActivationSummary(frozenQueryContext(ctx, params), request)",
"internal/exporter/operations_report_scene.go:289:func frozenQueryContext(ctx context.Context, params ExportParams) context.Context {",
"internal/exporter/operations_report_scene.go:298:func ensureOperationsReportExportAllowed(params ExportParams) error {",
"internal/exporter/operations_report_scene.go:302:\treturn errors.New(errors.CodeForbidden, constants.PlatformManagementForbiddenMessage)",
"internal/routes/operations_report.go:24:\t\t\treturn errors.New(errors.CodeForbidden, constants.PlatformManagementForbiddenMessage)"
],
"exit_status": 0
}
}
]