diff --git a/.scratch/opportunity-module/issues/05-opportunity-pool-rules.md b/.scratch/opportunity-module/issues/05-opportunity-pool-rules.md index 7c04bee..ba4fadd 100644 --- a/.scratch/opportunity-module/issues/05-opportunity-pool-rules.md +++ b/.scratch/opportunity-module/issues/05-opportunity-pool-rules.md @@ -121,7 +121,7 @@ Blocked by: 03, 04 - **新增 `last_valid_follow_time`**(datetime):最近一次有效跟进时间,回收计时锚点;跟进/附件/字段编辑时刷新(票 09 跟进/附件落地时联动)。 - **删 `recycle_deadline`**(初稿加的,锚点改为 last_valid_follow_time,回收由 Job 实时算 now−锚点,不预存 deadline)。 - **删 `pool_id`** 或改语义为"当前无意义":公海 = opp_status=1 逻辑视图,无池实体外键。保留 `owner_dept_id` 作公海可见范围锚点。 - - 新增 `pool_rule_version_id`(bigint,⚠ 候选):领取/回收时记录实际执行的规则版本快照(原型「版本快照规则」);或落跟进日志,待票 09。 + - **新增 `pool_rule_version_id` 语义定稿**:领取/回收时记录实际执行的规则版本快照(原型「版本快照规则」)——**落操作日志 `opportunity_oplog`(票 09),不上主表**。理由:一条商机会被多次领/回收,主表只能存最新一次,存不下历史;原型 A7-3-2-5 三处明写“历史提醒/回收/领取记录及操作日志继续关联原规则版本”(存档 research-a7325 line 533/1097/1832),指向日志而非主表字段。 - **回填票 04**: - 批量边 `executeRecycle(cutoff)` 保留,但触发条件改为"last_valid_follow_time + recycle_days < now"(非 recycle_deadline)。 - 抛公海 E3 / 回收:`owner=NULL, opp_status=1, owner_dept_id 保留`;无 pool_id 目标。 @@ -136,7 +136,7 @@ Blocked by: 03, 04 - ✅ **多规则命中优先级(已定)**:以最低层级规则为准;发布约束保证一个部门只被一条发布中规则覆盖,故命中唯一。 - ✅ **暂缓冻结回收计时(已定)**:暂缓期间计时暂停,`executeRecycle` 不扫暂缓中。 - ⚠ `recycle_days`/`remind_days` 数值样例(原型 30/7、45/7、20/5…)均为样例,非验收值。 -- ⚠ `pool_rule_version_id` 快照落主表还是落日志(票 09 联动)。 +- ✅ **`pool_rule_version_id` 快照落操作日志(已定)**:领取/回收时把实际执行的规则版本写进 `opportunity_oplog`(票 09),不上主表。作用:查旧账(说清当时凭哪版领/回收)+ 落实“历史不重算”。 ## 已核实事实(原型 A7-3-2-5 一手正文,存档 research-a7325-pool-remind-rules.md) diff --git a/.scratch/opportunity-module/issues/10-four-views-query.md b/.scratch/opportunity-module/issues/10-four-views-query.md index 39c9a0c..62b0235 100644 --- a/.scratch/opportunity-module/issues/10-four-views-query.md +++ b/.scratch/opportunity-module/issues/10-four-views-query.md @@ -4,6 +4,9 @@ Type: grilling Status: resolved Blocked by: 03, 04, 05 +> **⚠ Amendment(票 14 推翻/扩展本票的视图组织)**:产品以 demo `nginx/html/保伦CRM系统demo-重构版-S1_0803.html` 为准,销售机会工作区改为「内置四视图(最近访问 / 我负责的 / **我参与的**(新)/ 我关注的)+ 用户自定义视图」的下拉,自定义视图落 `crm-preference` 平台能力(按 scope)。**本票的数据范围矩阵、列表字段、计算列、三皮肤、公海 `pool_reason` 口径均不变**,仅「销售机会」工作区的视图组织形态被票 14 改写。详见 `issues/14`。 + + 定商机四视图查询口径 + 列表/详情字段与操作按视角差异。原型 A3-1-1 / A3-2-1 / A3-3-1 / A3-0-1 均已拉取核实,存档 `research-a3xx-four-views.md`。 --- diff --git a/.scratch/opportunity-module/issues/14-custom-views-and-builtin-view-family.md b/.scratch/opportunity-module/issues/14-custom-views-and-builtin-view-family.md new file mode 100644 index 0000000..15b13ea --- /dev/null +++ b/.scratch/opportunity-module/issues/14-custom-views-and-builtin-view-family.md @@ -0,0 +1,83 @@ +# 14 自定义视图 + 内置视图族(推翻/扩展票 10 视图组织) + +Type: grilling +Status: resolved +Blocked by: 10 + +## 缘起 + +产品以最新重构版 demo **`nginx/html/保伦CRM系统demo-重构版-S1_0803.html`** 的 `oppModule` 为准(用户明确「他是想做成这样」),推翻/扩展票 10 的四视图组织:销售机会下可**自定义视图**,并把内置视图从「一个我负责的」扩为**四个内置视图**族。 + +demo 出处:`oppModule`(HTML line 1228 `viewTitles` / line 1229 `viewItems` / line 1260–1305 视图菜单与自定义检索表单 / `saveCustomSearch`·`saveEditedSearch`·`saveAsNewSearch`·`runCustomSearch`·`editSearch`·`startRename`·`confirmRename`·`deleteSearch`·`pinView`·`persistCustomSearches`·`restoreCustomSearches`)。 + +## 与票 10 的差异(本票为 Amendment,票 10 其余口径不变) + +| 维度 | 票 10 已定稿 | demo 0803 新意图(本票拍板) | +|---|---|---| +| 视图组织 | 三个独立工作区(个人/管理/公海)各是独立页面 + 最近访问 | **单一「销售机会」工作区下的视图下拉**(常用检索 / 自定义检索两 Tab);商机管理 / 商机公海仍是并列 workspace(不变) | +| 内置个人视图 | 只有一个「我负责的(个人页)」 | **四个内置视图**:最近访问 / 我负责的 / **我参与的**(新增)/ 我关注的 | +| 「我参与的」 | 未定义 | 内置视图,见下【定义 1】 | +| 自定义视图 | 无 | 核心新增:保存检索为常用视图、编辑 / 重命名 / 删除、图钉设默认,见【定义 2】 | + +> 票 10 的**四视图数据范围矩阵、列表字段、计算列口径、三皮肤、公海 `pool_reason`** 等一律不变。本票只改**「销售机会」这一工作区内的视图组织形态**(内置视图族 + 用户自定义视图),并把「最近访问」「我关注的」从票 10 的独立视图并入销售机会工作区的视图下拉。 + +## 决策(已定稿) + +### 定义 1 —— 内置视图族(销售机会工作区,四个) + +四个内置视图,均在「销售机会」workspace 的「常用检索」Tab 内,顺序 = 最近访问 / 我负责的 / 我参与的 / 我关注的: + +| 视图 code | 名称 | 数据范围(WHERE 口径) | +|---|---|---| +| `recent` | 最近访问 | 当前用户打开过详情的商机,访问时间倒序、按商机 id 去重留最新,上限可配置(本期默认 50,票 10 P2-2 不变) | +| `mine` | 我负责的销售机会 | `owner_user_id = 当前用户`(票 10「销售机会(个人)」口径不变) | +| `participated` | 我参与的销售机会 | **我是该商机团队成员但不是负责人**:`EXISTS(团队成员表 WHERE opportunity_id=? AND user_id=当前用户) AND owner_user_id <> 当前用户`。团队成员表 = 票 02/04 建商机时落的团队成员(领取人=负责人 + 后续「添加团队成员」加入的成员)。**产品确认(本票确认点 1):参与 = 团队成员且非负责人**,与「我负责的」互斥不重叠。 | +| `followed` | 我关注的销售机会 | 当前用户关注表命中(票 10「我的关注」口径不变) | + +- **内置视图 = 系统固定四个,不可编辑 / 重命名 / 删除**;只可被「图钉」设为默认视图(见定义 3)。 +- 「商机管理」「商机公海」仍是**独立 workspace**(不进销售机会视图下拉),口径同票 10(管理按 `@DataScope`、公海 `opp_status=1 且 owner 空`)。demo 里 `workspace` 三档(sales/manage/publicPool)印证。 + +### 定义 2 —— 自定义视图(= 保存的检索) + +用户在「自定义检索」Tab 里配置检索条件并「保存为常用检索」,生成一个**自定义视图**,加入「常用检索」Tab 列表(排在四个内置视图之后)。一个自定义视图 =: + +- **检索名称**(用户填,同一用户同一 scope 内唯一,demo `validateCustomRule` 查重) +- **检索条件**:一组「字段 + 操作符 + 值」(demo 本期 1 组条件;「+ 添加更多条件」「启用高级公式」demo 标注为后续迭代 —— **本期后端契约按「条件列表」建模**,前端本期只提交 1 组,留扩展位) + - 字段(本票确认点 3)= **列表所有可筛选列**(不是 demo 简化的 4 个);字段池归业务方(商机模块)定义,对齐 `crm-preference`「字段池」约定 + - 操作符:`等于 / 不等于 / 包含 / 不包含 / 为空 / 不为空`(demo `customMatches`) +- **排序条件**:排序字段 + 方向(`asc/desc`)(demo `filteredRows` 里 `预计成交金额 / 计划成交时间` 是简化,后端排序字段同属字段池) + +生命周期动作(demo 已实现,全部按用户私有): +- **保存为常用检索** `saveCustomSearch`(新建)/ **另存为新检索** `saveAsNewSearch` +- **编辑检索** `editSearch` → **保存修改** `saveEditedSearch`(覆盖原视图) +- **重命名** `startRename`/`confirmRename` +- **删除** `deleteSearch`(删当前视图时回落到 `mine`;删默认视图时默认回 `mine`) + +### 定义 3 —— 默认视图(图钉) + +- 每个视图(内置四个 + 自定义任一)行尾有「图钉」,点击 = **设为该用户在此 scope 的默认视图** `pinView`(demo `defaultView`,持久化)。 +- 进入销售机会页时打开默认视图;默认视图未设 → `mine`。 +- 默认视图 = **用户级、单值**(一个 scope 只有一个默认)。 + +### 决策 4 —— 落 `crm-preference` 平台能力,不绑死商机(本票确认点 2) + +产品确认:**目前只在销售机会启用,但要有灵活性,后续其他菜单也会有**。故自定义视图**做成 `crm-preference` 的平台级能力**(与既有「列偏好」并列的第二种偏好),**按 `scope_key` 分组、用户级私有**,与 ADR-0024「每菜单独立」完全对称: + +- 新增偏好实体(建议 `user_saved_view`):`(user_id, scope_key, view_id, name, filter_json, sort_field, sort_direction, is_default, seq_no, create_time, update_time)`。 + - `filter_json` = 条件列表(`[{field, operator, value}, ...]`)序列化;**`crm-preference` 不校验字段合法性**(字段池归业务方,对齐既有「字段池」约定)。 + - `scope_key` 复用列偏好同一套 scope 命名;销售机会本期 scope = **`opportunity.sales`**(单一,因自定义视图只属销售机会个人工作区,见确认点 2)。管理 / 公海本期不开自定义视图。 + - `is_default` 承载定义 3 的默认视图(同 `(user_id, scope_key)` 至多一条 `is_default=1`)。 +- **契约**:`crm-preference` 现有 `get/save` 两法(列偏好)之外,为自定义视图加一组 `list / save / delete / setDefault`(用户级、按 scope)。具体方法签名留实现阶段定,本票只钉「落 crm-preference、按 scope、用户私有」的归属决策。 +- **确认点 2 明确**:本期只 `opportunity.sales` 一个 scope 启用;实体 / 契约按「任意 scope 通用」设计,不含商机专有字段,后续菜单加 scope 即可复用。 + +## 传出决策 / 回填其他票 + +- **回填票 10**:销售机会工作区的视图组织从「单一我负责的」改为「内置四视图(最近访问 / 我负责的 / **我参与的**(新)/ 我关注的)+ 自定义视图」;「最近访问」「我的关注」并入销售机会视图下拉(数据范围口径不变)。票 10 的列 / 计算列 / 三皮肤 / 公海不变。 +- **回填 `crm-opportunity/CONTEXT.md`**:【四视图】术语改写为【视图(内置 + 自定义)】;新增【我参与的】术语(团队成员且非负责人)。 +- **回填 `crm-preference/CONTEXT.md`**:新增【自定义视图】能力段(平台级、按 scope、用户私有、图钉默认视图);scope 清单增补 `opportunity.sales`。 +- **新增待办(Not yet specified / 后续实现票)**:自定义视图 `crm-preference` 契约方法签名 + `user_saved_view` 表 DDL(实现阶段);商机列表**可筛选列字段池清单**(确认点 3,字段池归商机模块,实现阶段列全)。 +- **明确 spin-out(本票不做)**:多组条件(`AND/OR` 复合)、高级公式 —— demo 标注后续迭代;本期契约按「条件列表」预留,前端只 1 组。 + +## Comments + +- 确认点回执(用户 2026-08-20):①「我参与的」= 团队成员且非负责人 —— **认可**;②自定义视图作用域 —— 目前只销售机会,但要灵活性、后续其他菜单也会有 → 落 `crm-preference` 平台能力按 scope;③检索字段 —— 用列表所有可筛选列;④先更新 spec(本票),不动代码。 diff --git a/.scratch/opportunity-module/map.md b/.scratch/opportunity-module/map.md index 8b76fd4..aaaf624 100644 --- a/.scratch/opportunity-module/map.md +++ b/.scratch/opportunity-module/map.md @@ -13,7 +13,7 @@ ### 交付物清单(对齐线索地图的阅读顺序) -1. **`商机业务-PRD.md`** — 端到端综合规格:两条轴(归属状态机 + 阶段模板)、归属、公海、关键流程、四视图、上下游集成。从这里开始读。 +1. **`商机业务-PRD.md`** ✅已产出 — 端到端综合规格(冻结自票 01–14):领域定义 / 商机主表 / 两条轴(归属状态机 + 阶段模板)/ 公海规则 / **视图体系(内置四视图 + 自定义视图)** / 详情子表族 / 上下游集成 / V-CONFIG 范式 / 字典清单 / 待确认索引。每条挂原型页码,供逐条核对。**从这里开始读。** 2. **`crm-opportunity/CONTEXT.md`** — 商机域领域语言(ubiquitous language)。 3. **`crm-rule/CONTEXT.md`(增补商机规则段)** — 商机规则三族的领域语言。 4. **`issues/01`–`13`** — 逐题决策底稿(票 13 = 商机-客户关联子表)。 @@ -50,6 +50,8 @@ - **商机-客户关联子表已定稿(票 13 resolved,原型 A3-1-1-1-1/-1-2 + A3-1-1-2-4)** — 新建实体 `opportunity_customer`(`crm-opportunity`):`customer_id`(引用 A4+名快照) / `customer_role`(→crm-dict 新增「客户角色」分组) / `is_primary_intended`(是否主要意向客户)。**产品拍板**:一商机多关联客户、**有且仅一个主要意向客户**;线索转商机填的意向客户→落为主客户;商机侧可加普通关联客户、可改主客户(切换标记)。约束:主客户唯一 + `UNIQUE(opportunity_id,customer_id)` + 无孤立。**联动**:主表冗余 `primary_customer_id`/名快照(票03 C3);方案卡一客户一卡 `UNIQUE`(票07);列表方案预算/状态列取主客户卡(票10 P0-4)。从“Not yet specified”钉死。**依赖**:`customer_id` 引用真实客户 + 下拉选客户能力挂 A4客户模块(未建,票11)。详见 `issues/13`。 +- **自定义视图 + 内置视图族已定稿(票 14 resolved,推翻/扩展票 10 视图组织,以 demo `保伦CRM系统demo-重构版-S1_0803.html` `oppModule` 为准)** — 销售机会工作区由「单一我负责的」改为**内置四视图**(`recent` 最近访问 / `mine` 我负责的 / **`participated` 我参与的(新,=团队成员且非负责人,与 mine 互斥)** / `followed` 我关注的)**+ 用户自定义视图**(保存的检索:名称+条件列表为空/不为空等6操作符+排序,可编辑/重命名/删除/图钉设默认)。**四大拍板**:①参与=团队成员且非负责人;②自定义视图**落 `crm-preference` 平台能力**(用户私有、按 `scope_key` 分组、与列偏好/ADR-0024 同构;新实体 `user_saved_view` + 契约 `list/save/delete/setDefault`;本期 scope=`opportunity.sales` 单一,后续菜单加 scope 即复用——产品确认“目前只销售机会、但要灵活性”);③内置视图固定不入库(口径硬编码在业务方);④自定义视图检索字段 = 列表所有可筛选列(字段池归商机模块)。管理/公海仍独立 workspace、本期不开自定义视图。**回填票 10**(视图组织改写,其余口径不变)+ **crm-opportunity/CONTEXT.md**(【四视图】→【视图(内置+自定义)】+新增【我参与的】)+ **crm-preference/CONTEXT.md**(新增【自定义视图】能力段 + scope 增 `opportunity.sales`)均已回写。spin-out:多组条件复合/高级公式(demo 标后续迭代,契约预留)。详见 `issues/14`。 + - **商机归属流转状态机已定稿(票 04 resolved)** — `opp_status` 5 态:`1 待领取(在公海)`/`2 推进中`(默认活跃)/`3 暂缓中`/`4 已关闭`(半终态可重启)/`5 已转项目`(绝对终态),对称线索 7 态而商机无导入分发/失效。迁移边:领取/分配(1→2)、抛公海(2→1)、暂缓(2→3)/取消暂缓(3→2)、关闭(2/3→4)/重启(4→2)、转项目(2→5)、移交(换 owner 不改 status、团队成员页、自环)。**无 owner guard**(可见即可点,票 01);移交权限靠入口位置不靠状态机 guard。CAS 乐观锁 + 每边写操作日志,对称线索 `LeadTransition`(sealed `OpportunityTransitionCmd` 体系)。已关闭=半终态唯一出边重启;已转项目=绝对终态无出边。**传出**:超时回收计时器/抛公海回哪个池 → 票 05;转项目端口 → A5。详见 `issues/04`。 - **目的地形态 = spec-only** — 对称线索地图,只产出决策/契约/领域模型,不写实现代码。(chart grill) - **数据可见性 = 复用 crm-auth DataScope** — 商机不新建可见性机制,套 `DataScopeEnum` / `DataScopeInterceptor` / ADR-0018 按模块可配。(chart grill) @@ -62,6 +64,8 @@ +- **自定义视图实现落地(票 14 传出)** — `crm-preference` 新契约方法签名 `list/save/delete/setDefault` + `user_saved_view` 表 DDL;商机列表**可筛选列字段池清单**(字段池归商机模块,实现阶段列全)。均为实现阶段工作,本 spec-only effort 不展开。 + - **客户关联子表的形态** — 已钉死为 **票 13**(见下 Decisions);仅“下拉选客户”数据源 + 客户联系人是否本子表冗余仍挂 **A4 客户模块(未建)** 边界(票 11 占位)。 - **商机批量操作与统计接口** — 对称线索 G1–G5(批量领取/分配/关注、列表统计卡片)。等主表 + 四视图定稿后再切成票。 - **商机相关字典项清单**(A7-3-2-6)— 行业 / 招标形式 / 关闭原因 / 暂缓原因 / 商机来源(含「线索转入」) / **商机类型** / **项目属地(本地/异地)** 等字典 code 清单(状态机票 04 + 字段票 03 已定稿,可汇总)。 diff --git a/.scratch/opportunity-module/产品待确认清单.md b/.scratch/opportunity-module/产品待确认清单.md index 384a6ed..e8888cd 100644 --- a/.scratch/opportunity-module/产品待确认清单.md +++ b/.scratch/opportunity-module/产品待确认清单.md @@ -1,7 +1,7 @@ # 商机模块 — 产品待确认清单 -> 生成自 `.scratch/opportunity-module/issues/01–12` 全部 ⚠ 待核项汇总。 -> **建模现状**:票 01–10 spec 已 resolved,下列待确认项**均不阻塞建模/表结构**(各票已给默认处置),仅在**落地实现前**须产品拍板。按「是否影响数据模型」分 P0/P1/P2 三档。 +> 生成自 `.scratch/opportunity-module/issues/01–14` 全部 ⚠ 待核项汇总。 +> **建模现状**:票 01–10 + 13/14 spec 已 resolved,下列待确认项**均不阻塞建模/表结构**(各票已给默认处置),仅在**落地实现前**须产品拍板。按「是否影响数据模型」分 P0/P1/P2 三档。 > 票 11/12 是实现票(改代码),另见文末。 --- @@ -69,6 +69,28 @@ - **依据**:原型 line 1745 给的是行为类别(字段变更/状态流转/附件/推送)非字段白名单;白名单是长期负债(加字段易漏审计),商机低频写全记扛得住且审计更全。 - **已落**:票 09(P6 写入点 + 待核项)。 +### ⚠ P1-6 归属起点计时初始化(E1/E2/两入口创建)— 挂起,待产品确认 +- **来源**:票 03/04/05 交叉缝隙(grill 20260821 发现,PRD §2.2/§3.2 已标注 ⚠) +- **问题**:回收公式 = `now − last_valid_follow_time ≥ recycle_days`,但该锚点在三处归属起点未统一初始化: + 1. **E1 领取**:已设 `claim_time` + 刷锚点=now(票 04) + 2. **E2 分配**:只设 `claim_time`,**不刷锚点**(票 04 疏漏) + 3. **两入口新建(线索转入/直接新建)**:出生即「推进中」+有负责人,不经过领取边,两字段均未定义(票 02 未提) +- **后果**:锚点为 NULL 时回收公式失效——分配/新建的商机若接手人从不跟进,将**永远不被回收**,回收机制有漏洞。 +- **候选方案**: + - A:四场景统一设 `claim_time=now` + 锚点=now(归属起点口径,锚点永不空,公式不改) + - B:只补 `claim_time`,公式改 `now − COALESCE(锚点, claim_time)` + - C:不补,锚点空不回收(有漏洞,不建议) +- **不阻塞建模**:两字段均已建表,仅侧效口径待定。 + +### ⚠ P1-7 暂缓期间子表可写范围 — 挂起,待产品确认 +- **来源**:票 04/09 缝隙(grill 20260821;原型 A3-1-1-2-6 只说「暂停部分推进动作和常规跟进提醒」,未枚举具体项) +- **问题**:暂缓中(3) 的流转限制已定(票 04:允许 E5 取消暂缓/E6 关闭/E9 移交;推进节点/转项目/抛公海需先取消暂缓),但**写跟进、方案卡/勘察/附件/关联客户/团队成员等子表写**是否允许,原型未明确。 +- **候选方案**: + - A:写跟进 + 子表写全部允许(仅推进节点/转项目/抛公海受限)——「暂停推进动作」≠禁记录留痕 + - B:写跟进允许、子表写禁 + - C:全禁只读(同关闭口径)——与原型「保留已有业务数据、暂停部分动作」不符 +- **已关闭(4) 不在此项内**:原型 A3-1-1-2-9 已明确(推进类隐藏/置灰/禁止、只读操作保留),已回填 PRD §3.3。 + ## P2 — 展示/交互口径 + 字典选项(可后置,不影响核心模型) ### P2-1 各类字典具体选项集(走 crm-dict) @@ -94,15 +116,28 @@ - **来源**:票 10(line 86) - **需产品定**:视角下拉「商机管理(领导)」的层级数据范围与 `@DataScope` 的精确映射。 -### P2-5 四视图默认排序 -- **来源**:票 10(line 87) -- **现状**:原型「建议按最近跟进/节点停留时间」等(预计成交时间列已删)。 -- **需产品定**:确定默认排序维度。 +### P2-5 视图默认排序 +- **来源**:票 10(line 87)/ 票 14 +- **现状**:原型「建议按最近跟进/节点停留时间」等(预计成交时间列已删)。自定义视图自带排序(票 14),内置视图默认排序仍待定。 +- **需产品定**:确定内置视图默认排序维度。 + +### P2-7 自定义视图(票 14)— 交互口径待确认 +- **来源**:票 14(demo 0803 `oppModule`) +- **已拍板**(票 14):内置视图四个(最近访问/我负责的/我参与的/我关注的,固定不可增删改);「我参与的」=团队成员且非负责人;自定义视图落 `crm-preference` 平台能力(用户私有、按 scope、本期 `opportunity.sales`);检索字段=列表所有可筛选列;图钉设默认视图。 +- **需产品定(不阻塞建模,落地前拍板)**: + - 每用户自定义视图**数量上限**(demo 无限制,前端 localStorage)。 + - **多组条件(AND/OR 复合)+ 高级公式**——demo 标注「后续迭代」,本期契约按条件列表预留、前端只 1 组;何时开放待产品定。 + - 自定义视图是否**仅个人私有**(票 14 定私有;是否需要「共享给团队/角色」为后续需求)。 + - 「商机管理」「商机公海」workspace 是否也开自定义视图(票 14 定本期不开)。 ### P2-6 公海规则数值样例 - **来源**:票 05(line 138) - **说明**:原型 recycle_days/remind_days = 30/7、45/7、20/5 均为**样例非验收值**,实际由规则配置录入,无需产品预先定死。 +### P2-8 grill 20260821 补录遗留项 +- **暂缓到期提醒渠道**:提醒对象=当前领取人已定(CONTEXT),到期不自动恢复、提醒后人工选 取消暂缓/继续暂缓/关闭已定;提醒发送渠道(站内信/短信/邮件)属平台通知基础设施,待平台通知能力确定时拍板。不阻塞建模。 +- 今日已拍板(已回填 PRD/CONTEXT):直接新建必填主要意向客户;公海规则锚点=商机 `owner_dept_id`、实时匹配当前发布版、已执行动作不回溯(版本快照写 oplog,PRD 补 §5.4);公海商机在管理视图按 DataScope 档位自然判定可见性;商机无删除动作;recent 视图任意详情皮肤都算访问;customer_role 全部选填;附件仅跟进限 10。 + --- ## 实现票(非 spec,待其他模块/编码阶段) diff --git a/.scratch/opportunity-module/商机业务-PRD.md b/.scratch/opportunity-module/商机业务-PRD.md new file mode 100644 index 0000000..477ce09 --- /dev/null +++ b/.scratch/opportunity-module/商机业务-PRD.md @@ -0,0 +1,636 @@ +# 商机业务 PRD + +> **版本**:V1.0(冻结自 spec effort `.scratch/opportunity-module`,票 01–14 全部 resolved) +> **用途**:给其他智能体 / 产品 / 开发按原型页逐条核对,每条决策挂原型页码。 +> **原型基准**:蓝湖 V1.0-20260815;demo `nginx/html/保伦CRM系统demo-重构版-S1_0803.html`。 +> **范围**:spec-only,不含实现代码。⚠ = 实现前须产品拍板的待确认项。 + +--- + +## §1 领域定义 + +**出处**:票 01 `domain-vocab-boundary`,原型 A3-x-x 总览。 + +### 1.1 什么是商机 + +商机(Opportunity)= 一条正在推进、准备转成项目的销售机会。 + +两条入口,进系统后**完全平权**(同样的流程 / 公海 / 视图,不因来路不同而区别对待): + +| 入口 | 说明 | 原型页 | +|---|---|---| +| **线索转入** | 销售在线索里跟进成熟后转成商机 | A3-1-1(线索详情转商机入口) | +| **直接新建** | 销售跳过线索,直接建一条商机 | A3-1-1-2-1 | + +直接新建时默认负责人 = 当前操作人(原型 A3-1-1-2-1 明证)。 + +### 1.2 两条正交轴 + +商机同时有两个独立维度,**不互相干扰**: + +| 轴 | 字段 | 通俗解释 | 原型对应 | +|---|---|---|---| +| **轴 1 归属流转状态** | `opp_status`(tinyint,5 态) | 这条商机归谁、现在处于什么归属处境 | 详情顶部「商机状态:推进中」A3-1-1-1-1 | +| **轴 2 业务阶段** | `current_stage_id`(指向节点) | 业务打法走到第几步 | 详情进度条 A3-1-1-1-1;阶段设置 A7-3-2-1 | + +> 一句话:**「状态」管归属,「阶段」管业务打法**。 + +### 1.3 核心角色 + +| 角色 | 定义 | 字段 | +|---|---|---| +| **负责人(领取人)** | 当前持有该商机的销售,归属 + 默认可见性双重含义 | `owner_user_id` | +| **团队成员** | 负责人拉入协作的人员,带项目角色(商机负责人 / 方案工程师 / 招标专员 / 现场工程师等)与权限(读写 / 只读 / 可写跟进 / 可推进节点等);是行级授权白名单,绕开 DataScope 部门天花板 | `opportunity_team`(关联表) | +| **创建人** | 全生命周期不变 | `creator_user_id` | + +- 抛公海只清空负责人,**不动团队成员**(产品明确,票 01)。 +- 转商机时自动插一条团队成员:领取人 = 商机负责人(票 02)。 +- **权限模型**:按钮可见即可点,权限全由权限点配置决定,与「是否负责人」无关(票 01 产品明确)。 + +### 1.4 模块边界 + +``` +crm-opportunity → crm-rule(阶段模板 / 方案卡模板 / 公海规则,零反依赖) + → crm-auth(DataScope / SysUser) + → crm-dict(字典码) + → crm-file(附件上传) + → crm-preference(列偏好 / 自定义视图) +``` + +商机规则(阶段模板 / 方案卡模板 / 公海规则 / 字典)住 `crm-rule`,商机模块只消费。 + +--- + +## §2 商机主表 + +**出处**:票 03 `opportunity-main-table`,原型 A3-1-1-2-1(新增表单)。 + +### 2.1 新增必填字段(原型 A3-1-1-2-1) + +| 字段 | 类型 | 必填 | 说明 | +|---|---|---|---| +| `opp_name` 商机名称 | varchar(200) | ✅ | | +| `opp_source` 商机来源 | varchar(64) 字典码 | ✅ | 渠道归因;线索转入时默认置「线索转入」 | +| `opp_type` 商机类型 | varchar(64) 字典码 | ✅ | | +| `industry_code` 行业 | varchar(64) 字典码 | ✅ | | +| `bid_form` 招标形式 | varchar(64) 字典码 | ✅ | | +| `locality_type` 项目属地 | varchar(64) 字典码 | ✅ | 本地/异地分类标签,非地理坐标 | +| `party_a_clear` 甲方是否明确 | tinyint 0/1 | ✅ | | +| `party_a` 甲方名称 | varchar(200) | 条件必填 | `party_a_clear=1` 时必填 | +| `province_code` 省份 | varchar(12) | ✅ | | +| `city_code` 市/区 | varchar(12) | ✅ | 既可填市码也可填区/县码,不单建 `district_code` | +| `remark` 备注 | text | ✕ | | +| `project_amount` 项目金额 | decimal(14,2) | ✕ | 项目整体总包金额,选填可后补;默认人民币元,不建币种字段(Amendment 1) | +| 意向客户(主要意向客户) | — | ✅ | 表单项,落 `opportunity_customer` 首条(`is_primary_intended=1`)+ 主表冗余快照;**两条入口均必填**(grill 补拍 20260821,与票 02 `customerId` 必填对齐);数据源依赖 A4 ⚠ | + +### 2.2 系统生成字段 + +| 字段 | 类型 | 说明 | +|---|---|---| +| `opp_status` | tinyint | 归属流转状态(轴 1),见 §3 | +| `owner_user_id` 负责人 | bigint | 抛公海后清空 | +| `owner_name_snapshot` 负责人姓名快照 | varchar(100) | 调岗不自动更新 | +| `owner_dept_id` 负责人部门快照 | bigint | 公海可见范围锚点;调岗不变 | +| `creator_user_id` 创建人 | bigint | 全生命周期不变 | +| `claim_time` 领取时间 | datetime | 领取/分配时设;⚠ 两入口新建是否初始化待产品确认(P1-6) | +| `last_valid_follow_time` 最近有效跟进时间 | datetime | 自动回收计时锚点(票 05);有效跟进(写跟进记录/上附件/编辑字段)时刷新;⚠ 分配/新建/转入是否刷锚点待产品确认(P1-6,锚点为 NULL 时回收公式失效) | +| `current_stage_id` 当前阶段 | bigint | 指向 `opportunity_stage_node.id`(版本绑定,票 06) | +| `stage_template_id` 阶段模板版本 | bigint | 存模板版本行 id,锁死,发新版不自动迁移(票 06) | +| `stage_template_version` 模板版本号冗余 | varchar(20) | 展示用,免 join(如 V1.2) | +| `source_lead_id` 来源线索 | bigint UNIQUE | 幂等键;NULL 不参与唯一(直接创建可多条 NULL) | +| `pool_reason` 进入公海原因 | varchar(32) | 抛公海/回收时写(见 §3.2);公海列表筛选 | +| `primary_customer_id` 主要意向客户 id | bigint | 冗余快查,= 关联客户表 `is_primary_intended=1` 那条 | +| `primary_customer_name_snapshot` 主客户名快照 | varchar(200) | 列表/详情顶部免 join | + +继承 `BaseEntity`(id / create_time / update_time / creator / updater / 逻辑删除)。 + +### 2.3 DataScope 注解 + +```java +@DataScope(module = "opportunity", ownerColumn = "owner_user_id", deptColumn = "owner_dept_id") +``` + +### 2.4 核心索引 + +| 索引 | 用途 | +|---|---| +| `UNIQUE idx_source_lead (source_lead_id)` | 一条线索只能关联一条商机(幂等键) | +| `idx_owner_status (owner_user_id, opp_status)` | 「我负责的」视图 | +| `idx_owner_dept_status (owner_dept_id, opp_status)` | 公海视图 + 部门天花板 | + +### 2.5 不建的字段(已明确排除) + +- ~~`pool_id`~~:票 05 定稿删——商机域无独立池实体,公海 = 逻辑视图 +- ~~`dept_id`~~:只用 `owner_dept_id` 快照做 DataScope 天花板 +- ~~`recycle_deadline`~~:票 05 推翻,改为 `last_valid_follow_time` 实时算 +- ~~`预计成交时间`~~:产品二次拍板——商机主表不建、商机列表不展示;仅方案卡内部字段(票 07/10) + +--- + +## §3 归属流转状态机(轴 1) + +**出处**:票 04 `ownership-state-machine`,原型 A3-1-1-1-1 / A3-1-1-2-x。 + +### 3.1 状态取值域(`opp_status` tinyint) + +| 值 | 常量 | 名称 | 说明 | 终态 | +|---|---|---|---|---| +| 1 | `STATUS_IN_POOL` | 待领取(在公海) | 无人持有,躺在商机公海 | ✕ | +| 2 | `STATUS_ADVANCING` | 推进中 | 有负责人、正常推进 | ✕ | +| 3 | `STATUS_PAUSED` | 暂缓中 | 短期挂起,保留负责人 | ✕ | +| 4 | `STATUS_CLOSED` | 已关闭 | 只读锁定,可重启 | 半终态 | +| 5 | `STATUS_CONVERTED` | 已转项目 | 锁死,无任何出边 | **终态** | + +### 3.2 迁移边全图 + +| # | 动作 | Cmd | 起始态→目标态 | 侧效摘要 | 原型页 | +|---|---|---|---|---|---| +| E1 | 领取 | `ClaimCmd` | 1→2 | 设 owner + 部门/姓名快照 + `claim_time`;刷新 `last_valid_follow_time`=now | A3-3-1-1(公海领取) | +| E2 | 分配 | `AssignToUserCmd` | 1→2 | 设 owner=指派对象 + 快照 + `claim_time`;⚠ 是否刷 `last_valid_follow_time` 待产品确认(P1-6) | A3-2-1(管理页分配) | +| E3 | 抛公海 | `ReleaseToPoolCmd` | 2→1 | 清 `owner_user_id` + `owner_name_snapshot` + `claim_time`;**`owner_dept_id` 保留**(公海可见范围锚点,票 05);记 `pool_reason`(销售主动释放/特殊原因暂停/领导释放);保留 `current_stage_id` 供公海展示;**不动团队成员**;入口受规则 `allow_manual_pool` 开关控制 | A3-1-1-2-7 | +| E4 | 暂缓 | `PauseCmd` | 2→3 | 保留 owner + 团队;记暂缓原因(字典) + 预计重启日期(必填≥今天) + 补充说明 | A3-1-1-2-6 | +| E5 | 取消暂缓 | `ResumeCmd` | 3→2 | 保留 owner;回暂缓前阶段(阶段字段未动);清暂缓附加字段 | A3-1-1-2-6(取消按钮) | +| E6 | 关闭 | `CloseCmd` | 2,3→4 | 保留 owner(只读锁定);记关闭原因(字典) + 补充说明(选「其他」时必填) + 确认勾选 | A3-1-1-2-9 | +| E7 | 重启 | `ReopenCmd` | 4→2 | 保留 owner;记重开原因(必填);统一回「推进中」 | A3-1-1(重新开启入口) | +| E8 | 转项目 | `ConvertToProjectCmd` | 2→5 | 保留 owner(锁死);对接 A5 项目模块(本模块只占端口) | A3-1-1-2-x(转项目) | +| E9 | 移交 | `HandoverCmd` | 2,3→自环 | 换 owner = 移交对象 + 姓名/部门快照同步刷新(`owner_dept_id` 换为移交对象部门,公海规则匹配锚点跟人走,grill 补拍 20260821);`claim_time` 不重置;入口在团队成员页 | A3-1-1-1-8(团队成员 Tab) | +| E10 | 超时回收 | `executeRecycle` | 2→1(批量) | 清 `owner_user_id` + `owner_name_snapshot` + `claim_time`;`owner_dept_id` 保留定公海可见范围;`pool_reason`=超期回收;保留 `current_stage_id` 快照;暂缓中(3)**不参与回收**;实际执行的规则版本快照写 `opportunity_oplog`(§5.4) | 后台 Job | + +> **「领导释放」**(E3 变体)= 管理侧强制抛公海,操作人 ≠ owner,`pool_reason`=领导释放(原型 A3-2-1 管理页)。 + +### 3.3 受限态出边约束 + +| 状态 | 禁止的动作 | +|---|---| +| 已关闭(4) | 仅允许 E7 重启;暂缓/抛公海/关闭/转项目/写跟进/推进节点 全禁;**整商机只读、所有子表写全禁**(原型 A3-1-1-2-9:推进类隐藏/置灰/禁止,只读操作保留) | +| 已转项目(5) | **无任何出边**,全禁 | +| 暂缓中(3) | 允许 E5/E6/E9;推进节点/转项目 受限(需先取消暂缓);⚠ 写跟进/子表写是否允许待产品确认(P1-7) | + +> **无删除动作**(grill 补拍 20260821):商机不提供删除接口,终法只有关闭(→4)/转项目(→5),历史数据(含子表)永不删。 + +### 3.4 守卫与 CAS + +- **无「操作人==负责人」guard**:商机侧详情页顶部操作区动作可见即可点,权限全由权限点决定(票 01 产品明确)。 +- CAS 乐观锁:每条迁移边在 `WHERE id=? AND opp_status IN (合法起始态)` 执行;更新 0 行抛 `CODE_STATUS_NOT_ALLOWED`。 +- 移交 E9 CAS:`WHERE id=? AND opp_status IN (2,3) AND owner_user_id=?原owner`(防并发移交/抛公海竞态)。 +- 操作日志(P6):每条 Cmd 执行后 INSERT 一条 `opportunity_oplog`,含动作类型/操作人/时间/附加说明。 + +### 3.5 `pool_reason` 枚举(进入公海原因) + +| 值 | 触发边 | 说明 | +|---|---|---| +| 销售主动释放 | E3(销售本人操作) | | +| 特殊原因暂停 | E3(销售本人操作,特殊原因) | | +| 领导释放 | E3(管理侧操作,操作人≠owner) | | +| 超期回收 | E10 `executeRecycle` | 自动 Job | + +公海列表可按 `pool_reason` 筛选(原型 A3-3-1 筛选项)。 + +--- + +## §4 业务阶段轴(轴 2) + +**出处**:票 06 `stage-template-versioning`,原型 A7-3-2-1(商机阶段设置,列表/详情/新增 3 页)。 + +### 4.1 模型分层 + +- **模板 + 节点住 `crm-rule`**(版本化配置,遵 V-CONFIG 范式,见 §9)。 +- **运行时位置 + 版本快照住 `crm-opportunity`**(主表 `current_stage_id` + `stage_template_id` + `stage_template_version`)。 + +### 4.2 阶段模板头表 `opportunity_stage_template`(crm-rule) + +| 字段 | 类型 | 必填 | 说明 | +|---|---|---|---| +| `template_code` 模板编码 | varchar(64) | ✅ | 系统生成(如 `OPP_GUIDE_01`),同模板多版本共用,只读 | +| `version_no` 版本号 | varchar(20) | ✅ | 系统生成(V1.0/V1.1…),只读 | +| `status` 状态 | tinyint | ✅ | 1=草稿 / 2=发布中 / 3=已停用 | +| `apply_scope` 适用范围 | tinyint | ✅ | 1=所有商机 / 2=指定部门(两档单选,本期不做「指定业务类型」档) | +| `is_default` 是否默认 | tinyint | ✅ | 同「适用范围+适用对象」内唯一 | +| `template_desc` 模板说明 | varchar | ✕ | | + +- **适用对象子表** `opportunity_stage_template_dept`(apply_scope=2 时,适用部门多值)。apply_scope=1 → 无适用对象(原型「适用对象展示 --」)。 + +### 4.3 阶段节点表 `opportunity_stage_node`(crm-rule,从属模板版本) + +| 字段 | 类型 | 必填 | 说明 | +|---|---|---|---| +| `template_id` 所属模板版本 | bigint | ✅ | → `opportunity_stage_template.id`(版本级,每版本一套节点快照) | +| `seq_no` 阶段序号 | int | ✅ | 推进顺序(轨道图左→右),增删/拖拽后系统重算 | +| `stage_dict_code` 阶段字典值 | varchar(64) | ✅ | 引用「商机阶段」字典分组项(如 `OPP_STAGE_04`),引用关系,自定义名不反写字典 | +| `custom_node_name` 自定义节点名称 | varchar(100) | ✕ | 本模板实际展示名,空则带入字典名 | +| `is_fixed` 固定节点 | tinyint | ✅ | 1=固定(「已转项目」)/ 0=普通 | + +### 4.4 「已转项目」固定节点 × 轴 1 联动 + +- 「已转项目」是每个模板的**固定节点**(`is_fixed=1`,字典值固定 `OPP_STAGE_05`),配模板时自带、**不可删**(原型校验「除固定节点外至少创建一个阶段节点」),且**不可拖到非末位**(语义要求,落地校验)。 +- 商机中任一方案卡转项目 → 触发轴 1 转项目终态(E8)+ 轴 2 落到固定节点。 + +### 4.5 绑定与不迁移(V-CONFIG 12) + +- 商机绑定模板时记录**具体版本**(`stage_template_id` = 版本行 id)。 +- 模板发新版后,**在途商机继续用原版本,不自动迁移**;新建/重新匹配的商机用当时**最新发布版本**(按适用范围匹配)。 +- 新建默认落首节点(客户圈定)。 + +⚠ P2-5:默认排序维度待产品定(建议最近跟进时间倒序)。 + +--- + +## §5 商机公海规则 + +**出处**:票 05 `opportunity-pool-rules`,原型 A7-3-2-5(3 页一手正文)。 + +> **重要**:商机公海规则 = **版本化规则配置**(遵 V-CONFIG),**非线索的 `lead_pool` 池实体**。商机域**无独立池实体**——「公海」= `opp_status=1 且 owner 为空` 的逻辑视图,可见范围由 `owner_dept_id` + 匹配规则适用范围判定。 + +### 5.1 规则表 `opportunity_pool_rule`(crm-rule,版本化) + +同一 `rule_code` 一套规则、多版本,每版本一行。核心配置五项: + +| 字段 | 类型 | 必填 | 说明 | +|---|---|---|---| +| `rule_code` 规则编码 | varchar(64) | ✅ | 系统生成(如 `POOL_RULE_01`),同规则多版本共用 | +| `version_no` 版本号 | varchar(20) | ✅ | 系统生成,只读 | +| `apply_scope` 适用范围 | tinyint | ✅ | 1=所有商机 / 2=指定部门 | +| **① `allow_manual_pool`** 允许负责人手动转入公海 | tinyint | ✅ | 0/1,控制「抛公海」入口是否对负责人开放 | +| **② `auto_recycle_enabled`** 启用自动回收 | tinyint | ✅ | 0/1 | +|   `recycle_days` 自动回收天数 N | int | ⚠ | `auto_recycle_enabled=1` 时必填;>0;连续未有效跟进达 N 天回收 | +| **③ `allow_free_claim`** 允许自由领取 | tinyint | ✅ | 0/1,控制适用范围内销售能否自领 | +| **④ `recycle_remind_enabled`** 回收前提醒 | tinyint | ✅ | 0/1,仅 `auto_recycle_enabled=1` 时可开 | +|   `remind_days` 提前提醒天数 | int | ⚠ | `recycle_remind_enabled=1` 时必填;>0 且 **< recycle_days**(原型硬校验) | + +### 5.2 版本约束 + +- 同一 `rule_code` 同时**仅一个草稿 + 仅一个发布中**。 +- **复制** = 生成独立新规则(新 `rule_code`,非后续版本)。 + +### 5.3 领取 / 回收行为 + +| 场景 | 行为 | +|---|---| +| `allow_free_claim=1` | 适用范围内且具备公海权限的销售可自领;领取成功 owner=领取人,记录领取时间/人/规则版本(E1) | +| `allow_free_claim=0` | 无自领入口,只能管理员分配(E2) | +| `auto_recycle_enabled=1` | 回收锚点 = `last_valid_follow_time`;连续未有效跟进天数 = now − 锚点,≥ `recycle_days` 触发 E10 自动回收 | +| 暂缓中(3) | **不参与回收**,回收计时暂停;取消暂缓回推进中后按剩余天数续算 | +| `recycle_remind_enabled=1` | 提醒时间 = 预计回收时间 − `remind_days` | + +- **有效跟进**(刷新锚点)= 写跟进记录 / 上附件 / 编辑字段(非浏览),票 09 联动。 + +### 5.4 规则版本:实时匹配 vs 锅版本(关键区分) + +**公海规则与阶段模板的版本口径不同**,核对时切勿混淆: + +| | 阶段模板(§4) | 公海规则(本节) | +|---|---|---| +| 绑定方式 | 主表存 `stage_template_id`(版本行 id),**锁死版本** | 商机**不存**规则 id,只存 `owner_dept_id` | +| 发新版 | 在途商机**继续用原版本,不迁移**(V-CONFIG 12) | **待处理判断实时按当前发布中的版本** | +| 典型场景 | 商机“走什么打法”,中途换会打乱在途流程→锁版 | “谁能碰这个部门公海”的当下策略,改规则就是要立刻生效→实时 | + +**口径(产品拍板,原型 A7-3-2-5):** + +1. **“谁能看/谁能领”判断→用当前发布中的版本。** 池里商机无需“迁移”:拿 `owner_dept_id` 实时匹配当前发布中规则。管理员发新版(如关掉自由领取 / 新增部门专属规则圈进该商机)→ **立即对存量公海商机生效**。 +2. **历史动作不重算(原型明写):** 已完成的领取/回收/提醒**锁旧版快照**,发新版不回溯。例:旧规则下已被领走的商机,不因发新版而撤销。 +3. **版本快照落操作日志(已定):** 领取/回收时把实际执行的规则版本写进 `opportunity_oplog`(票 09),**不上主表**(一商机多次领/回收,主表存不下历史)。作用:查旧账(说清“当时凭哪版规则领/回收”)+ 落实口径 2 “历史不重算”。 + +⏩ **一句记忆:公海规则跟着商机的部门、按当下发布中版本实时算;阶段模板锁版不迁移。** + + +⚠ P2-6:原型 recycle_days/remind_days 样例值(30/7、45/7、20/5)非验收值,由规则配置录入。 + +### 5.4 规则匹配与执行语义(票 05「三」补抄,grill 20260821 核对) + +- **匹配锚点 = 商机自身的 `owner_dept_id`**(规则跟着商机走,不跟查看人部门走):按该部门匹配 `apply_scope=2 且部门命中` 的发布中规则;匹配不到 → 发布中的所有部门通用默认规则兑底;部门专用 > 通用默认,发布约束保证唯一命中。专属规则同时决定该商机在公海**谁能看到、谁能领**(适用部门外不可见不可领)。 +- **实时匹配,不锁版本**:公海规则**不存于商机主表**,查看/领取/回收时拿 `owner_dept_id` 实时匹配**当前发布中**版本。管理员发新版立即对在池商机生效(如关掉「允许自由领取」→ 立刻不能自领)。⚠ 与阶段模板口径相反(阶段模板主表存 `stage_template_id` 锁死版本不迁移;公海规则实时匹配)。 +- **已完成的动作不重算,不回溯**:旧规则下已领走/已回收的记录继续关联原规则版本(原型 A7-3-2-5 明写)。 +- **落库手段**:每笔领取/回收把当时实际执行的**规则版本快照写操作日志 `opportunity_oplog`**,不上主表(一条商机会多次领/回收,主表只能存最新一次;存档 research-a7325 line 533/1097/1832)。 + +--- + +## §6 视图体系 + +**出处**:票 10 `four-views-query` + 票 14 `custom-views-and-builtin-view-family`,原型 A3-1-1/A3-2-1/A3-3-1/A3-0-1;demo 0803 `oppModule`。 + +### 6.1 三个工作区(workspace) + +| 工作区 | 数据范围 | 原型 | 自定义视图 | +|---|---|---|---| +| **销售机会**(个人) | 见 6.2 内置视图 + 自定义视图 | A3-1-1 | ✅ 本期开 | +| **商机管理** | 按 `@DataScope` 可见范围(复用 crm-auth,不自建数据权限) | A3-2-1 | 本期不开 | +| **商机公海** | `opp_status=1 且 owner 空` 逻辑视图 + 匹配公海规则适用范围 | A3-3-1 | 本期不开 | + +> **公海商机在管理视图的可见性**(grill 补拍 20260821):owner 空但 `owner_dept_id` 保留作 DataScope 部门锚点,按查看者档位自然判定——全部档可见;部门档看 `owner_dept_id` 是否在其可见部门集合;仅本人档永不可见(owner 空 ≠ 本人)。故 E2 分配入口(管理页)对全部/部门档管理员数据可达,无需去公海页。 + +> 视图不是状态切片:同一商机可同时出现在多视图(如自己负责 + 最近访问过)。 + +### 6.2 销售机会工作区 — 内置四视图(「常用检索」Tab,固定不可删/改) + +| code | 名称 | 数据范围 | +|---|---|---| +| `recent` | 最近访问 | 当前用户打开过详情的商机,访问时间倒序、按商机 id 去重留最新,上限可配置(本期默认 50);「访问」= 调用详情聚合接口,任意皮肤(个人/管理/公海)都算(grill 补拍 20260821) | +| `mine` | 我负责的销售机会 | `owner_user_id = 当前用户` | +| `participated` | 我参与的销售机会 | **团队成员且非负责人**:`EXISTS(opportunity_team WHERE opportunity_id=? AND user_id=当前用户) AND owner_user_id <> 当前用户`;与 `mine` 互斥 | +| `followed` | 我关注的销售机会 | 当前用户关注表 `opportunity_focus` 命中(§7.8) | + +### 6.3 销售机会工作区 — 自定义视图(「自定义检索」Tab) + +用户保存的检索,加入常用检索列表(排在内置四视图之后)。一个自定义视图 =: + +- **检索名称**(同一用户同一 scope 内唯一) +- **检索条件**:条件列表 `[{field, operator, value}, …]` + - `field` ∈ **列表所有可筛选列**(字段池归商机模块定义) + - `operator` ∈ `等于 / 不等于 / 包含 / 不包含 / 为空 / 不为空` + - 本期前端只提交 1 组条件;契约按列表建模,预留多组(AND/OR)/ 高级公式扩展位 +- **排序条件**:排序字段(∈字段池)+ 方向(asc/desc) + +**生命周期动作**(用户私有):保存为常用检索 / 另存为新检索 / 编辑保存 / 重命名 / 删除(删当前视图回落 `mine`)。 + +**默认视图(图钉)**:每个视图(内置四 + 自定义任一)可图钉设为该用户在此 scope 的默认视图,用户级单值;未设默认 → `mine`。 + +### 6.4 落 crm-preference 平台能力 + +- 自定义视图 = `crm-preference` 平台级能力(与「列偏好」并列),按 `scope_key` 分组、用户私有,对齐 ADR-0024「每菜单独立」。 +- 新实体 `user_saved_view(user_id, scope_key, view_id, name, filter_json, sort_field, sort_direction, is_default, seq_no)`;`filter_json` 序列化条件列表;`crm-preference` 不校验字段合法性(字段池归业务方)。 +- 本期 scope = **`opportunity.sales`**(单一);后续菜单加 scope 即复用(产品确认「目前只销售机会、但要灵活性」)。 +- **内置视图不入库**——口径硬编码在业务方,本表只存用户自建视图。 +- 契约:现有列偏好 `get/save` 之外,为自定义视图加 `list / save / delete / setDefault`(方法签名留实现阶段定)。 + +### 6.5 列表字段(个人/管理/最近访问共用基础列) + +序号 / 选择框 / 商机名称 / 甲方 / 意向客户 / 行业 / 地区 / 预计金额 / **当前工作节点** / **节点停留时间** / **最近跟进** / **下一节点** / **方案卡状态** / 操作。 + +**计算列口径(不冗余存,实时算/join):** + +| 列 | 口径 | +|---|---| +| 当前工作节点 | `current_stage_id` 对应节点名 | +| 下一节点 | 模板中当前节点的下一 `seq_no` 节点(展示用「建议下一步」) | +| 节点停留时间 | 今 − 进入当前节点日期(`opportunity_stage_history` 最新 to_time) | +| 最近跟进 | 最新 `opportunity_follow.create_time`;无则 `--` | +| 方案卡状态 | 取**主要意向客户**(`is_primary_intended=1`)那张方案卡的 `card_status`;无 → 「未创建」 | +| 方案预算 | 取主要意向客户那张方案卡的 `scheme_budget`;无则空 | +| 预计金额 | 主表 `project_amount` | +| 当前负责人 | `owner_user_id` 反查;**个人页默认隐藏**,管理/公海显示 | + +### 6.6 各视图差异 + +| 视图 | 专属列 | 操作按钮 | 新增入口 | +|---|---|---|---| +| 个人 A3-1-1 | 负责人默认隐藏 | 详情 / 写跟进 | **有**「+新增商机」(默认归当前用户) | +| 管理 A3-2-1 | 负责人显示 | 详情 / 分配 / 催办 / 关闭 + 批量催办/分配/关注/领导释放 | 无 | +| 公海 A3-3-1 | 入池前工作节点 / 进入公海原因 / 当前负责人 | 详情 / 关注 / 领取 + 批量领取/关注 | 无 | +| 最近访问 A3-0-1 | 按访问时间排序 | 详情 / 写跟进 / 关注 / 领取(按角色+商机态动态) | 无 | + +### 6.7 详情三皮肤 + +同一详情聚合,按视角展示不同操作区,**非三套数据**。Tab 结构一致(概览/客户信息/方案卡/现场勘察/相关附件/跟进记录/工作计划/团队成员/操作日志);差异在头部操作按钮 + 进入权限: + +- 个人 A3-1-1-1:写跟进/编辑/暂缓/抛公海/关闭/新增方案卡 +- 管理 A3-2-1-1:+ 分配/催办/关闭/督办 +- 公海 A3-3-1-1:领取/关注,无跟进/推进入口(未领取不可推进) + +⚠ P2-4:管理视角「领导 ▾」层级数据范围与 `@DataScope` 精确映射待实现阶段定。 +⚠ P2-7:自定义视图数量上限 / 多组条件开放时机 / 是否支持共享 待产品定。 + +--- + +## §7 详情子表族 + +### 7.1 关联客户 `opportunity_customer` + +**出处**:票 13 `opportunity-customer-relation`,原型 A3-1-1-2-4。 + +| 字段 | 类型 | 必填 | 说明 | +|---|---|---|---| +| `opportunity_id` | bigint | ✅ | 所属商机 | +| `customer_id` | bigint | ✅ | 引用客户(A4 客户模块,⚠ 未建) | +| `customer_role` | varchar(32) | ✕ | 客户角色(机电总包/工程商/集成商…)→ crm-dict「客户角色」分组;全部选填(grill 补拍 20260821) | +| `is_primary_intended` | tinyint 0/1 | ✅ | 是否主要意向客户,一商机唯一 =1 | + +- 线索转商机填的「意向客户」→ 落 `is_primary_intended=1`;商机侧添加普通关联客户 → `is_primary_intended=0`。 +- 约束:`UNIQUE(opportunity_id, customer_id)`(一客户一商机只关联一次);主要意向客户唯一(应用层事务 + 部分唯一索引)。 + +### 7.2 方案卡 `opportunity_scheme_card` + +**出处**:票 07 `scheme-card`,原型 A3-1-1-1-3 / A7-3-2-2/3。 + +- **一客户一卡**:`UNIQUE(opportunity_id, customer_id)`;每张卡必挂一个当前商机下的关联客户(无孤立卡)。 +- **状态机**:`草稿1 / 已提交2 / 已推送3`(「未创建」= 0 卡展示态,不入库)。 + - 保存草稿 → 草稿;确认 → 已提交(核心字段锁);推送 A7-3-2-3 → 已推送。 +- **可编辑性**:草稿(1)/已提交(2) 均可改全部字段;仅已推送(3) 整卡只读(`card_status < 3` 可编辑),要改新建卡。不做字段级锁。 + +| 关键字段 | 说明 | +|---|---| +| `card_status` | 草稿1/已提交2/已推送3(tinyint) | +| `scheme_budget` | 方案预算金额 decimal(14,2);商机列表「方案预算」列取主要意向客户卡的此值 | + +**模板驱动动态字段**: +- 模板头 `opportunity_scheme_card_template`(版本行,遵 V-CONFIG)+ 适用对象子表 `_template_dept` + 字段项 `opportunity_scheme_card_field`(有序)。 +- 字段定义库 `opportunity_scheme_field_def`(crm-rule,**自建不复用 crm-dict**):`field_key/field_name/field_type(控件类型)/option_source/ref_target/autofill_source/default_value/is_deletable`。**组合式**:单选下拉类字段的选项集可 `option_source=dict_group_code` 引用 crm-dict 分组。 +- 运行时值 `opportunity_scheme_card_value(card_id, field_key, field_value)`(随模板字段)。 +- 「预计成交时间」是方案卡内部字段(非商机主表、非列表列,票 10)。 + +**模板选择与锁定(grill 补拍 20260821)**: +- 创建方案卡时**手动选模板版本**(不自动匹配,产品明确)。 +- 候选范围 = 模板适用范围命中**商机 `owner_dept_id`**(所有商机档 + 指定部门命中档)的**「发布中」版本**;草稿/停用版本不可选。 +- 选定后卡存 `card_template_id`(版本行 id)**锁版本**,模板发新版后在途卡继续用原版(V-CONFIG 7),不自动迁移。 + +### 7.3 现场勘察 `opportunity_site_survey` + +**出处**:票 08 `site-survey`,原型 A3-1-1-1-4。 + +| 字段 | 必填 | 说明 | +|---|---|---| +| `survey_date` 现场勘察日期 | ✅ | | +| `engineer_user_id` 主要工程师 | ✅ | 引用 crm-auth SysUser;列表展姓名/部门/联系方式(脱敏,反查不冗余存) | +| `apply_way` 申请方式 | — | 引用字典 | +| `survey_seq` 勘察次数 | — | 引用字典 | +| `apply_no` 申请单号 | — | | + +- 勘察附件走统一附件表(`biz_type=SITE_SURVEY` + `biz_id`)。 + +### 7.4 跟进记录 `opportunity_follow` + +**出处**:票 09 P2,原型 A3-1-1-1-6 / 写跟进 A3-1-1-2-2。 + +- 字段 + `opp_id` + BaseEntity(create_by/create_time = 记录人/时间)。 +- 跟进附件 → 附件子表 `biz_type=FOLLOW_UP`,最多 10 个。 +- **保存跟进 → 刷新商机「最近跟进」 + `last_valid_follow_time`**(回收锚点刷新点,票 05 联动)。 + +### 7.5 附件 `opportunity_attachment`(统一表) + +**出处**:票 09 P4,原型 A3-1-1-1-5,复用 crm-file。 + +- **一张附件表** + `biz_type` + `biz_id` 定位,不为每个子表各建附件表。 +- `biz_type` 枚举:`OPP`(商机通用) / `FOLLOW_UP`(跟进) / `SITE_SURVEY`(勘察) / `SCHEME_CARD`(方案卡) … +- 数量上限:**仅跟进附件限 10 个/条**(原型明画);其余 biz_type 不设业务上限,由 crm-file 层统一管控(grill 补拍 20260821)。 +- 附件归属商机(无孤立附件,硬约束);文件本体走 crm-file,本表存资料类型/说明/上传人/时间。 + +### 7.6 工作计划 `opportunity_work_plan`(结构 only) + +**出处**:票 09 P5,原型 A3-1-1-1-7。 + +- 🅿️ **工作计划 = 独立业务模块 A1X,本期挂起**(产品拍板 P1-4)。本商机模块**只保留子表结构与归属契约**(多条/归属商机/内容/deadline/plan_status/finish_time)。 +- `plan_status`:未完成0/已完成1(逾期=未完成且 now>deadline,实时算)。**精确态枚举、状态机、与跟进联动、Tab 读中央模块** 全归 A1X 模块定。 + +### 7.7 操作日志 `opportunity_oplog` + +**出处**:票 09 P6,原型 A3-1-1-1-9。 + +- **字段级变更审计**:实体对象/字段名称/旧值/新值/操作说明/来源(系统自动 vs 人工)/时间/操作人。普通用户只读。 +- **与线索 `lead_history` 口径不同**:线索是动作枚举+JSON,商机是字段级旧新值审计。 +- 状态/阶段迁移另有专用快照表 `opportunity_status_history` / `opportunity_stage_history`,与本通用审计并存不重。 + +### 7.8 关注 `opportunity_focus` + +**出处**:票 14 视图引用,grill 补拍 20260821(新建,PRD 此前引用未定义的表)。 + +- **纯收藏夹语义,不带通知**(通知属平台通知基础设施,未建;后续要通知再加字段,grill 补拍 20260821)。 +- 字段:`opportunity_id` + `user_id` + BaseEntity;约束 `UNIQUE(opportunity_id, user_id)`。 +- 关注/取关无需原因、物理删行即可(关注不是审计对象,不入 oplog)。 +- 「我关注的」视图 = 该表命中当前用户;管理/公海/最近访问视图的「关注」按钮读写此表。 + +--- + +## §8 上下游集成 + +### 8.1 线索 → 商机(消费线索侧 port) + +**出处**:票 02 `lead-port-consumer-contract`,原型 A3-1-1(线索转商机)。 + +- 线索侧提供 `CreateOpportunityCmd`(out-port),商机模块消费,转商机时自动带 `source_lead_id`。 +- **契约变更(票 02 拍板,票 11 实现)**:`CreateOpportunityCmd` 新增 `customerId`(Long,**必填**);`intendedCustomer` 保留为客户名快照(走法 B:引用 + 快照并存)。 +- 线索转商机弹窗:意向客户改为**下拉选客户**(选中拿到 `customer_id`);线索转商机前置要求「必须有意向客户」。 +- 转商机时商机侧动作:建主表 + 建团队成员一条(领取人=商机负责人)+ 建关联客户一条(`is_primary_intended=1`,`customer_id`=cmd.customerId)。 +- **幂等**:`UNIQUE(source_lead_id)` 保证一条线索只能关联一条商机。 + +### 8.2 商机侧新增入口 → 关联线索校验 + +**出处**:票 12 `related-lead-validation`,原型 A3-1-1-2-1。 + +- 商机侧新增入口当用户选「商机来源 = 线索转入」时,手选一条【关联线索】写 `source_lead_id`(复用票 02 字段,**不新建** `related_lead_id`)。 +- 候选线索范围 = 线索 `status IN (已领取, 跟进中)` 且 `source_lead_id` 未被占用(与线索侧转商机准入对称)。 +- 命中已占用 → 返回错误码 `opp.related_lead.taken`;靠 `UNIQUE(source_lead_id)` 保唯一。 +- 直接创建且来源非「线索转入」→ `source_lead_id` NULL(可多条 NULL)。 + +### 8.3 商机 → 项目(转项目端口占位) + +- E8 转项目:对接 A5 项目模块(本模块只占端口,转项目后商机锁死终态 5)。 +- 触发源:商机中任一方案卡转项目 → 轴 1 终态 + 轴 2 固定节点「已转项目」。 + +### 8.4 商机 ↔ 客户(A4 客户模块) + +- ⚠ **A4 客户模块未建**。关联客户 `customer_id`、`primary_customer_id`、线索转商机「下拉选客户」均引用 A4。 +- 票 11(`customerId` 契约字段)、票 13(关联客户表)已定契约;「下拉选客户」UI 数据源待 A4 到达。 + +--- + +## §9 商机规则三族 · 版本化配置范式(V-CONFIG) + +**出处**:票 06 抽出,票 05/07 复用。 + +`crm-rule` 三族配置共享同一版本化范式: + +| 族 | 表 | 编码前缀样例 | +|---|---|---| +| 阶段模板 | `opportunity_stage_template` | `OPP_GUIDE_*` | +| 公海规则 | `opportunity_pool_rule` | `POOL_RULE_*` | +| 方案卡模板 | `opportunity_scheme_card_template` | — | + +**V-CONFIG 公共约定:** + +1. 同一 `code` 多版本,每版本一行。 +2. `version_no` 系统生成(V1.0/V1.1/V2.0…),只读。 +3. `status`:1=草稿 / 2=发布中 / 3=已停用。 +4. 同一 code 同时**仅一个草稿 + 仅一个发布中**。 +5. `apply_scope`:1=所有商机 / 2=指定部门(两档单选,本期不做「指定业务类型」);apply_scope=2 配适用对象子表(部门多值)。 +6. `is_default`:同「适用范围+适用对象」内唯一;发布新默认 → 原默认自动取消。 +7. **绑定快照**:业务实体绑定配置时记录具体版本;新版本发布后原绑定实体**继续用原版本不自动迁移**;新建/重新匹配用最新发布版本。 +8. **复制** = 生成独立新规则(新 code,非后续版本)。 +9. **列表按版本维度展示**:同 code 的草稿/发布中/停用各占一行,不合并。 + +> `crm-rule` 对上层零反依赖:规则表不知道 `opportunity` 实体,靠 `apply_scope`/部门 + `code` 弱关联。 + +--- + +## §10 字典清单(走 crm-dict,选项值由产品录入) + +⚠ P2-1:本模块只定「要做字典」,具体选项值由产品提供。 + +| 字典分组 | 用途 | 出处 | +|---|---|---| +| 商机来源 `opp_source` | 渠道归因(含「线索转入」) | 票 03 | +| 商机类型 `opp_type` | | 票 03 | +| 行业 `industry_code` | | 票 03 | +| 招标形式 `bid_form` | | 票 03 | +| 项目属地 `locality_type` | 本地/异地分类 | 票 03 | +| 商机阶段 `OPP_STAGE_*` | 客户圈定/关系摸排/资料采集/方案卡/已转项目… | 票 06 | +| 进入公海原因 `pool_reason` | 销售主动释放/特殊原因暂停/领导释放/超期回收 | 票 04/10 | +| 暂缓原因 | | 票 04 | +| 关闭原因 | | 票 04 | +| 客户角色 | 机电总包/工程商/集成商… | 票 13 | +| 跟进方式 | 电话/上门/微信/线上会议/展会… | 票 09 | +| 跟进结果标签 | 暂无进展/需求确认/方案推进… | 票 09 | +| 商机资料类型 | 现场资料/设计清单·图纸/客户需求资料/招投标相关资料/其它 | 票 09 | +| 申请方式(勘察) | 工单申请/线下申请… | 票 08 | +| 勘察次数 | | 票 08 | + +--- + +## §11 待确认清单索引(实现前须产品拍板) + +> 完整版见 `.scratch/opportunity-module/产品待确认清单.md`。以下均**不阻塞建模/表结构**(各票已给默认处置)。 + +| 编号 | 待确认项 | 档 | 现状 | +|---|---|---|---| +| P2-1 | 各字典具体选项集 | P2 | 走 crm-dict,产品录入 | +| P2-4 | 管理视角「领导 ▾」层级数据范围 vs `@DataScope` 精确映射 | P2 | 复用 crm-auth,映射待实现阶段定 | +| P2-5 | 内置视图默认排序维度 | P2 | 建议最近跟进倒序,待定 | +| P2-6 | 公海规则数值样例 | P2 | 样例非验收值,规则配置录入 | +| P2-7 | 自定义视图:数量上限 / 多组条件(AND/OR)+高级公式开放时机 / 是否支持共享 / 管理·公海是否开 | P2 | 本期私有、1 组条件、只销售机会开 | +| P1-4 | 工作计划精确状态枚举 | 🅿️ | 归 A1X 独立模块,本期挂起 | +| P1-6 | 归属起点计时初始化(E1/E2/两入口创建统一口径,候选方案见清单) | P1 | 挂起待产品确认(grill 20260821) | +| P1-7 | 暂缓期间子表可写范围(写跟进/方案卡/勘察/附件/客户/团队) | P1 | 挂起待产品确认(grill 20260821) | + +**已拍板存档(备查):** 预计成交时间归属(P0-1,方案卡内部/不上主表/列表删除)、阶段模板适用范围两档(P0-2)、字段定义库自建不复用 crm-dict(P0-3)、方案卡状态枚举+多卡取值锚点(P0-4)、方案卡可编辑性(P1-1,`card_status<3`)、勘察次数生成(P1-2)、勘察补充说明必填(P1-3)、操作日志关键字段(P1-5)、最近访问去重+上限(P2-2)、方案卡状态列多卡口径(P2-3)。 + +--- + +## 附:原型事实出处索引 + +| 原型页 | 内容 | +|---|---| +| A3-1-1 | 商机个人视图列表 / 线索转商机入口 | +| A3-1-1-1-1 | 商机详情概览(状态条 + 进度条) | +| A3-1-1-1-3~9 | 详情 Tab:方案卡/勘察/附件/跟进/工作计划/团队成员/操作日志 | +| A3-1-1-2-1 | 新增商机表单 | +| A3-1-1-2-2 | 写跟进 | +| A3-1-1-2-4 | 添加关联客户 | +| A3-1-1-2-6/7/9 | 暂缓 / 抛公海 / 关闭 | +| A3-2-1 | 商机管理视图 | +| A3-3-1 | 商机公海视图 | +| A3-0-1 | 最近访问视图 | +| A7-3-2-1 | 商机阶段设置(阶段模板) | +| A7-3-2-2/3 | 方案卡模板 / 方案推送 | +| A7-3-2-5 | 商机公海与提醒规则 | +| demo 0803 `oppModule` | 视图体系(内置四视图 + 自定义视图交互) | + +--- + +**票映射总表:** + +| 票 | 主题 | PRD 章节 | +|---|---|---| +| 01 | 领域词汇与边界 | §1 | +| 02 | 线索侧 port 消费契约 | §8.1 | +| 03 | 商机主表 | §2 | +| 04 | 归属流转状态机 | §3 | +| 05 | 商机公海规则 | §5 | +| 06 | 阶段模板版本化 | §4 §9 | +| 07 | 方案卡 | §7.2 | +| 08 | 现场勘察 | §7.3 | +| 09 | 子表族(跟进/附件/工作计划/日志) | §7.4–7.7 | +| 10 | 四视图与列表/详情查询 | §6 | +| 11 | CreateOpportunityCmd + customerId | §8.1(实现票) | +| 12 | 商机侧关联线索校验 | §8.2(实现票) | +| 13 | 商机-关联客户 | §7.1 | +| 14 | 自定义视图 + 内置视图族 | §6.2–6.4 | + +_(本 PRD 冻结自 spec,如与原型有出入,以原型 + 票正文为准并回填对应票。)_ diff --git a/crm-opportunity/CONTEXT.md b/crm-opportunity/CONTEXT.md index 8c8d703..c601732 100644 --- a/crm-opportunity/CONTEXT.md +++ b/crm-opportunity/CONTEXT.md @@ -23,7 +23,7 @@ _Avoid_: 状态(状态是另一条轴)、节点(口语可叫「节点」 _Avoid_: 负责人(负责人是公海池概念)、持有者、owner(英文单说) **创建人**: -录入该商机的用户,全生命周期不变。直接创建 / 线索转入均落创建人。删除权限判定依据之一(对称线索:终态为删除禁区)。 +录入该商机的用户,全生命周期不变。直接创建 / 线索转入均落创建人。**商机无删除动作**(grill 补拍 20260821)——终法只有关闭/转项目,历史数据永不删,故无删除权限判定问题。 _Avoid_: 录入人、owner(owner 是领取人) **团队成员**: @@ -39,11 +39,11 @@ _Avoid_: 来源线索、渠道(口语,字段名 oppSource)、来路 _Avoid_: 商机来源(那是 oppSource 渠道)、线索 id、parent **关联客户**: -商机关联的客户(原型「添加关联客户」A3-1-1-2-4、客户信息 Tab A3-1-1-1-2)。子表 = **`opportunity_customer`(票 13)**:`customer_id` 引用 A4 客户 + 名快照、`customer_role` 角色(→crm-dict)、`is_primary_intended` **是否主要意向客户**。规则(产品拍板):一商机可多个关联客户,**有且仅一个主要意向客户**;`UNIQUE(opportunity_id, customer_id)`。线索转商机时填的意向客户 = 落为主要意向客户;商机侧可加普通关联客户、可改主要意向客户(切换标记)。主表冗余 `primary_customer_id`/名快照(免 join)。**主要意向客户是列表派生列(方案预算/方案卡状态)的取值锚点**(票 07/10)。⚠ `customer_id` 引用 + 下拉选客户能力依赖 A4 客户模块(未建),见票 11。 +商机关联的客户(原型「添加关联客户」A3-1-1-2-4、客户信息 Tab A3-1-1-1-2)。子表 = **`opportunity_customer`(票 13)**:`customer_id` 引用 A4 客户 + 名快照、`customer_role` 角色(→crm-dict)、`is_primary_intended` **是否主要意向客户**。规则(产品拍板):一商机可多个关联客户,**有且仅一个主要意向客户**;`UNIQUE(opportunity_id, customer_id)`。**两条入口都必填主要意向客户**(直接新建表单含意向客户必选,与线索转商机 `customerId` 必填对齐;grill 补拍 20260821),保证主客户约束自创建起恒成立。线索转商机时填的意向客户 = 落为主要意向客户;商机侧可加普通关联客户、可改主要意向客户(切换标记)。主表冗余 `primary_customer_id`/名快照(免 join)。**主要意向客户是列表派生列(方案预算/方案卡状态)的取值锚点**(票 07/10)。⚠ `customer_id` 引用 + 下拉选客户能力依赖 A4 客户模块(未建),见票 11。 _Avoid_: 客户(客户是 A4 域的实体)、甲方(甲方是快照里的一个字段)、customer **暂缓**: -商机流转动作(原型 A3-1-1-2-6):短期推不动、但仍有价值时挂起。选**暂缓原因**(字典)+ **预计重启日期**(必填,不早于今天)+ 补充说明 → 状态「暂缓中」。**保留领取人+团队、不进公海、冻结回收计时**(暂缓期间不被超时回收;取消暂缓后按剩余天数续算,票 05)。保留暂缓前阶段/节点。到期不自动恢复——系统提醒领取人,人工选 取消暂缓(恢复推进)/ 继续暂缓 / 关闭。按钮可见性由权限点配置,可见即可点,与是否领取人无关。 +商机流转动作(原型 A3-1-1-2-6):短期推不动、但仍有价值时挂起。选**暂缓原因**(字典)+ **预计重启日期**(必填,不早于今天)+ 补充说明 → 状态「暂缓中」。**保留领取人+团队、不进公海、冻结回收计时**(暂缓期间不被超时回收;取消暂缓后按剩余天数续算,票 05)。保留暂缓前阶段/节点。到期不自动恢复——系统提醒领取人,人工选 取消暂缓(恢复推进)/ 继续暂缓 / 关闭。按钮可见性由权限点配置,可见即可点,与是否领取人无关。原型 A3-1-1-2-6 原文「暂停部分推进动作和常规跟进提醒」未枚举具体项:推进节点/转项目/抛公海受限(需先取消暂缓)已定(票 04);⚠ 写跟进/子表写是否允许待产品确认(P1-7)。 _Avoid_: 中止(不存在)、挂起、冻结、关闭(关闭是明确无价值) **取消暂缓**: @@ -63,7 +63,7 @@ _Avoid_: 中止(不存在)、作废(线索用「作废」)、删除、 _Avoid_: 恢复、取消暂缓(取消暂缓是暂缓的逆动作)、激活 **移交**: -负责人变更动作,**入口在团队成员页(原型 A3-1-1-1-8)**,非详情页顶部操作区。**移交只换领取人 `owner_user_id`**(产品明确),不动其他。**无独立「转让」动作**——「转让只有负责人能转」不是额外 guard,而是移交入口本身在负责人管理的团队成员页里(原型:「当前用户只能维护其权限范围内的团队成员及权限设置」),团队管理权实际落在领取人手上。 +负责人变更动作,**入口在团队成员页(原型 A3-1-1-1-8)**,非详情页顶部操作区。**移交只换领取人 `owner_user_id` + 姓名/部门快照同步刷新**(`owner_dept_id` 换为移交对象的部门,公海规则匹配锚点跟人走;grill 补拍 20260821),不动团队成员等其他。**无独立「转让」动作**——「转让只有负责人能转」不是额外 guard,而是移交入口本身在负责人管理的团队成员页里(原型:「当前用户只能维护其权限范围内的团队成员及权限设置」),团队管理权实际落在领取人手上。 _Avoid_: 转让(无此独立动作)、转移、指派(分配才是指派) **转项目**: @@ -106,16 +106,24 @@ _Avoid_: 中央工作计划模块(A1X,跨域,本票不定读写) 商机的**字段级变更审计** `opportunity_oplog`(票 09,原型 A3-1-1-1-9):实体对象/字段名称/旧值/新值/操作说明/来源(系统自动 vs 人工)/时间/操作人。普通用户只读。**与线索 `lead_history` 口径不同**:线索是动作类型枚举(CLAIM/ASSIGN…)+JSON,商机是字段级旧新值审计。状态/阶段迁移另有专用快照表(`opportunity_status_history`/`opportunity_stage_history`),与本通用审计并存不重。 _Avoid_: 线索 history 模型(动作枚举,口径不同)、状态/阶段快照表(专用表,非通用审计) -**四视图**: -商机列表四视图(票 10,原型 A3-1-1/A3-2-1/A3-3-1/A3-0-1):**销售机会(个人)** `owner=当前用户` / **商机管理** 按 `@DataScope` / **商机公海** `opp_status=1 且 owner 空` 逻辑视图 / **最近访问** 打开过详情的商机按时间倒序、按商机 id 去重留最新,**上限可配置(本期默认 50 条)**。计算列不冗余存:节点停留时间=今−进入当前节点日(stage_history)、最近跟进=最新 follow 时间、预计金额=`project_amount`。详情三皮肤(个人/管理/公海)=同一聚合按视角展不同操作区,非三套数据。 -_Avoid_: 视图=状态切片(同一商机可跨视图)、预计成交时间(产品二次拍板:商机列表删除该列、不上主表;仅方案卡内部字段票07) +**视图(内置 + 自定义)**: +销售机会工作区下的视图组织(票 14 推翻票 10「单一我负责的」,以 demo 0803 为准)。**内置视图四个**(「常用检索」Tab,固定不可删/改):**最近访问**(打开过详情的商机,时间倒序、按 id 去重留最新,上限可配 默认 50)/ **我负责的** `owner=当前用户` / **我参与的**(团队成员且非负责人,见下)/ **我关注的**(关注表 `opportunity_focus` 命中,见下)。**自定义视图**(「自定义检索」Tab)= 用户保存的检索:检索名称 + 条件列表(字段∈列表可筛选列 + 操作符 等于/不等于/包含/不包含/为空/不为空 + 值)+ 排序(字段+asc/desc),可编辑/重命名/删除/图钉设默认——**落 `crm-preference` 平台能力**(用户私有、按 `scope_key` 分组,本期 scope=`opportunity.sales`,后续菜单加 scope 即复用)。**商机管理**(按 `@DataScope`)/ **商机公海**(`opp_status=1 且 owner 空` 逻辑视图)仍是独立 workspace、不进销售机会视图下拉、本期不开自定义视图。计算列不冗余存:节点停留=今−进入当前节点日(stage_history)、最近跟进=最新 follow、预计金额=`project_amount`。详情三皮肤(个人/管理/公海)=同一聚合按视角展不同操作区,非三套数据。 +_Avoid_: 视图=状态切片(同一商机可跨视图)、预计成交时间(产品二次拍板:商机列表删除该列、不上主表;仅方案卡内部字段票07)、自定义视图绑死商机(落 crm-preference 按 scope 通用,票 14)、内置视图可编辑(内置固定,只自定义视图可增删改) + +**关注**: +商机级个人收藏动作,落 `opportunity_focus`(`opportunity_id`+`user_id`,`UNIQUE(opportunity_id, user_id)`;grill 补拍 20260821,票 14 视图引用前此表未定义)。**纯收藏夹语义,不带通知**(通知属平台通知基础设施,未建)。关注/取关无需原因、物理删行、不入操作日志。「我关注的」视图 = 该表命中当前用户;管理/公海/最近访问视图的「关注」按钮读写此表。 +_Avoid_: 团队成员(成员是行级授权白名单,关注是个人收藏)、收藏(统一叫关注) + +**我参与的**: +销售机会内置视图之一(票 14):**我是该商机团队成员但不是负责人** —— `EXISTS(团队成员 WHERE opportunity_id=? AND user_id=当前用户) AND owner_user_id <> 当前用户`。团队成员表 = 票 02/04 建商机落的领取人=负责人 + 后续「添加团队成员」加入者。与「我负责的」互斥不重叠(负责人走 `mine`,非负责成员走 `participated`)。 +_Avoid_: 参与=含负责人(负责人归「我负责的」,参与专指非负责成员)、参与=仅能看(团队成员带角色权限,非观察者) **进入公海原因**: 商机抢公海/回收时的分类 `pool_reason`(票 10 原型 A3-3-1 筛选项):销售主动释放 / 特殊原因暂停 / 领导释放 / 超期回收。公海列表按此筛选;「领导释放」=管理侧强制抛公海(操作人≠owner);「超期回收」=票 05 executeRecycle。抢公海时亦快照入池前工作节点。 _Avoid_: 抢公海无原因(已分四类,回填票 04/05) **方案卡模板**: -方案卡的**版本化字段集配置**(选哪些字段/顺序/显示/必填/适用范围),住 `crm-rule` 的 `opportunity_scheme_card_template`(票 07 原型 A7-3-2-2)。与票 05 公海规则、票 06 阶段模板**同族**(公共约定 V-CONFIG:`template_code`+`version_no`+草稿/发布中/已停用,绑定锁版发新版不迁移)。字段来自**系统字段库**非自由造(删字段不删系统字段定义);字段库 = **自建 `opportunity_scheme_field_def`**(字段元数据:控件类型/关联对象目标/自动回填来源,crm-dict 码表模型不匹配故不复用;下拉类字段选项集可 `option_source` 引用 crm-dict 分组);适用范围**两档**(所有部门通用/对应部门专用,同票 05,异于票 06 三档)。 +方案卡的**版本化字段集配置**(选哪些字段/顺序/显示/必填/适用范围),住 `crm-rule` 的 `opportunity_scheme_card_template`(票 07 原型 A7-3-2-2)。与票 05 公海规则、票 06 阶段模板**同族**(公共约定 V-CONFIG:`template_code`+`version_no`+草稿/发布中/已停用,绑定锁版发新版不迁移)。字段来自**系统字段库**非自由造(删字段不删系统字段定义);字段库 = **自建 `opportunity_scheme_field_def`**(字段元数据:控件类型/关联对象目标/自动回填来源,crm-dict 码表模型不匹配故不复用;下拉类字段选项集可 `option_source` 引用 crm-dict 分组);适用范围**两档**(所有部门通用/对应部门专用,同票 05,异于票 06 三档)。**创建方案卡时手动选模板版本**(不自动匹配):候选 = 适用范围命中商机 `owner_dept_id` 的「发布中」版本,选定后 `card_template_id` 锁版本、发新版在途卡不迁移(grill 补拍 20260821)。 _Avoid_: 方案卡(运行时单据,模板是配置) 商机业务阶段轴(轴2)的**版本化配置**,住 `crm-rule` 的 `opportunity_stage_template`(票 06 原型 A7-3-2-1)。范式 = `template_code`+`version_no`+草稿/发布中/已停用 + 适用范围(所有商机/指定部门 两档单选,本期不做指定业务类型) + 是否默认,与票 05 公海规则、票 07 方案卡模板**同族**(公共约定 V-CONFIG)。子表:`_dept`(适用对象,apply_scope=2)、`opportunity_stage_node`(有序阶段节点,引用「商机阶段」字典)。商机绑定后**锁死版本、发新版不迁移**(在途商机续用原版本,新建用最新发布版本)。 _Avoid_: 阶段字典(模板引用字典,不同层次)、阶段(阶段是运行时轴,模板是配置) @@ -125,7 +133,7 @@ _Avoid_: 阶段字典(模板引用字典,不同层次)、阶段(阶段 _Avoid_: 阶段字典项(被引用方)、current_stage(运行时指针) **商机公海规则**: -商机公海/回收/领取/提醒的**版本化规则配置**,住 `crm-rule` 的 `opportunity_pool_rule`(票 05 原型 A7-3-2-5 定稿)。范式 = 草稿/发布中/已停用 + `rule_code` + `version_no`(与票 06 阶段模板、票 07 方案卡模板同族),**不是** `lead_pool` 那种「一部门一池」实体(推翻初稿)。适用范围 = 所有部门通用 / 对应部门专用 + 默认规则兜底(无省/市/区地理维度)。**匹配以最低层级规则为准**(部门专用 > 通用默认,多部门专用取最具体);**一个部门同时只能被一条发布中规则覆盖**(发布时校验部门不重叠),故命中唯一(票 05 产品拍板)。五配置项:`allow_manual_pool`(允许手动抛公海)/ `auto_recycle_enabled`+`recycle_days` / `allow_free_claim`(允许自由领取)/ `recycle_remind_enabled`+`remind_days`。 +商机公海/回收/领取/提醒的**版本化规则配置**,住 `crm-rule` 的 `opportunity_pool_rule`(票 05 原型 A7-3-2-5 定稿)。范式 = 草稿/发布中/已停用 + `rule_code` + `version_no`(与票 06 阶段模板、票 07 方案卡模板同族),**不是** `lead_pool` 那种「一部门一池」实体(推翻初稿)。**匹配锚点 = 商机自身的 `owner_dept_id`**(规则跟着商机走,不跟查看人部门走;grill 补拍 20260821):抛公海/回收后 owner 清空但部门快照保留,系统读它匹配规则——专属规则(apply_scope=2 命中)决定两件事:该商机在公海**谁能看到、谁能领**(适用部门外的销售不可见不可领);无专属规则 → 用通用默认规则(apply_scope=1)兜底,全部门可见,能否领看 `allow_free_claim`。适用范围 = 所有部门通用 / 对应部门专用 + 默认规则兜底(无省/市/区地理维度)。**匹配以最低层级规则为准**(部门专用 > 通用默认,多部门专用取最具体);**一个部门同时只能被一条发布中规则覆盖**(发布时校验部门不重叠),故命中唯一(票 05 产品拍板)。五配置项:`allow_manual_pool`(允许手动抛公海)/ `auto_recycle_enabled`+`recycle_days` / `allow_free_claim`(允许自由领取)/ `recycle_remind_enabled`+`remind_days`。 _Avoid_: 商机公海池(无池实体)、商机池、线索池 **回收天数**: diff --git a/crm-preference/CONTEXT.md b/crm-preference/CONTEXT.md index ad21bf9..e83cc0a 100644 --- a/crm-preference/CONTEXT.md +++ b/crm-preference/CONTEXT.md @@ -1,6 +1,6 @@ # 列偏好(crm-preference) -平台级的用户列表列偏好能力,与 `crm-dict` 平行的通用模块:用户级列显隐 + 拖拽排序,按 scope 分组。本次只做到「线索够用的最小通用深度」,不做全平台通用化打磨。契约仅 `get / save` 两法。 +平台级的用户列表偏好能力,与 `crm-dict` 平行的通用模块:用户级、按 scope 分组、私有。含两种偏好:**①列偏好**(列显隐 + 拖拽排序,契约 `get / save`);**②自定义视图**(用户保存的检索,票 14,契约 `list / save / delete / setDefault`)。本次只做到「够用的最小通用深度」,不做全平台通用化打磨。 ## Language @@ -9,9 +9,13 @@ _Avoid_: 表格配置、字段配置、视图设置 **scope**: -列偏好的作用范围键 `scope_key`,由业务方约定的稳定 code。**每菜单独立**、互不同步(推翻原型「共用一套」的说法)。线索域 4 个取值已钉死(ADR-0024,与 viewType 一一对应):`lead.public_pool`(公海)/ `lead.my_lead`(我的线索)/ `lead.my_follow`(我的关注)/ `lead.manage`(线索管理)。 +偏好(列偏好 / 自定义视图共用)的作用范围键 `scope_key`,由业务方约定的稳定 code。**每菜单独立**、互不同步(推翻原型「共用一套」的说法)。线索域列偏好 4 个取值已钉死(ADR-0024,与 viewType 一一对应):`lead.public_pool`(公海)/ `lead.my_lead`(我的线索)/ `lead.my_follow`(我的关注)/ `lead.manage`(线索管理)。商机自定义视图(票 14)本期 1 个:`opportunity.sales`(销售机会工作区)。 _Avoid_: 页面 ID、菜单 key、视图类型 +**自定义视图**: +用户在某菜单保存的一条**检索**(票 14,demo 0803 `oppModule` 为准),存于 `user_saved_view(user_id, scope_key, view_id, name, filter_json, sort_field, sort_direction, is_default, seq_no)`。用户级私有、按 `scope_key` 分组,与列偏好同构。`filter_json` = 条件列表 `[{field, operator, value}, …]`(本期前端只提交 1 组,契约按列表预留复合/高级公式扩展位);**字段合法性 crm-preference 不校验**(字段池归业务方,同「字段池」约定)。同 `(user_id, scope_key)` 至多一条 `is_default=1`(图钉设的默认视图)。系统**内置视图**(如商机的 最近访问/我负责的/我参与的/我关注的)**不入本表**——内置视图口径硬编码在业务方,本表只存用户自建的视图。 +_Avoid_: 内置视图落库(内置固定在业务方,本表只存用户自建)、视图=页面路由、绑死某业务模块(按 scope 通用,加 scope 即复用) + **字段池**: 某 scope 下所有可显隐 / 可排序的候选列,**归业务方定义**,`crm-preference` 不持有字段清单,只存用户勾选与排序的结果(`visible_keys` / `column_order`)。 _Avoid_: 列定义、可选字段表、schema diff --git a/nginx/html/信息化平台数据取用方式对比.pdf b/nginx/html/信息化平台数据取用方式对比.pdf new file mode 100644 index 0000000..d31ab31 Binary files /dev/null and b/nginx/html/信息化平台数据取用方式对比.pdf differ