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.
 
 
 
 
 

9.3 KiB

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/menuBindingsbruno-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/summaryPOST /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 接上即完成跨会话形态记忆演示。