64 KiB
商机业务 PRD
版本:V1.0(冻结自 spec effort
.scratch/opportunity-module,票 01–14 全部 resolved) 用途:给其他智能体 / 产品 / 开发按原型页逐条核对,每条决策挂原型页码。 原型基准:蓝湖 V1.0-20260815;demonginx/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 |
| 团队成员 | 负责人拉入协作的人员,带**项目角色(纯回显标签:商机负责人 / 方案工程师 / 招标专员 / 现场工程师等,不带任何读写权限,grill 拍板 20260821)**与权限(读写 / 只读 / 可写跟进 / 可推进节点等,权限与角色无关、单独配);是行级授权白名单,绕开 DataScope 部门天花板——生效边界 = 详情/子表读 + 个人工作区视图;管理/公海工作区严格按 DataScope 不混白名单(grill 补拍 20260821) | 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,商机模块只消费。
1.5 权限点需求清单(grill 梳理 20260821,编码实现阶段按平台规范登记)
权限模型(§1.3):按钮可见即可点,权限全由权限点配置决定。以下为需要权限点的动作全集:
| 类 | 权限点动作 |
|---|---|
| 入口 | 销售机会工作区 / 商机管理工作区 / 商机公海工作区 |
| 商机主表 | 新增商机 / 编辑商机 / 关闭 / 重启 |
| 归属流转 | 领取(公海)/ 分配(管理)/ 抛公海(个人主动)/ 领导释放(管理强制)/ 暂缓 / 取消暂缓 / 移交 |
| 跟进 | 写跟进 / 删除跟进 |
| 方案卡 | 新增 / 编辑 / 提交 / 推送 |
| 团队成员 | 添加 / 调整角色与权限 / 移除(成员行上的读写/只读/可写跟进/可推进节点 = 行级权限配置,不走全局权限点;项目角色纯回显标签、不带权限,与权限列各自独立配置,grill 拍板 20260821) |
| 关联客户 | 新增 / 编辑(改客户角色、切主要意向标记)——无删除动作(原型/票 13 无删除,grill 纠偏 20260821) |
| 现场勘察 | 新增 / 编辑 / 删除 |
| 附件 | 上传 / 删除 |
| 视图 | 关注 / 取关;自定义视图新增/编辑/删除;批量领取/分配/关注/领导释放(同单条动作权限点,批量不加新点) |
| 审计 | 操作日志查看 |
| 后台(crm-rule 侧) | 阶段模板管理 / 方案卡模板管理 / 公海规则管理 / 有效跟进规则管理 |
- 颗粒度口径:动作级(一个动作一个点),不按「视图 × 动作」拆——同一动作在个人/管理/公海共用一个权限点,界面差异由视角 + 状态驱动(§6.7)。
- 催办/督办本期不做(§6.6 spin-out 留白),不登记权限点。
§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(64) | 调岗不自动更新(长度对齐票 03 B1「对称 lead」;此处原误记 varchar(100),回填 20260824) |
owner_dept_id 负责人部门快照 |
bigint | 公海可见范围锚点;调岗不变 |
origin_dept_id 原始血缘部门 |
bigint | 创建时写入 = 首个负责人部门,此后永不变(移交/抛公海/回收/领取/分配均不动;grill 拍板 20260821);仅审计/血缘追溯用(列表可选筛选/oplog 展示),不参与公海规则匹配、不参与 DataScope 可见性(匹配/可见性仍锚 owner_dept_id) |
creator_user_id 创建人 |
bigint | 全生命周期不变 |
claim_time 领取时间 |
datetime | 领取/分配/两入口创建时=now(「进入名下时间」,P1-6 拍板) |
last_valid_follow_time 最近有效跟进时间 |
datetime | 自动回收计时锚点(票 05);领取/分配重置=now;两入口创建以进入名下时间(=创建时刻)为起点;有效跟进时刷新(§5.3 定义,配置化 P1-8) |
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 |
pause_time 暂缓开始时刻 |
datetime | E4 暂缓写=now,E5 取消暂缓清空;取消暂缓时算 now−pause_time 顺推 last_valid_follow_time(冲突8) |
pause_reason 暂缓原因 |
varchar(32) | 字典码;E4 写、E5 清空 |
pause_expected_restart_date 预计重启日期 |
date | 必填≥今天;E4 写、E5 清空 |
pause_remark 暂缓补充说明 |
varchar(500) | E4 写、E5 清空 |
version 乐观锁版本号 |
int not null default 0 | 商机级写并发防护:状态迁移 CAS(§3.4,票 04)与切主客户(§7.1,票 13)复用;票 03 落列,生效接线归后续票(回填 20260824,补登 §7.1/§3.4 已引用的列) |
继承 BaseEntity(id / create_time / update_time / creator / updater / 逻辑删除)。
2.3 DataScope 注解
@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 不建的字段(已明确排除)
:票 05 定稿删——商机域无独立池实体,公海 = 逻辑视图pool_id:只用dept_idowner_dept_id快照做 DataScope 天花板:票 05 推翻,改为recycle_deadlinelast_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=now(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 + 团队;写 pause_time=now + pause_reason(字典) + pause_expected_restart_date(必填≥今天) + pause_remark |
A3-1-1-2-6 |
| E5 | 取消暂缓 | ResumeCmd |
3→2 | 保留 owner;回暂缓前阶段(阶段字段未动);清暂缓附加字段(pause_time/pause_reason/pause_expected_restart_date/pause_remark);回收锚点顺推(grill 拍板 20260821):last_valid_follow_time += now−pause_time(把暂缓时长从未跟进时长中扣除,等价「暂缓期间冻结回收」);此后回收判定回归无状态实时比较 |
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 | 本期可达触发源 = 仅特殊路径:项目侧建项目关联商机后回调(A5 调 OpportunityConvertPort);① 标准路径(推送方案卡→系统建项目)本期不可达、随推送票 spin-out(grill 拍板 20260821);无独立「转项目」动作;保留 owner(锁死);列表「转项目」按钮 = 推送流程快捷入口(推送票落地后启用) |
A3-1-1-2-x(转项目) |
| E9 | 移交 | HandoverCmd |
2,3→自环 | 换 owner = 移交对象 + 姓名/部门快照同步刷新(owner_dept_id 换为移交对象部门,公海规则匹配锚点跟人走,grill 补拍 20260821);claim_time 不重置;同步维护 team 表(grill 拍板 20260821):① 新负责人 upsert 一条 team 行、角色标签=「商机负责人」(已是成员则改标签);② 原负责人 team 行角色标签从「商机负责人」改为普通协作(保留其权限列不动——角色不带权限,原负责人是否还能写取决于其 team 行原本权限),留在团队继续协作、落入「我参与的」;入口在团队成员页 |
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) |
crm-opportunity 自建 @Scheduled 单 Job(先提醒后回收串行,对齐线索域 ADR-0019 竞态规避;cron 配置占位符默认每日凌晨,grill 补拍 20260821) |
「领导释放」(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 落到固定节点(标准路径,本期不可达、随推送票 spin-out,grill 拍板 20260821);本期 E8 仅由特殊路径(A5 回调)触发。无独立「转项目」动作,列表「转项目」按钮 = 推送流程快捷入口(推送票落地后启用)。
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) | 不参与回收(回收 Job 跳过暂缓中);取消暂缓时把 last_valid_follow_time 顺推暂缓时长(E5)→ 回收判定为无状态实时比较 now − last_valid_follow_time ≥ 当前匹配规则.recycle_days,不存「剩余天数」伪状态;移交换部门后续算自然用新部门当前规则(grill 拍板 20260821) |
recycle_remind_enabled=1 |
提醒时间 = 预计回收时间 − remind_days;由回收 Job 同一 @Scheduled 内先扫提醒再扫回收(提醒先于回收,对齐线索域 ADR-0019;grill 补拍 20260821)。提醒只落待发记录、不直接发送(grill 拍板 20260821):Job 扫到「预计回收时间 − remind_days ≤ now 且尚无待发记录」的商机 → upsert 一条 opportunity_pending_notice(notice_type=RECYCLE_REMIND,见 §7.10);实际发送走平台通知能力(spin-out 留白,与暂缓到期提醒/催办统一挂起) |
- 有效跟进(刷新锚点,P1-8 拍板)= 商机状态为推进中(2) 且动作 = 写跟进(本期唯一动作;上附件/编辑字段/方案卡/勘察等不计,产品二次修订 20260821)。动作类型集合是配置项:落
crm-rule第四族「有效跟进规则」opportunity_follow_rule(V-CONFIG 同族,动作类型多选子表,本期默认仅「写跟进」,以后经配置新增),回收 Job 按商机owner_dept_id实时匹配当前发布中规则拿动作集合(与公海规则同匹配引擎)。领取/分配重置锚点=now;两入口创建锚点=进入名下时间(P1-6)。
5.4 规则版本:实时匹配 vs 锅版本(关键区分)
公海规则与阶段模板的版本口径不同,核对时切勿混淆:
| 阶段模板(§4) | 公海规则(本节) | |
|---|---|---|
| 绑定方式 | 主表存 stage_template_id(版本行 id),锁死版本 |
商机不存规则 id,只存 owner_dept_id |
| 发新版 | 在途商机继续用原版本,不迁移(V-CONFIG 12) | 待处理判断实时按当前发布中的版本 |
| 典型场景 | 商机“走什么打法”,中途换会打乱在途流程→锁版 | “谁能碰这个部门公海”的当下策略,改规则就是要立刻生效→实时 |
口径(产品拍板,原型 A7-3-2-5):
- 「谁能领」判断→用当前发布中的版本(可见性归
@DataScope,见下节 grill 补拍口径)。池里商机无需“迁移”:拿owner_dept_id实时匹配当前发布中规则。管理员发新版(如关掉自由领取 / 新增部门专属规则圈进该商机)→ 立即对存量公海商机生效。 - 历史动作不重算(原型明写): 已完成的领取/回收/提醒锁旧版快照,发新版不回溯。例:旧规则下已被领走的商机,不因发新版而撤销。
- 版本快照落操作日志(已定): 领取/回收时把实际执行的规则版本写进
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 且部门命中的发布中规则;匹配不到 → 发布中的所有部门通用默认规则兑底;部门专用 > 通用默认,发布约束保证唯一命中。专属规则决定该商机在公海谁能领(适用部门外不可领,grill 补拍 20260821 暂定,待产品终审);可见性归@DataScope——公海视图查询自动叠加部门过滤(同管理视图口径),「看得到但领不了」场景领取按钮置灰提示。 - 实时匹配,不锁版本:公海规则不存于商机主表,查看/领取/回收时拿
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 空 逻辑视图;可见性 = @DataScope 自动叠加(按 owner_dept_id 部门锚点,同管理视图口径);公海规则适用范围管领取资格(grill 补拍 20260821,待产品终审) |
A3-3-1 | 本期不开 |
公海商机在管理视图的可见性(grill 补拍 20260821):owner 空但
owner_dept_id保留作 DataScope 部门锚点,按查看者档位自然判定——全部档可见;部门档看owner_dept_id是否在其可见部门集合;仅本人档永不可见(owner 空 ≠ 本人)。故 E2 分配入口(管理页)对全部/部门档管理员数据可达,无需去公海页。
详情聚合接口可见性(grill 补拍 20260821)= DataScope 可见 OR 团队成员白名单命中(仅存活行
deleted=0)——保证「我参与的」视图点进详情链路闭环;写操作再按成员自身权限点校验。
视图非权限旁路(通则,grill 拍板 20260821):任何视图(最近访问/我参与的/我关注的/自定义检索)只决定可见入口,不豁免任何动作校验。例:被回收的商机仍留在原负责人「最近访问」里(团队白名单命中可进详情),但「领取」按钮仍走 E1 完整校验(
allow_free_claim+ 适用部门 + 权限点 + 状态 CAS),与从公海页领取同一代码路径。
视图不是状态切片:同一商机可同时出现在多视图(如自己负责 + 最近访问过)。
6.2 销售机会工作区 — 内置四视图(「常用检索」Tab,固定不可删/改)
| code | 名称 | 数据范围 |
|---|---|---|
recent |
最近访问 | 当前用户打开过详情的商机,访问时间倒序、按商机 id 去重留最新,上限可配置(本期默认 50);「访问」= 调用详情聚合接口,任意皮肤(个人/管理/公海)都算(grill 补拍 20260821);落库 opportunity_view_log(§7.9,访问即 upsert) |
mine |
我负责的销售机会 | owner_user_id = 当前用户;不按状态切片(已关闭/已转项目仍在列——原型样例首行即「已转项目」商机;需只看活跃的用自定义视图按状态过滤,grill 补拍 20260821) |
participated |
我参与的销售机会 | 团队成员且非负责人:EXISTS(opportunity_team WHERE opportunity_id=? AND user_id=当前用户 AND deleted=0) AND owner_user_id <> 当前用户;与 mine 互斥。成员身份判定一律带存活过滤 deleted=0(grill 拍板 20260821):移除团队成员=软删 team 行(deleted 0→1),被移除后立即从本视图与详情白名单消失,仅 oplog/成员历史留痕;移交降级的旧负责人是存活行(只改角色)仍命中。 |
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 | 负责人显示 | 详情 / 分配 / 关闭 + 批量分配/关注/领导释放;催办/批量催办本期不做(spin-out 留白,grill 补拍 20260821) | 无 |
| 公海 A3-3-1 | 入池前工作节点 / 进入公海原因 / 当前负责人 | 详情 / 关注 / 领取 + 批量领取/关注 | 无 |
| 最近访问 A3-0-1 | 按访问时间排序 | 详情 / 写跟进 / 关注 / 领取(按角色+商机态动态) | 无 |
批量操作语义(grill 拍板 20260821):批量领取/关注/分配/领导释放 = 逐条处理、部分成功——成功的成功,失败的逐条给出失败原因,前端展示「成功 x 条,失败 y 条 + 失败明细」;无全批回滚、无长事务。
6.7 详情三皮肤
同一详情聚合,按视角展示不同操作区,非三套数据。Tab 结构一致(概览/客户信息/方案卡/现场勘察/相关附件/跟进记录/工作计划/团队成员/操作日志);差异在头部操作按钮 + 进入权限:
- 个人 A3-1-1-1:写跟进/编辑/暂缓/抛公海/关闭/新增方案卡
- 管理 A3-2-1-1:+ 分配/关闭;催办/督办本期不做(A3-2-1-1-1 督办功能 spin-out,催办随督办,grill 补拍 20260821)
- 公海 A3-3-1-1:领取/关注,无跟进/推进入口(未领取不可推进)
⚠ P2-4:管理视角「领导 ▾」层级数据范围与 @DataScope 精确映射待实现阶段定。
⚠ spin-out 留白(grill 补拍 20260821):催办/督办 = 管理侧督促功能(A3-2-1-1-1),本期不实现——催办本质是向负责人发通知,而平台通知基础设施未建(暂缓到期提醒的发送渠道同因挂起);留白待平台通知能力就绪后与督办功能一并规划落地,本期管理视图隐藏催办/督办相关按钮。 ⚠ P2-7:自定义视图数量上限 / 多组条件开放时机 / 是否支持共享 待产品定。
6.8 视图形态(正交于视图内容,grill 补拍 20260821)
出处:原型 line 1281「分屏 split / 列表 list / 看板 board——展示模式切换,与『哪个视图/检索』正交」;A3-2-1「标题栏左侧视图切换 [ 列表、分屏、看板 ]」;票 10/14 均未定义此维度。
三种展示形态,与「哪个视图/检索」(6.2/6.3)正交——同一份列表数据三种渲染,后端列表接口不变(同一查询、同一 DataScope、同一分页),形态 = 前端渲染维度:
| 形态 | code | 定义 | 默认 |
|---|---|---|---|
| 列表视图 | list |
传统表格(§6.5 基础列 + 列偏好) | 默认形态 |
| 分屏视图 | split |
左列表 + 右详情面板;面板复用详情聚合接口,非新数据源 | — |
| 看板视图 | board |
按「当前工作节点」分列的卡片流(列 = 阶段节点,列头带「共 N 条」计数;卡片 = 商机;底部「已展示 X/共 Y + 加载更多」——按列 or 整体加载的实现粒度留实现阶段定) | — |
- 适用范围:三个工作区(销售机会/商机管理/商机公海)+ 最近访问入口均支持三形态切换;切换入口 = 标题栏左侧,切换即刷新数据展示,操作按钮随工作区口径(原型 A3-2-1「切换不同入口时,标题栏、列表数据及可操作按钮同步刷新」)。
- 列配置仅列表视图生效(grill 拍板 20260821):自定义字段配置 / 列偏好只作用于列表视图;看板卡片字段与分屏面板字段为固定口径,不跟列偏好。
- 形态偏好落
crm-preference(与自定义视图同族,scope=opportunity.sales):用户切换后记住,下次打开该工作区保持上次形态;未设 →list。
§7 详情子表族
子表删除与唯一索引通用约定(grill 拍板 20260821,对齐
crm-ruleLeadPool既有实践):带业务字段/需审计的子表走软删除 + 唯一索引复用键delete_key(存活delete_key=0,逻辑删除时delete_key=id;唯一索引挂delete_key而非裸列,保证同键可重新添加)。纯行为/收藏表走物理硬删。
表 删除方式 唯一索引 opportunity_customer关联客户无独立删除,仅随商机级联逻辑删 UNIQUE(opportunity_id, customer_id)opportunity_scheme_card方案卡软删 + delete_keyUNIQUE(opportunity_id, customer_id, delete_key)opportunity_team团队成员软删 + delete_key(移交/移除保留历史)UNIQUE(opportunity_id, user_id, delete_key)opportunity_focus关注物理硬删 UNIQUE(opportunity_id, user_id)opportunity_view_log最近访问物理硬删/裁剪 UNIQUE(user_id, opportunity_id)硬删表的排查日志(grill 拍板 20260821,选项 C):
focus/view_log为纯行为/收藏流水,产品日志页无关注/浏览类目→不入 oplog、不软删;关注/取关/访问裁剪动作发生时往应用日志(logback)打一条 INFO(谁/何时/哪条商机/什么动作)供线上排查(时序完整、零表膨胀)——排查靠应用日志而非 DB 软删墓碎行。
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。 - 无删除动作(grill 纠偏 20260821,原型/票 13 均无删除):关联客户一律不可单独删(仅随商机级联清理,软删口径随商机)。要更换主要意向客户走「改主要意向客户」切标记(事务内旧主→0、新主→1),不删行。因无单独删 → 不存「删一客户又重加同 customer_id」场景,唯一索引无需
delete_key。 - 约束:
UNIQUE(opportunity_id, customer_id)(一客户一商机只关联一次);主要意向客户唯一(应用层事务 + 部分唯一索引)。 - 「改主要意向客户」一致性(grill 拍板 20260821):一个事务内三步原子完成——① 旧主
is_primary_intended1→0;② 新主 0→1;③ 刷主表冗余primary_customer_id/primary_customer_name_snapshot=新主(主表冗余与子表标记永不中间不一致)。并发防护 = 复用商机主表乐观锁version(切主客户=商机级写,CASWHERE opportunity_id=? AND version=?,并发切换仅一胜出、其余版本失配重试/报错);子表部分唯一索引UNIQUE(opportunity_id) WHERE is_primary_intended=1作 DB 最后防线。
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 → 已推送(本期不可达,推送链路随推送票 spin-out;本期卡最高至已提交 2,grill 拍板 20260821)。
- 推送成功 → 系统自动创建项目 → 商机转终态 5 + 落「已转项目」固定节点(E8 触发源,grill 补拍 20260821)。
- 可编辑性:草稿(1)/已提交(2) 均可改全部字段;仅已推送(3) 整卡只读(
card_status < 3可编辑),要改新建卡。不做字段级锁。 - 终态优先级高于方案卡自身状态机(grill 补拍 20260821):任一方案卡推送成功即触发整商机转项目终态(E8,2→5)——一商机多客户多卡,只要其中一张推送成功、系统建项目,整商机立刻进
STATUS_CONVERTED(5)锁死。此后其余草稿/已提交卡一律冻结为只读(终态压制方案卡自身「草稿/已提交可编辑」口径)。故上一行「card_status<3可编辑」的前提是商机未处于关闭(4)/终态(5);商机一旦关闭或转项目,全部卡只读。
| 关键字段 | 说明 |
|---|---|
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),不自动迁移。 - 无发布中模板→ 不能创建方案卡(grill 拍板 20260821):创建入口置灰,提示「当前无可用方案卡模板,请联系管理员」。候选匹配 = 专用优先、通用兵底(命中本部门专用发布中版本 ∪ 所有部门通用发布中版本,与公海规则引擎口径统一),两者皆无才置灰。
- 系统初始化种子模板(grill 拍板 20260821,由
crm-ruleseed 植入):初始化时种一条**「全公司通用」发布中方案卡模板作兵底(适用范围=所有部门),保证「无发布中→不能建卡」实践上几不触发。兵底模板可被管理员停用(二次确认警示,不硬锁)。字段集见附录 A。
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 申请单号 |
✕ | 人工录入、选填(外部勘察申请流程未对接,单号线下拿到后补录;grill 拍板 20260821) |
- 勘察附件走统一附件表(
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 联动)。 - 支持删除,删除不回退回收锚点(grill 拍板 20260821):删除后
last_valid_follow_time保持不变(不回退重算,防「写了又删」刷锚点);「最近跟进」计算列按删除后最新留存行自然变化;删除动作入 oplog。编辑本期不做(见清单 P2-9,写错以删除+补写方式修正)。
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。
- 双维度模型(grill 拍板 20260821):一条日志由两个正交枚举描述。
op_kind记录形态(怎么存):FIELD_CHANGE(有旧值/新值)/ROW_ADD(整行新增,无旧值)/ROW_DELETE(整行删除,无新值,带biz_ref被删行业务标识 + 快照摘要 JSON)。子表整行增删(关联客户/团队成员/勘察等)走ROW_ADD/ROW_DELETE,日志可读为「删除关联客户:上海XX公司」而非deleted 0→1废记录。log_type业务类别(原型 line 1745,决定前端日志页是否展示):FIELD_CHANGE字段变更 /STATUS_FLOW状态流转 /STAGE_FLOW阶段流转 /ATTACHMENT附件上传 /SCHEME_PUSH方案推送 /ROW_CHANGE子表增删(以上前端日志页可见)/SYSTEM_INTERNAL系统内部(记库、前端日志页过滤不显)。枚举不含浏览/关注(冲突6 定为硬删+logback、不入 oplog)。
- 记/不记口径(P1-5 + grill 20260821):默认全记 + 短黑名单排除(直接不记)。黑名单 = 纯技术字段(
create_time/update_time/乐观锁列)+ 无业务含义的系统自动刷新(last_valid_follow_time)→ 连SYSTEM_INTERNAL都不落;SYSTEM_INTERNAL只给「有排查价值、不给终端用户看」的少数系统动作(如 executeRecycle 系统侧写、推送联动终态),记库而不显(黑名单 ∩ SYSTEM_INTERNAL = ∅)。 - 删除留痕:软删子表的删除动作记一条
ROW_DELETEoplog(给人看轨迹)+ 依赖 BaseEntityupdater_id/update_time(给排查看谁在何时删),不单加deleted_by/deleted_time。 - 与线索
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)。
- 「我关注的」视图 = 该表命中当前用户;管理/公海/最近访问视图的「关注」按钮读写此表。
7.9 最近访问 opportunity_view_log
出处:grill 补拍 20260821(票 10 recent 视图落库方式未定,拍板新建)。
- 字段:
user_id+opportunity_id+last_view_time+ BaseEntity;约束UNIQUE(user_id, opportunity_id)。 - 写入时机 = 调用详情聚合接口即 upsert(刷新
last_view_time);个人/管理/公海任意皮肤都算(§6.2)。 - 不写
opportunity_oplog:访问是高频行为数据,oplog 是审计轨迹(行为类别不含浏览,票 09),两者分离。 - 裁剪:recent 视图只取上限条数(本期默认 50),表内历史行靠后台 Job 按容量/时间裁剪(超出视图范围的历史无业务用途)。
- 「最近访问」视图 = 该表命中当前用户,按
last_view_time倒序取上限。
7.10 待发通知 opportunity_pending_notice
出处:grill 拍板 20260821(回收提醒需落地、但平台通知能力未建)。
通用待发通知表——承接三处「向负责人发通知」需求(回收前提醒/暂缓到期提醒/催办)。本期只落待发记录,发送等平台通知能力就绪后由另一消费者补发。
| 字段 | 类型 | 说明 |
|---|---|---|
opportunity_id |
bigint | 所属商机 |
notice_type |
varchar(32) | RECYCLE_REMIND 回收前提醒(本期唯一实际写入);预留 PAUSE_EXPIRE 暂缓到期 / URGE 催办 |
target_user_id |
bigint | 应通知对象快照 = 当前负责人 |
plan_notify_time |
datetime | 应发提醒时刻 |
payload_json |
text | 提醒上下文(预计回收日/recycle_days/已无跟进天数等,无「剩余天数」伪状态) |
notice_status |
tinyint | 待发0 / 已发1 / 已失效2 |
- 幂等:
UNIQUE(opportunity_id, notice_type, 批次键)防重复堆积;回收 Job upsert 待发记录。 - 补发:平台通知能力就绪后,另起消费者扫
notice_status=待发发出、置「已发」。 - 失效:提醒发出前商机被回收/领取/暂缓/关闭 → 对应待发记录置「已失效」(不再补发过期提醒)。
- 本期只
RECYCLE_REMIND真实写入;PAUSE_EXPIRE/URGE随对应 spin-out 功能就绪时接入(不重建表)。
§8 上下游集成
8.1 线索 → 商机(消费线索侧 port)
出处:票 02 lead-port-consumer-contract,原型 A3-1-1(线索转商机)。
- 线索侧提供
CreateOpportunityCmd(out-port),商机模块消费,转商机时自动带source_lead_id。 - 契约现状 vs 目标(grill 补拍 20260821,定性):本 PRD 描述的是目标契约;
crm-lead侧代码CreateOpportunityCmd是现状(尚未含customerId,字段命名为intendedCustomer/regionCode/industryCode);票 11 = 差量(把代码改齐目标)。实现方勿把本节读成「代码已如此」。 - 契约变更(票 02 拍板,票 11 实现):目标 =
CreateOpportunityCmd新增customerId(Long,必填);intendedCustomer保留为客户名快照(走法 B:引用 + 快照并存)。字段命名以主表 §2.1 为准——票 11 落地时做一次字段对齐:regionCode→拆province_code/city_code;intendedCustomer→拆customerId(引用)+ 客户名快照。 - 线索转商机弹窗:意向客户改为下拉选客户(选中拿到
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_idNULL(可多条 NULL)。
8.3 商机 → 项目(转项目端口占位)
- E8 转项目:对接 A5 项目模块(本模块只占端口,转项目后商机锁死终态 5)。
- 标准路径(grill 补拍 20260821;本期 spin-out 不可达):创建商机 → 填写方案卡 → 推送方案卡 → 系统自动创建项目 → 商机触发轴 1 终态 + 轴 2 固定节点「已转项目」。列表「转项目」按钮 = 推送流程快捷入口(推送票未拆,本期不可达)。
- 特殊路径(grill 补拍 20260821):跳过商机录入,项目侧直接创建项目,可关联方案卡(选填)——归 A5 项目模块定义。关联即联动(grill 补拍 20260821):项目创建成功且关联了商机(或商机下的方案卡)时,A5 回调商机模块触发 E8(2→5)+ 落「已转项目」固定节点;本模块暴露回调端口(入参 = 商机 id 或方案卡 id),校验关联合法性后执行 E8 侧效(幂等:商机已终态则拒绝/跳过)。
- 转项目回调端口契约定死(grill 拍板 20260821,对称 lead→opp port)——本期就出 interface,不留给 A5:① 定义方 = 商机侧 inbound port(
crm-opportunity/port/inbound,命名OpportunityConvertPort),A5 调用(与 lead→opp 方向相反、范式一致);② 入参 recordConvertToProjectCmd(opportunityId, schemeCardId, projectId):opportunityId(Long 必填、幂等主键)/schemeCardId(Long 选填、标哪张卡触发、无则 null)/projectId(Long 已建项目 id,写快照/oplog)——“商机 id 或方案卡 id”含糊收敛为:商机 id 必填做幂等键、方案卡 id 选填标来源;③ 幂等命中(已终态 5)= 安静跳过返回(对齐 lead port 口径,不抛异常),校验失败(不存在/非可转态)= 抛OpportunityConvertException(对称OpportunityCreationException);④ 本模块出 interface + record + exception 三件(对称 lead port 三件)。 - 推送方案本期 spin-out(grill 拍板 20260821,翻转 20260821 初拍“本期做”):推送流程页 A3-1-1-2-5 + 方案推送规则 A7-3-2-3 + 项目查重规则 A7-3-2-4 整体随一张推送票 spin-out(尚未拆)。理由:推送链路(选卡→查重→建项目)依赖 A5 项目模块 + 查重规则,与 A5 同为未建下游,硬说“本期可达”是给实现方埋雷。本期 E8 仅特殊路径(A5 回调
OpportunityConvertPort)真实可达;标准路径推送不可达。
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 |
— |
| 有效跟进规则 | opportunity_follow_rule |
FOLLOW_RULE_* |
V-CONFIG 公共约定:
- 同一
code多版本,每版本一行。 version_no系统生成(V1.0/V1.1/V2.0…),只读。status:1=草稿 / 2=发布中 / 3=已停用。- 同一 code 同时仅一个草稿 + 仅一个发布中。
apply_scope:1=所有商机 / 2=指定部门(两档单选,本期不做「指定业务类型」);apply_scope=2 配适用对象子表(部门多值)。is_default:同「适用范围+适用对象」内唯一;发布新默认 → 原默认自动取消。- 绑定快照:业务实体绑定配置时记录具体版本;新版本发布后原绑定实体继续用原版本不自动迁移;新建/重新匹配用最新发布版本。
- 复制 = 生成独立新规则(新 code,非后续版本)。
- 列表按版本维度展示:同 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 |
附录 A 方案卡种子模板「全公司通用」字段集(grill 拍板 20260821)
来源:原型 A7-3-2-2 预览填写页(OCR 校准 1787558225260_*.png)。由 crm-rule 初始化 seed 植入一条适用范围=所有部门、状态=发布中的模板,作为建卡兵底(冲突11)。crm-opportunity 只消费不负责 seed。
| 序 | 字段名称 | 字段类型 | 显示 | 必填 | A4 依赖 | 本期 seed | 备注 |
|---|---|---|---|---|---|---|---|
| 1 | 客户名称 | 关联对象 | 是 | 是 | ✅依赖 A4 | ⛳暂缓植入 | 从卡所挂关联客户引用带出(非卡冗余存储) |
| 2 | 客户类型 | 单选下拉 | 是 | 是 | — | ✅植入 | 选项可 option_source 引 crm-dict「客户角色/类型」分组 |
| 3 | 客户联系人 | 关联对象 | 是 | 是 | ✅依赖 A4 | ⛳暂缓植入 | 引用带出(依赖 A4) |
| 4 | 客户联系电话 | 自动回填 | 是 | 否 | ✅依赖 A4 | ⛳暂缓植入 | autofill_source=联系人电话 |
| 5 | 项目阶段 | 单选下拉 | 是 | 是 | — | ✅植入 | |
| 6 | 招标形式 | 单选下拉 | 是 | 是 | — | ✅植入 | option_source 引字典「招标形式」 |
| 7 | 是否有看现场 | 是/否 | 是 | 否 | — | ✅植入 | 布尔型 |
| 8 | 现场勘察资料 | 附件上传 | 是 | 否 | — | ✅植入 | crm-file |
| 9 | 方案预算金额 | 金额 | 是 | 否 | — | ✅植入 | = scheme_budget,列表「方案预算」列取主客户卡此值 |
| 10 | 设计配置 | 单选下拉 | 是 | 否 | — | ✅植入 | |
| 11 | 客户需求描述 | 多行文本 | 是 | 是 | — | ✅植入 |
A4 缺席期的最小可跑子集(grill 拍板 20260821,选项 A):本期 crm-rule seed 只植入不依赖 A4 的字段(序 2/5/6/7/8/9/10/11),保证 A4 缺席时建卡链路真可跑。依赖 A4 的 3 字段(序 1/3/4)字段定义保留于 opportunity_scheme_field_def、但本期不进 seed 模板字段集(标「A4 上线后启用」);A4 到达后管理员发新版模板把这 3 字段加回即可(V-CONFIG 发新版,无需改表)。
字段类型初始枚举(opportunity_scheme_field_def.field_type,P0-3 自建字段库):关联对象 / 单选下拉 / 自动回填 / 是/否 / 附件上传 / 金额 / 多行文本(取自原型 11 字段,grill 20260821)。
对齐说明:① 前 4 字段(客户名称/类型/联系人/电话)为关联客户引用带出的展示字段,非卡新存业务数据;② 原型本版未列「预计成交时间」(P0-1 它是方案卡内部字段、可由模板配置,不强塞进种子模板)。
§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 | 归属起点计时初始化(领取/分配重置、两入口创建=进入名下时间) | P1 | ✅ 已拍板(产品回复 20260821) |
| P1-7 | 暂缓期间子表可写范围 | P1 | ✅ 已拍板(暂缓/关闭先解除状态才能编辑) |
| P1-8 | 有效跟进定义修订+配置化 | P1 | ✅ 已拍板(本期仅写跟进;配置化保留可新增) |
已拍板存档(备查): 预计成交时间归属(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,如与原型有出入,以原型 + 票正文为准并回填对应票。)