Plan: Reports Module Comprehensive Refactor
目标是将 Reports 从“实时拼装+硬编码时区+单实例调度假设”升级为“可审计快照优先、按楼栋时区治理、可扩展高性能查询、统一鉴权策略”的架构。基于你的决策:历史报表改为 as-of 快照;时区改为楼栋级持久化;API 时间字段允许破坏性升级为 DateTime(不保留旧 datePoint 字段)。
Steps
- Phase 1 - Baseline & Gap Freeze 1.1 建立现状基线(数据模型、API、时区、调度、鉴权、性能)并冻结改造边界:
- 后端现状来源:MongoReport/MongoReportTemplate、ReportProvider/ReportTemplateProvider、ReportSchedulerService、Resolver/Schema/Policy。
- 前端现状来源:Reports 页面、历史列表、query/mutation、时间工具。 1.2 输出差异矩阵并确认“必须修复项”清单:
- 历史语义从“最新状态”切换为“as-of 快照”。
- DateTime 全替代 startDatePoint/endDatePoint。
- building timezone 从常量迁移到 building 配置。
- scheduler 多实例并发保护与吞吐策略。
- 大查询拆分与缓存策略。
- 权限策略从 section null-return 升级为 policy+字段级鉴权可观测。
- Phase 2 - Database Redesign (重点)
2.1 新增快照主表
reports(周期级不可变快照)字段:
- 主键与业务键:id, buildingId, periodType(WEEKLY/MONTHLY), periodStartUtc, periodEndUtc, asOfUtc。
- 时区与口径:buildingTimezone, weekStartRule, templateVersion。
- 生命周期:status(DRAFT/LOCKED/REGENERATING), lockedAt, generatedByJobId。
- 审计元数据:createdDate, updatedDate, creatorId, lastEditorId。 2.2 新增快照明细/汇总表(避免读时重算):
report_sections:按 sectionName 存标准化 payload(Decision/KPI/Summary/Upcoming)。report_kpi_metrics:按 metricKey + dimensions + value + computedAt 存聚合结果(支持趋势图直接读取)。report_inclusion_refs:记录快照中纳入的事件引用与判定规则(eventType/eventId/includedByRule/rangeMatchedOn)。 2.3 模板与用户视图分离:- 保留
report_templates作为“展示/口径定义”。 - 新增
report_template_versions(不可变版本),快照绑定 templateVersion,保证历史可重放。 - 用户私有 Commentary 从 report 主文档分离到
report_private_notes(ownerUserId + reportId 唯一)。 2.4 Discussion 维持 comment-system 但补索引与权限字段: - parentEntityId/reportId + createdDate 复合索引;追加 buildingId 冗余字段用于快速过滤和策略校验。 2.5 关键索引与约束:
reports唯一索引:(buildingId, periodType, periodStartUtc, periodEndUtc, templateVersion, asOfUtc)。- 热查询索引:(buildingId, periodType, periodStartUtc desc)、(buildingId, status, lockedAt)。
report_kpi_metrics索引:(reportId, metricKey)、(buildingId, metricKey, periodStartUtc desc)。report_private_notes唯一索引:(reportId, ownerUserId)。
- Phase 3 - Timezone & Time Model Unification 3.1 统一时间协议:
- API 输入/输出全部使用 DateTime(ISO UTC)。
- 数据库存储全部 Mongo Date(UTC)。
- 周/月周期边界由 buildingTimezone + weekStartRule 计算后转换为 UTC 落库。 3.2 引入楼栋时区配置:
- building 模型增加 timezone 与 weekStartRule;Reports 读取 building 配置,不再使用常量 Toronto。 3.3 前端时间规范:
- 请求参数只传 UTC DateTime。
- 展示层按 buildingTimezone 渲染标题和日期。
- 历史筛选与分页按 server 返回周期主键,不再由前端本地推导 5 年窗口。
- Phase 4 - Scheduler Re-architecture & Performance 4.1 调度任务拆分:
ReportPlanJob(扫描应生成周期) +ReportGenerateJob(按 building+period 实际生成)。- 支持批处理和并发窗口(如每批 N 个 building,动态限流)。 4.2 幂等与分布式锁:
- 任务级幂等键:buildingId+periodType+periodStartUtc+templateVersion。
- 使用分布式锁/租约(Redis 或 Mongo 锁集合)避免多实例重复生成。 4.3 性能优化:
- 由“读取时计算”改为“生成时预聚合”;读接口直接命中快照。
- 失败重试采用指数退避+死信队列;补偿任务支持手工重放。 4.4 可观测性:
- 指标:生成耗时、成功率、重复命中率、每 section 耗时、DB 扫描量。
- 日志携带 jobId/reportId/buildingId 便于追踪。
- Phase 5 - API & Interface Performance 5.1 GraphQL Schema 改造(破坏性升级):
- 删除 DateTimeRange.startDatePoint/endDatePoint,改为 startDate/endDate(DateTime!)。
- reports 查询默认返回轻量字段;section 数据改为按需字段解析。 5.2 查询分层:
- List API 只返回 report header + KPI 摘要。
- Detail API 再拉取 section payload(Decision/KPI chart/Summary/Upcoming)。 5.3 缓存策略:
- LOCKED report 使用短期缓存(Redis/APQ);DRAFT report 缩短 TTL。
- 前端 Apollo 使用 normalized cache + avoid no-cache 全量 refetch。
- Phase 6 - Authorization Hardening 6.1 资源与动作矩阵明确化:
report,reportTemplate,reportComment,reportPrivateNote,reportJob分资源。- 动作拆分 read/list/create/edit/delete/lock/regenerate/export。 6.2 鉴权下沉与一致性:
- Resolver 保留
@RequirePermission;Provider 增加统一 scope 校验(viewerId/buildingId/ownerId)。 - Section 级权限不再只靠 null 返回,改为带 reason 的可观测拒绝事件。 6.3 policy 治理:
- 统一 PM/MCC/BM 的最小权限集合;Commentary 私有笔记强 owner-only。
- 权限变更后历史快照可见性按当前权限评估,私有笔记仅 owner 可见。
- Phase 7 - Migration & Rollout 7.1 数据迁移顺序:
- DDL/索引 -> building timezone 回填 -> 模板版本化 -> 快照回填(最近 N 期) -> API 切换。 7.2 灰度策略:
- 先只读双写验证(老接口继续服务,新快照旁路验证)。
- 指标达标后切主流量并下线旧字段/旧计算路径。 7.3 回滚策略:
- 保留旧 reports 读取链路和 feature flag,异常时一键回退。
Relevant files
- /home/jiangjiang/nexxus-directory-graphql/src/buildingos/report/mongo-model/MongoReport.ts — 现有 report 文档结构与索引,需迁移为快照主表设计。
- /home/jiangjiang/nexxus-directory-graphql/src/buildingos/report/mongo-model/MongoReportTemplate.ts — 模板结构,需引入版本化模型与活跃模板策略。
- /home/jiangjiang/nexxus-directory-graphql/src/buildingos/report/provider/ReportProvider.ts — 查询/创建/section 计算核心,需拆为“生成时预聚合 + 读取时轻量访问”。
- /home/jiangjiang/nexxus-directory-graphql/src/buildingos/report/provider/ReportTemplateProvider.ts — 模板 CRUD 与 active 模板逻辑,需兼容模板版本绑定。
- /home/jiangjiang/nexxus-directory-graphql/src/buildingos/report/service/ReportSchedulerService.ts — 当前 setTimeout 调度,需改分布式可并发 job 架构。
- /home/jiangjiang/nexxus-directory-graphql/src/buildingos/report/utils/ReportDateUtils.ts — 当前硬编码时区与周期计算,需改按 building timezone/weekStartRule。
- /home/jiangjiang/nexxus-directory-graphql/src/buildingos/report/schema/report.graphql — Report type 字段需切 DateTime 并按 header/detail 拆分。
- /home/jiangjiang/nexxus-directory-graphql/src/buildingos/report/schema/reportQuery.graphql — list 查询入参与返回模型优化。
- /home/jiangjiang/nexxus-directory-graphql/src/common/schema/common.graphql — DateTimeRange 定义由 datePoint 字符串改 DateTime。
- /home/jiangjiang/nexxus-directory-graphql/src/common/authorizer/access/policy.csv — 资源动作矩阵与角色策略收敛。
- /home/jiangjiang/nexxus-directory-graphql/src/common/authorizer/constants/permissionResource.ts — 新资源类型补充(private note/job)。
- /home/jiangjiang/nexxus-portal-webapp/src/pages/Reports/report-page.tsx — 查询拆分、缓存策略、标题时间渲染改造。
- /home/jiangjiang/nexxus-portal-webapp/src/pages/Reports/utils.ts — 去硬编码 Toronto,统一 UTC 输入 + building tz 展示。
- /home/jiangjiang/nexxus-portal-webapp/src/apis/report/getReports.ts — 字段切换到 DateTime 与轻量/明细查询分离。
- /home/jiangjiang/nexxus-portal-webapp/src/pages/Reports/View/historical-reports.tsx — 历史列表改为服务端游标与真实周期来源。
Verification
- Schema 与类型校验:后端 codegen 与前端 GraphQL types 通过,且不存在 startDatePoint/endDatePoint 引用。
- 时间边界测试:跨时区(Toronto/Vancouver/UTC+8)、DST 切换周、月末/年末、周起始规则变更用例全部通过。
- 调度压测:多实例下同周期不重复生成;10k building 仿真下任务完成时间和 DB 峰值在阈值内。
- 接口性能:reports list P95、detail P95、DB 查询次数和扫描文档数对比改造前下降。
- 鉴权回归:PM/MCC/BM 权限矩阵、owner-only 私有 commentary、权限变更后可见性行为符合预期。
- 数据一致性:同一 reportId 的 section/kpi/ref 快照可完整重建页面,无读时补算偏差。
Decisions
- 历史报表采用 as-of 快照并可锁定,默认不可变。
- 时区按楼栋配置持久化,完全移除 Toronto 硬编码依赖。
- API 时间字段直接切换为 DateTime(允许破坏性升级),前后端同步改造。
- Reports 优先实现“可审计一致性 + 性能稳定性”,实时增量推送(Nice-to-have)不在首批必达范围。
Further Considerations
- 快照粒度建议:周/月完整快照 + section 级 payload 分表,避免单文档过大与热字段竞争。
- KPI 口径变更策略建议:新规则只影响新快照;历史快照保持原口径并标记 templateVersion。
- 若需保留“最新状态回看”体验,可在 UI 增加 Live View 标签,与 Snapshot View 并存但数据源隔离。