Files
Neo-ZQYY/.kiro/specs/rns1-customer-coach-api/tasks.md

19 KiB
Raw Blame History

Implementation Plan: RNS1.2 客户与助教接口

Overview

基于 design.md 架构,按 T2-1 ~ T2-6 任务结构增量实现 CUST-1、CUST-2、COACH-1 三个接口。先扩展 FDW 查询层,再逐步构建 service → router → 集成测试 → 属性测试。所有金额使用 items_sum 口径,助教费用使用 assistant_pd_money + assistant_cx_money 拆分,会员信息通过 member_id JOIN v_dim_member

Tasks

  • 1. Pydantic Schema 定义与项目结构搭建

    • 1.1 创建 apps/backend/app/schemas/xcx_customers.py,定义 CUST-1 和 CUST-2 所有响应模型
      • CustomerDetailResponseCustomerRecordsResponse 及所有嵌套模型(AiInsightAiStrategyMetricItemCoachTaskFavoriteCoachCoachServiceItemConsumptionRecordRetentionClueCustomerNoteServiceRecordItem
      • 所有模型继承 CamelModel,确保 camelCase 序列化
      • Requirements: 1.1-1.7, 2.1-2.3, 3.1-3.2, 4.1-4.6, 5.1-5.4, 6.1-6.4, 7.1-7.9
    • 1.2 创建 apps/backend/app/schemas/xcx_coaches.py,定义 COACH-1 所有响应模型
      • CoachDetailResponse 及所有嵌套模型(PerformanceMetricsIncomeItemIncomeSectionCoachTaskItemAbandonedTaskTopCustomerCoachServiceRecordHistoryMonthCoachNoteItem
      • Requirements: 8.1-8.5, 9.1-9.4, 10.1-10.7, 11.1-11.5, 12.1-12.6
  • 2. FDW 查询层扩展T2-1 基础)

    • 2.1 在 apps/backend/app/services/fdw_queries.py 新增客户相关查询函数
      • get_consumption_60d(conn, site_id, member_id) — 近 60 天消费,使用 ledger_amountitems_sum过滤 is_delete=0
      • get_relation_index(conn, site_id, member_id) — 关系指数列表,来源 v_dws_member_assistant_relation_index,按 relation_index 降序
      • get_consumption_records(conn, site_id, member_id, limit, offset) — 消费记录嵌套查询JOIN v_dim_assistant,过滤 settle_type IN (1,3) + is_delete=0
      • get_total_service_count(conn, site_id, member_id) — 累计服务总次数
      • get_coach_60d_stats(conn, site_id, assistant_id, member_id) — 特定助教对特定客户近 60 天统计
      • 所有 SQL 使用 AS 别名映射design.md 列名映射表)
      • Requirements: 1.5, 1.6, 4.3-4.6, 5.3, 6.1, 7.4, 7.7-7.8, 13.2-13.7, 13.10
    • 2.2 在 apps/backend/app/services/fdw_queries.py 新增助教相关查询函数
      • get_assistant_info(conn, site_id, assistant_id) — 助教基本信息,来源 v_dim_assistant
      • get_salary_calc_multi_months(conn, site_id, assistant_id, months) — 批量多月绩效数据
      • get_monthly_customer_count(conn, site_id, assistant_id, months) — 各月不重复客户数,COUNT(DISTINCT tenant_member_id),过滤 is_delete=0
      • get_coach_top_customers(conn, site_id, assistant_id, limit=20) — TOP 客户JOIN v_dim_memberDQ-6+ v_dim_member_card_accountDQ-7consume 使用 ledger_amount
      • get_customer_service_records(conn, site_id, member_id, year, month, table, limit, offset) — 按月服务记录 + 月度统计汇总
      • Requirements: 8.2, 8.4, 9.3, 10.2-10.4, 10.6-10.7, 12.3, 12.6, 13.5-13.7, 13.10
    • 2.3 为新增 FDW 查询函数编写单元测试
      • 测试文件:apps/backend/tests/unit/test_fdw_queries_rns12.py
      • 验证 DQ-6 JOIN 正确性、DQ-7 余额查询、is_delete=0 排除、items_sum 口径
      • Requirements: 13.2-13.7
  • 3. CUST-1 客户详情 Service + RouterT2-1 ~ T2-3

    • 3.1 创建 apps/backend/app/services/customer_service.py,实现 get_customer_detail()
      • 核心字段:调用 fdw_queries.get_member_info() → 基础信息,get_member_balance() → balanceget_consumption_60d() → consumption60dget_last_visit_days() → daysSinceVisit
      • 手机号脱敏逻辑("139****5678" 格式)
      • Banner 字段查询失败返回 null(需求 1.7
      • Requirements: 1.1-1.7, 13.1-13.2
    • 3.2 在 customer_service.py 实现 _build_ai_insight()_build_retention_clues()_build_notes()
      • aiInsight查询 biz.ai_cache WHERE cache_type='app4_analysis' AND target_id=customerId,解析 cache_value JSON
      • retentionClues查询 public.member_retention_clue,按 created_at 倒序
      • notes查询 biz.notes WHERE target_type='member',最多 20 条,按 created_at 倒序
      • 每个模块独立 try/except 优雅降级
      • Requirements: 2.1-2.3, 3.1-3.2, 13.8-13.9
    • 3.3 在 customer_service.py 实现 _build_consumption_records()
      • 调用 fdw_queries.get_consumption_records() 获取结算单列表
      • 构建 coaches 子数组:fee 使用 assistant_pd_money(基础课)/ assistant_cx_money(激励课)
      • totalAmount 使用 items_sum 口径,tableFee 使用 table_charge_moneyfoodAmount 使用 goods_money
      • 过滤 settle_type IN (1, 3) + is_delete=0
      • Requirements: 4.1-4.6, 13.3-13.4, 13.7
    • 3.4 在 customer_service.py 实现 _build_coach_tasks()T2-2
      • 查询 biz.coach_tasks WHERE member_id=customerId
      • 对每位助教:fdw_queries.get_salary_calc() 获取等级,get_coach_60d_stats() 获取近 60 天统计
      • 映射 levelColor/taskColor/bgClass
      • metrics 返回:服务次数、总时长、次均时长
      • Requirements: 5.1-5.4
    • 3.5 在 customer_service.py 实现 _build_favorite_coaches()T2-3
      • 调用 fdw_queries.get_relation_index() 获取关系指数列表,按降序排列
      • emoji 映射:relationIndex >= 0.7"💖"< 0.7"💛"
      • stats 4 项指标:基础课时(assistant_pd_money)、激励课时(assistant_cx_money)、上课次数、充值金额
      • Requirements: 6.1-6.4
    • 3.6 创建 apps/backend/app/routers/xcx_customers.py,注册 CUST-1 端点
      • GET /{customer_id}customer_service.get_customer_detail()
      • Depends(require_approved()) 权限检查
      • main.py 注册 router
      • Requirements: 13.1
    • 3.7 为 CUST-1 编写单元测试
      • 测试文件:apps/backend/tests/unit/test_customer_detail.py
      • 验证完整响应结构、Banner 字段、aiInsight 降级、consumptionRecords 嵌套、coachTasks metrics、favoriteCoaches 排序
      • Requirements: 1.1-6.4
  • 4. Checkpoint — 确保 CUST-1 所有测试通过

    • 确保所有测试通过ask the user if questions arise.
  • 5. CUST-2 客户服务记录 Service + RouterT2-4

    • 5.1 在 customer_service.py 实现 get_customer_records()
      • 接受 yearmonthtable(可选)、pagepage_size 参数
      • 调用 fdw_queries.get_member_info() → customerName/customerPhoneDQ-6
      • 调用 fdw_queries.get_customer_service_records() → 按月分页记录
      • 聚合 monthCount/monthHours
      • 调用 fdw_queries.get_total_service_count() → totalServiceCount跨月
      • 每条记录含 recordTypecourse/recharge)和 isEstimate
      • income 使用 items_sum 口径,排除 is_delete!=0
      • create_time 倒序,返回 hasMore
      • Requirements: 7.1-7.9
    • 5.2 在 xcx_customers.py router 注册 CUST-2 端点
      • GET /{customer_id}/recordscustomer_service.get_customer_records()
      • Query 参数:year: intmonth: int (ge=1, le=12)table: str | Nonepage: int (ge=1)page_size: int (ge=1, le=100)
      • Depends(require_approved()) 权限检查
      • Requirements: 7.1, 13.1
    • 5.3 为 CUST-2 编写单元测试
      • 测试文件:apps/backend/tests/unit/test_customer_records.py
      • 验证按月查询、monthCount/monthHours 汇总、totalServiceCount 跨月、hasMore 分页、recordType/isEstimate
      • Requirements: 7.1-7.9
  • 6. COACH-1 助教详情 Service + RouterT2-5

    • 6.1 创建 apps/backend/app/services/coach_service.py,实现 get_coach_detail()
      • 基础信息:fdw_queries.get_assistant_info() → name/avatar/skills/workYears/hireDate
      • 绩效:fdw_queries.get_salary_calc() → monthlyHourseffective_hours/monthlySalarygross_salary/perfCurrent/perfTarget
      • customerBalancefdw_queries.get_member_balance() 聚合该助教所有客户余额
      • tasksCompletedbiz.coach_tasks 当月 status='completed' 计数
      • Requirements: 8.1-8.5
    • 6.2 在 coach_service.py 实现 _build_income()_build_tier_nodes()
      • incomethisMonth/lastMonth 各含 4 项(基础课时费 assistant_pd_money/base_income、激励课时费 assistant_cx_money/bonus_income、充值提成、酒水提成)
      • v_dws_assistant_salary_calc 分别查询当月和上月(salary_monthYYYY-MM-01
      • tierNodes档位节点数组[0, 100, 130, 160, 190, 220]
      • Requirements: 9.1-9.4
    • 6.3 在 coach_service.py 实现 _build_top_customers()_build_service_records()
      • topCustomers调用 fdw_queries.get_coach_top_customers(),最多 20 条
      • heartEmoji 三级映射:score >= 0.7"❤️"0.3 <= score < 0.7"💛"score < 0.3"🤍"
      • consume 使用 items_sum 口径balance 通过 v_dim_member_card_accountDQ-7客户姓名通过 v_dim_memberDQ-6
      • serviceRecords近期服务记录income 使用 ledger_amount,排除 is_delete!=0
      • Requirements: 10.1-10.7
    • 6.4 在 coach_service.py 实现 _build_task_groups()_build_notes()
      • 查询 biz.coach_tasks WHERE assistant_id=coachId
      • 按 status 分组:active → visibleTasksinactive → hiddenTasksabandoned → abandonedTasks
      • visible/hidden关联 biz.notes 获取备注列表(task_id 关联,按 created_at 倒序)
      • abandonedabandon_reason
      • notes助教相关备注最多 20 条
      • Requirements: 11.1-11.5
    • 6.5 在 coach_service.py 实现 _build_history_months()T2-6
      • fdw_queries.get_salary_calc_multi_months() → 最近 6 个月工时/工资
      • fdw_queries.get_monthly_customer_count() → 各月客户数
      • biz.coach_tasks → 各月回访完成数(task_type='follow_up_visit' AND status='completed')和召回完成数(task_type IN ('high_priority_recall', 'priority_recall') AND status='completed'
      • 本月 estimated=True,历史月份 estimated=False
      • 格式化customers → "22人"hours → "87.5h"salary → "¥6,950"
      • Requirements: 12.1-12.6
    • 6.6 创建 apps/backend/app/routers/xcx_coaches.py,注册 COACH-1 端点
      • GET /{coach_id}coach_service.get_coach_detail()
      • Depends(require_approved()) 权限检查
      • main.py 注册 router
      • Requirements: 13.1
    • 6.7 为 COACH-1 编写单元测试
      • 测试文件:apps/backend/tests/unit/test_coach_detail.py
      • 验证完整响应结构、performance 6 指标、income 本月/上月、topCustomers heartEmoji、historyMonths 排序与 estimated、任务分组
      • Requirements: 8.1-12.6
  • 7. Checkpoint — 确保 CUST-1 + CUST-2 + COACH-1 所有测试通过

    • 确保所有测试通过ask the user if questions arise.
  • 8. 优雅降级与权限校验测试

    • 8.1 为优雅降级编写单元测试
      • 测试文件:apps/backend/tests/unit/test_degradation_rns12.py
      • 验证 aiInsight/coachTasks/favoriteCoaches/consumptionRecords/historyMonths 各模块查询失败时返回空默认值,不影响 HTTP 200
      • Requirements: 13.8-13.9
    • 8.2 为权限校验编写单元测试
      • 测试文件:apps/backend/tests/unit/test_auth_rns12.py
      • 验证未审核用户 403、客户不存在 404、助教不存在 404
      • Requirements: 13.1
  • 9. 属性测试T2-6 PBT

    • 9.1 编写属性测试:消费记录金额拆分不变量
      • Property 1: 消费记录金额拆分不变量
      • 测试文件:tests/test_rns12_properties.py
      • 生成器:st.floats(min_value=0, max_value=1e5) 生成 tableFee/foodAmount/coachFees
      • 验证:abs(totalAmount - (tableFee + foodAmount + sum(fees))) < 0.01
      • Validates: Requirements 14.1, 4.4
    • 9.2 编写属性测试:废单排除一致性
      • Property 2: 废单排除一致性
      • 生成器:st.lists(st.fixed_dictionaries({is_delete: st.integers(0,2), ...}))
      • 验证:过滤后结果中所有 is_delete == 0
      • Validates: Requirements 14.8, 4.6, 7.8, 10.7
    • 9.3 编写属性测试:助教费用拆分正确性
      • Property 3: 助教费用拆分正确性
      • 生成器:st.floats 生成 pd_money/cx_money + st.sampled_from(["基础课","激励课"])
      • 验证:基础课 → pd_money激励课 → cx_money两者之和 = 总额
      • Validates: Requirements 14.2, 4.3, 9.2
    • 9.4 编写属性测试favoriteCoaches 排序不变量
      • Property 4: favoriteCoaches 排序不变量
      • 生成器:st.lists(st.floats(0, 1)) 生成 relationIndex 列表
      • 验证:排序后每项 ≥ 下一项
      • Validates: Requirements 14.5, 6.1
    • 9.5 编写属性测试historyMonths 排序与预估标记
      • Property 5: historyMonths 排序与预估标记
      • 生成器:st.lists(st.dates(), min_size=1) 生成月份列表
      • 验证:降序排列,首项 estimated=True,其余 False
      • Validates: Requirements 14.6, 12.5
    • 9.6 编写属性测试:列表上限约束
      • Property 6: 列表上限约束
      • 生成器:st.integers(0, 100) 生成记录数
      • 验证notes ≤ 20topCustomers ≤ 20
      • Validates: Requirements 3.2, 10.1, 11.5
    • 9.7 编写属性测试:月度汇总聚合正确性
      • Property 7: 月度汇总聚合正确性
      • 生成器:st.lists(st.fixed_dictionaries({hours: st.floats(0,10), income: st.floats(0,1e4)}))
      • 验证count=lenmonthHours=sum(hours)
      • Validates: Requirements 7.6, 5.3
    • 9.8 编写属性测试daysSinceVisit 计算正确性
      • Property 8: daysSinceVisit 计算正确性
      • 生成器:st.dates(max_value=date.today())
      • 验证days = (today - date).days非负整数
      • Validates: Requirements 1.6
    • 9.9 编写属性测试emoji 映射正确性
      • Property 9: emoji 映射正确性
      • 生成器:st.floats(0, 1) 生成 relationIndex
      • 验证CUST-1 两级映射≥0.7→💖<0.7→💛COACH-1 三级映射≥0.7→❤️0.3-0.7→💛<0.3→🤍
      • Validates: Requirements 6.4
    • 9.10 编写属性测试:优雅降级
      • Property 10: 优雅降级
      • 生成器:st.sampled_from(MODULES) 选择失败模块
      • 验证失败模块返回空默认值其他模块正常HTTP 200
      • Validates: Requirements 1.7, 13.8
    • 9.11 编写属性测试:任务分组正确性
      • Property 11: 任务分组正确性
      • 生成器:st.lists(st.fixed_dictionaries({status: st.sampled_from(STATUSES)}))
      • 验证active→visibleinactive→hiddenabandoned→abandoned无交集并集=原集合
      • Validates: Requirements 11.1
    • 9.12 编写属性测试:数据隔离不变量
      • Property 12: 数据隔离不变量
      • 生成器:st.integers(1, 1000) 生成 customerId/coachId
      • 验证coachTasks 每条 member_id=customerIdserviceRecords 每条 assistant_id=coachId
      • Validates: Requirements 14.3, 14.4
    • 9.13 编写属性测试:分页与 hasMore 正确性
      • Property 13: 分页与 hasMore 正确性
      • 生成器:st.integers(1,100) total + st.integers(1,10) page/pageSize
      • 验证:记录数 ≤ pageSizehasMore = (total > page*pageSize)
      • Validates: Requirements 7.9
    • 9.14 编写属性测试:幂等性
      • Property 14: 幂等性
      • 生成器:st.integers(1,12) month + st.integers(2020,2026) year
      • 验证f(x) == f(x) 对 monthCount/monthHours
      • Validates: Requirements 14.7
  • 10. Final checkpoint — 确保所有测试通过

    • 确保所有测试通过ask the user if questions arise.
  • 11. 前端到数据库全链路测试

    • 11.1 启动后端服务,使用测试库(test_zqyy_app)验证 CUST-1、CUST-2、COACH-1 三个端点的完整请求-响应链路
      • 使用真实 FDW 连接(test_etl_feiqiu)验证 SQL 查询正确性
      • 验证 JSON 响应结构与 Schema 定义一致camelCase 序列化)
      • 验证权限校验(require_approved())在真实请求中生效
    • 11.2 小程序前端联调验证(如已有对应页面)
      • 确认前端页面能正确调用新增 API 并渲染数据
      • 验证空数据/降级场景下前端不崩溃
      • 如前端页面尚未开发,记录待联调清单供后续 RNS 任务使用
  • 12. 项目文档更新与落地

    • 12.1 更新 docs/contracts/openapi/backend-api.json,补充 CUST-1、CUST-2、COACH-1 三个端点的 OpenAPI 定义
    • 12.2 更新 docs/architecture/backend-architecture.md,补充新增的 customer_servicecoach_service 模块及路由注册说明
    • 12.3 更新 docs/database/BD_Manual_biz_tables.md,补充本次新增/引用的 biz.coach_tasksbiz.notesbiz.ai_cache 表的使用说明(如有新增字段或新用法)
    • 12.4 更新 docs/DOCUMENTATION-MAP.md,确保新增文档条目已索引
    • 12.5 更新 docs/miniprogram-dev/API-contract.md,补充 CUST-1、CUST-2、COACH-1 的接口契约(请求/响应示例)
  • 13. 数据库变更审计与 DDL 合并

    • 13.1 审计本次实现中对数据库的改动新建表、新增字段、新增索引、FDW 映射变更等)
      • 检查 biz.coach_tasksbiz.notesbiz.ai_cachepublic.member_retention_clue 是否需要新建或变更
      • 检查 FDW 外部表映射是否需要更新(新增视图引用等)
    • 13.2 将所有数据库变更合并到主 DDL 文件
      • 业务库变更 → db/zqyy_app/ 对应 DDL 文件
      • FDW 变更 → db/fdw/ 对应 DDL 文件
      • 编写日期前缀迁移脚本(如有 schema 变更)
    • 13.3 更新 BD 手册记录变更
      • 业务库 → docs/database/BD_Manual_biz_tables.md
      • FDW → docs/database/BD_Manual_fdw.md(如有变更)
      • 记录变更原因、影响范围、回滚 SQL

Notes

  • Tasks marked with * are optional and can be skipped for faster MVP
  • 所有金额字段统一使用 items_sum 口径DWD-DOC 强制规则 1禁止 consume_money
  • 助教费用使用 assistant_pd_money + assistant_cx_money 拆分DWD-DOC 强制规则 2禁止 service_fee
  • 会员信息通过 member_id JOIN v_dim_memberDQ-6余额通过 v_dim_member_card_accountDQ-7
  • 废单排除统一使用 is_delete=0,禁止引用已废弃的 dwd_assistant_trash_event
  • Property tests validate universal correctness properties from design.md
  • Checkpoints ensure incremental validation