Files
junhong_cmp_fiber/internal/routes/operations_report.go
break 5e78809b93
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 15m33s
feat(运营报表): AUG26-015 设备激活与套餐续费日报快照、查询趋势与受控导出
- 新增成对迁移 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
2026-09-18 09:42:28 +08:00

85 lines
4.6 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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,
})
}