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

@@ -109,6 +109,9 @@ func RegisterAdminRoutes(router fiber.Router, handlers *bootstrap.Handlers, midd
if handlers.AssetAutoRenewal != nil {
registerAssetAutoRenewalRoutes(authGroup, handlers.AssetAutoRenewal, doc, basePath)
}
if handlers.OperationsReport != nil {
registerOperationsReportRoutes(authGroup, handlers.OperationsReport, doc, basePath)
}
if handlers.ShopPackageBatchAllocation != nil {
registerShopPackageBatchAllocationRoutes(authGroup, handlers.ShopPackageBatchAllocation, doc, basePath)
}

View File

@@ -0,0 +1,84 @@
package routes
import (
"github.com/gofiber/fiber/v2"
"github.com/break/junhong_cmp_fiber/internal/handler/admin"
"github.com/break/junhong_cmp_fiber/internal/model/dto"
"github.com/break/junhong_cmp_fiber/pkg/constants"
"github.com/break/junhong_cmp_fiber/pkg/errors"
"github.com/break/junhong_cmp_fiber/pkg/middleware"
"github.com/break/junhong_cmp_fiber/pkg/openapi"
)
// registerOperationsReportRoutes 注册运营报表(设备激活情况与套餐续费情况)的查询与受控导出路由。
//
// 沿用超管/平台路由组级 gate 先例:代理、企业与个人客户账号一律 403且无权限与目标不存在不形成可枚举差异。
// gate 必须挂在功能路径组上Fiber 的组中间件按路径前缀生效,挂在空路径组上会落到 /api/admin 前缀,
// 从而拦截该层其余全部接口。
// 路径全部为静态路径(无动态参数),导出子路径比汇总路径更具体,注册顺序不影响匹配。
func registerOperationsReportRoutes(router fiber.Router, handler *admin.OperationsReportHandler, doc *openapi.Generator, basePath string) {
group := router.Group("/operations-reports", func(c *fiber.Ctx) error {
userType := middleware.GetUserTypeFromContext(c.UserContext())
if userType != constants.UserTypeSuperAdmin && userType != constants.UserTypePlatform {
return errors.New(errors.CodeForbidden, constants.PlatformManagementForbiddenMessage)
}
return c.Next()
})
path := basePath + "/operations-reports"
Register(group, doc, path, "GET", "/activation-summary", handler.ActivationSummary, RouteSpec{
Summary: "查询设备激活情况汇总",
Description: "只读日报快照:采购数量、累计激活数、激活率、新增激活数、累计在网数、活跃用户数、累计用量与卡均;累计类取结束日快照,结束日无快照时为空且不回退;仅超级管理员与平台账号可访问",
Tags: []string{"运营报表"},
Input: new(dto.OperationsActivationSummaryRequest),
Output: new(dto.OperationsActivationSummaryResponse),
Auth: true,
})
Register(group, doc, path, "GET", "/activation-trend", handler.ActivationTrend, RouteSpec{
Summary: "查询设备激活情况日/月趋势",
Description: "按日每个快照日一点,按月每个自然月一点;累计类取该期最后一个有快照日的快照值,新增激活数取相邻期之差;无快照的期不出现",
Tags: []string{"运营报表"},
Input: new(dto.OperationsActivationTrendRequest),
Output: new(dto.OperationsActivationTrendResponse),
Auth: true,
})
Register(group, doc, path, "POST", "/activation-summary/export", handler.ExportActivationSummary, RouteSpec{
Summary: "导出设备激活情况",
Description: "复用既有异步导出任务:创建时冻结筛选条件、操作者与可见店铺范围,导出列与页面字段一致并含合计行,分母为零写「-」,不含文字总结",
Tags: []string{"运营报表"},
Body: new(dto.ExportOperationsReportRequest),
Output: new(dto.CreateExportTaskResponse),
Auth: true,
})
Register(group, doc, path, "GET", "/package-renewal-summary", handler.RenewalSummary, RouteSpec{
Summary: "查询套餐续费情况汇总",
Description: "只读日报快照:到期资产数、续费资产数、续费率与新增未续费数;到期与续费按资产去重,续费资产为到期资产子集,续费率不超过 100% 由构造保证",
Tags: []string{"运营报表"},
Input: new(dto.OperationsRenewalSummaryRequest),
Output: new(dto.OperationsRenewalSummaryResponse),
Auth: true,
})
Register(group, doc, path, "GET", "/package-renewal-trend", handler.RenewalTrend, RouteSpec{
Summary: "查询套餐续费情况日/月趋势",
Description: "按日每个快照日一点,按月每个自然月一点;到期与续费为期内按资产去重的流式指标;无快照的期不出现",
Tags: []string{"运营报表"},
Input: new(dto.OperationsRenewalTrendRequest),
Output: new(dto.OperationsRenewalTrendResponse),
Auth: true,
})
Register(group, doc, path, "POST", "/package-renewal-summary/export", handler.ExportRenewalSummary, RouteSpec{
Summary: "导出套餐续费情况",
Description: "复用既有异步导出任务:创建时冻结筛选条件、操作者与可见店铺范围,导出列与页面字段一致并含合计行,分母为零写「-」,不含文字总结",
Tags: []string{"运营报表"},
Body: new(dto.ExportOperationsReportRequest),
Output: new(dto.CreateExportTaskResponse),
Auth: true,
})
}