chore(docs): Wave 0 调研产出 + P0/P1/P2 反馈调研
建立项目级标杆文档 docs/_overview/ 作为产品全景索引, 解决"PRD 零碎、文档膨胀、跨子系统调研无入口"的问题。 主要内容: - 00-index 总索引 + 维护协议 + 与 CLAUDE.md 关系 - 01-product-overview 产品全景脑图(6 角色 / 6 子系统 / 数据流 / 7 业务概念 / 8+1 AI 矩阵 / 22 术语) - 02a-miniprogram-page-matrix 小程序 21 页业务指纹 - 02b-adminweb-page-matrix admin-web 19 路由业务指纹 - 03-test-spec 测试规范 (L1-L5 分层 + 走查模板 + 75-95 case 估算) - 04-doc-conflicts 39 条冲突索引(P0×8 / P1×13 / P2×13 + 5 子项) - 04a/b/c-conflicts-*-detail 业务故事卡(7 字段:关联/逻辑/影响/选项/判定) - 05-orphan-pages-cleanup admin-web 6 孤儿页面处置(1 归档 + 4 保留) - WAVES-MASTER-PLAN.md 全 Wave 主计划(0-5,共 22-32 工作日) - WAVE-1-KICKOFF.md Wave 1 实施 kickoff - GLOBAL-DECISION-DASHBOARD.md 全局决策仪表板 反馈调研产物: - 04a-feedback/ P0 两轮反馈(8+8 项决策 + D-1/2/3 + F-1/2 子代理产出) - 04b-feedback/ P1 两轮反馈(13+1+5 项 + E-1/2/3/4 + G-1/2 子代理产出) - 04c-feedback/ P2 反馈(13 项 + 5 子项 + H-1/2/3 子代理产出) - NEO-DECISIONS-LOG 累积决策记录 关键追加发现 8 处 D Bug(原蓝本 0): - P0-3 看板沙箱接入(Wave 1 W1-T1) - P0-5 致命 1 (4 处 fdw_etl 残留, 已修 commit17f045a) - P0-5 致命 2 (JWT aud 缺失, 已修 commit17f045a) - P0-6 clearAllTasks 守卫 (Wave 3) - P0-8 DBViewer 黑名单漏 (已修 commit17f045a) - P1-3 task-detail 跳转传 task_id 而非 customer_id - P2-7 board-finance 隐式 null - 2 个独立 Bug (page_context.created_at + ClueCategory 字典) 参考: docs/_overview/00-index.md
This commit is contained in:
425
docs/_overview/04c-feedback/P2-4-and-P2-7-research.md
Normal file
425
docs/_overview/04c-feedback/P2-4-and-P2-7-research.md
Normal file
@@ -0,0 +1,425 @@
|
||||
# P2-4 课程体系 + P2-7 看板切换限制 调研
|
||||
|
||||
> 日期:2026-05-04
|
||||
> 触发:Neo 在 04c 反馈中提出
|
||||
> 调研环境:测试库 `test_etl_feiqiu`(只 SELECT)+ ETL/后端/小程序源码
|
||||
> 子代理:无(主流程)
|
||||
|
||||
---
|
||||
|
||||
## 一、P2-4 课程体系实证
|
||||
|
||||
### 1.1 数据库层(SQL 结果)
|
||||
|
||||
#### A. cfg_skill_type — 课程类型主映射(skill_id → BASE/BONUS/ROOM)
|
||||
|
||||
实测共 6 行(`SELECT * FROM dws.cfg_skill_type ORDER BY skill_type_id`):
|
||||
|
||||
| skill_type_id | skill_id | skill_name | course_type_code | course_type_name | description |
|
||||
|---|---|---|---|---|---|
|
||||
| 1 | 2791903611396869 | 台球基础陪打 | **BASE** | 基础课 | 按助教等级计价 |
|
||||
| 2 | 2807440316432197 | 台球超休服务 | **BONUS** | 附加课 | 固定 190 元/小时 |
|
||||
| 3 | 2807440316432198 | 包厢服务 | **BASE** | 基础课 | 包厢服务:**归入基础课统计**,统一 138 元/小时 |
|
||||
| 4 | 2790683529513797 | 基础课 | **BASE** | 基础课 | 飞球系统原始课程类型 |
|
||||
| 5 | 2790683529513798 | 附加课 | **BONUS** | 附加课 | 飞球系统原始课程类型 |
|
||||
| 6 | 3039912271463941 | 包厢课 | **BASE** | 基础课 | 飞球系统原始课程类型(2026-03-24 补录) |
|
||||
|
||||
> **关键事实**:数据库里**只有 2 个课程类型代码**:`BASE` / `BONUS`,**没有 `ROOM`**。
|
||||
> "包厢服务/包厢课"两个 skill 都被标记为 `BASE`。
|
||||
> 这与 BD 手册 `BD_manual_cfg_skill_type.md`(写有 ROOM 枚举)不一致 — **数据库种子数据是真相**。
|
||||
|
||||
#### B. cfg_assistant_level_price — 助教等级单价(只区分 BASE/BONUS,无 ROOM)
|
||||
|
||||
实测 5 行:
|
||||
|
||||
| level_code | level_name | base_course_price | bonus_course_price |
|
||||
|---|---|---|---|
|
||||
| 10 | 初级 | 98.00 | 190.00 |
|
||||
| 20 | 中级 | 108.00 | 190.00 |
|
||||
| 30 | 高级 | 118.00 | 190.00 |
|
||||
| 40 | 星级 | 138.00 | 190.00 |
|
||||
| 8 | 助教管理 | 98.00 | 190.00 |
|
||||
|
||||
> 表本身**只有 base/bonus 两列**,包厢课没有独立列,全靠代码层硬编码 138 元(`dws.salary.room_course_price`)。
|
||||
|
||||
#### C. dws_assistant_daily_detail / monthly_summary — 服务计数把"包厢"独立成第 3 类
|
||||
|
||||
实测字段(`information_schema.columns`):
|
||||
|
||||
```text
|
||||
total_service_count / base_service_count / bonus_service_count / room_service_count
|
||||
total_seconds / base_seconds / bonus_seconds / room_seconds
|
||||
total_hours / base_hours / bonus_hours / room_hours
|
||||
total_ledger_amount / base_ledger_amount / bonus_ledger_amount / room_ledger_amount
|
||||
```
|
||||
|
||||
> **结论**:配置层只有 2 类(BASE/BONUS),但 DWS 汇总层把 `room_*` 单独抽出 → 是**派生第 3 个统计维度**,不是配置数据维度。
|
||||
|
||||
#### D. dws_assistant_order_contribution — **没有 course_type 字段**
|
||||
|
||||
字段总览(14 列):`contribution_id, site_id, tenant_id, assistant_id, assistant_nickname, stat_date, order_gross_revenue, order_net_revenue, time_weighted_revenue, time_weighted_net_revenue, order_count, total_service_seconds, created_at, updated_at`
|
||||
|
||||
> **没有按课程类型拆分**。该表是助教订单营收聚合,与课程分类无关。
|
||||
|
||||
#### E. cfg_area_category / dws_assistant_project_tag — **完全独立的"项目分类"体系**
|
||||
|
||||
```text
|
||||
cfg_area_category 6 行: BILLIARD/SNOOKER/MAHJONG/KTV/SPECIAL/OTHER
|
||||
dws_assistant_project_tag 实测 4 大项目: BILLIARD(158)/KTV(120)/MAHJONG(112)/SNOOKER(106)
|
||||
```
|
||||
|
||||
> 这是**与课程类型平行的另一个分类体系**:从台桌名映射到台球/斯诺克/麻将/K歌/补时长/其他。
|
||||
> 字段名 `category_code`,**不复用 course_type 字段**。
|
||||
|
||||
#### F. 全库扫描:**没有任何"二级嵌套课程表"**
|
||||
|
||||
```sql
|
||||
SELECT table_schema, table_name FROM information_schema.tables
|
||||
WHERE table_name ILIKE '%course%';
|
||||
-- 结果: 0 行
|
||||
```
|
||||
|
||||
course_type 概念只通过 `cfg_skill_type.course_type_code` 这一个字段存在,**不存在父子层级、不存在 dim_course、不存在 cfg_course_*`**。
|
||||
|
||||
---
|
||||
|
||||
### 1.2 后端层映射
|
||||
|
||||
#### A. ETL `apps/etl/connectors/feiqiu/tasks/dws/base_dws_task.py`
|
||||
|
||||
```python
|
||||
class CourseType(Enum):
|
||||
BASE = "BASE"
|
||||
BONUS = "BONUS"
|
||||
ROOM = "ROOM" # 包厢课 — 代码层定义,但数据库没有 ROOM 行,实际走默认 BASE 分支
|
||||
|
||||
def get_course_type(self, skill_id: int) -> CourseType:
|
||||
skill_config = config.skill_types.get(skill_id)
|
||||
if skill_config:
|
||||
code = skill_config.get('course_type_code', 'BASE')
|
||||
if code == 'BONUS': return CourseType.BONUS
|
||||
if code == 'ROOM': return CourseType.ROOM # 永不命中
|
||||
return CourseType.BASE
|
||||
return CourseType.BASE
|
||||
```
|
||||
|
||||
> **关键**:`CourseType.ROOM` 是代码层枚举,但 cfg_skill_type 里没有 `course_type_code='ROOM'` 的行。
|
||||
> 所有"包厢"相关的 skill 实际返回 `CourseType.BASE`。
|
||||
|
||||
#### B. ETL `apps/etl/connectors/feiqiu/tasks/dws/assistant_daily_task.py:313-321`
|
||||
|
||||
```python
|
||||
course_type = self.get_course_type(skill_id) if skill_id else CourseType.BASE
|
||||
is_base = course_type == CourseType.BASE
|
||||
is_bonus = course_type == CourseType.BONUS
|
||||
is_room = course_type == CourseType.ROOM # 永远 False
|
||||
```
|
||||
|
||||
> 但是 daily/monthly 表却有 `base/bonus/room` 三列。**这说明 room_* 三列实测一直是 0**(或被其他逻辑填充)。
|
||||
> 需进一步用 SQL 验证(本次未跑该样本数据 SELECT,留作下一步)。
|
||||
> **暂判定**:`room_*` 三列在配置正确的情况下应为 0,实际"包厢服务"全部累加进 `base_*` 三列。
|
||||
|
||||
#### C. ETL `apps/etl/connectors/feiqiu/tasks/dws/assistant_salary_task.py:31-36`
|
||||
|
||||
工资计算公式:
|
||||
|
||||
```python
|
||||
# 基础课收入 = base_hours × (base_price - base_deduction)
|
||||
# 附加课收入 = bonus_hours × bonus_price × (1 - bonus_deduction_ratio)
|
||||
# 包厢课收入 = room_hours × (room_course_price - base_deduction) # room_course_price=138 配置
|
||||
total_course_income = base_income + bonus_income + room_income
|
||||
```
|
||||
|
||||
> 工资公式**显式按 3 类计算**。但因 `room_hours` 在数据流中始终为 0,实际只有 base+bonus 两路。
|
||||
> 这是**代码层为未来留接口**,但当前数据流上**等价于两类**。
|
||||
|
||||
#### D. ETL `coach_area_hours_task.py:36`
|
||||
|
||||
```python
|
||||
"包厢课": "room",
|
||||
```
|
||||
|
||||
唯一一处把"包厢课" skill_name 显式映射到 `room` 标签的地方(用于 area_hours 统计)。
|
||||
|
||||
#### E. 后端 `apps/backend/app/routers/xcx_*` 没有发现 `course_type` 直接字段返回
|
||||
|
||||
- `xcx_board.py` 中 BOARD-1 助教看板返回字段不包含课程类型(返回 perf/salary/sv/task 四维度数据)
|
||||
- `apps/miniprogram/miniprogram/services/api.ts:221` 有 `courseType: string`,这是后端 PERF-2 接口返回(performance-records / coach-service-records 用的同一接口形式)
|
||||
|
||||
> **真实 API 响应里 courseType 直接是中文字符串**(基础课/包厢课/激励课),**不是枚举代码**。
|
||||
|
||||
---
|
||||
|
||||
### 1.3 前端层使用
|
||||
|
||||
#### 三个页面的课程标签映射(完全一致)
|
||||
|
||||
`performance.ts:17-23` / `performance-records.ts:18-24` / `coach-service-records.ts:20-26`,COURSE_TAG_MAP 定义:
|
||||
|
||||
```typescript
|
||||
const COURSE_TAG_MAP: Record<string, string> = {
|
||||
'陪打': 'basic', '基础课': 'basic',
|
||||
'包厢': 'room', '包厢课': 'room',
|
||||
'超休': 'incentive', '激励课': 'incentive', '打赏课': 'incentive',
|
||||
}
|
||||
function courseTagClass(courseType: string): string {
|
||||
return COURSE_TAG_MAP[courseType] || 'basic'
|
||||
}
|
||||
```
|
||||
|
||||
WXSS:三个页面都定义 `.course-tag--basic` / `.course-tag--room` / `.course-tag--incentive` 三个 CSS 类(performance.wxss:699-712 等)。
|
||||
|
||||
> **前端 3 类:basic / room / incentive**,与后端 ETL 的 BASE / BONUS / ROOM 一一对应:
|
||||
|
||||
| 后端 CourseType | 中文(API courseType) | 前端 CSS class |
|
||||
|---|---|---|
|
||||
| BASE | 陪打 / 基础课 | basic |
|
||||
| ROOM(代码层未命中) | 包厢 / 包厢课 | room |
|
||||
| BONUS | 超休 / 激励课 / 打赏课 | incentive |
|
||||
|
||||
#### 不一致点:service-record-card 组件的 typeClass
|
||||
|
||||
`service-record-card.ts:13-16`:
|
||||
|
||||
```typescript
|
||||
/** 课程标签文字,如 "基础课" "包厢课" "打赏课" */
|
||||
courseLabel: { type: String, value: '' },
|
||||
/** 课程样式 class 后缀:basic / vip / tip / recharge */
|
||||
typeClass: { type: String, value: 'basic' },
|
||||
```
|
||||
|
||||
> **此组件用 `vip/tip/recharge`,而非 `room/incentive`**。被 task-detail / customer-service-records 引用。
|
||||
> 这是**第三套 CSS 命名(并非配置/统计维度差异),只是组件局部 class)**,会和上面三页的 `room/incentive` 形成视觉割裂。
|
||||
|
||||
#### board-finance.wxml:101 还有:
|
||||
|
||||
```xml
|
||||
{ value: 'vip', text: '台球包厢' }
|
||||
```
|
||||
|
||||
> 这里 vip 是**区域筛选枚举**(area filter),不是课程标签 — 与 service-record-card 的 vip 含义不同,容易混淆。
|
||||
|
||||
---
|
||||
|
||||
### 1.4 结论:两套 / 两级 / 同源
|
||||
|
||||
**答案:A. 两套独立体系 + B. 两级隐含口径,但 NOT 严格父子嵌套。**
|
||||
|
||||
具体拆分:
|
||||
|
||||
| 体系 | 来源 | 字段 | 取值 | 用途 |
|
||||
|---|---|---|---|---|
|
||||
| **课程类型(course_type)** | `cfg_skill_type` | `course_type_code` | BASE / BONUS(数据库实际只有这 2 个) | 助教薪资 / 服务次数 / 工时统计 |
|
||||
| **项目分类(category_code)** | `cfg_area_category` | `category_code` | BILLIARD / SNOOKER / MAHJONG / KTV / SPECIAL / OTHER | 区域偏好 / 项目标签 / 客户分群 / 财务分区 |
|
||||
|
||||
**两个体系完全独立**,通过 `dwd_assistant_service_log.skill_id` 的不同关联路径分别落到不同 DWS 表:
|
||||
- `skill_id` → `cfg_skill_type` → `course_type_code` → 服务次数三分类
|
||||
- `dim_table.area_name` → `cfg_area_category` → `category_code` → 项目标签
|
||||
|
||||
**额外的"派生第三类":**
|
||||
- 在 `dws_assistant_daily_detail` / `dws_assistant_monthly_summary` 中,**包厢被从 BASE 中拆出来**,形成 `base_* / bonus_* / room_*` 三列。
|
||||
- 但**配置数据(cfg_skill_type)里没有 ROOM 这个 course_type_code**,所以代码层 `is_room = course_type == CourseType.ROOM` **永远为 False**,实际 room_* 三列**一直是 0 或失效**。
|
||||
- 这是**代码层到配置层的脱节**,需要 Neo 决定:
|
||||
- **方案甲**:补一行 `cfg_skill_type` 让"包厢服务/包厢课"的 course_type_code 改为 `ROOM` → 启用三分类
|
||||
- **方案乙**:接受现状,**前端把 room/包厢标签合并到 basic**,删除 `course-tag--room` CSS,workspaceWidth 仍按 138 元/小时计费
|
||||
- **方案丙**:改前端文案,让"包厢"在 UI 上显示为基础课分支(让用户感知两类即可)
|
||||
|
||||
**"看似两级"的错觉来源**:
|
||||
|
||||
- 工资计算公式确实有 3 个变量:`base_income / bonus_income / room_income`,但 room_income 实际为 0(因 `room_hours` 为 0)
|
||||
- 前端 3 个 CSS class:basic/room/incentive 配合 3 种颜色,看起来像"两级"
|
||||
- BD 手册 `BD_manual_cfg_skill_type.md` 写了 ROOM 枚举,但**与生产数据冲突**
|
||||
|
||||
---
|
||||
|
||||
### 1.5 修正后的 P2-4 推荐选项
|
||||
|
||||
> 原 P2-4 推荐方案(假设)调整方向:
|
||||
|
||||
#### 推荐:方案 B(对齐当前生产现状)
|
||||
|
||||
1. **数据层不动**:`cfg_skill_type` 保持 BASE/BONUS 两类(尊重现状)
|
||||
2. **代码层清理**:
|
||||
- 移除 `CourseType.ROOM` 枚举(或注释为 deprecated)
|
||||
- 删除 `room_course_price` 配置,所有包厢服务统一走 base 价格(`cfg_assistant_level_price.base_course_price`)
|
||||
- 但**注意**:目前包厢统一 138 元 = 星级 base 价,初级/中级助教做包厢按各自 base 价 → **会改变工资**,**需求需 Neo 决策**
|
||||
3. **DWS 表瘦身**:
|
||||
- `dws_assistant_daily_detail` / `monthly_summary` 删除 `room_*` 三列(或保留为兼容字段一直填 0)
|
||||
4. **前端三页统一**:删除 `course-tag--room` CSS 与 COURSE_TAG_MAP 中的"包厢/包厢课" key,统一映射到 basic
|
||||
5. **service-record-card 命名清理**:把 `vip/tip` 重命名为 `basic/incentive`,与三页一致
|
||||
6. **BD 手册修正**:`BD_manual_cfg_skill_type.md` 删除 ROOM 枚举说明
|
||||
|
||||
#### 备选:方案 A(启用 ROOM 三分类,贴合飞球原始数据)
|
||||
|
||||
1. **数据层补 cfg_skill_type 行**:把 skill_id=2807440316432198(包厢服务)和 3039912271463941(包厢课)改为 `ROOM`
|
||||
2. **配置层补 cfg_assistant_level_price**:增加 `room_course_price` 字段(可选,目前用代码配置)
|
||||
3. **代码层不动**(已有 ROOM 分支)
|
||||
4. **前端不动**(已有 room CSS)
|
||||
5. **影响**:
|
||||
- 历史 DWS 数据需要回填(2026-03-24 之前所有"包厢"被错误标 BASE)
|
||||
- 工资重算口径:包厢按 138 元统一 vs 各等级 base 价的差异
|
||||
|
||||
#### 我建议:**方案 B**(不动数据,清理代码与前端冗余),原因:
|
||||
|
||||
- 当前生产数据稳定且符合用户感知("包厢就是基础课的一种")
|
||||
- 改 cfg_skill_type 会触发 ETL 全量回算,风险大于收益
|
||||
- 命名一致性问题(service-record-card vip/tip)是更迫切的真问题
|
||||
|
||||
---
|
||||
|
||||
## 二、P2-7 看板切换限制全调研
|
||||
|
||||
### 2.1 board-coach 全部组合矩阵
|
||||
|
||||
#### TIME_OPTIONS(6 项) × SORT_OPTIONS(6 项)= 36 个组合
|
||||
|
||||
| time \ sort | perf_desc | perf_asc | salary_desc | salary_asc | sv_desc | task_desc |
|
||||
|---|---|---|---|---|---|---|
|
||||
| month | OK | OK | OK | OK | OK | OK |
|
||||
| quarter | OK | OK | OK | OK | OK | OK |
|
||||
| last_month | OK | OK | OK | OK | OK | OK |
|
||||
| last_3m | OK | OK | OK | OK | OK | OK |
|
||||
| last_quarter | OK | OK | OK | OK | OK | OK |
|
||||
| **last_6m** | OK | OK | OK | OK | **400 报错** | OK |
|
||||
|
||||
**唯一非法组合**:`time=last_6m + sort=sv_desc` → 后端 `board_service.py:258-262` 抛 `HTTPException(400, "最近6个月不支持客源储值排序")`。
|
||||
|
||||
#### SKILL_OPTIONS(5 项)× 上述 6×6:无任何额外组合限制
|
||||
|
||||
#### 现状(双向都没拦截)
|
||||
|
||||
- **前端**:TIME_OPTIONS 第 6 项的 `text` 为 `"最近6个月(不含本月,不支持客源储值最高)"`,**仅文字提示**,filter-dropdown 没有任何 disabled 状态
|
||||
- **后端**:仅服务端 if 校验 → 返回 HTTP 400
|
||||
- **用户体验路径**:用户先选 sv_desc(成功),再切到 last_6m(切换瞬间触发 loadData → 后端报错 → 前端 toast/console.error)
|
||||
|
||||
### 2.2 board-finance 类似问题(扫描结果)
|
||||
|
||||
#### TIME_OPTIONS:8 项
|
||||
|
||||
`month / lastMonth / week / lastWeek / quarter3 / quarter / lastQuarter / half6`
|
||||
|
||||
#### AreaFilterEnum:9 项
|
||||
|
||||
`all / hall / hallA / hallB / hallC / vip / snooker / mahjong / ktv`
|
||||
|
||||
#### **隐式组合限制(后端 `board_service.py:707-760`)**
|
||||
|
||||
`area ≠ all` 时,**后端直接把 3 个板块返回 null**:
|
||||
|
||||
| 板块 | area=all | area≠all |
|
||||
|---|---|---|
|
||||
| overview(经营一览) | 完整 | 完整 |
|
||||
| recharge(预收资产) | 完整 | **null** |
|
||||
| revenue(应计收入) | 完整 | 完整(部分子项不同) |
|
||||
| cashflow(现金流入) | 完整 | **null** |
|
||||
| expense(现金流出) | 完整 | **null** |
|
||||
| coach_analysis(助教分析) | 完整 | 完整 |
|
||||
|
||||
> **前端没有任何提示**,也没有把 area 选项灰掉。用户切到"台球包厢"等区域 → 页面 3 个板块凭空消失,用户无法理解为什么。
|
||||
|
||||
#### **额外限制:`isCurrentMonthFilter` 函数(line 16-18)**
|
||||
|
||||
```typescript
|
||||
function isCurrentMonthFilter(selectedTime: string): boolean {
|
||||
return selectedTime === 'month' && new Date().getDate() <= 5
|
||||
}
|
||||
```
|
||||
|
||||
> 月初 5 号前选"本月"会有特殊提示,但不阻塞操作。属于隐性约束。
|
||||
|
||||
### 2.3 board-customer 类似问题(扫描结果)
|
||||
|
||||
- DIMENSION_OPTIONS:8 项(recall/potential/balance/recharge/recent/spend60/freq60/loyal)
|
||||
- PROJECT_OPTIONS:5 项(ALL/BILLIARD/SNOOKER/MAHJONG/KTV)
|
||||
- **未扫到任何组合限制**(代码 grep 无 disabled / 不支持 / HTTPException 命中)
|
||||
- 后端 `board_service.py:584-640` 也只有 `if not query_fn_name: raise 400`(参数本身非法)
|
||||
|
||||
> board-customer 暂无组合限制问题。
|
||||
|
||||
### 2.4 修正后的 P2-7 实施清单
|
||||
|
||||
#### 修正核心:Neo 要求"双向禁止/变灰"
|
||||
|
||||
> 原选项 B(只在 sv_desc 选中时禁用 last_6m)需要扩展为:
|
||||
> - 选 last_6m → sv_desc 选项变灰禁选
|
||||
> - 选 sv_desc → last_6m 选项变灰禁选
|
||||
|
||||
#### 改动点 1:board-coach 双向禁用(P2-7 主要诉求)
|
||||
|
||||
**文件**:`apps/miniprogram/miniprogram/pages/board-coach/board-coach.ts` + `.wxml`
|
||||
|
||||
**ts 改动**(SORT_OPTIONS / TIME_OPTIONS 加 `disabled` 字段,onSortChange / onTimeChange 联动重算):
|
||||
|
||||
```typescript
|
||||
const SORT_OPTIONS = [
|
||||
{ value: 'perf_desc', text: '定档业绩最高' },
|
||||
{ value: 'perf_asc', text: '定档业绩最低' },
|
||||
{ value: 'salary_desc', text: '工资最高' },
|
||||
{ value: 'salary_asc', text: '工资最低' },
|
||||
{ value: 'sv_desc', text: '客源储值最高', disabledWhen: ['last_6m'] }, // 新增
|
||||
{ value: 'task_desc', text: '任务完成最多' },
|
||||
]
|
||||
|
||||
const TIME_OPTIONS = [
|
||||
...
|
||||
{ value: 'last_6m', text: '最近6个月(不含本月)', disabledWhen: ['sv_desc'] }, // 提示文字简化
|
||||
]
|
||||
|
||||
// 联动:onSortChange / onTimeChange 触发后,recompute disabled 标记并 setData
|
||||
// 如果当前组合已经非法(用户先选 sv,再点 last_6m),应阻止切换并 toast
|
||||
```
|
||||
|
||||
**wxml 改动**:filter-dropdown 组件需支持 `disabled` 数组 prop,被禁选项渲染为灰色不可点。
|
||||
|
||||
**filter-dropdown 组件改动**:增加 `disabledValues: string[]` prop,内部渲染时给禁用项加 `option--disabled` 样式并阻止 tap。
|
||||
|
||||
#### 改动点 2:board-finance 隐式 null 板块的可见性提示(扫描发现)
|
||||
|
||||
**问题**:area≠all 时,recharge/cashflow/expense 三个板块凭空消失,无任何提示。
|
||||
|
||||
**改动方案两选一**:
|
||||
|
||||
- **方案 a(强提示)**:在 board-finance.wxml 每个板块外层加 `wx:if="{{recharge}}"`,并在板块位置渲染占位 `view`:`"该指标暂不支持按区域拆分,请切换到全部区域查看"`
|
||||
- **方案 b(过滤源头)**:在 area 选项 dropdown 上,把"非 all"选项加副标题"(部分指标仅全部区域可见)"
|
||||
|
||||
> 我建议方案 a。零 UI 状态变化(三板块直接变成提示卡片),用户不会困惑。
|
||||
|
||||
#### 改动点 3:service-record-card 命名一致性(P2-4 调研副产物)
|
||||
|
||||
**改动文件**:
|
||||
- `apps/miniprogram/miniprogram/components/service-record-card/service-record-card.ts` 注释
|
||||
- `apps/miniprogram/miniprogram/components/service-record-card/service-record-card.wxss` 类名
|
||||
- 引用方:`apps/miniprogram/miniprogram/pages/customer-service-records/`、`apps/miniprogram/miniprogram/pages/task-detail/`
|
||||
|
||||
把 `vip/tip` 重命名为 `room/incentive`,与三个 -records 页面统一。
|
||||
|
||||
#### 改动点 4:文档对齐
|
||||
|
||||
- `BD_manual_cfg_skill_type.md`:删除 ROOM 枚举说明(或加注"仅代码层保留")
|
||||
- `04c-conflicts-P2-detail.md`:补充本调研结论
|
||||
|
||||
---
|
||||
|
||||
## 三、给 Neo 的决策清单
|
||||
|
||||
### P2-4 课程体系
|
||||
|
||||
1. [ ] **方案选择**:A(启用 ROOM 三分类,改 cfg_skill_type) / **B(代码清理,合并到 BASE)** / 维持现状不动
|
||||
2. [ ] **service-record-card 命名**:是否同步把 vip/tip 重命名为 room/incentive
|
||||
3. [ ] **room_course_price 138 元配置去留**:工资计算时,星级以下助教做包厢应按"星级 base 价(138)" 还是"自身等级 base 价"?(决策依据是真实业务)
|
||||
|
||||
### P2-7 看板切换限制(全看板共发现 2 个组合限制问题)
|
||||
|
||||
1. [ ] **board-coach 双向禁用**:确认按"改动点 1"方案实施(filter-dropdown 加 disabledValues prop + 联动 ts)
|
||||
2. [ ] **board-finance area≠all 三板块消失问题**:
|
||||
- 是否同样修复?(方案 a 占位提示 / 方案 b 选项副标题)
|
||||
- 还是接受现状(area≠all 是高级用法,暂不优化)
|
||||
3. [ ] **board-customer**:本次未发现组合限制问题,无需修复
|
||||
4. [ ] **后端 422 校验补强**:目前 board-coach 是 HTTP 400(detail 中文),是否升级为 422 + 标准化错误码?
|
||||
5. [ ] **可访问性兜底**:如果用户用直链/分享 URL 进入非法组合页面,前端是否需在 onLoad 兜底重置一个合法默认值?
|
||||
|
||||
### 调研副产物(本轮发现的其他问题)
|
||||
|
||||
1. [ ] cfg_skill_type 数据库实际无 ROOM 行,但 BD 手册写了 ROOM 枚举 → 文档过期
|
||||
2. [ ] dws_assistant_daily_detail / monthly_summary 的 room_* 三列实际可能恒为 0 → 需 SQL 验证
|
||||
3. [ ] dws_assistant_order_contribution 表完全没有 course_type 字段,与文档预期不符 → 与 P2-4 关联
|
||||
Reference in New Issue
Block a user