11 KiB
11 KiB
渠道拓展 ⇄ CRM拓展 数据互移功能方案
版本:V1.0 日期:2026-08-28 适用范围:CRM 系统渠道拓展 / CRM拓展
一、功能概述
- 在「渠道拓展详情」底部操作区新增「移至CRM拓展」按钮:将整条渠道拓展数据(主表 + 联系人子表 + 跟进记录)迁移为一条 CRM 拓展记录,删除源渠道记录,并同步更新所有引用该渠道的地方。
- 对称地,在「CRM拓展详情」底部操作区新增「移至渠道拓展」按钮,实现 CRM 拓展数据反向迁移为渠道拓展,逻辑对称。
- 点击按钮后先弹出「迁移前补填」表单,用户补填源数据中缺失的目标必填字段,确认后执行迁移。
- 按钮权限与现有「编辑资料」按钮保持一致:仅记录本人(负责人)可见可点。
二、权限控制
- 复用前端
canEditSelectedItem = ownerUserId === currentUserId,与编辑按钮同一套权限逻辑,不新增权限点。 - 非本人记录:按钮禁用并显示「仅本人可操作」,行为与编辑按钮一致。
- 后端同样校验记录存在且
owner_user_id = 当前用户,防止越权调用。
三、后端接口设计
| 方法 | 路径 | 说明 |
|---|---|---|
| POST | /api/expansion/channel/{id}/move-to-crm |
渠道 → CRM,请求体携带弹窗补填字段 |
| POST | /api/expansion/crm/{id}/move-to-channel |
CRM → 渠道,请求体携带弹窗补填字段 |
服务层新增方法(均使用 @Transactional,任一步失败整体回滚):
Long moveChannelToCrm(Long userId, Long channelId, MoveChannelToCrmRequest payload);
Long moveCrmToChannel(Long userId, Long crmId, MoveCrmToChannelRequest payload);
迁移流程(以渠道 → CRM 为例):
- 校验记录存在且归属当前用户(复用
countOwnedChannelExpansion)。 - 查询源主表数据(复用现有单条查询)。
- 自动映射字段 + 合并弹窗补填字段,构造
CreateCrmExpansionRequest(不走表单校验)。 insertCrmExpansion创建目标主表,获取新 id。- 联系人子表逐条复制(
name / mobile / title / sort_order)。 - 引用同步(详见第四节):跟进记录、外勤打卡、日报消息迁移至新记录,商机、其它CRM进货商引用置空。
- 删除源:先删联系人子表,再删渠道主表。
四、引用同步清单(决策①:删除源 + 同步引用)
4.1 渠道 → CRM(删除渠道源记录后)
| 引用表.字段 | 原值 | 处理后 | 说明 |
|---|---|---|---|
crm_opportunity.channel_expansion_id |
源渠道id | 置空 NULL | 存在外键约束,删除前必须置空,否则删除失败 |
crm_crm_expansion.supplier_id |
源渠道id | 置空 NULL | 存在外键约束,删除前必须置空 |
crm_expansion_followup.biz_type / biz_id |
'channel' / 源id |
'crm' / 新id |
拜访/跟进记录随迁 |
work_checkin.biz_type / biz_id / biz_name |
'channel' / 源id / 旧名 |
'crm' / 新id / 新名 |
外勤打卡关联同步 |
work_report_message.biz_type / biz_id / biz_name |
'channel' / 源id / 旧名 |
'crm' / 新id / 新名 |
日报消息关联同步 |
4.2 CRM → 渠道(删除CRM源记录后)
| 引用表.字段 | 原值 | 处理后 | 说明 |
|---|---|---|---|
crm_expansion_followup.biz_type / biz_id |
'crm' / 源id |
'channel' / 新id |
拜访/跟进记录随迁 |
work_checkin.biz_type / biz_id / biz_name |
'crm' / 源id / 旧名 |
'channel' / 新id / 新名 |
外勤打卡关联同步 |
work_report_message.biz_type / biz_id / biz_name |
'crm' / 源id / 旧名 |
'channel' / 新id / 新名 |
日报消息关联同步 |
补充说明
crm_channel_expansion_contact子表外键为ON DELETE CASCADE,删除渠道主表时联系人自动级联删除,无需单独处理。work_todo(待办)仅用于日报「明日计划」待办(biz_type='report'),不涉及渠道/CRM拓展,无需同步。sys_activity_log(首页动态日志)为历史遗留表,当前代码无写入路径;如存在历史引用建议一并清理(可选)。crm_crm_expansion.h3c_contact_id指向销售拓展(crm_sales_expansion),与本次迁移无关,保持不变。
五、字段对应关系 + 弹窗补填范围(决策②:迁移前弹窗填写)
5.1 渠道拓展 → CRM拓展:自动映射(有对应关系)
| 渠道拓展字段(列) | 渠道拓展含义 | CRM拓展字段(列) | CRM拓展含义 / 说明 |
|---|---|---|---|
channel_name |
渠道名称 | end_user |
最终用户 |
province |
省份 | office_name |
代表处(按 tz_bsc 字典转换,未匹配保留原值) |
channel_industry |
聚焦行业 | industry_attr |
行业属性(按 tz_sshy 字典转换) |
contact_established_date |
建立联系时间 | purchase_date |
采购时间 |
contact_name / mobile / title |
主联系人 | contact_name / phone / title |
冗余字段 |
crm_channel_expansion_contact(子表) |
多联系人 | crm_crm_expansion_contact(子表) |
逐条平移 |
remark |
备注 | remark |
写入DB列(CRM表单无备注字段) |
owner_user_id |
负责人 | owner_user_id |
保持不变 |
渠道特有、CRM 无对应 → 不迁移: channel_code、city、office_address、certification_level、annual_revenue、staff_size、intent_level、has_desktop_exp、channel_attribute、internal_attribute、stage、landed_flag、expected_sign_date
弹窗补填(CRM无来源的必填字段):
| 弹窗字段 | 默认值 | 说明 |
|---|---|---|
extension_type(类型) |
空 | 字典 crm_extension_type 单选,必填 |
online_status(在线情况) |
空 | 字典 crm_online_status 单选,必填 |
has_expansion_opportunity(是否有扩容机会) |
空 | 字典 sys_is 单选,必填 |
supplier_id(进货商) |
空 | AdaptiveSelect 选渠道,必填(不能选自身) |
h3c_contact_id(新华三对接人) |
空 | AdaptiveSelect 选销售拓展,必填 |
warranty_expiry(过保时间) |
采购时间+3年 | 预填可改 |
5.2 CRM拓展 → 渠道拓展:自动映射(有对应关系)
| CRM拓展字段(列) | CRM拓展含义 | 渠道拓展字段(列) | 渠道拓展含义 / 说明 |
|---|---|---|---|
end_user |
最终用户 | channel_name |
渠道名称 |
office_name |
代表处 | province |
省份(反查 tz_bsc,未匹配保留原值) |
industry_attr |
行业属性 | channel_industry |
聚焦行业 |
purchase_date |
采购时间 | contact_established_date |
建立联系时间 |
contact_name / phone / title |
冗余联系人 | contact_name / mobile / title |
主联系人 |
crm_crm_expansion_contact(子表) |
多联系人 | crm_channel_expansion_contact(子表) |
逐条平移 |
remark |
备注 | remark |
直接映射 |
owner_user_id |
负责人 | owner_user_id |
保持不变 |
CRM特有、渠道无对应 → 不迁移: extension_type、warranty_expiry、online_status、supplier_id、h3c_contact_id、has_expansion_opportunity
弹窗补填(渠道无来源的必填字段):
| 弹窗字段 | 默认值 | 说明 |
|---|---|---|
city(市) |
空 | 必填 |
office_address(办公地址) |
空 | 必填 |
certification_level(认证级别) |
空 | 字典单选,必填 |
annual_revenue(年度营业额·万元) |
空 | 数字>0,必填 |
staff_size(人员规模) |
空 | 正整数,必填 |
channel_attribute(渠道属性) |
空 | 字典单选,必填 |
internal_attribute(内部属性) |
空 | 字典单选,必填 |
intent_level(合作意向) |
medium |
可改 |
has_desktop_exp(桌面扩展能力) |
false |
可改 |
stage(阶段) |
initial_contact |
预填 |
landed_flag(是否落地) |
false |
预填 |
expected_sign_date(预计签约时间) |
空 | 可空 |
说明:渠道表单编辑时校验
province / city / certificationLevel / officeAddress / channelIndustry / annualRevenue / staffSize / channelAttribute / internalAttribute等字段非空,因此迁移弹窗将这些字段设为必填,确保迁移后的记录完整、可继续编辑保存。
六、并发与重复防护
迁移为「删除源 + 创建目标」的复合写操作,必须保证并发安全与幂等,防止重复迁移、竞态丢失或脏数据。
6.1 后端并发控制
- 迁移接口在事务内对源记录执行
SELECT ... FOR UPDATE(行锁),锁定源记录,避免与并发的编辑/其它迁移操作产生竞态。 - 若源记录已被其它事务迁移或删除,
SELECT FOR UPDATE返回空 → 抛出「记录不存在或已被迁移」提示。 - 引用同步、目标创建、源删除均在同一个事务内完成,任一步失败整体回滚,不产生中间态。
6.2 幂等与重复提交防护
- 迁移成功后源记录即被删除;重复调用接口时源记录不存在,后端返回明确错误(「记录不存在或已被迁移」),不会重复创建目标。
- 前端提交后立即进入 loading 态并禁用按钮,防止用户重复点击。
6.3 迁移与编辑的互斥
- 迁移期间源记录被行锁锁定,其它用户的编辑操作(
updateChannelExpansion/updateCrmExpansion)在锁释放前等待,避免「迁移了旧数据」的脏读。 - 弹窗补填的字段在后端二次校验(必填、数值范围、进货商/对接人不能为空),校验失败整体回滚。
七、前端实现
- 按钮位置:详情页底部操作区(现有「编辑资料」按钮旁),按
selectedItem.type显示:type='channel'显示「移至CRM拓展」,type='crm'显示「移至渠道拓展」,type='sales'不显示。 - 样式与交互:与编辑按钮同款按钮样式;点击后弹「迁移前补填」表单(复用现有表单字段组件与字典选项),校验通过后二次确认,再调用接口。
- 状态处理:提交时 loading 态(按钮禁用防重复提交);成功后刷新列表、关闭详情并切换到新记录;失败时 toast 展示错误信息(如「记录不存在或已被迁移」)。
八、实现清单(后续开发步骤)
- 后端:新增
MoveChannelToCrmRequest/MoveCrmToChannelRequestDTO;ExpansionController新增两个接口;ExpansionService新增两个@Transactional方法。 - Mapper:新增按源 id 更新
work_checkin/work_report_message的biz_type/biz_id/biz_name、商机channel_expansion_id置空、其它CRMsupplier_id置空、删除渠道/CRM主表等 SQL。 - 并发防护:迁移查询源记录时使用
SELECT ... FOR UPDATE行锁;重复迁移返回「记录不存在或已被迁移」;后端对弹窗补填字段二次校验。 - 前端:新增两个补填表单弹窗组件 + 两个迁移按钮 + 二次确认与成功刷新逻辑,提交时 loading 禁用防重复点击。
- 验证:渠道→CRM 与 CRM→渠道 各执行一遍,核对主表、联系人、跟进记录、打卡/日报关联、商机引用均正确迁移或置空。