# 交接:票 11(Bruno 真实示例落地)+ Map 产出总结 日期:2026-08-28 晚(交接自中断会话,用户换机续做)。effort 状态机:`.scratch/opportunity-e2e/`(map.md + issues/)。 本文档供新会话冷启动续做票 11 剩余步骤,并回答用户的「Map 产出总结」问题(§6 素材现成)。 --- ## 0. 新环境先决检查(第一件事) 1. `d:\code\crm-backend-matt`(代码仓)与 `D:\code\crm-api-docs`(独立文档仓,Bruno 集合所在)都存在且可写。 2. 后端 8080 是否可达(原机器有后台 java 进程,换机后自然没了)。**只影响一个可选补采**(§3.3),不影响生成 .bru 主体工作。起服须带 `CRM_MINIO_AK/SK`(见 map.md 环境节)。 3. Python + requests 可用(.scratch 脚本依赖)。PowerShell 5.1 无 `&&`,用 `;`。 ## 1. 当前进度 | 步骤 | 状态 | |---|---| | t11-1 盘点 + 读源码 + 采标本 | ✅ specimens.json 30 标本(29 只读 code=0 + board_cards 补采) | | t11-3 注入商机模块 48 个 .bru | ✅ 完成并验证(参数块值/请求示例/响应示例三类全换真实值) | | t11-4 生成规则族 25 + 自定义视图 4 新 .bru | ⬅ **从这里开始**,完整设计在 §3 | | t11-5 bruno-sync 口径对账验证 | 待做,做法在 §4 | | t11-6 销票 + map.md 回写 + research 补记录 | 待做,清单在 §5 | | t11-7 回答用户 Map 产出总结 | 答案素材在 §6(用户在等这个) | 已验证不再动的:`.scratch/opportunity-e2e/inject-examples.py` 已修好 mreq 缩进正则并重跑通过 (changed=47 kept=1;22 条「未找到机械推导响应示例段」warning 是幂等正常表现——响应示例早已换成实测摘录,不再匹配旧占位正则)。抽查过 `商机模块\商机详情\主体\商机详情.bru` 请求示例已是 `GET /api/opportunity/detail?id=748556126794022912`。 ## 2. 票 11 背景速查(不重复,给路径) - 票面与验收口:`.scratch/opportunity-e2e/issues/11-bruno-examples.md`(验收口=用户在 Bruno 点开任一商机接口照抄即得真实响应) - 机制结论(铁律「结构听源码表述听人」、合并式 sync 手改安全):`.scratch/opportunity-e2e/research-bruno-examples.md` - 全局状态:`.scratch/opportunity-e2e/map.md`(Decisions 区 64-71 行一带,依赖图票 11 待标 ✅) - skill 全文:`C:\Users\luowj\.qoder\skills\bruno-sync/SKILL.md`(.bru 模板/示例生成规则/铁律,写文件时对照;换机后路径可能不同,用 Skill 工具调 bruno-sync 即可加载) ## 3. t11-4:写 gen-rules-views.py 生成 29 个 .bru(完整设计,照做即可) 脚本落 `.scratch/opportunity-e2e/gen-rules-views.py`,读 `specimens.json` 取真实值,写 `D:\code\crm-api-docs\商机模块\` 下新目录。 ### 3.1 目录与 folder.bru ``` 商机模块/商机管理/商机规则/folder.bru (seq = 商机模块下现有 folder.bru seq 的 max+10,动态读) 商机模块/商机管理/商机规则/公海规则/folder.bru + 8 个 .bru 商机模块/商机管理/商机规则/阶段模板/folder.bru + 8 个 .bru 商机模块/商机管理/商机规则/方案卡模板/folder.bru + 9 个 .bru 商机模块/销售机会/自定义视图/folder.bru + 4 个 .bru ``` (目录落点裁决:源码 @Tag 是「商机管理/商机规则」「销售机会/自定义视图」,但现有集合树是 gen_v2_folders.py 的人为规划(顶层「商机模块/」),遵循现有树避免顶层重复文件夹。) folder.bru 格式(**无 type、无 tags**,照现有 `商机模块/商机管理/folder.bru`): ``` meta { name: 公海规则 seq: 1 } ``` 子目录内接口文件 seq 从 1 递增。接口 .bru 骨架照现有 `商机公海/商机分页.bru` 风格: `meta{name,type: http,seq,tags:[generated]}` + `post{url: {{baseUrl}}路径, body: formUrlEncoded|json, auth: inherit}` 或 `get{...body: none}` + `params:query{}` / `body:form-urlencoded{}` / `body:json{}` + `docs{}`。 **docs 块所有行 2 空格缩进**;首行 `# 动作语义名`、第二行反引号 `` `METHOD 路径` ``(对账钥匙勿动);节顺序:说明段 → `## 请求参数` → `## 请求示例` → `## 响应 data 结构` → `## 响应示例`。 ### 3.2 文件清单与真实值(29 个) 真实值来源:`specimens.json` 键 `pool_page/stage_page/scheme_page/pool_detail/scheme_detail/field_defs/pool_versions/stage_detail/view_list`。 关键 id:POOL_RULE=`748507245062389760`、SCHEME_TPL=`748508098259320832`、stage 种子 id 从 `specimens['stage_detail']['params']['id']` 现取。seed viewId 从 `specimens['view_list']['resp']['data']` 里 name 含「测试视图」的条目现取。 **公海规则 /api/rule/opp-pool-rule(8)** | # | 文件名 | 钥匙 | 参数块值 | 响应示例 | |---|---|---|---|---| | 1 | 公海规则分页 | POST /page | form `current=1&size=10`(照 specimens.pool_page.form 回填) | slim(pool_page) | | 2 | 公海规则详情 | GET /detail | query `id=748507245062389760` | pool_detail 完整 data | | 3 | 保存公海规则草稿 | POST /save-draft | form(见下「写构造」) | `{"code":0,...,"data":null}` | | 4 | 发布公海规则 | POST /publish | 同 3 + `id=1` ⚠警告 | data:null | | 5 | 复制公海规则 | POST /copy | form `id=748507245062389760`(照抄=新建草稿副本,无害) | data:"1"(机械) | | 6 | 停用公海规则 | POST /disable | form `id=1`(说明:仅发布中可停用,须替换) | data:null | | 7 | 删除公海规则 | POST /delete | form `id=1`(说明:仅草稿可删) | data:null | | 8 | 公海规则版本列表 | GET /versions | query `id=748507245062389760` | slim(pool_versions) | 3 的写构造(e2e-rules.py 实测通过口径):`ruleName=e2e-演示-公海规则&applyScope=1&isDefault=0&allowManualPool=1&autoRecycleEnabled=1&recycleDays=30&allowFreeClaim=1&recycleRemindEnabled=1&remindDays=7&ruleDesc=票11演示`。 **阶段模板 /api/rule/opp-stage-template(8,与公海规则对称)**:分页/详情/保存草稿/发布/复制/停用/删除/版本列表。差异点: - 详情与复制/版本的 id 用 stage 种子 id(specimens 现取)。 - 保存草稿 form(**nodes 索引式绑定是 e2e R01 实测通过的关键知识**): `templateName=e2e-演示-阶段模板&applyScope=1&isDefault=0&templateDesc=票11演示&nodes[0].stageDictCode=OPP_STAGE_01&nodes[0].workGoal=需求确认&nodes[1].stageDictCode=OPP_STAGE_02&nodes[1].customNodeName=方案深化` - 发布 = 同 form + `id=1` + 警告「发布将顶替同编码发布中版本,严禁对种子 OPP_STAGE_TPL_01 直接执行;id 须替换为 save-draft 产出的草稿 id」。 **方案卡模板 /api/rule/opp-scheme-template(9)**:比阶段模板多一个「系统字段库列表 GET /field-defs」(无参,响应=slim(field_defs))。差异点: - 保存草稿 form 的 fields 索引式,fieldKey **必须取 field_defs 标本实测值**(非自由造): `templateName=e2e-演示-方案卡模板&applyScope=1&isDefault=0&templateDesc=票11演示&fields[0].fieldKey=&fields[0].isVisible=1&fields[0].isRequired=1&fields[1].fieldKey=&fields[1].isVisible=1&fields[1].isRequired=0` - 详情/复制/版本 id=`748508098259320832`;发布同警告(种子=OPP_SCHEME_TPL_04)。 **自定义视图 /api/preference/view(4)** | # | 文件名 | 钥匙 | 参数块 | 响应示例 | |---|---|---|---|---| | 1 | 自定义视图列表 | GET /list | query `scopeKey=opportunity` | slim(view_list) | | 2 | 保存自定义视图 | POST /save | **query `scopeKey=opportunity` + body:json**(集合首个 json body) | data=seed 真实 viewId(19 位雪花串) | | 3 | 删除自定义视图 | POST /delete | form `scopeKey=opportunity&viewId=1` ⚠说明「替换为本人视图 id(view/list 查),勿对他人/种子视图执行」 | data:null | | 4 | 设为默认视图 | POST /set-default | form 同 3 ⚠同警告 | data:null | save 的 JSON body(seed-opportunity.py 实测通过构造,viewId 不传=新建): ```json {"name": "e2e-B-测试视图-我的推进", "conditions": [{"field": "opportunityName", "operator": "like", "value": "e2e-"}, {"field": "oppStatus", "operator": "eq", "value": "2"}], "sortField": "createTime", "sortDirection": "desc", "isDefault": false, "seqNo": 99} ``` 请求示例里 JSON 部分包 ```json 代码块(SKILL 规则,否则 Markdown 渲染吞缩进)。 ### 3.3 可选补采(仅当 8080 可达) stage/scheme 的 versions 无标本。可写 10 行脚本(debug token 照 `collect-specimens.py` 开头:`GET /api/auth/debug/token?userId=739564171091247104` 取 admin token): `GET /api/rule/opp-stage-template/versions?id=`、`GET /api/rule/opp-scheme-template/versions?id=748508098259320832`,追加进 specimens.json(键 stage_versions/scheme_versions)。 8080 不可达则降级:versions 响应示例按 SKILL「示例生成」规则从结构表机械推导(数组 1 条元素),不加「实测」注释行。 ### 3.4 docs 结构表内容来源(源码已全部读过,字段如下) - **请求参数表**:page = BaseParam(current 默认1/size 默认10/keyword/orderBy/asc 默认false)+ applyScope/status(各 PageParam @Schema 原文);detail/copy/disable/delete/versions = 单参数 id。 - **pool save-draft/publish 入参**(OpportunityPoolRuleDTO 表单绑定):id(编辑时)/ruleName*/applyScope(1所有部门通用 2对应部门专用)/isDefault/allowManualPool/autoRecycleEnabled/recycleDays/allowFreeClaim/recycleRemindEnabled/remindDays/ruleDesc/deptIds(逗号分隔,applyScope=2 必填至少一个);ruleCode/versionNo/status/deptNames 标「系统生成只读,入参忽略」。 - **stage**:id/templateName*/applyScope(1所有商机 2指定部门)/isDefault/templateDesc/deptIds/nodes[N].stageDictCode*(索引式 N 从 0 递增)/nodes[N].customNodeName/nodes[N].workGoal;说明注明末位固定节点 OPP_STAGE_05 系统保证、入参不可伪造(发了 64000 类校验拒)。 - **scheme**:id/templateName*/applyScope/isDefault/templateDesc/deptIds/fields[N].fieldKey*/fields[N].isVisible/fields[N].isRequired;说明注明 fieldName/fieldType 入参忽略取系统字段快照、关显示清必填。 - **view/save**:query scopeKey + JSON 字段表 viewId(空=新建非空=覆盖编辑)/name*/conditions[].field,operator,value/sortField/sortDirection/isDefault/seqNo。 **响应 data 结构表**(从源码 DTO/实体,逐字段一行): - pool DTO = id/createTime/updateTime(BaseDTO) + ruleCode/ruleName/versionNo/status(1草稿2发布中3已停用)/applyScope/isDefault/allowManualPool/autoRecycleEnabled/recycleDays/allowFreeClaim/recycleRemindEnabled/remindDays/ruleDesc/deptIds[]/deptNames[] - stage DTO 另加 nodes[](NodeItem:id/seqNo/stageDictCode/customNodeName/workGoal/isFixed) - scheme DTO 另加 fields[](FieldItem:id/seqNo/fieldKey/fieldName/fieldType/isVisible/isRequired) - field-defs = FieldDefDTO:id/createTime/updateTime/fieldKey/fieldName/fieldType(RELATED_OBJECT/SINGLE_SELECT/AUTOFILL/YES_NO/ATTACHMENT/AMOUNT/TEXT_SINGLE/TEXT_MULTI/DATE)/optionSource/refTarget/autofillSource/defaultValue/isDeletable - versions = 实体直出(pool 12 业务列;stage/scheme 各 7 业务列 templateCode/templateName/versionNo/status/applyScope/isDefault/desc + BaseEntity 3 列 id/createTime/updateTime;**无** deptIds/nodes/fields 子表) - view/list = SavedView:viewId/name/conditions[]/sortField/sortDirection/isDefault/seqNo;conditions[]{field/operator/value};出参不回显 scopeKey(票 07 契约事实) - 写操作 data=null(copy 返回 Long→"1"、view/save 返回 viewId 字符串) - 分页表加 PageResult 外壳(content/total/size/current/pages/empty,类型照现有 `商机公海/商机分页.bru` 64-87 行现成模板) 响应示例注释行:有标本的写 `// 实测响应摘录(20260828 · 用户X · e2e 种子库;数组字段只示第 1 条)`(X=view_list 用 B、其余 admin);机械推导的写 `// 示例数据,字段结构以上表为准,非真实返回`。 ### 3.5 写文件纪律 - UTF-8 无 BOM、`\n` 换行(`open(..., 'w', encoding='utf-8', newline='\n')`);生成后可跑 BOM 扫描(AGENTS.md 有现成 PowerShell 片段) - 幂等:目标文件已存在则跳过(防重复生成) - 完成后跑 inspect-bru.py(root 改成 docs 根)盘点钥匙,应 77 个接口文件全列出 ## 4. t11-5:bruno-sync 口径对账验证 写 `.scratch/opportunity-e2e/verify_sync.py`: 1. 源码侧:正则扫 crm-opportunity 8 控制器 + crm-rule 3 控制器(`OpportunityPoolRuleController/OpportunityStageTemplateController/OpportunitySchemeCardTemplateController`)+ crm-preference `SavedViewController` 的 `@(Get|Post)Mapping`,拼类级 @RequestMapping 得全量钥匙集。 2. 文件侧:遍历 `D:\code\crm-api-docs` 全部 .bru(排除 folder.bru/collection.bru/environments),有 `tags:[generated]` 者从 docs 首行反引号提钥匙。 3. 报告三向差集:源码有文件无 / 文件有源码无 / 两侧都有计数。预期:商机模块 48 + 新增 29 = 77 全命中、双零差集。 4. 结论表述:sync 若重跑,结构已镜像源码 → 全部「不动」;实参值与示例在 docs 块属表述(铁律 1)→ 不会丢。票面「跑一次 /bruno-sync 验证示例不丢」以此对账 + 抽查 2-3 个文件结构达成(skill 的执行者就是 agent 本身,无可独立执行的脚本)。 ## 5. t11-6:销票收尾清单 1. `issues/11-bruno-examples.md`:Status: open → resolved,写 Answer:48 注入 + 29 新建 + 对账结论 + 新契约事实: - **board 两端点(stage-summary/cards)源码无 viewType 参数**(`OpportunityCollabController` 实锤:stage-summary 只收 stageTemplateId、cards 只收 stageId/offset/limit)——e2e 发的 viewType 被 Spring 静默忽略 = **D-20「公海看板未过滤」根因**;.bru 参数表本来就对。 - scopeKey 实测值=`opportunity`(seed/demo/e2e 全用此值);注意 `OpportunityViewQueryImpl` 内部常量 `SAVED_VIEW_SCOPE="opportunity.sales"` 与前端写入 scope 不一致——列表页 savedViewId 叠加可能读不到已保存视图,值得在 Answer 里挂一句「疑似新缺陷候选,未实测」。 - nodes/fields 索引式表单绑定、deptIds 逗号分隔、publish 顶替语义、viewId upsert、`Result` data=null 非失败。 2. `map.md`:依赖图票 11 标 ✅;Decisions 区按 64-71 行格式追加一行(产出=77 接口真实示例 + 29 新端点入集合)。 3. `research-bruno-examples.md`:追加执行记录段(collect/inject/gen/verify 四脚本 + 结果数字)。 4. 向用户交付 §6 总结(即 t11-7)。 ## 6. t11-7:Map 产出总结(用户在等,答案素材现成) 用户已知 3 件:demo html、bug 汇总、接口文档。完整清单(前 3 + 补充 8): **用户已知** 1. `demo/index.html`(969 行单文件 + 8 张自测截图)——浏览器验收自测 2. `e2e-report.md`(132 行六章节,150 用例 ✅125⚠17❌9,缺陷 D-01..D-22 分级清单)——bug 汇总 3. `D:/code/crm-api-docs` Bruno 集合——接口文档(本票后:商机模块 48 接口全真实示例 + 规则族 25/自定义视图 4 新入集合) **补充产出** 4. `spec-flows.md` v2——F01-F15 核心域 + R01-R03 规则族流程规格冻结(199 行,含 U01-U08 拍板记录),后续修缺陷/回归的对账基线 5. `env-inventory.md`——环境盘点:账号矩阵(admin+A/B/C 跨部门)、部门树、字典 22 组、存量数据与规则缺口 6. `seed-opportunity.py` + `seed-manifest.json`/`seed-data-manifest.md`——幂等种子库(12 商机跨 3 部门 + 跟进/勘察/方案卡/关注/自定义视图 + 2 条发布中规则),重跑 5 次验证,任何环境一条命令复现全套测试数据 7. `e2e-core.py`(894 行)+ `e2e-rules.py`(534 行)——150 用例可重跑 E2E 套件(缺陷修复后直接回归) 8. `research-bruno-examples.md`——bruno-sync 机制调研结论(合并式 sync 手改安全,票 11 的执行依据) 9. `boot-verify.md`——起服指引与环境事实三连(MinIO 凭据、PS5.1 中文 mojibake、一次性 401 重试) 10. `specimens.json` + 采集/注入/生成脚本——30 接口实测标本库,本次注入与未来文档再生的真实数据源 11. 散装新契约事实(已写入各票 Answer):U01 新建即推进中、board 无 viewType=D-20 根因实锤、savedView upsert、surveySeq 必填(调用方传)、valuesJson 只认绑定模板显示字段(66007)等 一句话版本:「除了你说的三件,还有:冻结的流程规格、环境盘点、幂等种子库、可重跑的 150 用例 E2E 套件、sync 机制结论、起服指引,以及 30 个接口的实测标本库——『种子→E2E→报告→回归』的闭环整套都是可复用资产。」 ## 7. Suggested skills - `bruno-sync`:t11-4 写文件与 t11-5 对账时对照 SKILL.md 铁律与模板(用 Skill 工具调用加载) - `wayfinder`:一般不必调;map.md + issues/ 既有格式直接模仿即可 ## 8. 关键路径速查 | 什么 | 哪里 | |---|---| | 标本库 | `.scratch/opportunity-e2e/specimens.json` | | 注入脚本(已完成勿动) | `.scratch/opportunity-e2e/inject-examples.py` | | 采集脚本(补采参考) | `.scratch/opportunity-e2e/collect-specimens.py` | | 写操作实测构造 | `.scratch/opportunity-e2e/e2e-rules.py`(R01-R03)、`e2e-core.py` f15() | | .bru 风格基准 | `D:\code\crm-api-docs\商机模块\商机公海\商机分页.bru`(form 类)、`商机详情\主体\商机详情.bru`(GET 类) | | 源码(结构表依据) | `crm-rule/.../controller/Opportunity*Controller.java` + `domain/dto|param|entity`、`crm-preference/.../SavedViewController.java` + `domain/dto/SavedView*.java`、`crm-base/.../BaseDTO.java`/`BaseParam.java` | | 钥匙盘点工具 | `.scratch/opportunity-e2e/inspect-bru.py`(root 改成 docs 根即可复用) |