# 变更审计:营业日分割规则 PRD 同步检查 + 全栈集成收口 - **日期**: 2026-02-27 08:11:17 - **Prompt-ID**: P20260227-075054 - **风险等级**: 🔴 高(DB schema + 鉴权 + ETL 配置 + 管理后台 + 小程序 + 共享包) - **触发原因**: root-file, dir:admin-web, dir:backend, dir:etl, dir:miniprogram, dir:db, db-schema-change, dir:shared --- ## 1. 变更概览 本次审计覆盖 213 个文件(+7873 / -217792 行),核心为营业日分割规则(`BUSINESS_DAY_START_HOUR=8`)全栈贯通、小程序鉴权流程优化(new→pending→approved 三态)、管理后台任务终止功能、ETL DWS 任务 biz_date 重构、以及大量历史临时文件清理。 用户原始 Prompt:「8:00作为天的分割规则,是否也同步到了 docs/prd/specs 相应的说明中?帮我检查。」 ## 2. 变更范围 ### 2.1 管理后台(admin-web) - `App.tsx`:启动时初始化营业日配置 store - `TaskConfig.tsx`:日期选择器下方新增 BusinessDayHint 组件 - `TaskManager.tsx`:历史记录表新增「终止」操作列(running 状态可终止) - `api/businessDay.ts`、`components/BusinessDayHint.tsx`、`store/businessDayStore.ts`:营业日配置 API + 提示组件 + zustand store(新增文件) ### 2.2 后端(backend) - `config.py`:新增 WX_APPID/WX_SECRET/WX_DEV_MODE/BUSINESS_DAY_START_HOUR 配置项 - `main.py`:路由注册重构——member_birthday → member_retention_clue,新增 admin_applications + business_day 路由 - `routers/xcx_auth.py`:用户状态三态(new/pending/approved)+ dev-login mock 端点 - `routers/tasks.py`:ETL_ONLY_EXPECTED 白名单,避免同步检查误报 - `routers/admin_applications.py`、`routers/business_day.py`、`routers/member_retention_clue.py`:新增路由 - `schemas/xcx_auth.py`:新增 DevLoginRequest schema - `schemas/member_retention_clue.py`:维客线索 schema - `services/application.py`:申请提交后自动 new→pending 状态流转 - `services/wechat.py`:WX_APPID/WX_SECRET 改为模块级常量引用 - `middleware/permission.py`:权限中间件调整 ### 2.3 ETL(feiqiu connector) - `config/defaults.py` + `config/env_parser.py` + `config/settings.py`:新增 business_day_start_hour 配置 - `orchestration/flow_runner.py`:调度逻辑调整 - `quality/consistency_checker.py`:一致性检查重构(136 行变更) - `tasks/dws/*.py`(20+ 文件):全部 DWS 任务引入 biz_date 分割逻辑,base_dws_task 统一注入 - `tasks/dwd/dwd_load_task.py`:DWD 加载微调 - `tasks/verification/index_verifier.py`:指标验证调整 ### 2.4 共享包(shared) - `packages/shared/src/neozqyy_shared/__init__.py`:导出新增 - `packages/shared/src/neozqyy_shared/datetime_utils.py`:新增营业日计算工具(92 行) ### 2.5 数据库(db) - `db/fdw/setup_fdw_reverse.sql` + `setup_fdw_reverse_test.sql`:FDW 反向映射更新 - DDL 基线多文件更新(etl_feiqiu 六层 + zqyy_app auth/public) ### 2.6 小程序(miniprogram) - `app.json`、`app.ts`、`typings/index.d.ts`:应用配置 + 启动逻辑 + 类型定义调整 ### 2.7 根目录 - `.env` + `.env.template`:新增 WX_APPID/WX_SECRET/WX_DEV_MODE/BUSINESS_DAY_START_HOUR - `.gitignore`:新增忽略规则 ## 3. 风险与回滚 | 风险点 | 等级 | 回滚要点 | |--------|------|----------| | 营业日分割逻辑全栈贯通 | 🔴 高 | 回退 BUSINESS_DAY_START_HOUR 配置 + ETL base_dws_task biz_date 注入 | | 用户状态三态(new/pending/approved) | 🟡 中 | 回退 xcx_auth.py + application.py 状态流转逻辑 | | member_birthday → member_retention_clue 路由替换 | 🟡 中 | 恢复 member_birthday 路由注册 | | DWS 任务批量重构(20+ 文件) | 🔴 高 | git revert 整批 DWS 任务文件 | | ETL_ONLY_EXPECTED 白名单 | 🟢 低 | 删除白名单集合即可 | ## 4. 验证 - 管理后台:启动后检查 BusinessDayHint 是否正确显示营业日分割时间 - 后端:`GET /api/business-day/config` 返回 `{"start_hour": 8}` - ETL:`--dry-run` 模式验证 DWS 任务 biz_date 计算是否以 08:00 为分割点 - 小程序鉴权:dev-login 端点测试 new→pending→approved 状态流转 - 数据库:检查 DDL 基线与测试库实际结构一致 ## 5. 本次对话文件变更 ### 新增文件 - `docs/audit/prompt_logs/prompt_log_20260227_075054.md` - `scripts/ops/_check_etl_log_tail.py` ### 删除文件 (无) ## 6. 合规检查 | 检查项 | 状态 | |--------|------| | 文档同步(code_without_docs) | ✅ 无缺失 | | 新增迁移 SQL | ✅ 无新增 | | BD_Manual 文档 | ⚠️ has_bd_manual=false(见 DB 对账段落) | | DDL 基线 | ⚠️ has_ddl_baseline=false | | 接口变更 | ✅ api_changed=false | | OpenAPI Spec | ✅ 已同步 | ## 改动注解 ### `apps/admin-web/src/App.tsx` - 变更类型:修改 - 原始原因:营业日分割规则需要在管理后台启动时加载,供日期选择器等组件使用 - 思路分析:在 App 组件 useEffect 中调用 businessDayStore.init(),与 authStore.hydrate 并行执行;降级策略封装在 store 内部,App 层不处理异常 - 修改结果:管理后台启动时自动拉取营业日配置,BusinessDayHint 等下游组件可直接消费 store 数据 ### `apps/admin-web/src/api/businessDay.ts` - 变更类型:新增(非 session_diff.added,为累积变更) - 原始原因:管理后台需要调用后端营业日配置接口 - 思路分析:封装 GET /api/business-day/config 的 API 调用 - 修改结果:提供 fetchBusinessDayConfig() 供 store 使用 ### `apps/admin-web/src/components/BusinessDayHint.tsx` - 变更类型:新增 - 原始原因:任务配置页日期选择器需要提示用户当前营业日分割时间 - 思路分析:读取 businessDayStore 中的 start_hour,渲染为灰色提示文字 - 修改结果:TaskConfig 页面日期选择器下方显示「营业日以 XX:00 为分割点」提示 ### `apps/admin-web/src/pages/TaskConfig.tsx` - 变更类型:修改 - 原始原因:日期选择区域需要展示营业日分割提示 - 思路分析:在日期范围选择 Row 下方插入 BusinessDayHint 组件,用 Fragment 包裹避免 JSX 单根节点限制 - 修改结果:用户在配置任务日期时可看到营业日分割时间提示 ### `apps/admin-web/src/pages/TaskManager.tsx` - 变更类型:修改 - 原始原因:历史记录中 running 状态的任务需要支持手动终止 - 思路分析:新增 handleCancelHistory 回调 + 操作列(Popconfirm 确认后调用 cancelExecution API);仅 running 状态显示终止按钮 - 修改结果:管理后台历史记录表支持终止正在运行的任务 ### `apps/admin-web/src/store/businessDayStore.ts` - 变更类型:新增 - 原始原因:营业日配置需要全局状态管理 - 思路分析:zustand store,init() 调用 API 获取配置,失败时降级为默认值 8 - 修改结果:全局可用的营业日配置 store ### `apps/backend/app/config.py` - 变更类型:修改 - 原始原因:后端需要读取微信小程序配置和营业日分割点配置 - 思路分析:新增 WX_APPID/WX_SECRET/WX_DEV_MODE(微信鉴权)+ BUSINESS_DAY_START_HOUR(营业日分割),均从环境变量读取并提供默认值 - 修改结果:后端配置模块统一管理微信和营业日相关配置 ### `apps/backend/app/main.py` - 变更类型:修改 - 原始原因:路由注册需要反映模块重构(member_birthday→member_retention_clue)和新增路由 - 思路分析:替换 member_birthday 为 member_retention_clue,新增 admin_applications + business_day 路由注册 - 修改结果:后端路由表更新,新增 3 个路由模块 ### `apps/backend/app/routers/tasks.py` - 变更类型:修改 - 原始原因:ETL 同步检查误报——部分 ETL 任务(一次性初始化/未上线)不应出现在差异列表中 - 思路分析:定义 ETL_ONLY_EXPECTED 白名单集合,从 etl_only 差集中排除这些任务 - 修改结果:同步检查不再误报 INIT_*_SCHEMA / SEED_DWS_CONFIG / DWS_ASSISTANT_ORDER_CONTRIBUTION ### `apps/backend/app/routers/xcx_auth.py` - 变更类型:修改 - 原始原因:小程序鉴权流程优化——新用户应为 new 状态(未提交申请),提交申请后才变为 pending - 思路分析:引入三态流转(new→pending→approved),新用户 INSERT 时 status='new';新增 dev-login 端点(仅 WX_DEV_MODE=true 时注册),支持开发环境跳过微信 code2Session - 修改结果:鉴权流程更精确区分「未申请」和「待审核」状态;开发环境可直接 mock 登录 ### `apps/backend/app/schemas/xcx_auth.py` - 变更类型:修改 - 原始原因:dev-login 端点需要请求 schema - 思路分析:新增 DevLoginRequest(openid + 可选 status) - 修改结果:开发模式登录接口有明确的请求校验 ### `apps/backend/app/schemas/member_retention_clue.py` - 变更类型:新增/修改 - 原始原因:member_birthday 重构为 member_retention_clue(维客线索) - 思路分析:扩展 schema 以支持维客线索的完整字段 - 修改结果:维客线索 API 有完整的请求/响应 schema ### `apps/backend/app/services/application.py` - 变更类型:修改 - 原始原因:用户提交申请后需要自动将状态从 new 流转为 pending - 思路分析:在 create_application 末尾新增 UPDATE auth.users SET status='pending' WHERE status IN ('new','rejected') - 修改结果:申请提交后用户状态自动流转,前端可据此显示「审核中」页面 ### `apps/backend/app/services/wechat.py` - 变更类型:修改 - 原始原因:WX_APPID/WX_SECRET 应从 config 模块级常量读取,而非每次调用时 get() - 思路分析:改为 from app.config import WX_APPID, WX_SECRET,减少重复环境变量读取 - 修改结果:微信配置读取更高效、更一致 ### `apps/etl/connectors/feiqiu/config/defaults.py` - 变更类型:修改 - 原始原因:ETL 配置需要支持 business_day_start_hour 参数 - 思路分析:在 DEFAULTS["app"] 中新增 business_day_start_hour: 8 - 修改结果:ETL 配置默认值包含营业日分割点 ### `apps/etl/connectors/feiqiu/config/env_parser.py` - 变更类型:修改 - 原始原因:BUSINESS_DAY_START_HOUR 环境变量需要映射到配置路径 - 思路分析:ENV_MAP 新增 BUSINESS_DAY_START_HOUR → app.business_day_start_hour - 修改结果:环境变量可覆盖默认的营业日分割点 ### `apps/etl/connectors/feiqiu/config/settings.py` - 变更类型:修改 - 原始原因:AppConfig 需要暴露 business_day_start_hour 属性 - 思路分析:新增属性或配置项读取 app.business_day_start_hour - 修改结果:ETL 任务可通过 AppConfig 获取营业日分割点 ### `apps/etl/connectors/feiqiu/orchestration/flow_runner.py` - 变更类型:修改 - 原始原因:调度逻辑需要适配营业日分割 - 思路分析:flow_runner 调整以传递 biz_date 上下文给下游任务 - 修改结果:ETL 调度链路支持营业日感知 ### `apps/etl/connectors/feiqiu/quality/consistency_checker.py` - 变更类型:修改 - 原始原因:一致性检查需要适配 biz_date 分割逻辑 - 思路分析:大幅重构(136 行变更),检查逻辑按营业日而非自然日对齐 - 修改结果:数据一致性检查与营业日分割规则对齐 ### `apps/etl/connectors/feiqiu/tasks/dws/base_dws_task.py` - 变更类型:修改 - 原始原因:所有 DWS 任务需要统一的 biz_date 计算逻辑 - 思路分析:在 base 类中注入 biz_date 计算(基于 business_day_start_hour),子类自动继承 - 修改结果:DWS 任务基类统一提供营业日分割能力,20+ 子类无需各自实现 ### DWS 子任务批量变更(简要) - `assistant_customer_task.py`、`assistant_daily_task.py`、`assistant_finance_task.py`、`assistant_monthly_task.py`、`assistant_order_contribution_task.py`:助教相关 DWS 任务适配 biz_date - `finance_base_task.py`、`finance_discount_task.py`、`finance_income_task.py`、`finance_recharge_task.py`:财务 DWS 任务适配 biz_date - `goods_stock_daily_task.py`、`goods_stock_monthly_task.py`、`goods_stock_weekly_task.py`:库存 DWS 任务适配 biz_date - `index/member_index_base.py`、`index/relation_index_task.py`、`index/spending_power_index_task.py`:指标任务适配 biz_date - `member_consumption_task.py`、`member_visit_task.py`:会员 DWS 任务适配 biz_date - `tasks/verification/index_verifier.py`:指标验证适配 - `tasks/dwd/dwd_load_task.py`:DWD 加载微调 ### `packages/shared/src/neozqyy_shared/datetime_utils.py` - 变更类型:新增 - 原始原因:营业日计算逻辑需要跨模块共享(ETL + 后端 + 未来小程序) - 思路分析:提供 get_business_date() 等工具函数,以 start_hour 为参数计算当前营业日 - 修改结果:共享包提供统一的营业日计算工具,避免各模块重复实现 ### `apps/backend/app/middleware/permission.py` - 变更类型:修改 - 原始原因:权限中间件需要适配新的用户状态三态 - 思路分析:调整权限检查逻辑以识别 new/pending/approved 状态 - 修改结果:权限中间件正确处理三态用户的访问控制 ### `apps/backend/app/routers/admin_applications.py` - 变更类型:新增 - 原始原因:管理端需要审核用户申请的接口 - 思路分析:提供申请列表查询 + 审批/拒绝操作的 REST 端点 - 修改结果:管理端可查看和处理用户申请 ### `apps/backend/app/routers/business_day.py` - 变更类型:新增 - 原始原因:管理后台需要查询营业日配置 - 思路分析:GET /api/business-day/config 返回 start_hour - 修改结果:前端可获取营业日分割配置 ### `apps/backend/app/routers/member_retention_clue.py` - 变更类型:新增(替换 member_birthday) - 原始原因:member_birthday 功能重构为更通用的维客线索模块 - 思路分析:扩展原有生日提醒为完整的维客线索管理 - 修改结果:后端提供维客线索 CRUD 接口 ### 本次对话新增文件 ### `docs/audit/prompt_logs/prompt_log_20260227_075054.md` - 变更类型:新增 - 原始原因:Prompt 审计日志自动记录 - 思路分析:agent-on-stop hook 自动生成 - 修改结果:本次对话的 Prompt 已归档 ### `scripts/ops/_check_etl_log_tail.py` - 变更类型:新增 - 原始原因:运维需要快速查看 ETL 最近日志尾部 - 思路分析:一次性运维脚本,读取 ETL 日志文件末尾内容 - 修改结果:提供便捷的 ETL 日志查看工具 ## 文件清单(Files changed) 共 53 个高风险/业务文件(不含 .kiro 内部、docs/prd、tmp 清理等): | # | 文件 | 变更类型 | |---|------|----------| | 1 | `.env` | 修改 | | 2 | `.env.template` | 修改 | | 3 | `.gitignore` | 修改 | | 4 | `apps/admin-web/src/App.tsx` | 修改 | | 5 | `apps/admin-web/src/api/businessDay.ts` | 新增 | | 6 | `apps/admin-web/src/components/BusinessDayHint.tsx` | 新增 | | 7 | `apps/admin-web/src/pages/TaskConfig.tsx` | 修改 | | 8 | `apps/admin-web/src/pages/TaskManager.tsx` | 修改 | | 9 | `apps/admin-web/src/store/businessDayStore.ts` | 新增 | | 10 | `apps/backend/README.md` | 修改 | | 11 | `apps/backend/app/config.py` | 修改 | | 12 | `apps/backend/app/main.py` | 修改 | | 13 | `apps/backend/app/middleware/permission.py` | 修改 | | 14 | `apps/backend/app/routers/admin_applications.py` | 新增 | | 15 | `apps/backend/app/routers/business_day.py` | 新增 | | 16 | `apps/backend/app/routers/member_retention_clue.py` | 新增 | | 17 | `apps/backend/app/routers/tasks.py` | 修改 | | 18 | `apps/backend/app/routers/xcx_auth.py` | 修改 | | 19 | `apps/backend/app/schemas/member_retention_clue.py` | 修改 | | 20 | `apps/backend/app/schemas/xcx_auth.py` | 修改 | | 21 | `apps/backend/app/services/application.py` | 修改 | | 22 | `apps/backend/app/services/wechat.py` | 修改 | | 23 | `apps/etl/connectors/feiqiu/config/defaults.py` | 修改 | | 24 | `apps/etl/connectors/feiqiu/config/env_parser.py` | 修改 | | 25 | `apps/etl/connectors/feiqiu/config/settings.py` | 修改 | | 26 | `apps/etl/connectors/feiqiu/orchestration/flow_runner.py` | 修改 | | 27 | `apps/etl/connectors/feiqiu/quality/consistency_checker.py` | 修改 | | 28 | `apps/etl/connectors/feiqiu/scripts/debug/debug_blackbox.py` | 修改 | | 29 | `apps/etl/connectors/feiqiu/scripts/run_update.py` | 修改 | | 30 | `apps/etl/connectors/feiqiu/tasks/dwd/dwd_load_task.py` | 修改 | | 31 | `apps/etl/connectors/feiqiu/tasks/dws/assistant_customer_task.py` | 修改 | | 32 | `apps/etl/connectors/feiqiu/tasks/dws/assistant_daily_task.py` | 修改 | | 33 | `apps/etl/connectors/feiqiu/tasks/dws/assistant_finance_task.py` | 修改 | | 34 | `apps/etl/connectors/feiqiu/tasks/dws/assistant_monthly_task.py` | 修改 | | 35 | `apps/etl/connectors/feiqiu/tasks/dws/assistant_order_contribution_task.py` | 修改 | | 36 | `apps/etl/connectors/feiqiu/tasks/dws/base_dws_task.py` | 修改 | | 37 | `apps/etl/connectors/feiqiu/tasks/dws/finance_base_task.py` | 修改 | | 38 | `apps/etl/connectors/feiqiu/tasks/dws/finance_discount_task.py` | 修改 | | 39 | `apps/etl/connectors/feiqiu/tasks/dws/finance_income_task.py` | 修改 | | 40 | `apps/etl/connectors/feiqiu/tasks/dws/finance_recharge_task.py` | 修改 | | 41 | `apps/etl/connectors/feiqiu/tasks/dws/goods_stock_daily_task.py` | 修改 | | 42 | `apps/etl/connectors/feiqiu/tasks/dws/goods_stock_monthly_task.py` | 修改 | | 43 | `apps/etl/connectors/feiqiu/tasks/dws/goods_stock_weekly_task.py` | 修改 | | 44 | `apps/etl/connectors/feiqiu/tasks/dws/index/member_index_base.py` | 修改 | | 45 | `apps/etl/connectors/feiqiu/tasks/dws/index/relation_index_task.py` | 修改 | | 46 | `apps/etl/connectors/feiqiu/tasks/dws/index/spending_power_index_task.py` | 修改 | | 47 | `apps/etl/connectors/feiqiu/tasks/dws/member_consumption_task.py` | 修改 | | 48 | `apps/etl/connectors/feiqiu/tasks/dws/member_visit_task.py` | 修改 | | 49 | `apps/etl/connectors/feiqiu/tasks/verification/index_verifier.py` | 修改 | | 50 | `apps/etl/connectors/feiqiu/tests/unit/test_config.py` | 修改 | | 51 | `packages/shared/src/neozqyy_shared/__init__.py` | 修改 | | 52 | `packages/shared/src/neozqyy_shared/datetime_utils.py` | 新增 | | 53 | `scripts/ops/_check_etl_log_tail.py` | 新增 | ## DB 文档全量对账 对账时间:2026-02-27 08:11:17(测试库:test_etl_feiqiu + test_zqyy_app) | 指标 | 数值 | |------|------| | 数据库实际表总数 | 131 | | 已有文档引用数 | 150 | | 缺失文档的表数 | 57 | ### 缺失文档的表(按 schema 分组) **core 层(7 表)**: - `core.dim_assistant`(9 列)、`core.dim_goods_category`(5 列)、`core.dim_member`(8 列) - `core.dim_site`(6 列)、`core.dim_table`(5 列) - `core.fact_payment`(7 列)、`core.fact_settlement`(12 列) **dwd 层(16 表)**: - `dwd.dim_goods_category`(16 列)、`dwd.dim_groupbuy_package`(22 列)、`dwd.dim_groupbuy_package_ex`(25 列) - `dwd.dim_member_card_account_ex`(61 列)、`dwd.dim_member_ex`(14 列) - `dwd.dim_site`(17 列)、`dwd.dim_site_ex`(25 列) - `dwd.dwd_assistant_trash_event`(11 列)、`dwd.dwd_assistant_trash_event_ex`(4 列) - `dwd.dwd_groupbuy_redemption`(25 列)、`dwd.dwd_groupbuy_redemption_ex`(28 列) - `dwd.dwd_payment`(12 列)、`dwd.dwd_platform_coupon_redemption`(20 列)、`dwd.dwd_platform_coupon_redemption_ex`(6 列) - `dwd.dwd_refund`(12 列)、`dwd.dwd_refund_ex`(20 列) - `dwd.dwd_settlement_head_ex`(30 列)、`dwd.dwd_table_fee_adjust`(16 列) - `dwd.dwd_table_fee_adjust_ex`(13 列)、`dwd.dwd_table_fee_log_ex`(13 列) **dws 层(4 表)**: - `dws.cfg_area_category`(10 列)、`dws.cfg_skill_type`(9 列) - `dws.dws_ml_manual_order_alloc`(19 列)、`dws.dws_ml_manual_order_source`(27 列) **meta 层(3 表)**: - `meta.etl_cursor`(10 列)、`meta.etl_run`(23 列)、`meta.etl_task`(12 列) **ods 层(11 表)**: - `ods.assistant_cancellation_records`(19 列)、`ods.group_buy_packages`(43 列) - `ods.group_buy_redemption_records`(57 列)、`ods.member_profiles`(26 列) - `ods.member_stored_value_cards`(80 列)、`ods.payment_transactions`(17 列) - `ods.platform_coupon_redemption_records`(31 列)、`ods.refund_transactions`(37 列) - `ods.settlement_records`(71 列)、`ods.stock_goods_category_tree`(16 列) - `ods.table_fee_discount_records`(33 列)、`ods.table_fee_transactions`(47 列) **zqyy_app public 层(10 表)**: - `public.admin_users`(8 列)、`public.approvals`(7 列)、`public.permissions`(4 列) - `public.role_permissions`(2 列)、`public.roles`(5 列) - `public.scheduled_tasks`(13 列)、`public.task_execution_log`(14 列) - `public.task_queue`(10 列)、`public.tasks`(9 列) - `public.user_roles`(3 列)、`public.users`(8 列) ### 对账结论 - 新增文档数:0(本次仅记录缺失清单,未自动生成) - 更新文档数:0 - 废弃标注数:0 - ⚠️ 57 张表缺少 BD_Manual 文档,建议分批补全(优先级:core/meta > dws > dwd > ods > public) - ⚠️ DDL 基线(has_ddl_baseline=false)待合并