{ "built_at": "2026-03-15T09:58:27.186159+08:00", "prompt_id": "P20260315-095422", "prompt_at": "2026-03-15T09:54:22.179324+08:00", "audit_required": true, "db_docs_required": true, "reasons": [ "dir:backend", "dir:etl", "dir:miniprogram", "dir:db", "db-schema-change" ], "changed_files": [ "_DEL/", "apps/backend/app/ai/apps/app1_chat.py", "apps/backend/app/ai/apps/app2_finance.py", "apps/backend/app/ai/apps/app3_clue.py", "apps/backend/app/ai/apps/app4_analysis.py", "apps/backend/app/ai/apps/app5_tactics.py", "apps/backend/app/ai/apps/app6_note.py", "apps/backend/app/ai/apps/app7_customer.py", "apps/backend/app/ai/apps/app8_consolidation.py", "apps/backend/app/ai/dispatcher.py", "apps/backend/app/ai/prompts/app2_finance_prompt.py", "apps/backend/app/ai/prompts/app8_consolidation_prompt.py", "apps/backend/app/main.py", "apps/backend/app/routers/xcx_ai_cache.py", "apps/backend/app/routers/xcx_ai_chat.py", "apps/backend/app/schemas/xcx_tasks.py", "apps/backend/app/services/note_reclassifier.py", "apps/backend/app/services/note_service.py", "apps/backend/app/services/recall_detector.py", "apps/backend/app/services/task_executor.py", "apps/backend/app/services/task_manager.py", "apps/backend/app/services/task_queue.py", "apps/backend/app/services/task_registry.py", "apps/backend/app/services/trigger_scheduler.py", "apps/backend/pyproject.toml", "apps/backend/tests/test_task_queue.py", "apps/etl/connectors/feiqiu/.env", "apps/etl/connectors/feiqiu/database/connection.py", "apps/etl/connectors/feiqiu/database/operations.py", "apps/etl/connectors/feiqiu/docs/database/DWD/changes/BD_Manual_dim_groupbuy_package_ex_detail_fields.md", "apps/etl/connectors/feiqiu/docs/database/DWD/main/BD_manual_dwd_settlement_head.md", "apps/etl/connectors/feiqiu/docs/database/DWS/changes/BD_Manual_dws_assistant_order_contribution.md", "apps/etl/connectors/feiqiu/docs/database/DWS/changes/BD_Manual_dws_goods_stock_summary.md", "apps/etl/connectors/feiqiu/docs/database/DWS/changes/BD_Manual_dws_member_spending_power_index.md", "apps/etl/connectors/feiqiu/docs/database/DWS/changes/BD_Manual_dws_project_tags.md", "apps/etl/connectors/feiqiu/docs/database/DWS/main/BD_manual_cfg_area_category.md", "apps/etl/connectors/feiqiu/docs/database/DWS/main/BD_manual_dws_finance_income_structure.md", "apps/etl/connectors/feiqiu/docs/database/README.md", "apps/etl/connectors/feiqiu/docs/database/cross_layer/BD_Manual_assistant_accounts_master.md", "apps/etl/connectors/feiqiu/docs/database/cross_layer/BD_Manual_assistant_service_records.md", "apps/etl/connectors/feiqiu/docs/database/cross_layer/BD_Manual_goods_stock_movements.md", "apps/etl/connectors/feiqiu/docs/database/cross_layer/BD_Manual_goods_stock_summary.md", "apps/etl/connectors/feiqiu/docs/database/cross_layer/BD_Manual_goods_stock_warning_info.md", "apps/etl/connectors/feiqiu/docs/database/cross_layer/BD_Manual_group_buy_package_details.md", "apps/etl/connectors/feiqiu/docs/database/cross_layer/BD_Manual_member_balance_changes.md", "apps/etl/connectors/feiqiu/docs/database/cross_layer/BD_Manual_recharge_settlements.md", "apps/etl/connectors/feiqiu/docs/database/cross_layer/BD_Manual_site_tables_master.md", "apps/etl/connectors/feiqiu/docs/database/cross_layer/BD_Manual_store_goods_master.md", "apps/etl/connectors/feiqiu/docs/database/cross_layer/BD_Manual_store_goods_sales_records.md", "apps/etl/connectors/feiqiu/docs/database/cross_layer/BD_Manual_tenant_goods_master.md", "apps/etl/connectors/feiqiu/docs/etl_tasks/dws_tasks.md", "apps/etl/connectors/feiqiu/tasks/dws/assistant_order_contribution_task.py", "apps/etl/connectors/feiqiu/tasks/dws/assistant_project_tag_task.py", "apps/etl/connectors/feiqiu/tasks/dws/base_dws_task.py", "apps/etl/connectors/feiqiu/tasks/dws/member_project_tag_task.py", "apps/miniprogram - /345/211/257/346/234/254/.gitignore", "apps/miniprogram - /345/211/257/346/234/254/.gitkeep", "apps/miniprogram - /345/211/257/346/234/254/README.md", "apps/miniprogram - /345/211/257/346/234/254/doc/auth-integration-guide.md", "apps/miniprogram - /345/211/257/346/234/254/doc/h5-input-material-guide.md", "apps/miniprogram - /345/211/257/346/234/254/doc/h5-to-miniprogram-pitfalls.md", "apps/miniprogram - /345/211/257/346/234/254/doc/howtodo.md", "apps/miniprogram - /345/211/257/346/234/254/doc/migration-guide.md", "apps/miniprogram - /345/211/257/346/234/254/doc/migration-method-full-path.md", "apps/miniprogram - /345/211/257/346/234/254/doc/migration-tracker.md", "apps/miniprogram - /345/211/257/346/234/254/doc/prd.md", "apps/miniprogram - /345/211/257/346/234/254/doc/shared-component-specs.md", "apps/miniprogram - /345/211/257/346/234/254/i18n/base.json", "docs/database/README.md", "docs/database/_archived/BD_Manual_20260301_cleanup_and_fixes.md", "docs/database/_archived/BD_Manual_biz_date_function_and_mv_rebuild.md", "docs/database/_archived/BD_Manual_fix_dim_staff_ex_rankname.md", "docs/database/_archived/BD_Manual_fix_dws_assistant_daily_table_area.md", "docs/database/_archived/BD_Manual_tenant_id_int_to_bigint.md", "scripts/ops/reorganize_bd_manuals.py" ], "high_risk_files": [ "apps/backend/app/ai/apps/app1_chat.py", "apps/backend/app/ai/apps/app2_finance.py", "apps/backend/app/ai/apps/app3_clue.py", "apps/backend/app/ai/apps/app4_analysis.py", "apps/backend/app/ai/apps/app5_tactics.py", "apps/backend/app/ai/apps/app6_note.py", "apps/backend/app/ai/apps/app7_customer.py", "apps/backend/app/ai/apps/app8_consolidation.py", "apps/backend/app/ai/dispatcher.py", "apps/backend/app/ai/prompts/app2_finance_prompt.py", "apps/backend/app/ai/prompts/app8_consolidation_prompt.py", "apps/backend/app/main.py", "apps/backend/app/routers/xcx_ai_cache.py", "apps/backend/app/routers/xcx_ai_chat.py", "apps/backend/app/schemas/xcx_tasks.py", "apps/backend/app/services/note_reclassifier.py", "apps/backend/app/services/note_service.py", "apps/backend/app/services/recall_detector.py", "apps/backend/app/services/task_executor.py", "apps/backend/app/services/task_manager.py", "apps/backend/app/services/task_queue.py", "apps/backend/app/services/task_registry.py", "apps/backend/app/services/trigger_scheduler.py", "apps/etl/connectors/feiqiu/database/connection.py", "apps/etl/connectors/feiqiu/database/operations.py", "apps/etl/connectors/feiqiu/tasks/dws/assistant_order_contribution_task.py", "apps/etl/connectors/feiqiu/tasks/dws/assistant_project_tag_task.py", "apps/etl/connectors/feiqiu/tasks/dws/base_dws_task.py", "apps/etl/connectors/feiqiu/tasks/dws/member_project_tag_task.py" ], "session_diff": { "added": [ "apps/etl/connectors/feiqiu/docs/database/DWD/changes/BD_Manual_dim_groupbuy_package_ex_detail_fields.md", "apps/etl/connectors/feiqiu/docs/database/DWS/changes/BD_Manual_dws_assistant_order_contribution.md", "apps/etl/connectors/feiqiu/docs/database/DWS/changes/BD_Manual_dws_goods_stock_summary.md", "apps/etl/connectors/feiqiu/docs/database/DWS/changes/BD_Manual_dws_member_spending_power_index.md", "apps/etl/connectors/feiqiu/docs/database/DWS/changes/BD_Manual_dws_project_tags.md", "apps/etl/connectors/feiqiu/docs/database/cross_layer/BD_Manual_assistant_accounts_master.md", "apps/etl/connectors/feiqiu/docs/database/cross_layer/BD_Manual_assistant_service_records.md", "apps/etl/connectors/feiqiu/docs/database/cross_layer/BD_Manual_goods_stock_movements.md", "apps/etl/connectors/feiqiu/docs/database/cross_layer/BD_Manual_goods_stock_summary.md", "apps/etl/connectors/feiqiu/docs/database/cross_layer/BD_Manual_goods_stock_warning_info.md", "apps/etl/connectors/feiqiu/docs/database/cross_layer/BD_Manual_group_buy_package_details.md", "apps/etl/connectors/feiqiu/docs/database/cross_layer/BD_Manual_member_balance_changes.md", "apps/etl/connectors/feiqiu/docs/database/cross_layer/BD_Manual_recharge_settlements.md", "apps/etl/connectors/feiqiu/docs/database/cross_layer/BD_Manual_site_tables_master.md", "apps/etl/connectors/feiqiu/docs/database/cross_layer/BD_Manual_store_goods_master.md", "apps/etl/connectors/feiqiu/docs/database/cross_layer/BD_Manual_store_goods_sales_records.md", "apps/etl/connectors/feiqiu/docs/database/cross_layer/BD_Manual_tenant_goods_master.md", "docs/audit/prompt_logs/prompt_log_20260315_095422.md", "docs/audit/session_logs/2026-03/15/17_1fc8e375_033312/main_01_d10f49f0.md", "docs/audit/session_logs/2026-03/15/17_1fc8e375_033312/sub_01_d10f49f0.md", "docs/audit/session_logs/2026-03/15/17_1fc8e375_033312/sub_02_d10f49f0.md", "docs/database/_archived/BD_Manual_20260301_cleanup_and_fixes.md", "docs/database/_archived/BD_Manual_biz_date_function_and_mv_rebuild.md", "docs/database/_archived/BD_Manual_fix_dim_staff_ex_rankname.md", "docs/database/_archived/BD_Manual_fix_dws_assistant_daily_table_area.md", "docs/database/_archived/BD_Manual_tenant_id_int_to_bigint.md", "scripts/ops/reorganize_bd_manuals.py" ], "modified": [ "apps/etl/connectors/feiqiu/docs/database/README.md", "docs/audit/session_logs/2026-02/11/_day_index.json", "docs/audit/session_logs/2026-02/11/_day_index_full.json", "docs/audit/session_logs/2026-02/12/_day_index.json", "docs/audit/session_logs/2026-02/12/_day_index_full.json", "docs/audit/session_logs/2026-02/13/_day_index.json", "docs/audit/session_logs/2026-02/13/_day_index_full.json", "docs/audit/session_logs/2026-02/14/_day_index.json", "docs/audit/session_logs/2026-02/14/_day_index_full.json", "docs/audit/session_logs/2026-02/15/_day_index.json", "docs/audit/session_logs/2026-02/15/_day_index_full.json", "docs/audit/session_logs/2026-02/16/_day_index.json", "docs/audit/session_logs/2026-02/16/_day_index_full.json", "docs/audit/session_logs/2026-02/17/_day_index.json", "docs/audit/session_logs/2026-02/17/_day_index_full.json", "docs/audit/session_logs/2026-02/18/_day_index.json", "docs/audit/session_logs/2026-02/18/_day_index_full.json", "docs/audit/session_logs/2026-02/19/_day_index.json", "docs/audit/session_logs/2026-02/19/_day_index_full.json", "docs/audit/session_logs/2026-02/20/_day_index.json", "docs/audit/session_logs/2026-02/20/_day_index_full.json", "docs/audit/session_logs/2026-02/21/_day_index.json", "docs/audit/session_logs/2026-02/21/_day_index_full.json", "docs/audit/session_logs/2026-02/22/_day_index.json", "docs/audit/session_logs/2026-02/22/_day_index_full.json", "docs/audit/session_logs/2026-02/23/_day_index.json", "docs/audit/session_logs/2026-02/23/_day_index_full.json", "docs/audit/session_logs/2026-02/24/_day_index.json", "docs/audit/session_logs/2026-02/24/_day_index_full.json", "docs/audit/session_logs/2026-02/25/_day_index.json", "docs/audit/session_logs/2026-02/25/_day_index_full.json", "docs/audit/session_logs/2026-02/26/_day_index.json", "docs/audit/session_logs/2026-02/26/_day_index_full.json", "docs/audit/session_logs/2026-02/27/_day_index.json", "docs/audit/session_logs/2026-02/27/_day_index_full.json", "docs/audit/session_logs/2026-02/28/_day_index.json", "docs/audit/session_logs/2026-02/28/_day_index_full.json", "docs/audit/session_logs/2026-03/01/_day_index.json", "docs/audit/session_logs/2026-03/01/_day_index_full.json", "docs/audit/session_logs/2026-03/02/_day_index.json", "docs/audit/session_logs/2026-03/02/_day_index_full.json", "docs/audit/session_logs/2026-03/03/_day_index.json", "docs/audit/session_logs/2026-03/03/_day_index_full.json", "docs/audit/session_logs/2026-03/04/_day_index.json", "docs/audit/session_logs/2026-03/04/_day_index_full.json", "docs/audit/session_logs/2026-03/05/_day_index.json", "docs/audit/session_logs/2026-03/05/_day_index_full.json", "docs/audit/session_logs/2026-03/06/_day_index.json", "docs/audit/session_logs/2026-03/06/_day_index_full.json", "docs/audit/session_logs/2026-03/07/_day_index.json" ], "deleted": [ "docs/audit/session_logs/2026-03/15/17_1fc8e375_033312/main_01_64957242.md", "docs/database/BD_Manual_20260301_cleanup_and_fixes.md", "docs/database/BD_Manual_assistant_accounts_master.md", "docs/database/BD_Manual_assistant_service_records.md", "docs/database/BD_Manual_biz_date_function_and_mv_rebuild.md", "docs/database/BD_Manual_dim_groupbuy_package_ex_detail_fields.md", "docs/database/BD_Manual_dws_assistant_order_contribution.md", "docs/database/BD_Manual_dws_goods_stock_summary.md", "docs/database/BD_Manual_dws_member_spending_power_index.md", "docs/database/BD_Manual_dws_project_tags.md", "docs/database/BD_Manual_fix_dim_staff_ex_rankname.md", "docs/database/BD_Manual_fix_dws_assistant_daily_table_area.md", "docs/database/BD_Manual_goods_stock_movements.md", "docs/database/BD_Manual_goods_stock_summary.md", "docs/database/BD_Manual_goods_stock_warning_info.md", "docs/database/BD_Manual_group_buy_package_details.md", "docs/database/BD_Manual_member_balance_changes.md", "docs/database/BD_Manual_recharge_settlements.md", "docs/database/BD_Manual_site_tables_master.md", "docs/database/BD_Manual_store_goods_master.md", "docs/database/BD_Manual_store_goods_sales_records.md", "docs/database/BD_Manual_tenant_goods_master.md", "docs/database/BD_Manual_tenant_id_int_to_bigint.md" ] }, "compliance": { "code_without_docs": [], "new_migration_sql": [], "has_bd_manual": false, "has_audit_record": false, "has_ddl_baseline": false, "api_changed": false, "openapi_spec_stale": false }, "diff_stat": ".kiro/agents/audit-writer.md | 2 +-\n .kiro/settings/mcp.json | 12 +-\n .kiro/specs/05-miniapp-ai-integration/tasks.md | 80 +-\n .kiro/specs/h5-miniprogram-migration/tasks.md | 425 +-\n .kiro/state/.audit_context.json | 234 +-\n .kiro/state/.audit_state.json | 101 +-\n .kiro/state/.compliance_state.json | 2 +-\n .kiro/state/.file_baseline.json | 2 +-\n .kiro/state/.last_prompt_id.json | 4 +-\n .kiro/steering/doc-map.md | 3 +-\n .kiro/steering/dwd-doc-authority.md | 2 +\n .kiro/steering/export-paths.md | 4 +-\n .kiro/steering/miniprogram-h5-conversion.md | 152 -\n .kiro/steering/pre-change-research.md | 2 +-\n .kiro/steering/weixin-devtools-mcp.md | 29 -\n apps/backend/app/main.py | 25 +-\n apps/backend/app/schemas/xcx_tasks.py | 2 +\n apps/backend/app/services/note_reclassifier.py | 242 +-\n apps/backend/app/services/note_service.py | 59 +-\n apps/backend/app/services/recall_detector.py | 21 +-\n apps/backend/app/services/task_executor.py | 55 +-\n apps/backend/app/services/task_manager.py | 24 +-\n apps/backend/app/services/task_queue.py | 88 +\n apps/backend/app/services/task_registry.py | 3 +\n apps/backend/app/services/trigger_scheduler.py | 46 +-\n apps/backend/pyproject.toml | 1 +\n apps/backend/tests/test_task_queue.py | 37 +-\n apps/etl/connectors/feiqiu/.env | 2 +-\n apps/etl/connectors/feiqiu/database/connection.py | 5 +-\n apps/etl/connectors/feiqiu/database/operations.py | 6 +-\n .../DWD/main/BD_manual_dwd_settlement_head.md | 8 +-\n .../DWS/main/BD_manual_cfg_area_category.md | 139 +-\n .../main/BD_manual_dws_finance_income_structure.md | 21 +-\n apps/etl/connectors/feiqiu/docs/database/README.md | 12 +-\n .../connectors/feiqiu/docs/etl_tasks/dws_tasks.md | 10 +-\n .../tasks/dws/assistant_order_contribution_task.py | 9 +-\n .../feiqiu/tasks/dws/assistant_project_tag_task.py | 3 +-\n .../connectors/feiqiu/tasks/dws/base_dws_task.py | 21 +-\n .../feiqiu/tasks/dws/member_project_tag_task.py | 3 +-\n .../.gitignore\" | 2 -\n .../.gitkeep\" | 0\n .../README.md\" | 202 -\n .../doc/auth-integration-guide.md\" | 214 -\n .../doc/h5-input-material-guide.md\" | 404 -\n .../doc/h5-to-miniprogram-pitfalls.md\" | 476 -\n .../doc/howtodo.md\" | 97 -\n .../doc/migration-guide.md\" | 560 -\n .../doc/migration-method-full-path.md\" | 337 -\n .../doc/migration-tracker.md\" | 65 -\n .../doc/prd.md\" | 997 -\n .../doc/shared-component-specs.md\" | 114 -\n .../i18n/base.json\" | 11 -\n .../jest.config.js\" | 12 -\n .../miniprogram/app.json\" | 65 -\n .../miniprogram/app.miniapp.json\" | 5 -\n .../miniprogram/app.ts\" | 70 -\n .../miniprogram/app.wxss\" | 111 -\n .../miniprogram/assets/icons/ai-robot.svg\" | 21 -\n .../miniprogram/assets/icons/arrow-left.svg\" | 3 -\n .../miniprogram/assets/icons/chart.svg\" | 3 -\n .../miniprogram/assets/icons/chat-gray.svg\" | 3 -\n .../miniprogram/assets/icons/chat.svg\" | 3 -\n .../miniprogram/assets/icons/check-bold.svg\" | 3 -\n .../miniprogram/assets/icons/check-circle.svg\" | 3 -\n .../miniprogram/assets/icons/clock.svg\" | 4 -\n .../miniprogram/assets/icons/forbidden.svg\" | 4 -\n .../miniprogram/assets/icons/help-circle.svg\" | 3 -\n .../miniprogram/assets/icons/icon-ai-float.png\" | Bin 70 -> 0 bytes\n .../miniprogram/assets/icons/icon-ai-inline.png\" | Bin 70 -> 0 bytes\n .../assets/icons/icon-clock-circle.svg\" | 4 -\n .../miniprogram/assets/icons/icon-forbidden.svg\" | 4 -\n .../miniprogram/assets/icons/icon-wechat.svg\" | 3 -\n .../miniprogram/assets/icons/info-circle.svg\" | 3 -\n .../miniprogram/assets/icons/info-error.svg\" | 3 -\n .../miniprogram/assets/icons/info-warning.svg\" | 3 -\n .../miniprogram/assets/icons/logo-billiard.svg\" | 5 -\n .../miniprogram/assets/icons/logout.svg\" | 5 -\n .../miniprogram/assets/icons/tab-board-active.png\" | Bin 66 -> 0 bytes\n .../assets/icons/tab-board-nav-active.svg\" | 5 -\n .../miniprogram/assets/icons/tab-board-nav.svg\" | 5 -\n .../miniprogram/assets/icons/tab-board.png\" | Bin 66 -> 0 bytes\n .../miniprogram/assets/icons/tab-my-active.png\" | Bin 66 -> 0 bytes\n .../assets/icons/tab-my-nav-active.svg\" | 4 -\n .../miniprogram/assets/icons/tab-my-nav.svg\" | 4 -\n .../miniprogram/assets/icons/tab-my.png\" | Bin 66 -> 0 bytes\n .../miniprogram/assets/icons/tab-task-active.png\" | Bin 66 -> 0 bytes\n .../assets/icons/tab-task-nav-active.svg\" | 5 -\n .../miniprogram/assets/icons/tab-task-nav.svg\" | 5 -\n .../miniprogram/assets/icons/tab-task.png\" | Bin 66 -> 0 bytes\n .../miniprogram/assets/icons/task.svg\" | 3 -\n .../miniprogram/assets/icons/wechat.svg\" | 3 -\n .../miniprogram/assets/images/banner-blue.png\" | Bin 70 -> 0 bytes\n .../ai-float-button/ai-float-button.json\" | 4 -\n .../components/ai-float-button/ai-float-button.ts\" | 39 -\n .../ai-float-button/ai-float-button.wxml\" | 14 -\n .../ai-float-button/ai-float-button.wxss\" | 57 -\n .../miniprogram/components/banner/banner.json\" | 4 -\n .../miniprogram/components/banner/banner.ts\" | 26 -\n .../miniprogram/components/banner/banner.wxml\" | 12 -\n .../miniprogram/components/banner/banner.wxss\" | 66 -\n .../components/board-tab-bar/board-tab-bar.json\" | 4 -\n .../components/board-tab-bar/board-tab-bar.ts\" | 25 -\n .../components/board-tab-bar/board-tab-bar.wxml\" | 39 -\n .../components/board-tab-bar/board-tab-bar.wxss\" | 39 -\n .../miniprogram/components/dev-fab/dev-fab.json\" | 3 -\n .../miniprogram/components/dev-fab/dev-fab.ts\" | 28 -\n .../miniprogram/components/dev-fab/dev-fab.wxml\" | 15 -\n .../miniprogram/components/dev-fab/dev-fab.wxss\" | 29 -\n .../filter-dropdown/filter-dropdown.json\" | 6 -\n .../components/filter-dropdown/filter-dropdown.ts\" | 83 -\n .../filter-dropdown/filter-dropdown.wxml\" | 27 -\n .../filter-dropdown/filter-dropdown.wxss\" | 107 -\n .../components/heart-icon/heart-icon.json\" | 4 -\n .../components/heart-icon/heart-icon.ts\" | 30 -\n .../components/heart-icon/heart-icon.wxml\" | 1 -\n .../components/heart-icon/heart-icon.wxss\" | 6 -\n .../components/hobby-tag/hobby-tag.json\" | 4 -\n .../miniprogram/components/hobby-tag/hobby-tag.ts\" | 36 -\n .../components/hobby-tag/hobby-tag.wxml\" | 4 -\n .../components/hobby-tag/hobby-tag.wxss\" | 19 -\n .../components/metric-card/metric-card.json\" | 6 -\n .../components/metric-card/metric-card.ts\" | 67 -\n .../components/metric-card/metric-card.wxml\" | 22 -\n .../components/metric-card/metric-card.wxss\" | 82 -\n .../components/note-modal/note-modal.json\" | 9 -\n .../components/note-modal/note-modal.ts\" | 79 -\n .../components/note-modal/note-modal.wxml\" | 46 -\n .../components/note-modal/note-modal.wxss\" | 68 -\n .../components/star-rating/star-rating.json\" | 6 -\n .../components/star-rating/star-rating.ts\" | 38 -\n .../components/star-rating/star-rating.wxml\" | 7 -\n .../components/star-rating/star-rating.wxss\" | 3 -\n .../miniprogram/i18n/base.json\" | 11 -\n .../miniprogram/pages/apply/apply.json\" | 8 -\n .../miniprogram/pages/apply/apply.ts\" | 146 -\n .../miniprogram/pages/apply/apply.wxml\" | 107 -\n .../miniprogram/pages/apply/apply.wxss\" | 271 -\n .../pages/board-coach/board-coach.json\" | 13 -\n .../miniprogram/pages/board-coach/board-coach.ts\" | 263 -\n .../pages/board-coach/board-coach.wxml\" | 153 -\n .../pages/board-coach/board-coach.wxss\" | 330 -\n .../pages/board-customer/board-customer.json\" | 14 -\n .../pages/board-customer/board-customer.ts\" | 300 -\n .../pages/board-customer/board-customer.wxml\" | 301 -\n .../pages/board-customer/board-customer.wxss\" | 637 -\n .../pages/board-finance/board-finance.json\" | 12 -\n .../pages/board-finance/board-finance.ts\" | 345 -\n .../pages/board-finance/board-finance.wxml\" | 734 -\n .../pages/board-finance/board-finance.wxss\" | 1237 -\n .../pages/chat-history/chat-history.json\" | 10 -\n .../pages/chat-history/chat-history.ts\" | 76 -\n .../pages/chat-history/chat-history.wxml\" | 48 -\n .../pages/chat-history/chat-history.wxss\" | 89 -\n .../miniprogram/pages/chat/chat.json\" | 7 -\n .../miniprogram/pages/chat/chat.ts\" | 168 -\n .../miniprogram/pages/chat/chat.wxml\" | 125 -\n .../miniprogram/pages/chat/chat.wxss\" | 289 -\n .../pages/coach-detail/coach-detail.json\" | 12 -\n .../pages/coach-detail/coach-detail.ts\" | 217 -\n .../pages/coach-detail/coach-detail.wxml\" | 142 -\n .../pages/coach-detail/coach-detail.wxss\" | 431 -\n .../pages/customer-detail/customer-detail.json\" | 12 -\n .../pages/customer-detail/customer-detail.ts\" | 161 -\n .../pages/customer-detail/customer-detail.wxml\" | 215 -\n .../pages/customer-detail/customer-detail.wxss\" | 680 -\n .../customer-service-records.json\" | 10 -\n .../customer-service-records.ts\" | 223 -\n .../customer-service-records.wxml\" | 111 -\n .../customer-service-records.wxss\" | 283 -\n .../miniprogram/pages/dev-tools/dev-tools.json\" | 4 -\n .../miniprogram/pages/dev-tools/dev-tools.ts\" | 182 -\n .../miniprogram/pages/dev-tools/dev-tools.wxml\" | 109 -\n .../miniprogram/pages/dev-tools/dev-tools.wxss\" | 171 -\n .../miniprogram/pages/index/index.js\" | 66 -\n .../miniprogram/pages/index/index.json\" | 5 -\n .../miniprogram/pages/index/index.ts\" | 54 -\n .../miniprogram/pages/index/index.wxml\" | 30 -\n .../miniprogram/pages/index/index.wxss\" | 62 -\n .../miniprogram/pages/login/login.json\" | 8 -\n .../miniprogram/pages/login/login.ts\" | 112 -\n .../miniprogram/pages/login/login.wxml\" | 74 -\n .../miniprogram/pages/login/login.wxss\" | 281 -\n .../miniprogram/pages/mvp/mvp.json\" | 4 -\n .../miniprogram/pages/mvp/mvp.ts\" | 45 -\n .../miniprogram/pages/mvp/mvp.wxml\" | 19 -\n .../miniprogram/pages/mvp/mvp.wxss\" | 48 -\n .../miniprogram/pages/my-profile/my-profile.json\" | 7 -\n .../miniprogram/pages/my-profile/my-profile.ts\" | 31 -\n .../miniprogram/pages/my-profile/my-profile.wxml\" | 52 -\n .../miniprogram/pages/my-profile/my-profile.wxss\" | 102 -\n .../pages/no-permission/no-permission.json\" | 7 -\n .../pages/no-permission/no-permission.ts\" | 74 -\n .../pages/no-permission/no-permission.wxml\" | 67 -\n .../pages/no-permission/no-permission.wxss\" | 241 -\n .../miniprogram/pages/notes/notes.json\" | 8 -\n .../miniprogram/pages/notes/notes.ts\" | 45 -\n .../miniprogram/pages/notes/notes.wxml\" | 45 -\n .../miniprogram/pages/notes/notes.wxss\" | 130 -\n .../performance-records/performance-records.json\" | 9 -\n .../performance-records/performance-records.ts\" | 189 -\n .../performance-records/performance-records.wxml\" | 104 -\n .../performance-records/performance-records.wxss\" | 347 -\n .../pages/performance/performance.json\" | 9 -\n .../miniprogram/pages/performance/performance.ts\" | 179 -\n .../pages/performance/performance.wxml\" | 272 -\n .../pages/performance/performance.wxss\" | 657 -\n .../miniprogram/pages/reviewing/reviewing.json\" | 9 -\n .../miniprogram/pages/reviewing/reviewing.ts\" | 99 -\n .../miniprogram/pages/reviewing/reviewing.wxml\" | 116 -\n .../miniprogram/pages/reviewing/reviewing.wxss\" | 427 -\n .../task-detail-callback.json\" | 11 -\n .../task-detail-callback/task-detail-callback.ts\" | 94 -\n .../task-detail-callback.wxml\" | 203 -\n .../task-detail-callback.wxss\" | 458 -\n .../task-detail-priority.json\" | 11 -\n .../task-detail-priority/task-detail-priority.ts\" | 92 -\n .../task-detail-priority.wxml\" | 204 -\n .../task-detail-priority.wxss\" | 459 -\n .../task-detail-relationship.json\" | 11 -\n .../task-detail-relationship.ts\" | 92 -\n .../task-detail-relationship.wxml\" | 193 -\n .../task-detail-relationship.wxss\" | 443 -\n .../pages/task-detail/task-detail.json\" | 11 -\n .../miniprogram/pages/task-detail/task-detail.ts\" | 104 -\n .../pages/task-detail/task-detail.wxml\" | 125 -\n .../pages/task-detail/task-detail.wxss\" | 288 -\n .../miniprogram/pages/task-list/task-list.json\" | 10 -\n .../miniprogram/pages/task-list/task-list.ts\" | 123 -\n .../miniprogram/pages/task-list/task-list.wxml\" | 88 -\n .../miniprogram/pages/task-list/task-list.wxss\" | 173 -\n .../miniprogram/utils/chat.ts\" | 49 -\n .../miniprogram/utils/config.ts\" | 26 -\n .../miniprogram/utils/filter.ts\" | 23 -\n .../miniprogram/utils/format.wxs\" | 9 -\n .../miniprogram/utils/heart.ts\" | 21 -\n .../miniprogram/utils/mock-data.ts\" | 624 -\n .../miniprogram/utils/rating.ts\" | 24 -\n .../miniprogram/utils/render.ts\" | 21 -\n .../miniprogram/utils/request.ts\" | 204 -\n .../miniprogram/utils/router.ts\" | 45 -\n .../miniprogram/utils/sort.ts\" | 52 -\n .../miniprogram/utils/task.ts\" | 36 -\n .../miniprogram/utils/util.ts\" | 19 -\n .../package-lock.json\" | 4651 -\n .../package.json\" | 19 -\n .../project.config.json\" | 58 -\n .../project.miniapp.json\" | 68 -\n .../project.private.config.json\" | 24 -\n .../tsconfig.json\" | 34 -\n .../tsconfig.test.json\" | 10 -\n .../typings/index.d.ts\" | 22 -\n .../typings/types/index.d.ts\" | 1 -\n .../typings/types/wx/index.d.ts\" | 74 -\n .../typings/types/wx/lib.wx.api.d.ts\" | 19671 -\n .../typings/types/wx/lib.wx.app.d.ts\" | 270 -\n .../typings/types/wx/lib.wx.behavior.d.ts\" | 68 -\n .../typings/types/wx/lib.wx.cloud.d.ts\" | 924 -\n .../typings/types/wx/lib.wx.component.d.ts\" | 636 -\n .../typings/types/wx/lib.wx.event.d.ts\" | 1435 -\n .../typings/types/wx/lib.wx.miniapp.d.ts\" | 2606 -\n .../typings/types/wx/lib.wx.page.d.ts\" | 259 -\n apps/miniprogram/README.md | 4 +-\n apps/miniprogram/miniprogram/app.json | 21 +-\n apps/miniprogram/miniprogram/app.wxss | 173 +\n .../miniprogram/assets/icons/arrow-left.svg | 3 -\n .../miniprogram/miniprogram/assets/icons/chart.svg | 3 -\n .../miniprogram/assets/icons/chat-gray.svg | 3 -\n apps/miniprogram/miniprogram/assets/icons/chat.svg | 3 -\n .../miniprogram/assets/icons/check-bold.svg | 3 -\n .../miniprogram/assets/icons/check-circle.svg | 3 -\n .../miniprogram/miniprogram/assets/icons/clock.svg | 4 -\n .../miniprogram/assets/icons/forbidden.svg | 4 -\n .../miniprogram/assets/icons/help-circle.svg | 3 -\n .../miniprogram/assets/icons/icon-ai-float.png | Bin 70 -> 0 bytes\n .../miniprogram/assets/icons/icon-ai-inline.png | Bin 70 -> 0 bytes\n .../miniprogram/assets/icons/info-circle.svg | 3 -\n .../miniprogram/assets/icons/info-error.svg | 3 -\n .../miniprogram/assets/icons/info-warning.svg | 3 -\n .../miniprogram/assets/icons/logout.svg | 5 -\n .../miniprogram/assets/icons/tab-board-active.png | Bin 66 -> 0 bytes\n .../assets/icons/tab-board-nav-active.svg | 2 +-\n .../miniprogram/assets/icons/tab-board-nav.svg | 2 +-\n .../miniprogram/assets/icons/tab-board.png | Bin 66 -> 0 bytes\n .../miniprogram/assets/icons/tab-my-active.png | Bin 66 -> 0 bytes\n .../miniprogram/assets/icons/tab-my-nav-active.svg | 2 +-\n .../miniprogram/assets/icons/tab-my-nav.svg | 2 +-\n .../miniprogram/assets/icons/tab-my.png | Bin 66 -> 0 bytes\n .../miniprogram/assets/icons/tab-task-active.png | Bin 66 -> 0 bytes\n .../assets/icons/tab-task-nav-active.svg | 2 +-\n .../miniprogram/assets/icons/tab-task-nav.svg | 2 +-\n .../miniprogram/assets/icons/tab-task.png | Bin 66 -> 0 bytes\n apps/miniprogram/miniprogram/assets/icons/task.svg | 3 -\n .../miniprogram/assets/icons/wechat.svg | 3 -\n .../miniprogram/assets/images/banner-blue.png | Bin 70 -> 0 bytes\n .../ai-float-button/ai-float-button.wxss | 3 +-\n .../filter-dropdown/filter-dropdown.wxml | 2 +-\n .../filter-dropdown/filter-dropdown.wxss | 21 +-\n .../components/heart-icon/heart-icon.ts | 12 +-\n .../components/note-modal/note-modal.ts | 219 +-\n .../components/note-modal/note-modal.wxml | 95 +-\n .../components/note-modal/note-modal.wxss | 203 +-\n .../components/star-rating/star-rating.ts | 4 +-\n .../components/star-rating/star-rating.wxml | 2 +\n .../components/star-rating/star-rating.wxss | 1 +\n apps/miniprogram/miniprogram/pages/apply/apply.ts | 2 +-\n .../miniprogram/pages/board-coach/board-coach.json | 7 +-\n .../miniprogram/pages/board-coach/board-coach.ts | 8 +-\n .../miniprogram/pages/board-coach/board-coach.wxml | 11 +-\n .../miniprogram/pages/board-coach/board-coach.wxss | 84 +-\n .../pages/board-customer/board-customer.json | 5 +-\n .../pages/board-customer/board-customer.ts | 22 +-\n .../pages/board-customer/board-customer.wxml | 21 +-\n .../pages/board-customer/board-customer.wxss | 217 +-\n .../pages/board-finance/board-finance.json | 2 +\n .../pages/board-finance/board-finance.ts | 186 +-\n .../pages/board-finance/board-finance.wxml | 175 +-\n .../pages/board-finance/board-finance.wxss | 696 +-\n .../pages/chat-history/chat-history.json | 5 +-\n .../miniprogram/pages/chat-history/chat-history.ts | 46 +-\n .../pages/chat-history/chat-history.wxml | 27 +-\n .../pages/chat-history/chat-history.wxss | 96 +-\n apps/miniprogram/miniprogram/pages/chat/chat.json | 5 +-\n apps/miniprogram/miniprogram/pages/chat/chat.ts | 62 +-\n apps/miniprogram/miniprogram/pages/chat/chat.wxml | 12 +\n apps/miniprogram/miniprogram/pages/chat/chat.wxss | 86 +-\n .../pages/coach-detail/coach-detail.json | 6 +-\n .../miniprogram/pages/coach-detail/coach-detail.ts | 280 +-\n .../pages/coach-detail/coach-detail.wxml | 245 +-\n .../pages/coach-detail/coach-detail.wxss | 752 +-\n .../pages/customer-detail/customer-detail.json | 3 +\n .../pages/customer-detail/customer-detail.ts | 182 +-\n .../pages/customer-detail/customer-detail.wxml | 210 +-\n .../pages/customer-detail/customer-detail.wxss | 408 +-\n .../customer-service-records.json | 3 +\n .../customer-service-records.ts | 7 +-\n .../customer-service-records.wxml | 42 +-\n .../customer-service-records.wxss | 76 +-\n .../miniprogram/pages/dev-tools/dev-tools.ts | 16 +-\n apps/miniprogram/miniprogram/pages/index/index.js | 66 -\n .../miniprogram/miniprogram/pages/index/index.json | 5 -\n apps/miniprogram/miniprogram/pages/index/index.ts | 54 -\n .../miniprogram/miniprogram/pages/index/index.wxml | 30 -\n .../miniprogram/miniprogram/pages/index/index.wxss | 62 -\n apps/miniprogram/miniprogram/pages/login/login.ts | 4 +-\n apps/miniprogram/miniprogram/pages/logs/logs.js | 66 -\n apps/miniprogram/miniprogram/pages/logs/logs.json | 4 -\n apps/miniprogram/miniprogram/pages/logs/logs.ts | 21 -\n apps/miniprogram/miniprogram/pages/logs/logs.wxml | 8 -\n apps/miniprogram/miniprogram/pages/logs/logs.wxss | 16 -\n apps/miniprogram/miniprogram/pages/mvp/mvp.json | 4 -\n apps/miniprogram/miniprogram/pages/mvp/mvp.ts | 45 -\n apps/miniprogram/miniprogram/pages/mvp/mvp.wxml | 19 -\n apps/miniprogram/miniprogram/pages/mvp/mvp.wxss | 48 -\n .../miniprogram/pages/my-profile/my-profile.json | 3 +-\n .../miniprogram/pages/my-profile/my-profile.ts | 7 +\n .../miniprogram/pages/my-profile/my-profile.wxml | 22 +-\n .../miniprogram/pages/my-profile/my-profile.wxss | 42 +-\n .../pages/no-permission/no-permission.ts | 2 +-\n .../miniprogram/miniprogram/pages/notes/notes.json | 11 +-\n apps/miniprogram/miniprogram/pages/notes/notes.ts | 38 +-\n .../miniprogram/miniprogram/pages/notes/notes.wxml | 70 +-\n .../miniprogram/miniprogram/pages/notes/notes.wxss | 183 +-\n .../performance-records/performance-records.json | 3 +\n .../performance-records/performance-records.ts | 140 +-\n .../performance-records/performance-records.wxml | 31 +-\n .../performance-records/performance-records.wxss | 75 +\n .../miniprogram/pages/performance/performance.json | 3 +\n .../miniprogram/pages/performance/performance.ts | 35 +-\n .../miniprogram/pages/performance/performance.wxml | 66 +-\n .../miniprogram/pages/performance/performance.wxss | 394 +-\n .../miniprogram/pages/reviewing/reviewing.ts | 2 +-\n .../task-detail-callback/task-detail-callback.json | 11 -\n .../task-detail-callback/task-detail-callback.ts | 94 -\n .../task-detail-callback/task-detail-callback.wxml | 203 -\n .../task-detail-callback/task-detail-callback.wxss | 458 -\n .../task-detail-priority/task-detail-priority.json | 11 -\n .../task-detail-priority/task-detail-priority.ts | 92 -\n .../task-detail-priority/task-detail-priority.wxml | 204 -\n .../task-detail-priority/task-detail-priority.wxss | 459 -\n .../task-detail-relationship.json | 11 -\n .../task-detail-relationship.ts | 92 -\n .../task-detail-relationship.wxml | 193 -\n .../task-detail-relationship.wxss | 443 -\n .../miniprogram/pages/task-detail/task-detail.json | 6 +-\n .../miniprogram/pages/task-detail/task-detail.ts | 340 +-\n .../miniprogram/pages/task-detail/task-detail.wxml | 254 +-\n .../miniprogram/pages/task-detail/task-detail.wxss | 568 +-\n .../miniprogram/pages/task-list/task-list.json | 9 +-\n .../miniprogram/pages/task-list/task-list.ts | 393 +-\n .../miniprogram/pages/task-list/task-list.wxml | 343 +-\n .../miniprogram/pages/task-list/task-list.wxss | 983 +-\n apps/miniprogram/miniprogram/utils/mock-data.ts | 138 +-\n apps/miniprogram/project.config.json | 11 +-\n apps/miniprogram/project.private.config.json | 2 +-\n db/README.md | 24 +-\n ...4__alter_assistant_daily_add_penalty_fields.sql | 67 -\n ...lter_member_consumption_add_recharge_fields.sql | 89 -\n ...24__create_dws_assistant_order_contribution.sql | 89 -\n ...reate_rls_view_assistant_order_contribution.sql | 34 -\n .../2026-02-22__add_staff_info_tables.sql | 139 -\n ...2-23_create_dws_member_spending_power_index.sql | 70 -\n .../2026-02-24__add_goods_stock_warning_info.sql | 78 -\n ...6-02-24__cleanup_assistant_abolish_residual.sql | 21 -\n .../2026-02-24__p1_create_app_schema_rls_views.sql | 196 -\n .../2026-02-27__add_biz_date_function.sql | 34 -\n ...7__fix_order_contribution_site_id_to_bigint.sql | 22 -\n .../2026-02-27__fix_spi_site_id_to_bigint.sql | 14 -\n .../2026-02-27__rebuild_mv_with_biz_date.sql | 138 -\n .../2026-03-03__alter_tenant_id_int_to_bigint.sql | 63 -\n ...dd_detail_fields_to_dim_groupbuy_package_ex.sql | 27 -\n ..._cfg_area_category_add_fields_and_remap_vip.sql | 102 -\n .../2026-03-07__create_project_tag_tables.sql | 95 -\n .../2026-03-07__unify_card_pay_field_names.sql | 67 -\n .../20260301_dws_numeric_precision_fix.sql | 58 -\n ...20260301_dws_numeric_precision_fix_rollback.sql | 28 -\n ...20260301_ods_goods_stock_summary_add_siteid.sql | 22 -\n db/etl_feiqiu/seeds/seed_dws_config.sql | 334 -\n db/etl_feiqiu/seeds/seed_index_parameters.sql | 244 -\n db/etl_feiqiu/seeds/seed_ods_tasks.sql | 38 -\n db/etl_feiqiu/seeds/seed_scheduler_tasks.sql | 68 -\n .../2026-02-24__p1_create_auth_biz_schemas.sql | 53 -\n .../migrations/2026-02-24__p1_setup_fdw_etl.sql | 71 -\n .../2026-02-25__p3_create_auth_tables.sql | 280 -\n .../2026-02-25__p3_seed_roles_permissions.sql | 75 -\n ...-02-26__refactor_birthday_to_retention_clue.sql | 99 -\n .../2026-02-27__add_new_to_users_status_check.sql | 21 -\n .../2026-02-27__add_source_to_retention_clue.sql | 52 -\n .../2026-02-27__p4_create_biz_tables.sql | 107 -\n .../2026-02-27__p4_seed_trigger_jobs.sql | 43 -\n .../2026-03-03__alter_tenant_id_int_to_bigint.sql | 50 -\n .../2026-03-07__add_enqueued_by_to_task_queue.sql | 11 -\n ...6-03-08__align_retention_clue_category_enum.sql | 82 -\n .../migrations/2026-03-08__create_ai_tables.sql | 65 -\n .../20260306_add_schedule_id_to_execution_log.sql | 13 -\n .../20260306_add_trigger_backfill_schedule_id.sql | 29 -\n db/zqyy_app/seeds/admin_web_seed.sql | 18 -\n docs/DOCUMENTATION-MAP.md | 35 +-\n docs/audit/audit_dashboard.md | 33 +-\n .../BD_Manual_20260301_cleanup_and_fixes.md | 203 -\n .../BD_Manual_assistant_accounts_master.md | 106 -\n .../BD_Manual_assistant_service_records.md | 114 -\n .../BD_Manual_biz_date_function_and_mv_rebuild.md | 186 -\n ...Manual_dim_groupbuy_package_ex_detail_fields.md | 88 -\n .../BD_Manual_dws_assistant_order_contribution.md | 266 -\n docs/database/BD_Manual_dws_goods_stock_summary.md | 80 -\n .../BD_Manual_dws_member_spending_power_index.md | 251 -\n .../BD_Manual_fix_dim_staff_ex_rankname.md | 94 -\n ...BD_Manual_fix_dws_assistant_daily_table_area.md | 132 -\n docs/database/BD_Manual_goods_stock_movements.md | 62 -\n docs/database/BD_Manual_goods_stock_summary.md | 62 -\n .../database/BD_Manual_goods_stock_warning_info.md | 102 -\n .../BD_Manual_group_buy_package_details.md | 92 -\n docs/database/BD_Manual_member_balance_changes.md | 74 -\n docs/database/BD_Manual_recharge_settlements.md | 103 -\n docs/database/BD_Manual_site_tables_master.md | 61 -\n docs/database/BD_Manual_store_goods_master.md | 103 -\n .../BD_Manual_store_goods_sales_records.md | 93 -\n docs/database/BD_Manual_tenant_goods_master.md | 80 -\n docs/database/BD_Manual_tenant_id_int_to_bigint.md | 128 -\n docs/database/README.md | 48 +-\n docs/database/ddl/etl_feiqiu__app.sql | 10 +-\n docs/database/ddl/etl_feiqiu__core.sql | 2 +-\n docs/database/ddl/etl_feiqiu__dwd.sql | 8 +-\n docs/database/ddl/etl_feiqiu__dws.sql | 739 +-\n docs/database/ddl/etl_feiqiu__meta.sql | 104 +-\n docs/database/ddl/etl_feiqiu__ods.sql | 32 +-\n docs/database/ddl/fdw.sql | 2 +-\n docs/database/ddl/zqyy_app__auth.sql | 52 +-\n docs/database/ddl/zqyy_app__biz.sql | 67 +-\n docs/database/ddl/zqyy_app__public.sql | 34 +-\n .../anchors/board-finance-mp-instructions.json | 56 -\n docs/h5_ui/anchors/board-finance.json | 129 -\n docs/h5_ui/computed-styles.json | 32 -\n docs/h5_ui/design-tokens.json | 49 -\n docs/h5_ui/icon-mapping.md | 114 -\n docs/h5_ui/img/Windows11.png | Bin 1347665 -> 0 bytes\n docs/h5_ui/interactions/apply.md | 60 -\n docs/h5_ui/interactions/board-coach.md | 104 -\n docs/h5_ui/interactions/board-customer.md | 90 -\n docs/h5_ui/interactions/board-finance.md | 115 -\n docs/h5_ui/interactions/chat-history.md | 47 -\n docs/h5_ui/interactions/chat.md | 81 -\n docs/h5_ui/interactions/coach-detail.md | 100 -\n docs/h5_ui/interactions/customer-detail.md | 71 -\n .../h5_ui/interactions/customer-service-records.md | 54 -\n docs/h5_ui/interactions/login.md | 52 -\n docs/h5_ui/interactions/my-profile.md | 48 -\n docs/h5_ui/interactions/no-permission.md | 44 -\n docs/h5_ui/interactions/notes.md | 56 -\n docs/h5_ui/interactions/performance-records.md | 64 -\n docs/h5_ui/interactions/performance.md | 88 -\n docs/h5_ui/interactions/reviewing.md | 44 -\n docs/h5_ui/interactions/task-detail-callback.md | 26 -\n docs/h5_ui/interactions/task-detail-priority.md | 17 -\n .../h5_ui/interactions/task-detail-relationship.md | 26 -\n docs/h5_ui/interactions/task-detail.md | 148 -\n docs/h5_ui/interactions/task-list.md | 134 -\n docs/h5_ui/pages/ai-icon-demo.html | 80 -\n docs/h5_ui/pages/home-settings.html | 155 -\n docs/h5_ui/pages/task-detail-callback.html | 174 +-\n docs/h5_ui/rendered/ai-icon-demo.html | 80 -\n docs/h5_ui/rendered/apply.html | 124 -\n docs/h5_ui/rendered/board-coach.html | 812 -\n docs/h5_ui/rendered/board-customer.html | 1014 -\n docs/h5_ui/rendered/board-finance.html | 1791 -\n docs/h5_ui/rendered/chat-history.html | 167 -\n docs/h5_ui/rendered/chat.html | 155 -\n docs/h5_ui/rendered/coach-detail.html | 487 -\n docs/h5_ui/rendered/customer-detail.html | 430 -\n docs/h5_ui/rendered/customer-service-records.html | 186 -\n docs/h5_ui/rendered/home-settings.html | 104 -\n docs/h5_ui/rendered/login.html | 145 -\n docs/h5_ui/rendered/my-profile.html | 113 -\n docs/h5_ui/rendered/no-permission.html | 109 -\n docs/h5_ui/rendered/notes.html | 139 -\n docs/h5_ui/rendered/performance-records.html | 789 -\n docs/h5_ui/rendered/performance.html | 1932 -\n docs/h5_ui/rendered/reviewing.html | 129 -\n docs/h5_ui/rendered/task-detail-callback.html | 356 -\n docs/h5_ui/rendered/task-detail-priority.html | 356 -\n docs/h5_ui/rendered/task-detail-relationship.html | 335 -\n docs/h5_ui/rendered/task-detail.html | 445 -\n docs/h5_ui/rendered/task-list.html | 665 -\n docs/h5_ui/screenshots/apply.png | Bin 615233 -> 0 bytes\n docs/h5_ui/screenshots/board-coach--perf-high.png | Bin 323313 -> 0 bytes\n .../h5_ui/screenshots/board-coach--salary-high.png | Bin 337054 -> 0 bytes\n .../screenshots/board-coach--skill-dropdown.png | Bin 192255 -> 0 bytes\n .../screenshots/board-coach--sort-dropdown.png | Bin 200441 -> 0 bytes\n .../screenshots/board-coach--storage-value.png | Bin 333138 -> 0 bytes\n .../screenshots/board-coach--task-complete.png | Bin 285362 -> 0 bytes\n .../screenshots/board-coach--time-dropdown.png | Bin 197500 -> 0 bytes\n docs/h5_ui/screenshots/board-coach.png | Bin 322694 -> 0 bytes\n docs/h5_ui/screenshots/board-customer--balance.png | Bin 199183 -> 0 bytes\n docs/h5_ui/screenshots/board-customer--freq60.png | Bin 199325 -> 0 bytes\n docs/h5_ui/screenshots/board-customer--loyal.png | Bin 392361 -> 0 bytes\n .../screenshots/board-customer--potential.png | Bin 200577 -> 0 bytes\n .../board-customer--project-dropdown.png | Bin 149103 -> 0 bytes\n docs/h5_ui/screenshots/board-customer--recall.png | Bin 245767 -> 0 bytes\n docs/h5_ui/screenshots/board-customer--recent.png | Bin 183776 -> 0 bytes\n .../h5_ui/screenshots/board-customer--recharge.png | Bin 245362 -> 0 bytes\n docs/h5_ui/screenshots/board-customer--spend60.png | Bin 191574 -> 0 bytes\n .../screenshots/board-customer--type-dropdown.png | Bin 131668 -> 0 bytes\n docs/h5_ui/screenshots/board-customer.png | Bin 245906 -> 0 bytes\n .../screenshots/board-finance--filter-dropdown.png | Bin 1225561 -> 0 bytes\n .../h5_ui/screenshots/board-finance--tip-modal.png | Bin 1316102 -> 0 bytes\n .../h5_ui/screenshots/board-finance--toc-panel.png | Bin 1343206 -> 0 bytes\n docs/h5_ui/screenshots/board-finance.png | Bin 1338815 -> 0 bytes\n docs/h5_ui/screenshots/chat-history.png | Bin 173064 -> 0 bytes\n docs/h5_ui/screenshots/chat.png | Bin 349102 -> 0 bytes\n .../h5_ui/screenshots/coach-detail--note-modal.png | Bin 848642 -> 0 bytes\n docs/h5_ui/screenshots/coach-detail.png | Bin 1237737 -> 0 bytes\n docs/h5_ui/screenshots/customer-detail.png | Bin 2280044 -> 0 bytes\n .../h5_ui/screenshots/customer-service-records.png | Bin 520527 -> 0 bytes\n docs/h5_ui/screenshots/home-settings.png | Bin 42557 -> 0 bytes\n docs/h5_ui/screenshots/login.png | Bin 677606 -> 0 bytes\n docs/h5_ui/screenshots/my-profile.png | Bin 147663 -> 0 bytes\n docs/h5_ui/screenshots/no-permission.png | Bin 293165 -> 0 bytes\n docs/h5_ui/screenshots/notes.png | Bin 633136 -> 0 bytes\n docs/h5_ui/screenshots/performance-records.png | Bin 1325177 -> 0 bytes\n docs/h5_ui/screenshots/performance.png | Bin 1507927 -> 0 bytes\n docs/h5_ui/screenshots/reviewing.png | Bin 244384 -> 0 bytes\n .../screenshots/task-detail--abandon-modal.png | Bin 1056489 -> 0 bytes\n docs/h5_ui/screenshots/task-detail--note-modal.png | Bin 1043329 -> 0 bytes\n docs/h5_ui/screenshots/task-detail-callback.png | Bin 1326915 -> 0 bytes\n docs/h5_ui/screenshots/task-detail-priority.png | Bin 1306978 -> 0 bytes\n .../h5_ui/screenshots/task-detail-relationship.png | Bin 1290043 -> 0 bytes\n docs/h5_ui/screenshots/task-detail.png | Bin 1374239 -> 0 bytes\n docs/h5_ui/screenshots/task-list--context-menu.png | Bin 905489 -> 0 bytes\n docs/h5_ui/screenshots/task-list--note-modal.png | Bin 659230 -> 0 bytes\n docs/h5_ui/screenshots/task-list.png | Bin 1041604 -> 0 bytes\n docs/prd/MIGRATION-PLAYBOOK.md | 2097 -\n docs/prd/specs/P4-miniapp-core-business.md | 60 +-\n docs/prd/specs/P6-miniapp-fe-tasks.md | 91 +-\n docs/prd/specs/P8-miniapp-fe-boards.md | 6 +-\n docs/prd/specs/P9-miniapp-fe-details.md | 77 +-\n export/ETL-Connectors/feiqiu/JSON/.gitkeep | 0\n .../assistant_cancellation_records.json | 44 -\n .../assistant_cancellation_records.json | 44 -\n .../assistant_cancellation_records.json | 44 -\n .../assistant_cancellation_records.json | 44 -\n .../assistant_cancellation_records.json | 44 -\n .../assistant_accounts_master.json | 4429 -\n .../assistant_accounts_master.json | 4429 -\n .../assistant_accounts_master.json | 4429 -\n .../assistant_accounts_master.json | 4429 -\n .../assistant_accounts_master.json | 4429 -\n .../assistant_service_records.json | 2040 -\n .../assistant_service_records.json | 44 -\n .../assistant_service_records.json | 355 -\n .../assistant_service_records.json | 44 -\n .../assistant_service_records.json | 22103 -\n .../stock_goods_category_tree.json | 386 -\n .../stock_goods_category_tree.json | 386 -\n .../stock_goods_category_tree.json | 386 -\n .../stock_goods_category_tree.json | 386 -\n .../stock_goods_category_tree.json | 386 -\n .../group_buy_redemption_records.json | 420374 -------\n .../group_buy_redemption_records.json | 406209 -------\n .../group_buy_redemption_records.json | 397282 -------\n .../group_buy_redemption_records.json | 397282 -------\n .../group_buy_redemption_records.json | 393070 -------\n .../group_buy_packages.json | 867 -\n .../group_buy_packages.json | 867 -\n .../group_buy_packages.json | 867 -\n .../group_buy_packages.json | 867 -\n .../group_buy_packages.json | 867 -\n .../goods_stock_movements.json | 9552 -\n .../goods_stock_movements.json | 44 -\n .../goods_stock_movements.json | 44 -\n .../goods_stock_movements.json | 44 -\n .../goods_stock_movements.json | 44892 -\n .../goods_stock_summary.json | 2817 -\n .../goods_stock_summary.json | 2817 -\n .../goods_stock_summary.json | 2823 -\n .../goods_stock_summary.json | 2823 -\n .../goods_stock_summary.json | 2823 -\n .../assistant_accounts_master__p0001.json | 4 -\n .../assistant_cancellation_records__p0001.json | 4 -\n .../assistant_service_records__p0001.json | 4 -\n .../assistant_service_records__p0002.json | 4 -\n .../goods_stock_movements__p0001.json | 4 -\n .../goods_stock_movements__p0002.json | 4 -\n .../goods_stock_summary__p0001.json | 4 -\n .../group_buy_packages__p0001.json | 4 -\n .../group_buy_redemption_records__p0001.json | 4 -\n .../manifest.json | 166 -\n .../member_balance_changes__p0001.json | 35 -\n .../member_balance_changes__p0002.json | 4 -\n .../member_profiles__p0001.json | 4405 -\n .../member_profiles__p0002.json | 4405 -\n .../member_profiles__p0003.json | 3459 -\n .../member_stored_value_cards__p0001.json | 4 -\n .../payment_transactions__p0001.json | 4 -\n .../payment_transactions__p0002.json | 4 -\n .../platform_coupon_redemption_records__p0001.json | 4 -\n .../recharge_settlements__p0001.json | 4 -\n .../recharge_settlements__p0002.json | 4 -\n .../refund_transactions__p0001.json | 4 -\n .../settlement_records__p0001.json | 4 -\n .../settlement_records__p0002.json | 4 -\n .../site_tables_master__p0001.json | 4 -\n .../stock_goods_category_tree__p0001.json | 352 -\n .../store_goods_master__p0001.json | 4 -\n .../store_goods_sales_records__p0001.json | 4 -\n .../store_goods_sales_records__p0002.json | 4 -\n .../table_fee_discount_records__p0001.json | 4 -\n .../table_fee_discount_records__p0002.json | 4 -\n .../table_fee_transactions__p0001.json | 4 -\n .../table_fee_transactions__p0002.json | 4 -\n .../tenant_goods_master__p0001.json | 6240 -\n .../member_profiles.json | 12325 -\n .../member_profiles.json | 12325 -\n .../member_profiles.json | 12325 -\n .../member_profiles.json | 12325 -\n .../member_profiles.json | 12325 -\n .../member_balance_changes.json | 70565 --\n .../member_balance_changes.json | 68585 --\n .../member_balance_changes.json | 66605 --\n .../member_balance_changes.json | 66605 --\n .../member_balance_changes.json | 65749 --\n .../member_stored_value_cards.json | 72746 --\n .../member_stored_value_cards.json | 72746 --\n .../member_stored_value_cards.json | 72746 --\n .../member_stored_value_cards.json | 72746 --\n .../member_stored_value_cards.json | 72746 --\n .../payment_transactions.json | 455295 --------\n .../payment_transactions.json | 441199 -------\n .../payment_transactions.json | 436103 -------\n .../payment_transactions.json | 430967 -------\n .../payment_transactions.json | 425887 -------\n .../payment_transactions.json | 425887 -------\n .../payment_transactions.json | 425887 -------\n .../payment_transactions.json | 425887 -------\n .../platform_coupon_redemption_records.json | 1008692 -----------------\n .../platform_coupon_redemption_records.json | 1008692 -----------------\n .../platform_coupon_redemption_records.json | 1008692 -----------------\n .../platform_coupon_redemption_records.json | 1008692 -----------------\n .../platform_coupon_redemption_records.json | 1008692 -----------------\n .../recharge_settlements.json | 44 -\n .../recharge_settlements.json | 44 -\n .../recharge_settlements.json | 44 -\n .../recharge_settlements.json | 44 -\n .../recharge_settlements.json | 2397 -\n .../refund_transactions.json | 2235 -\n .../refund_transactions.json | 1991 -\n .../refund_transactions.json | 1869 -\n .../refund_transactions.json | 1869 -\n .../refund_transactions.json | 1808 -\n .../refund_transactions.json | 1808 -\n .../refund_transactions.json | 1808 -\n .../refund_transactions.json | 1808 -\n .../settlement_records.json | 33677 -\n .../settlement_records.json | 44 -\n .../settlement_records.json | 44 -\n .../settlement_records.json | 44 -\n .../settlement_records.json | 154717 ---\n .../settlement_records.json | 154717 ---\n .../settlement_records.json | 154717 ---\n .../settlement_records.json | 154717 ---\n .../payment_transactions.json | 14023 -\n .../payment_transactions.json | 44 -\n .../store_goods_master.json | 9906 -\n .../store_goods_master.json | 9906 -\n .../store_goods_master.json | 9906 -\n .../store_goods_master.json | 9906 -\n .../store_goods_master.json | 9906 -\n .../store_goods_sales_records.json | 38 -\n .../store_goods_sales_records.json | 38 -\n .../store_goods_sales_records.json | 38 -\n .../store_goods_sales_records.json | 38 -\n .../store_goods_sales_records.json | 38 -\n .../site_tables_master.json | 2111 -\n .../site_tables_master.json | 2111 -\n .../site_tables_master.json | 2111 -\n .../site_tables_master.json | 2111 -\n .../site_tables_master.json | 2111 -\n .../table_fee_discount_records.json | 106063 --\n .../table_fee_discount_records.json | 102586 --\n .../table_fee_discount_records.json | 100024 --\n .../table_fee_discount_records.json | 100024 --\n .../table_fee_discount_records.json | 98743 --\n .../table_fee_transactions.json | 690146 -----------\n .../table_fee_transactions.json | 669540 -----------\n .../table_fee_transactions.json | 654472 -----------\n .../table_fee_transactions.json | 654472 -----------\n .../table_fee_transactions.json | 647072 -----------\n .../tenant_goods_master.json | 6274 -\n .../tenant_goods_master.json | 6274 -\n .../tenant_goods_master.json | 6274 -\n .../tenant_goods_master.json | 6274 -\n .../tenant_goods_master.json | 6274 -\n scripts/README.md | 37 -\n scripts/ops/_daily_revenue_0305.py | 4 +-\n scripts/ops/anchor_compare.py | 866 +-\n scripts/ops/daily_revenue_report.py | 4 +-\n scripts/ops/extract_kiro_session.py | 112 +-\n scripts/ops/screenshot_h5_pages.py | 11 +-\n 737 files changed, 9706 insertions(+), 16108780 deletions(-)", "high_risk_diff": "--- /dev/null\n+++ b/apps/backend/app/ai/apps/app1_chat.py\n@@ -0,0 +1 @@\n\"\"\"应用 1:通用对话(SSE 流式)。\n\n每次进入 chat 页面新建 ai_conversations 记录(不复用),\n首条消息注入页面上下文,流式返回 AI 回复。\n\napp_id = \"app1_chat\"\n\"\"\"\n\nfrom __future__ import annotations\n\nimport json\nimport logging\nfrom typing import AsyncGenerator\n\nfrom app.ai.bailian_client import BailianClient\nfrom app.ai.cache_service import AICacheService\nfrom app.ai.conversation_service import ConversationService\nfrom app.ai.schemas import SSEEvent\n\nlogger = logging.getLogger(__name__)\n\nAPP_ID = \"app1_chat\"\n\n\nasync def chat_stream(\n *,\n message: str,\n user_id: int | str,\n nickname: str,\n role: str,\n site_id: int,\n source_page: str | None = None,\n page_context: dict | None = None,\n screen_content: str | None = None,\n bailian: BailianClient,\n conv_svc: ConversationService,\n) -> AsyncGenerator[SSEEvent, None]:\n \"\"\"流式对话入口,返回 SSEEvent 异步生成器。\n\n 流程:\n 1. 创建 conversation 记录\n 2. 写入 user message\n 3. 构建 system prompt(注入页面上下文)\n 4. 调用 bailian.chat_stream 流式获取回复\n 5. 逐 chunk yield SSEEvent(type=\"chunk\")\n 6. 完成后写入 assistant message,yield SSEEvent(type=\"done\")\n 7. 异常时 yield SSEEvent(type=\"error\")\n \"\"\"\n conversation_id: int | None = None\n\n try:\n # 1. 每次新建 conversation(不复用)\n source_ctx = _build_source_context(\n source_page=source_page,\n page_context=page_context,\n screen_content=screen_content,\n )\n conversation_id = conv_svc.create_conversation(\n user_id=user_id,\n nickname=nickname,\n app_id=APP_ID,\n site_id=site_id,\n source_page=source_page,\n source_context=source_ctx,\n )\n logger.info(\n \"App1 新建对话: conversation_id=%s user_id=%s site_id=%s\",\n conversation_id, user_id, site_id,\n )\n\n # 2. 立即写入 user message\n conv_svc.add_message(\n conversation_id=conversation_id,\n role=\"user\",\n content=message,\n )\n\n # 3. 构建消息列表(system prompt + user message)\n messages = _build_messages(\n message=message,\n user_id=user_id,\n nickname=nickname,\n role=role,\n source_page=source_page,\n page_context=page_context,\n screen_content=screen_content,\n )\n\n # 4-5. 流式调用百炼,逐 chunk yield\n full_reply_parts: list[str] = []\n async for chunk in bailian.chat_stream(messages):\n full_reply_parts.append(chunk)\n yield SSEEvent(type=\"chunk\", content=chunk)\n\n # 6. 流式完成,拼接完整回复并写入 assistant message\n full_reply = \"\".join(full_reply_parts)\n # 百炼流式模式不返回 tokens_used,按字符数估算(粗略)\n estimated_tokens = len(full_reply)\n conv_svc.add_message(\n conversation_id=conversation_id,\n role=\"assistant\",\n content=full_reply,\n tokens_used=estimated_tokens,\n )\n\n yield SSEEvent(\n type=\"done\",\n conversation_id=conversation_id,\n tokens_used=estimated_tokens,\n )\n\n except Exception as e:\n logger.error(\n \"App1 对话异常: conversation_id=%s error=%s\",\n conversation_id, e,\n exc_info=True,\n )\n yield SSEEvent(type=\"error\", message=str(e))\n\n\ndef _build_messages(\n *,\n message: str,\n user_id: int | str,\n nickname: str,\n role: str,\n source_page: str | None,\n page_context: dict | None,\n screen_content: str | None,\n) -> list[dict]:\n \"\"\"构建发送给百炼的消息列表。\n\n 首条 system 消息注入页面上下文和用户信息。\n \"\"\"\n system_content = _build_system_prompt(\n user_id=user_id,\n nickname=nickname,\n role=role,\n source_page=source_page,\n page_context=page_context,\n screen_content=screen_content,\n )\n return [\n {\"role\": \"system\", \"content\": json.dumps(system_content, ensure_ascii=False)},\n {\"role\": \"user\", \"content\": message},\n ]\n\n\ndef _build_system_prompt(\n *,\n user_id: int | str,\n nickname: str,\n role: str,\n source_page: str | None,\n page_context: dict | None,\n screen_content: str | None,\n) -> dict:\n \"\"\"构建 system prompt JSON。\n\n 通过 biz_params.user_prompt_params 传入用户信息,\n 注入页面上下文供 AI 理解当前场景。\n \"\"\"\n prompt: dict = {\n \"task\": \"你是台球门店的 AI 助手,根据用户的问题和当前页面上下文提供帮助。\",\n \"biz_params\": {\n \"user_prompt_params\": {\n \"User_ID\": str(user_id),\n \"Role\": role,\n \"Nickname\": nickname,\n },\n },\n }\n\n # 注入页面上下文(首条消息)\n page_ctx = _build_page_context(\n source_page=source_page,\n page_context=page_context,\n screen_content=screen_content,\n )\n if page_ctx:\n prompt[\"page_context\"] = page_ctx\n\n return prompt\n\n\ndef _build_page_context(\n *,\n source_page: str | None,\n page_context: dict | None,\n screen_content: str | None,\n) -> dict:\n \"\"\"构建页面上下文信息。\n\n P5-A 阶段:直接透传前端传入的上下文字段。\n P5-B 阶段:各页面逐步实现文本化工具,丰富 screen_content。\n \"\"\"\n # TODO: P5-B 各页面文本化工具细化\n ctx: dict = {}\n if source_page:\n ctx[\"source_page\"] = source_page\n if page_context:\n ctx[\"page_context\"] = page_context\n if screen_content:\n ctx[\"screen_content\"] = screen_content\n return ctx\n\n\ndef _build_source_context(\n *,\n source_page: str | None,\n page_context: dict | None,\n screen_content: str | None,\n) -> dict | None:\n \"\"\"构建存入 ai_conversations.source_context 的 JSON。\"\"\"\n ctx: dict = {}\n if source_page:\n ctx[\"source_page\"] = source_page\n if page_context:\n ctx[\"page_context\"] = page_context\n if screen_content:\n ctx[\"screen_content\"] = screen_content\n return ctx if ctx else None\n\n--- /dev/null\n+++ b/apps/backend/app/ai/apps/app2_finance.py\n@@ -0,0 +1 @@\n\"\"\"应用 2:财务洞察。\n\n8 个时间维度独立调用,每次调用结果写入 ai_cache,\n同时创建 ai_conversations + ai_messages 记录。\n\n营业日分界点:每日 08:00(BUSINESS_DAY_START_HOUR 环境变量,默认 8)。\n\napp_id = \"app2_finance\"\n\"\"\"\n\nfrom __future__ import annotations\n\nimport json\nimport logging\nimport os\nfrom datetime import date, datetime, timedelta\n\nfrom app.ai.bailian_client import BailianClient\nfrom app.ai.cache_service import AICacheService\nfrom app.ai.conversation_service import ConversationService\nfrom app.ai.prompts.app2_finance_prompt import build_prompt\nfrom app.ai.schemas import CacheTypeEnum\n\nlogger = logging.getLogger(__name__)\n\nAPP_ID = \"app2_finance\"\n\n# 8 个时间维度编码\nTIME_DIMENSIONS = (\n \"this_month\",\n \"last_month\",\n \"this_week\",\n \"last_week\",\n \"last_3_months\",\n \"this_quarter\",\n \"last_quarter\",\n \"last_6_months\",\n)\n\n\ndef get_business_date() -> date:\n \"\"\"根据营业日分界点计算当前营业日。\n\n 分界点前(如 07:59)视为前一天营业日,\n 分界点及之后(如 08:00)视为当天营业日。\n \"\"\"\n hour = int(os.environ.get(\"BUSINESS_DAY_START_HOUR\", \"8\"))\n now = datetime.now()\n if now.hour < hour:\n return (now - timedelta(days=1)).date()\n return now.date()\n\n\ndef compute_time_range(dimension: str, business_date: date) -> tuple[date, date]:\n \"\"\"计算时间维度对应的日期范围 [start, end](闭区间)。\n\n Args:\n dimension: 时间维度编码\n business_date: 当前营业日\n\n Returns:\n (start_date, end_date) 元组\n \"\"\"\n y, m, d = business_date.year, business_date.month, business_date.day\n\n if dimension == \"this_month\":\n start = date(y, m, 1)\n return start, business_date\n\n if dimension == \"last_month\":\n prev = _month_offset(y, m, -1)\n start = date(prev[0], prev[1], 1)\n end = date(y, m, 1) - timedelta(days=1)\n return start, end\n\n if dimension == \"this_week\":\n # 周一起算\n weekday = business_date.weekday() # 0=周一\n start = business_date - timedelta(days=weekday)\n return start, business_date\n\n if dimension == \"last_week\":\n weekday = business_date.weekday()\n this_monday = business_date - timedelta(days=weekday)\n last_monday = this_monday - timedelta(days=7)\n last_sunday = this_monday - timedelta(days=1)\n return last_monday, last_sunday\n\n if dimension == \"last_3_months\":\n # 当前月 - 3 ~ 当前月 - 1\n end_ym = _month_offset(y, m, -1)\n start_ym = _month_offset(y, m, -3)\n start = date(start_ym[0], start_ym[1], 1)\n # end = 上月最后一天\n end = date(y, m, 1) - timedelta(days=1)\n return start, end\n\n if dimension == \"this_quarter\":\n q_start_month = ((m - 1) // 3) * 3 + 1\n start = date(y, q_start_month, 1)\n return start, business_date\n\n if dimension == \"last_quarter\":\n q_start_month = ((m - 1) // 3) * 3 + 1\n # 上季度结束 = 本季度第一天 - 1\n this_q_start = date(y, q_start_month, 1)\n end = this_q_start - timedelta(days=1)\n # 上季度开始\n ly, lm = end.year, end.month\n lq_start_month = ((lm - 1) // 3) * 3 + 1\n start = date(ly, lq_start_month, 1)\n return start, end\n\n if dimension == \"last_6_months\":\n # 当前月 - 6 ~ 当前月 - 1\n end_ym = _month_offset(y, m, -1)\n start_ym = _month_offset(y, m, -6)\n start = date(start_ym[0], start_ym[1], 1)\n end = date(y, m, 1) - timedelta(days=1)\n return start, end\n\n raise ValueError(f\"未知时间维度: {dimension}\")\n\n\nasync def run(\n context: dict,\n bailian: BailianClient,\n cache_svc: AICacheService,\n conv_svc: ConversationService,\n) -> dict:\n \"\"\"执行 App2 财务洞察调用。\n\n Args:\n context: 包含 site_id, time_dimension, user_id(默认'system'), nickname(默认'')\n bailian: 百炼客户端\n cache_svc: 缓存服务\n conv_svc: 对话服务\n\n Returns:\n 百炼返回的结构化 JSON(insights 数组)\n \"\"\"\n site_id = context[\"site_id\"]\n time_dimension = context[\"time_dimension\"]\n user_id = context.get(\"user_id\", \"system\")\n nickname = context.get(\"nickname\", \"\")\n\n # 构建 Prompt\n prompt_context = {\n \"site_id\": site_id,\n \"time_dimension\": time_dimension,\n \"current_data\": context.get(\"current_data\", {}),\n \"previous_data\": context.get(\"previous_data\", {}),\n }\n messages = build_prompt(prompt_context)\n\n # 创建对话记录\n conversation_id = conv_svc.create_conversation(\n user_id=user_id,\n nickname=nickname,\n app_id=APP_ID,\n site_id=site_id,\n source_context={\"time_dimension\": time_dimension},\n )\n\n # 写入 system prompt 消息\n conv_svc.add_message(\n conversation_id=conversation_id,\n role=\"system\",\n content=messages[0][\"content\"],\n )\n # 写入 user 消息\n conv_svc.add_message(\n conversation_id=conversation_id,\n role=\"user\",\n content=messages[1][\"content\"],\n )\n\n # 调用百炼 API\n result, tokens_used = await bailian.chat_json(messages)\n\n # 写入 assistant 消息\n conv_svc.add_message(\n conversation_id=conversation_id,\n role=\"assistant\",\n content=json.dumps(result, ensure_ascii=False),\n tokens_used=tokens_used,\n )\n\n # 写入缓存\n cache_svc.write_cache(\n cache_type=CacheTypeEnum.APP2_FINANCE.value,\n site_id=site_id,\n target_id=time_dimension,\n result_json=result,\n triggered_by=f\"user:{user_id}\",\n )\n\n logger.info(\n \"App2 财务洞察完成: site_id=%s dimension=%s conversation_id=%s tokens=%d\",\n site_id, time_dimension, conversation_id, tokens_used,\n )\n\n return result\n\n\ndef _month_offset(year: int, month: int, offset: int) -> tuple[int, int]:\n \"\"\"计算月份偏移,返回 (year, month)。\"\"\"\n # 转为 0-based 计算\n total = (year * 12 + (month - 1)) + offset\n return total // 12, total % 12 + 1\n\n--- /dev/null\n+++ b/apps/backend/app/ai/apps/app3_clue.py\n@@ -0,0 +1 @@\n\"\"\"应用 3:客户数据维客线索分析(骨架)。\n\n客户新增消费时自动触发,通过 AI 分析客户数据提取维客线索。\n线索 category 限定 3 个枚举值:客户基础、消费习惯、玩法偏好。\n线索提供者统一标记为\"系统\"。\n\n使用 items_sum 口径(= table_charge_money + goods_money\n+ assistant_pd_money + assistant_cx_money + electricity_money),\n禁止使用 consume_money。\n\napp_id = \"app3_clue\"\n\"\"\"\n\nfrom __future__ import annotations\n\nimport json\nimport logging\n\nfrom app.ai.bailian_client import BailianClient\nfrom app.ai.cache_service import AICacheService\nfrom app.ai.conversation_service import ConversationService\nfrom app.ai.schemas import CacheTypeEnum\n\nlogger = logging.getLogger(__name__)\n\nAPP_ID = \"app3_clue\"\n\n\ndef build_prompt(\n context: dict,\n cache_svc: AICacheService | None = None,\n) -> list[dict]:\n \"\"\"构建 Prompt 消息列表。\n\n P5-A 阶段:返回占位 Prompt,标注待细化字段。\n P5-B 阶段(P9-T1):补充 consumption_records 等完整数据。\n\n Args:\n context: 包含 site_id, member_id, nickname 等\n cache_svc: 缓存服务,用于获取 reference 历史数据\n\n Returns:\n 消息列表 [{\"role\": \"system\", \"content\": ...}, {\"role\": \"user\", ...}]\n \"\"\"\n site_id = context[\"site_id\"]\n member_id = context[\"member_id\"]\n\n # 构建 reference:App6 线索 + 最近 2 套 App8 历史(附 generated_at)\n reference = _build_reference(site_id, member_id, cache_svc)\n\n system_content = {\n \"task\": \"分析客户消费数据,提取维客线索。\",\n \"app_id\": APP_ID,\n \"rules\": {\n \"category_enum\": [\"客户基础\", \"消费习惯\", \"玩法偏好\"],\n \"providers\": \"系统\",\n \"amount_caliber\": \"items_sum = table_charge_money + goods_money + assistant_pd_money + assistant_cx_money + electricity_money\",\n \"禁止使用\": \"consume_money\",\n },\n \"output_format\": {\n \"clues\": [\n {\n \"category\": \"枚举值(客户基础/消费习惯/玩法偏好)\",\n \"summary\": \"一句话摘要\",\n \"detail\": \"详细说明\",\n \"emoji\": \"表情符号\",\n }\n ]\n },\n # TODO: P9-T1 细化 - consumption_records 等客户消费数据\n \"data\": {\n \"consumption_records\": \"待 P9-T1 补充\",\n \"member_info\": \"待 P9-T1 补充\",\n },\n \"reference\": reference,\n }\n\n user_content = (\n f\"请分析会员 {member_id} 的消费数据,提取维客线索。\"\n \"每条线索包含 category、summary、detail、emoji 四个字段。\"\n \"category 必须是:客户基础、消费习惯、玩法偏好 之一。\"\n )\n\n return [\n {\"role\": \"system\", \"content\": json.dumps(system_content, ensure_ascii=False)},\n {\"role\": \"user\", \"content\": user_content},\n ]\n\n\ndef _build_reference(\n site_id: int,\n member_id: int,\n cache_svc: AICacheService | None,\n) -> dict:\n \"\"\"构建 Prompt reference 字段。\n\n 包含:\n - App6 备注分析线索(最新一条,如有)\n - 最近 2 套 App8 维客线索整理历史(附 generated_at)\n\n 缓存不存在时返回空对象 {}。\n \"\"\"\n if cache_svc is None:\n return {}\n\n reference: dict = {}\n target_id = str(member_id)\n\n # App6 备注分析线索\n app6_latest = cache_svc.get_latest(\n CacheTypeEnum.APP6_NOTE_ANALYSIS.value, site_id, target_id,\n )\n if app6_latest:\n reference[\"app6_note_clues\"] = {\n \"result_json\": app6_latest.get(\"result_json\"),\n \"generated_at\": app6_latest.get(\"created_at\"),\n }\n\n # 最近 2 套 App8 历史\n app8_history = cache_svc.get_history(\n CacheTypeEnum.APP8_CLUE_CONSOLIDATED.value, site_id, target_id, limit=2,\n )\n if app8_history:\n reference[\"app8_history\"] = [\n {\n \"result_json\": h.get(\"result_json\"),\n \"generated_at\": h.get(\"created_at\"),\n }\n for h in app8_history\n ]\n\n return reference\n\n\nasync def run(\n context: dict,\n bailian: BailianClient,\n cache_svc: AICacheService,\n conv_svc: ConversationService,\n) -> dict:\n \"\"\"执行 App3 客户数据维客线索分析。\n\n 流程:\n 1. build_prompt 构建 Prompt\n 2. bailian.chat_json 调用百炼\n 3. 写入 conversation + messages\n 4. 写入 ai_cache\n 5. 返回结果\n\n Args:\n context: site_id, member_id, user_id(默认'system'), nickname(默认'')\n bailian: 百炼客户端\n cache_svc: 缓存服务\n conv_svc: 对话服务\n\n Returns:\n 百炼返回的结构化 JSON(clues 数组)\n \"\"\"\n site_id = context[\"site_id\"]\n member_id = context[\"member_id\"]\n user_id = context.get(\"user_id\", \"system\")\n nickname = context.get(\"nickname\", \"\")\n\n # 1. 构建 Prompt\n messages = build_prompt(context, cache_svc)\n\n # 2. 创建对话记录\n conversation_id = conv_svc.create_conversation(\n user_id=user_id,\n nickname=nickname,\n app_id=APP_ID,\n site_id=site_id,\n source_context={\"member_id\": member_id},\n )\n\n # 写入 system + user 消息\n conv_svc.add_message(\n conversation_id=conversation_id,\n role=\"system\",\n content=messages[0][\"content\"],\n )\n conv_svc.add_message(\n conversation_id=conversation_id,\n role=\"user\",\n content=messages[1][\"content\"],\n )\n\n # 3. 调用百炼 API\n result, tokens_used = await bailian.chat_json(messages)\n\n # 4. 写入 assistant 消息\n conv_svc.add_message(\n conversation_id=conversation_id,\n role=\"assistant\",\n content=json.dumps(result, ensure_ascii=False),\n tokens_used=tokens_used,\n )\n\n # 5. 写入缓存\n cache_svc.write_cache(\n cache_type=CacheTypeEnum.APP3_CLUE.value,\n site_id=site_id,\n target_id=str(member_id),\n result_json=result,\n triggered_by=f\"user:{user_id}\",\n )\n\n logger.info(\n \"App3 线索分析完成: site_id=%s member_id=%s conversation_id=%s tokens=%d\",\n site_id, member_id, conversation_id, tokens_used,\n )\n\n return result\n\n--- /dev/null\n+++ b/apps/backend/app/ai/apps/app4_analysis.py\n@@ -0,0 +1 @@\n\"\"\"应用 4:关系分析/任务建议(骨架)。\n\n助教参与新结算或被分配召回任务时自动触发,\n生成关系分析和任务建议。\n\nPrompt reference 包含 App8 最新 + 最近 2 套历史(附 generated_at)。\n缓存不存在时 reference 传空对象,标注\"暂无历史线索\"。\n\napp_id = \"app4_analysis\"\n\"\"\"\n\nfrom __future__ import annotations\n\nimport json\nimport logging\n\nfrom app.ai.bailian_client import BailianClient\nfrom app.ai.cache_service import AICacheService\nfrom app.ai.conversation_service import ConversationService\nfrom app.ai.schemas import CacheTypeEnum\n\nlogger = logging.getLogger(__name__)\n\nAPP_ID = \"app4_analysis\"\n\n\ndef build_prompt(\n context: dict,\n cache_svc: AICacheService | None = None,\n) -> list[dict]:\n \"\"\"构建 Prompt 消息列表。\n\n P5-A 阶段:返回占位 Prompt,标注待细化字段。\n P5-B 阶段(P6-T4):补充 service_history、assistant_info 等完整数据。\n\n Args:\n context: 包含 site_id, assistant_id, member_id\n cache_svc: 缓存服务,用于获取 reference 历史数据\n\n Returns:\n 消息列表\n \"\"\"\n site_id = context[\"site_id\"]\n assistant_id = context[\"assistant_id\"]\n member_id = context[\"member_id\"]\n\n # 构建 reference:App8 最新 + 最近 2 套历史\n reference = _build_reference(site_id, member_id, cache_svc)\n\n system_content = {\n \"task\": \"分析助教与客户的关系,生成任务建议。\",\n \"app_id\": APP_ID,\n \"output_format\": {\n \"task_description\": \"任务描述文本\",\n \"action_suggestions\": [\"建议1\", \"建议2\"],\n \"one_line_summary\": \"一句话总结\",\n },\n # TODO: P6-T4 细化 - service_history、assistant_info\n \"data\": {\n \"service_history\": \"待 P6-T4 补充\",\n \"assistant_info\": \"待 P6-T4 补充\",\n },\n \"reference\": reference,\n }\n\n # 缓存不存在时在 user prompt 中标注\n no_history_hint = \"\"\n if not reference:\n no_history_hint = \"(暂无历史线索,请基于现有信息分析)\"\n\n user_content = (\n f\"请分析助教 {assistant_id} 与会员 {member_id} 的关系,\"\n f\"生成任务建议。{no_history_hint}\"\n \"返回 task_description、action_suggestions、one_line_summary 三个字段。\"\n )\n\n return [\n {\"role\": \"system\", \"content\": json.dumps(system_content, ensure_ascii=False)},\n {\"role\": \"user\", \"content\": user_content},\n ]\n\n\ndef _build_reference(\n site_id: int,\n member_id: int,\n cache_svc: AICacheService | None,\n) -> dict:\n \"\"\"构建 Prompt reference 字段。\n\n 包含:\n - App8 最新维客线索(如有)\n - 最近 2 套 App8 历史(附 generated_at)\n\n 缓存不存在时返回空对象 {}。\n \"\"\"\n if cache_svc is None:\n return {}\n\n reference: dict = {}\n target_id = str(member_id)\n\n # App8 最新\n app8_latest = cache_svc.get_latest(\n CacheTypeEnum.APP8_CLUE_CONSOLIDATED.value, site_id, target_id,\n )\n if app8_latest:\n reference[\"app8_latest\"] = {\n \"result_json\": app8_latest.get(\"result_json\"),\n \"generated_at\": app8_latest.get(\"created_at\"),\n }\n\n # 最近 2 套 App8 历史\n app8_history = cache_svc.get_history(\n CacheTypeEnum.APP8_CLUE_CONSOLIDATED.value, site_id, target_id, limit=2,\n )\n if app8_history:\n reference[\"app8_history\"] = [\n {\n \"result_json\": h.get(\"result_json\"),\n \"generated_at\": h.get(\"created_at\"),\n }\n for h in app8_history\n ]\n\n return reference\n\n\nasync def run(\n context: dict,\n bailian: BailianClient,\n cache_svc: AICacheService,\n conv_svc: ConversationService,\n) -> dict:\n \"\"\"执行 App4 关系分析。\n\n Args:\n context: site_id, assistant_id, member_id\n bailian: 百炼客户端\n cache_svc: 缓存服务\n conv_svc: 对话服务\n\n Returns:\n 百炼返回的结构化 JSON(task_description, action_suggestions, one_line_summary)\n \"\"\"\n site_id = context[\"site_id\"]\n assistant_id = context[\"assistant_id\"]\n member_id = context[\"member_id\"]\n user_id = context.get(\"user_id\", \"system\")\n nickname = context.get(\"nickname\", \"\")\n\n # 1. 构建 Prompt\n messages = build_prompt(context, cache_svc)\n\n # 2. 创建对话记录\n conversation_id = conv_svc.create_conversation(\n user_id=user_id,\n nickname=nickname,\n app_id=APP_ID,\n site_id=site_id,\n source_context={\"assistant_id\": assistant_id, \"member_id\": member_id},\n )\n\n # 写入 system + user 消息\n conv_svc.add_message(\n conversation_id=conversation_id,\n role=\"system\",\n content=messages[0][\"content\"],\n )\n conv_svc.add_message(\n conversation_id=conversation_id,\n role=\"user\",\n content=messages[1][\"content\"],\n )\n\n # 3. 调用百炼 API\n result, tokens_used = await bailian.chat_json(messages)\n\n # 4. 写入 assistant 消息\n conv_svc.add_message(\n conversation_id=conversation_id,\n role=\"assistant\",\n content=json.dumps(result, ensure_ascii=False),\n tokens_used=tokens_used,\n )\n\n # 5. 写入缓存(target_id = {assistant_id}_{member_id})\n cache_svc.write_cache(\n cache_type=CacheTypeEnum.APP4_ANALYSIS.value,\n site_id=site_id,\n target_id=f\"{assistant_id}_{member_id}\",\n result_json=result,\n triggered_by=f\"user:{user_id}\",\n )\n\n logger.info(\n \"App4 关系分析完成: site_id=%s assistant=%s member=%s conversation_id=%s tokens=%d\",\n site_id, assistant_id, member_id, conversation_id, tokens_used,\n )\n\n return result\n\n--- /dev/null\n+++ b/apps/backend/app/ai/apps/app5_tactics.py\n@@ -0,0 +1 @@\n\"\"\"应用 5:话术参考(骨架)。\n\nApp4 完成后自动联动触发,接收 App4 完整返回结果\n作为 Prompt 中的 task_suggestion 字段。\n\nPrompt reference 包含最近 2 套 App8 历史(附 generated_at)。\n\napp_id = \"app5_tactics\"\n\"\"\"\n\nfrom __future__ import annotations\n\nimport json\nimport logging\n\nfrom app.ai.bailian_client import BailianClient\nfrom app.ai.cache_service import AICacheService\nfrom app.ai.conversation_service import ConversationService\nfrom app.ai.schemas import CacheTypeEnum\n\nlogger = logging.getLogger(__name__)\n\nAPP_ID = \"app5_tactics\"\n\n\ndef build_prompt(\n context: dict,\n cache_svc: AICacheService | None = None,\n) -> list[dict]:\n \"\"\"构建 Prompt 消息列表。\n\n P5-A 阶段:返回占位 Prompt,标注待细化字段。\n P5-B 阶段(P6-T4):补充 service_history、assistant_info(随 App4 同步)。\n\n Args:\n context: 包含 site_id, assistant_id, member_id, app4_result(dict)\n cache_svc: 缓存服务,用于获取 reference 历史数据\n\n Returns:\n 消息列表\n \"\"\"\n site_id = context[\"site_id\"]\n assistant_id = context[\"assistant_id\"]\n member_id = context[\"member_id\"]\n app4_result = context.get(\"app4_result\", {})\n\n # 构建 reference:最近 2 套 App8 历史\n reference = _build_reference(site_id, member_id, cache_svc)\n\n system_content = {\n \"task\": \"基于关系分析和任务建议,生成沟通话术参考。\",\n \"app_id\": APP_ID,\n \"task_suggestion\": app4_result,\n \"output_format\": {\n \"tactics\": [\n {\"scenario\": \"场景描述\", \"script\": \"话术内容\"}\n ]\n },\n # TODO: P6-T4 细化 - service_history、assistant_info(随 App4 同步)\n \"data\": {\n \"service_history\": \"待 P6-T4 补充\",\n \"assistant_info\": \"待 P6-T4 补充\",\n },\n \"reference\": reference,\n }\n\n user_content = (\n f\"请为助教 {assistant_id} 生成与会员 {member_id} 沟通的话术参考。\"\n \"返回 tactics 数组,每条包含 scenario 和 script 字段。\"\n )\n\n return [\n {\"role\": \"system\", \"content\": json.dumps(system_content, ensure_ascii=False)},\n {\"role\": \"user\", \"content\": user_content},\n ]\n\n\ndef _build_reference(\n site_id: int,\n member_id: int,\n cache_svc: AICacheService | None,\n) -> dict:\n \"\"\"构建 Prompt reference 字段。\n\n 包含最近 2 套 App8 历史(附 generated_at)。\n 缓存不存在时返回空对象 {}。\n \"\"\"\n if cache_svc is None:\n return {}\n\n reference: dict = {}\n target_id = str(member_id)\n\n # 最近 2 套 App8 历史\n app8_history = cache_svc.get_history(\n CacheTypeEnum.APP8_CLUE_CONSOLIDATED.value, site_id, target_id, limit=2,\n )\n if app8_history:\n reference[\"app8_history\"] = [\n {\n \"result_json\": h.get(\"result_json\"),\n \"generated_at\": h.get(\"created_at\"),\n }\n for h in app8_history\n ]\n\n return reference\n\n\nasync def run(\n context: dict,\n bailian: BailianClient,\n cache_svc: AICacheService,\n conv_svc: ConversationService,\n) -> dict:\n \"\"\"执行 App5 话术参考。\n\n Args:\n context: site_id, assistant_id, member_id, app4_result(dict)\n bailian: 百炼客户端\n cache_svc: 缓存服务\n conv_svc: 对话服务\n\n Returns:\n 百炼返回的结构化 JSON(tactics 数组)\n \"\"\"\n site_id = context[\"site_id\"]\n assistant_id = context[\"assistant_id\"]\n member_id = context[\"member_id\"]\n user_id = context.get(\"user_id\", \"system\")\n nickname = context.get(\"nickname\", \"\")\n\n # 1. 构建 Prompt\n messages = build_prompt(context, cache_svc)\n\n # 2. 创建对话记录\n conversation_id = conv_svc.create_conversation(\n user_id=user_id,\n nickname=nickname,\n app_id=APP_ID,\n site_id=site_id,\n source_context={\"assistant_id\": assistant_id, \"member_id\": member_id},\n )\n\n # 写入 system + user 消息\n conv_svc.add_message(\n conversation_id=conversation_id,\n role=\"system\",\n content=messages[0][\"content\"],\n )\n conv_svc.add_message(\n conversation_id=conversation_id,\n role=\"user\",\n content=messages[1][\"content\"],\n )\n\n # 3. 调用百炼 API\n result, tokens_used = await bailian.chat_json(messages)\n\n # 4. 写入 assistant 消息\n conv_svc.add_message(\n conversation_id=conversation_id,\n role=\"assistant\",\n content=json.dumps(result, ensure_ascii=False),\n tokens_used=tokens_used,\n )\n\n # 5. 写入缓存(target_id = {assistant_id}_{member_id})\n cache_svc.write_cache(\n cache_type=CacheTypeEnum.APP5_TACTICS.value,\n site_id=site_id,\n target_id=f\"{assistant_id}_{member_id}\",\n result_json=result,\n triggered_by=f\"user:{user_id}\",\n )\n\n logger.info(\n \"App5 话术参考完成: site_id=%s assistant=%s member=%s conversation_id=%s tokens=%d\",\n site_id, assistant_id, member_id, conversation_id, tokens_used,\n )\n\n return result\n\n--- /dev/null\n+++ b/apps/backend/app/ai/apps/app6_note.py\n@@ -0,0 +1 @@\n\"\"\"应用 6:备注分析(骨架)。\n\n助教提交备注后自动触发,通过 AI 分析备注内容,\n提取维客线索并评分。\n\n返回 score(1-10)+ clues 数组。\n评分规则:6 分为标准分,重复/低价值/时效性低酌情扣分,高价值信息酌情加分。\n线索 category 限定 6 个枚举值。\n线索提供者标记为当前备注提供人(context.noted_by_name)。\n\napp_id = \"app6_note\"\n\"\"\"\n\nfrom __future__ import annotations\n\nimport json\nimport logging\n\nfrom app.ai.bailian_client import BailianClient\nfrom app.ai.cache_service import AICacheService\nfrom app.ai.conversation_service import ConversationService\nfrom app.ai.schemas import CacheTypeEnum\n\nlogger = logging.getLogger(__name__)\n\nAPP_ID = \"app6_note\"\n\n\ndef build_prompt(\n context: dict,\n cache_svc: AICacheService | None = None,\n) -> list[dict]:\n \"\"\"构建 Prompt 消息列表。\n\n P5-A 阶段:返回占位 Prompt,标注待细化字段。\n P5-B 阶段(P9-T1):补充 consumption_data 等完整数据。\n\n Args:\n context: 包含 site_id, member_id, note_content, noted_by_name\n cache_svc: 缓存服务,用于获取 reference 历史数据\n\n Returns:\n 消息列表\n \"\"\"\n site_id = context[\"site_id\"]\n member_id = context[\"member_id\"]\n note_content = context.get(\"note_content\", \"\")\n noted_by_name = context.get(\"noted_by_name\", \"\")\n\n # 构建 reference:App3 线索 + 最近 2 套 App8 历史\n reference = _build_reference(site_id, member_id, cache_svc)\n\n system_content = {\n \"task\": \"分析备注内容,提取维客线索并评分。\",\n \"app_id\": APP_ID,\n \"rules\": {\n \"category_enum\": [\n \"客户基础\", \"消费习惯\", \"玩法偏好\",\n \"促销偏好\", \"社交关系\", \"重要反馈\",\n ],\n \"providers\": noted_by_name,\n \"scoring\": \"6 分为标准分,重复/低价值/时效性低酌情扣分,高价值信息酌情加分\",\n \"score_range\": \"1-10\",\n },\n \"output_format\": {\n \"score\": \"1-10 整数\",\n \"clues\": [\n {\n \"category\": \"枚举值(6 选 1)\",\n \"summary\": \"一句话摘要\",\n \"detail\": \"详细说明\",\n \"emoji\": \"表情符号\",\n }\n ],\n },\n \"note_content\": note_content,\n \"noted_by_name\": noted_by_name,\n # TODO: P9-T1 细化 - consumption_data 等客户消费数据\n \"data\": {\n \"consumption_data\": \"待 P9-T1 补充\",\n },\n \"reference\": reference,\n }\n\n user_content = (\n f\"请分析以下备注内容,提取维客线索并评分。\\n\"\n f\"备注提供人:{noted_by_name}\\n\"\n f\"备注内容:{note_content}\\n\"\n \"返回 score(1-10 整数)和 clues 数组。\"\n \"category 必须是:客户基础、消费习惯、玩法偏好、促销偏好、社交关系、重要反馈 之一。\"\n )\n\n return [\n {\"role\": \"system\", \"content\": json.dumps(system_content, ensure_ascii=False)},\n {\"role\": \"user\", \"content\": user_content},\n ]\n\n\ndef _build_reference(\n site_id: int,\n member_id: int,\n cache_svc: AICacheService | None,\n) -> dict:\n \"\"\"构建 Prompt reference 字段。\n\n 包含:\n - App3 客户数据线索(最新一条,如有)\n - 最近 2 套 App8 维客线索整理历史(附 generated_at)\n\n 缓存不存在时返回空对象 {}。\n \"\"\"\n if cache_svc is None:\n return {}\n\n reference: dict = {}\n target_id = str(member_id)\n\n # App3 客户数据线索\n app3_latest = cache_svc.get_latest(\n CacheTypeEnum.APP3_CLUE.value, site_id, target_id,\n )\n if app3_latest:\n reference[\"app3_clues\"] = {\n \"result_json\": app3_latest.get(\"result_json\"),\n \"generated_at\": app3_latest.get(\"created_at\"),\n }\n\n # 最近 2 套 App8 历史\n app8_history = cache_svc.get_history(\n CacheTypeEnum.APP8_CLUE_CONSOLIDATED.value, site_id, target_id, limit=2,\n )\n if app8_history:\n reference[\"app8_history\"] = [\n {\n \"result_json\": h.get(\"result_json\"),\n \"generated_at\": h.get(\"created_at\"),\n }\n for h in app8_history\n ]\n\n return reference\n\n\nasync def run(\n context: dict,\n bailian: BailianClient,\n cache_svc: AICacheService,\n conv_svc: ConversationService,\n) -> dict:\n \"\"\"执行 App6 备注分析。\n\n Args:\n context: site_id, member_id, note_content, noted_by_name\n bailian: 百炼客户端\n cache_svc: 缓存服务\n conv_svc: 对话服务\n\n Returns:\n 百炼返回的结构化 JSON(score + clues 数组)\n \"\"\"\n site_id = context[\"site_id\"]\n member_id = context[\"member_id\"]\n user_id = context.get(\"user_id\", \"system\")\n nickname = context.get(\"nickname\", \"\")\n\n # 1. 构建 Prompt\n messages = build_prompt(context, cache_svc)\n\n # 2. 创建对话记录\n conversation_id = conv_svc.create_conversation(\n user_id=user_id,\n nickname=nickname,\n app_id=APP_ID,\n site_id=site_id,\n source_context={\"member_id\": member_id},\n )\n\n # 写入 system + user 消息\n conv_svc.add_message(\n conversation_id=conversation_id,\n role=\"system\",\n content=messages[0][\"content\"],\n )\n conv_svc.add_message(\n conversation_id=conversation_id,\n role=\"user\",\n content=messages[1][\"content\"],\n )\n\n # 3. 调用百炼 API\n result, tokens_used = await bailian.chat_json(messages)\n\n # 4. 写入 assistant 消息\n conv_svc.add_message(\n conversation_id=conversation_id,\n role=\"assistant\",\n content=json.dumps(result, ensure_ascii=False),\n tokens_used=tokens_used,\n )\n\n # 5. 写入缓存(score 存入 ai_cache.score)\n score = result.get(\"score\")\n cache_svc.write_cache(\n cache_type=CacheTypeEnum.APP6_NOTE_ANALYSIS.value,\n site_id=site_id,\n target_id=str(member_id),\n result_json=result,\n triggered_by=f\"user:{user_id}\",\n score=score,\n )\n\n logger.info(\n \"App6 备注分析完成: site_id=%s member_id=%s score=%s conversation_id=%s tokens=%d\",\n site_id, member_id, score, conversation_id, tokens_used,\n )\n\n return result\n\n\n[TRUNCATED: diff exceeds 30KB]", "latest_prompt_log": "- [P20260315-095422] 2026-03-15 09:54:22 +0800\n - summary: CONTEXT TRANSFER: We are continuing a conversation that had gotten too long. Here is a summary: --- ## TASK 1: Consolida…\n - prompt:\n```text\nCONTEXT TRANSFER: We are continuing a conversation that had gotten too long. Here is a summary:\n\n---\n## TASK 1: Consolidate DDL files from database to `docs/database/ddl/`\n\n**STATUS**: done\n\n**USER QUERIES**: 1 (\"依据现在数据库现状,整理所有DDL文件\")\n\n**DETAILS**:\n- Ran `python scripts/ops/gen_consolidated_ddl.py` from `C:\\NeoZQYY` to export fresh DDL from test databases (`test_etl_feiqiu` and `test_zqyy_app`)\n- Generated 10 DDL files in `docs/database/ddl/`, merging all migrations from 2026-02-27 through 2026-03-09\n- Key changes reflected: dwd 44→42 tables (trash_event cleanup), dws 34→36 tables (project tags), biz 4→7 tables (AI tables), tenant_id int→bigint, DWS numeric precision expansion, cfg_area_category table-level mapping, card pay field unification\n- Verified all key changes with grep searches\n- Updated `db/README.md` and `docs/database/README.md` with new statistics\n\n**FILEPATHS**: `docs/database/ddl/`, `scripts/ops/gen_consolidated_ddl.py`, `db/README.md`, `docs/database/README.md`\n\n## TASK 2: Merge seed data into DDL files and delete migration/seed SQL files\n\n**STATUS**: done\n\n**USER QUERIES**: 2 (\"把已经纳入的sql文件删除吧\"), 3 (\"种子数据也合并到相应的sql文件。4 个 FDW 配置脚本保留\")\n\n**DETAILS**:\n- Merged 7 seed data files into corresponding DDL files:\n - `seed_ods_tasks.sql` + `seed_scheduler_tasks.sql` → `etl_feiqiu__meta.sql`\n - `seed_dws_config.sql` + `seed_index_parameters.sql` → `etl_feiqiu__dws.sql`\n - `admin_web_seed.sql` → `zqyy_app__public.sql`\n - `p3_seed_roles_permissions.sql` → `zqyy_app__auth.sql`\n - `p4_seed_trigger_jobs.sql` → `zqyy_app__biz.sql`\n- Deleted all 22 etl_feiqiu migration scripts + 4 seed files\n- Deleted all 15 zqyy_app migration scripts + 1 seed file\n- Preserved 4 FDW config scripts in `db/fdw/` and all `.gitkeep` files\n- Updated `db/README.md` directory structure and migration management section\n- Updated `docs/database/README.md` related resources section\n\n**FILEPATHS**: `docs/database/ddl/etl_feiqiu__meta.sql`, `docs/database/ddl/etl_feiqiu__dws.sql`, `docs/database/ddl/zqyy_app__public.sql`, `docs/database/ddl/zqyy_app__auth.sql`, `docs/database/ddl/zqyy_app__biz.sql`, `db/README.md`, `docs/database/README.md`\n\n## TASK 3: Analyze relationship between `docs/database/` and `apps/etl/connectors/feiqiu/docs/database/` and determine if they can be merged\n\n**STATUS**: in-progress\n\n**USER QUERIES**: 4 (\"docs\\database 下的文档和apps\\etl\\connectors\\feiqiu\\docs\\database下的文档 什么关系?能合并么?\")\n\n**DETAILS**:\n- Analysis completed via context-gatherer subagent. Key findings:\n - `docs/database/`: Project-level — DDL baseline + ODS→DWD cross-layer mappings + migration change records + business DB docs (auth/biz/public)\n - `apps/etl/.../docs/database/`: ETL module-specific — per-table field docs organized by schema layer (ODS 24 + DWD 41 + DWS 31 + ETL_Admin 3), API→OD\n[TRUNCATED]" }