You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

68 lines
9.3 KiB

2 days ago
# 04 saved-view 与看板视图契约
Type: research
Status: resolved
Blocked by: —
## Question
brief 核心域要求「三 workspace 列表/**看板**/**saved-view**」,这两项是现有 demo 的已知薄弱点(P3「saved-view 未接」、I-06「pool·board 契约漂移」)。核实:
1. **saved-view**:后端有无保存视图端点(CustomerWorkspaceController 或别处)?「未接」的确切含义是后端缺端点还是 demo 没接线?若有端点:出入参、视图 CRUD 形态。
2. **看板视图**:pool/overview/mine 三 workspace 的 board 端点与分组维度(按 stage?);I-06 漂移的准确事实——文档允许 pool·board、实现白名单无 pool 特判:当前 jar 实测 pool+board 返回什么(空/报错/降级列表?)。
3. Demo 侧呈现决策输入:若后端不支持,如何「仅展示现象」;若支持,字段与交互形态。
## Pointers
- `.scratch/customer-frontend-handover/gap-report.md` P3 条目 + `.scratch/customer-e2e-r2/e2e-incr-checks.json` I-06 原文。
- `crm-customer` 的 CustomerWorkspaceController 源码(视图类型白名单就在其中或其 service)。
- 现有 demo workspace 切换实现(index.html 搜 workspace / board)。
## Answer
(研究子代理 2026-09-11,全部结论读码 + 既有 e2e 实测 JSON 佐证,未起服务。)
### 1. saved-view:后端端点齐全,P3-2「未接」= demo 没挂 UI,不是缺端点
**平台端点(crm-preference,`SavedViewController` @ `/api/preference/view`)**——CRUD 四件套全在:
| 端点 | 形态 | 证据 |
|---|---|---|
| `GET /view/list?scopeKey=` | 列当前用户该 scope 自定义视图(按 seqNo) | SavedViewController.java:37-41 |
| `POST /view/save?scopeKey=` + `@RequestBody SavedView` | viewId 空=新建(服务端 UUID)、非空=覆盖编辑;isDefault 单值互斥(save/setDefault 前清同 scope 其余默认位) | SavedViewController.java:45-50;SavedViewServiceImpl.java:48-75,108-114 |
| `POST /view/delete?scopeKey=&viewId=` | 删除 | SavedViewController.java:54-60 |
| `POST /view/set-default?scopeKey=&viewId=` | 图钉设默认(单值互斥) | SavedViewController.java:64-70 |
- **出入参**:`SavedView(viewId, name, conditions[], sortField, sortDirection, isDefault, seqNo)`;`SavedViewCondition(field, operator, value)`(SavedView.java:19-27、SavedViewCondition.java:13-17)。注意 save 是**全平台唯一 @RequestBody JSON 例外**(ADR-0017 不覆盖平台端点,API-SUMMARY.md:8、256)——demo 调它要发 JSON 不是表单。
- **客户侧消费已接线**:`POST /api/customer/workspace/page` 入参带 `savedViewId`(CustomerWorkspacePageParam.java:32);`CustomerWorkspaceServiceImpl.applySavedView`(:156-173)按 workspace 推 scopeKey `customer.overview|mine|pool`(:176-182,常量 CustomerConstants.java:67-69)读视图,经 `CustomerSavedViewFilter.translate` 翻成结构化条件 AND 叠加在 workspace 数据集上(不改 @DataScope 边界);**RECENT 内置视图不接、视图不存在静默不叠加、board 两端点不接**(仅 page 路径设 savedViewConditions,CustomerMapper.java:72-73)。
- **demo 做自定义检索编辑器的合法值域**:字段池 17 个(CustomerSavedViewFilter.java:36-52;其中 ownerUserId/ownerDeptId 仅 overview|mine、enterPoolTime 仅 pool,越 scope 静默跳过);操作符 8 值 `in/notIn/contains/notContains/lt/gt/isNull/isNotNull`(in/notIn 多值逗号分隔,CustomerSavedViewOperator.java:17-31);排序字段池仅 `createTime/lastValidFollowTime/enterPoolTime`(CustomerSavedViewFilter.java:61)。未知字段/操作符/需值空值一律静默跳过(不报错)。
- **bruno/menuBindings**:`bruno-sync.config.json` menuBindings 三客户菜单齐(`A4 客户管理/客户总览|我的客户|客户公海` ↔ scopeKey `customer.*`);但 SavedViewController Swagger tags 只挂「A3 商机管理/销售机会/自定义视图」(SavedViewController.java:36 等方法级),客户侧未挂深 tag——bruno 文档里客户 workspace 的 saved-view 归在商机菜单下。
- **结论**:P3-2 原文即「API 全通,demo 未挂 UI」(gap-report.md:46)。后端无缺口,纯 demo 接线工作。
### 2. 看板视图与 I-06:两件事,须拆开
**(a) 看板数据端点(crm-customer 域)——pool 已硬拒 67001,overview/mine 全通**
- 端点:`POST /api/customer/workspace/board/summary`、`POST /api/customer/workspace/board/cards`(CustomerWorkspaceController.java:43-54;tags 仅「A4 客户管理/客户总览|我的客户/看板列表」)。
- 分组维度:`groupColumn` 白名单三值 `stage→customer_stage / star→customer_star_level / 其他(含缺省)→relation_star_level`(CustomerWorkspaceServiceImpl.java:185-191);summary 只返回**有数据**的分组 `{groupValue, cnt}`(CustomerBoardSummaryDTO.java:17-20),空列骨架前端补;cards 必传 `groupValue` 否则 67001(:70-72)。
- **pool 特判已实现**:`assertBoardAllowed`(CustomerWorkspaceServiceImpl.java:80-85)——pool 调任一 board 端点 → `BusinessErrorException 67001「公海无卡片视图,看板仅支持客户总览/我的客户」`(返工票 03,commit fb6044f 2026-09-04 引入,早于 r2 jar 2026-09-05 22:29 全量重建;API-SUMMARY.md:68 同口径)。
- **实测佐证**:e2e-core-r2-checks.json F-02——overview 的 summary stage/star/relation 三分组全 ✅(groups=[1,2,3] 等,sum=37)、cards stage=2 取卡命中样例 ✅。三套件 JSON 里**没有** pool+board/summary 直测用例;按代码路径推断 pool+board = **67001 报错**(不是空集、不是降级列表)。
- **原型预期**(lanhu-tree-v29.md:42-44):A4 三列表原型只有**列表/分屏/卡片**视图页,无独立「看板」页(「看板视图」页只在 A5-1 项目管理、A6 数据看板);客户列表页顶部是「数据看板」统计条(a4-2-1*.txt 多处 u20/u58…「数据看板」),检索区有「常用检索 / 自定义检索 / 编辑检索」三段(a4-1-1/a4-2-1 txt:231-242)。返工票 03 拍板「公海只有列表/分屏两视图」。「看板列表」是 bruno 文档侧对 board 端点的命名。
**(b) 形态偏好 view-form(crm-preference 域)——I-06 漂移的真正对象,且至今未修**
- 端点:`GET /api/preference/view-form/get?scopeKey=`(null=未设,前端默认 list)、`POST /api/preference/view-form/save?scopeKey=&viewForm=`(ViewFormController.java:32-48)——记忆用户在该 scope 上次选的**视图形态**(list/split/board),与 (a) 的数据端点无关。
- **I-06 准确事实**(e2e-incr-checks.json 原文):`{"case": "pool·board 被接受 ⚠ 契约漂移(§2.14 称拒绝;实现白名单无 pool 特判)——记档,票 06 聚合", "verdict": "⚠", "detail": "code=0(漂移非缺陷:归 Bruno/API-SUMMARY 文档侧处置)"}`——即 save `scopeKey=customer.pool&viewForm=board` 实回 code=0。白名单指 `ViewFormServiceImpl.ALLOWED_VIEW_FORMS = 全局 {list,split,board}`(ViewFormServiceImpl.java:27-30,45-48),**scope 无关,crm-preference 全模块无 pool 分支——当前源码仍是如此,漂移持续存在**。文档侧已收口:API-SUMMARY.md:268 约定「pool 仅 list/split」+ :270 ⚠ 注记「前端不得依赖拒绝行为」;gap-report.md:68、matrix-a4-1-a4-3.md:13,71 同口径记档。
- 错误码区分:view-form 白名单越界(如 `viewForm=card`)= **68001**(PreferenceConstants.java:15,I-06 实测吻合);pool 调看板**数据**端点 = **67001**。两码别混。
### 3. Demo 呈现决策输入
现状(`.scratch/customer-e2e/demo/index.html`):
- 三 workspace 切换在(`switchWs` :537-540);viewType 下拉只有 ASSIGNED/COLLABORATING/FOCUSED 三值(:180-184,缺 FOLLOW_UP_DUE/RECENT);**全无 board 视图形态、无 view-form 存取、无 preference/view 四端点调用**(全文件 0 命中)。
- 已有隐患:`loadSummary`(:586)把 board/summary 当「汇总卡」用但**没传 groupColumn**(后端缺省落 relation_star_level 分组),且字段名对不上 DTO(demo 读 `it.count/it.name/it.key`,DTO 实为 `cnt/groupValue`)→ 数值大概率显 '--';ws=pool 时该调用吃 67001,汇总框落入 catch 显示报错文案。
建议(按「支持→接线、不支持→摆现象」):
- **saved-view 支持**:viewbar 加「自定义检索」下拉(GET /preference/view/list?scopeKey=customer.{ws})+ 选中后 page 带 savedViewId 重查;「编辑检索」弹窗按 17 字段 × 8 操作符造条件(save 发 **JSON**)。pool scope 同样可用(saved-view 不拒 pool,字段池自动限 enterPoolTime)。
- **看板支持(overview/mine)**:加形态切换 list/board,board 形态调 summary(groupColumn=stage,空组前端补骨架) + cards(groupValue=…) 三列渲染;**pool 不给看板入口**(原型即无),若要展示 I-06 现象:形态偏好仍允许存 board(实测 code=0)但数据端点 67001——可摆「形态记忆成功 + 看板数据拒绝」双现象注记。
- **最小摆法**(不做完整交互时):pool 页签固定列表形态 + 汇总条显「公海无看板(67001)」提示;overview/mine 汇总条修字段名 cnt/groupValue 并显式传 groupColumn=stage;view-form get/save 接上即完成跨会话形态记忆演示。