From abb76e472fdadd7e75ed473d05b95b03e6c8a801 Mon Sep 17 00:00:00 2001 From: kangwenjing <1138819403@qq.com> Date: Tue, 8 Sep 2026 15:12:01 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=AE=8C=E6=88=90CRM=E7=B3=BB=E7=BB=9F?= =?UTF-8?q?=E5=A4=9A=E6=A8=A1=E5=9D=97=E8=BF=AD=E4=BB=A3=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 本次提交包含了大量功能改进与基础优化: 1. 重命名项目标识与前端展示文案,统一为SCC(Sales-Channel-Customer) 2. 新增CRM拓展业务相关DTO、Mapper与服务接口,支持CRM拓展管理 3. 优化签到、日报等业务的biz_type校验,新增crm类型支持 4. 拆分前端Vite打包配置,实现依赖分包优化 5. 新增全局异常处理器,完善资源不存在等场景的错误返回 6. 新增商机查重、仪表盘多卡片批量查询接口 7. 完善渠道拓展、销售拓展的区域覆盖字段支持 8. 新增OMS字典映射服务与回调控制器,实现跨系统数据转换 9. 新增渠道与CRM拓展互转功能,支持业务类型迁移 10. 修复部分前端ECharts按需引入配置,优化打包体积 同时补充了初始化SQL脚本与系统测试记录文档。 --- ...%BB%E5%8A%9F%E8%83%BD%E6%96%B9%E6%A1%88.md | 163 + .idea/workspace.xml | 43 +- .workbuddy/memory/2026-09-03.md | 12 + .workbuddy/memory/2026-09-08.md | 21 + .../crm/common/CrmGlobalExceptionHandler.java | 7 + .../common/WorkCheckInSchemaInitializer.java | 2 +- .../crm/controller/DashboardController.java | 8 + .../crm/controller/ExpansionController.java | 61 + .../crm/controller/OmsCallbackController.java | 74 + .../crm/controller/OpportunityController.java | 11 + .../dashboard/DashboardAnalyticsCardDTO.java | 9 + .../expansion/ChannelExpansionContactDTO.java | 36 + .../ChannelExpansionContactRequest.java | 38 + .../expansion/ChannelExpansionItemDTO.java | 37 + .../ChannelExpansionMoveSourceDTO.java | 182 + .../crm/dto/expansion/CoverageItemDTO.java | 27 + .../CreateChannelExpansionRequest.java | 41 + .../expansion/CreateCrmExpansionRequest.java | 250 + .../CreateSalesExpansionRequest.java | 12 + .../dto/expansion/CrmExpansionContactDTO.java | 50 + .../expansion/CrmExpansionContactRequest.java | 32 + .../dto/expansion/CrmExpansionItemDTO.java | 287 + .../expansion/CrmExpansionMoveSourceDTO.java | 154 + .../crm/dto/expansion/ExpansionMetaDTO.java | 50 + .../dto/expansion/ExpansionOverviewDTO.java | 13 + .../expansion/MoveChannelToCrmRequest.java | 178 + .../expansion/MoveCrmToChannelRequest.java | 201 + .../dto/expansion/SalesExpansionItemDTO.java | 28 + .../UpdateChannelExpansionRequest.java | 41 + .../expansion/UpdateCrmExpansionRequest.java | 229 + .../UpdateSalesExpansionRequest.java | 12 + .../OpportunityDuplicateCheckDTO.java | 82 + .../dto/opportunity/OpportunityMetaDTO.java | 9 + .../unis/crm/mapper/CrmExpansionMapper.java | 68 + .../crm/mapper/CrmOmsDictMappingMapper.java | 24 + .../com/unis/crm/mapper/ExpansionMapper.java | 40 + .../unis/crm/mapper/OpportunityMapper.java | 15 + .../crm/service/CrmOmsDictMappingService.java | 79 + .../DashboardAnalyticsConfigService.java | 93 +- .../unis/crm/service/DashboardService.java | 3 + .../unis/crm/service/ExpansionService.java | 16 + .../java/com/unis/crm/service/OmsClient.java | 8 +- .../unis/crm/service/OpportunityService.java | 3 + .../service/impl/DashboardServiceImpl.java | 22 +- .../service/impl/ExpansionServiceImpl.java | 580 +- .../service/impl/OpportunityServiceImpl.java | 78 +- .../crm/service/impl/WorkServiceImpl.java | 81 +- backend/src/main/resources/application.yml | 2 +- .../mapper/CrmOmsDictMappingMapper.xml | 24 + .../mapper/expansion/CrmExpansionMapper.xml | 270 + .../mapper/expansion/ExpansionMapper.xml | 217 +- .../mapper/opportunity/OpportunityMapper.xml | 71 +- .../main/resources/mapper/work/WorkMapper.xml | 2 - .../impl/ExpansionServiceImplTest.java | 44 +- .../impl/OpportunityServiceImplTest.java | 20 + frontend/index.html | 2 +- .../node_modules/.vite/deps/_metadata.json | 93 +- .../node_modules/.vite/deps/motion_react.js | 12624 +--------------- .../.vite/deps/motion_react.js.map | 8 +- frontend/src/App.tsx | 67 +- frontend/src/components/AdaptiveSelect.tsx | 38 +- frontend/src/components/Layout.tsx | 4 +- .../src/components/SearchOrInputSelect.tsx | 350 + frontend/src/components/SearchableSelect.tsx | 371 + .../dashboard/DashboardAnalyticsEChart.tsx | 17 +- .../src/features/crmQuickCreate/shared.tsx | 266 +- frontend/src/lib/auth.ts | 247 +- frontend/src/pages/Dashboard.tsx | 113 +- frontend/src/pages/Expansion.tsx | 3089 +++- frontend/src/pages/Login.tsx | 4 +- frontend/src/pages/Opportunities.tsx | 554 +- frontend/src/pages/Work.tsx | 121 +- frontend/src/pages/login.css | 6 +- frontend/tsconfig.tsbuildinfo | 1 + frontend/vite.config.ts | 11 + frontend1/dist/index.html | 5 +- .../.vite/deps/@ant-design_icons.js | 2 +- .../node_modules/.vite/deps/_metadata.json | 94 +- frontend1/node_modules/.vite/deps/antd.js | 8 +- .../components/AnalyticsChartPreview.tsx | 22 +- frontend1/vite.config.ts | 12 +- sql/20260827.sql | 283 + sql/20260828update.sql | 128 + sql/20260903update.sql | 63 + sql/init_full_pg17.sql | 8 +- 代码改动审计报告.md | 172 + 系统审计分析报告.md | 143 + 系统测试报告-20260903.md | 141 + 88 files changed, 9970 insertions(+), 13257 deletions(-) create mode 100644 %E6%B8%A0%E9%81%93%E6%8B%93%E5%B1%95CRM%E6%8B%93%E5%B1%95%E4%BA%92%E7%A7%BB%E5%8A%9F%E8%83%BD%E6%96%B9%E6%A1%88.md create mode 100644 .workbuddy/memory/2026-09-03.md create mode 100644 .workbuddy/memory/2026-09-08.md create mode 100644 backend/src/main/java/com/unis/crm/controller/OmsCallbackController.java create mode 100644 backend/src/main/java/com/unis/crm/dto/expansion/ChannelExpansionMoveSourceDTO.java create mode 100644 backend/src/main/java/com/unis/crm/dto/expansion/CoverageItemDTO.java create mode 100644 backend/src/main/java/com/unis/crm/dto/expansion/CreateCrmExpansionRequest.java create mode 100644 backend/src/main/java/com/unis/crm/dto/expansion/CrmExpansionContactDTO.java create mode 100644 backend/src/main/java/com/unis/crm/dto/expansion/CrmExpansionContactRequest.java create mode 100644 backend/src/main/java/com/unis/crm/dto/expansion/CrmExpansionItemDTO.java create mode 100644 backend/src/main/java/com/unis/crm/dto/expansion/CrmExpansionMoveSourceDTO.java create mode 100644 backend/src/main/java/com/unis/crm/dto/expansion/MoveChannelToCrmRequest.java create mode 100644 backend/src/main/java/com/unis/crm/dto/expansion/MoveCrmToChannelRequest.java create mode 100644 backend/src/main/java/com/unis/crm/dto/expansion/UpdateCrmExpansionRequest.java create mode 100644 backend/src/main/java/com/unis/crm/dto/opportunity/OpportunityDuplicateCheckDTO.java create mode 100644 backend/src/main/java/com/unis/crm/mapper/CrmExpansionMapper.java create mode 100644 backend/src/main/java/com/unis/crm/mapper/CrmOmsDictMappingMapper.java create mode 100644 backend/src/main/java/com/unis/crm/service/CrmOmsDictMappingService.java create mode 100644 backend/src/main/resources/mapper/CrmOmsDictMappingMapper.xml create mode 100644 backend/src/main/resources/mapper/expansion/CrmExpansionMapper.xml create mode 100644 frontend/src/components/SearchOrInputSelect.tsx create mode 100644 frontend/src/components/SearchableSelect.tsx create mode 100644 frontend/tsconfig.tsbuildinfo create mode 100644 sql/20260827.sql create mode 100644 sql/20260828update.sql create mode 100644 sql/20260903update.sql create mode 100644 代码改动审计报告.md create mode 100644 系统审计分析报告.md create mode 100644 系统测试报告-20260903.md diff --git a/%E6%B8%A0%E9%81%93%E6%8B%93%E5%B1%95CRM%E6%8B%93%E5%B1%95%E4%BA%92%E7%A7%BB%E5%8A%9F%E8%83%BD%E6%96%B9%E6%A1%88.md b/%E6%B8%A0%E9%81%93%E6%8B%93%E5%B1%95CRM%E6%8B%93%E5%B1%95%E4%BA%92%E7%A7%BB%E5%8A%9F%E8%83%BD%E6%96%B9%E6%A1%88.md new file mode 100644 index 00000000..d15ed439 --- /dev/null +++ b/%E6%B8%A0%E9%81%93%E6%8B%93%E5%B1%95CRM%E6%8B%93%E5%B1%95%E4%BA%92%E7%A7%BB%E5%8A%9F%E8%83%BD%E6%96%B9%E6%A1%88.md @@ -0,0 +1,163 @@ +# 渠道拓展 ⇄ CRM拓展 数据互移功能方案 + +> 版本:V1.0  日期:2026-08-28  适用范围:CRM 系统渠道拓展 / CRM拓展 + +## 一、功能概述 + +1. 在「渠道拓展详情」底部操作区新增「移至CRM拓展」按钮:将整条渠道拓展数据(主表 + 联系人子表 + 跟进记录)迁移为一条 CRM 拓展记录,删除源渠道记录,并同步更新所有引用该渠道的地方。 +2. 对称地,在「CRM拓展详情」底部操作区新增「移至渠道拓展」按钮,实现 CRM 拓展数据反向迁移为渠道拓展,逻辑对称。 +3. 点击按钮后先弹出「迁移前补填」表单,用户补填源数据中缺失的目标必填字段,确认后执行迁移。 +4. 按钮权限与现有「编辑资料」按钮保持一致:仅记录本人(负责人)可见可点。 + +## 二、权限控制 + +- 复用前端 `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`,任一步失败整体回滚): + +```java +Long moveChannelToCrm(Long userId, Long channelId, MoveChannelToCrmRequest payload); +Long moveCrmToChannel(Long userId, Long crmId, MoveCrmToChannelRequest payload); +``` + +迁移流程(以渠道 → CRM 为例): + +1. 校验记录存在且归属当前用户(复用 `countOwnedChannelExpansion`)。 +2. 查询源主表数据(复用现有单条查询)。 +3. 自动映射字段 + 合并弹窗补填字段,构造 `CreateCrmExpansionRequest`(不走表单校验)。 +4. `insertCrmExpansion` 创建目标主表,获取新 id。 +5. 联系人子表逐条复制(`name / mobile / title / sort_order`)。 +6. 引用同步(详见第四节):跟进记录、外勤打卡、日报消息迁移至新记录,商机、其它CRM进货商引用置空。 +7. 删除源:先删联系人子表,再删渠道主表。 + +## 四、引用同步清单(决策①:删除源 + 同步引用) + +### 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 展示错误信息(如「记录不存在或已被迁移」)。 + +## 八、实现清单(后续开发步骤) + +1. 后端:新增 `MoveChannelToCrmRequest` / `MoveCrmToChannelRequest` DTO;`ExpansionController` 新增两个接口;`ExpansionService` 新增两个 `@Transactional` 方法。 +2. Mapper:新增按源 id 更新 `work_checkin` / `work_report_message` 的 `biz_type` / `biz_id` / `biz_name`、商机 `channel_expansion_id` 置空、其它CRM `supplier_id` 置空、删除渠道/CRM主表等 SQL。 +3. 并发防护:迁移查询源记录时使用 `SELECT ... FOR UPDATE` 行锁;重复迁移返回「记录不存在或已被迁移」;后端对弹窗补填字段二次校验。 +4. 前端:新增两个补填表单弹窗组件 + 两个迁移按钮 + 二次确认与成功刷新逻辑,提交时 loading 禁用防重复点击。 +5. 验证:渠道→CRM 与 CRM→渠道 各执行一遍,核对主表、联系人、跟进记录、打卡/日报关联、商机引用均正确迁移或置空。 diff --git a/.idea/workspace.xml b/.idea/workspace.xml index 480b4c0c..51006c76 100644 --- a/.idea/workspace.xml +++ b/.idea/workspace.xml @@ -5,14 +5,50 @@ - + + + + + + + + + - + + + + + + + + + - + + + + + + + + + + + + + + + + + + + + +