From d68aa067e22d22d3f73318b217c64b8718481978 Mon Sep 17 00:00:00 2001 From: luoweijian <1329394916@qq.com> Date: Tue, 25 Aug 2026 14:19:18 +0800 Subject: [PATCH] =?UTF-8?q?feat(dict):=20=E5=88=9D=E5=A7=8B=E5=8C=96=209?= =?UTF-8?q?=20=E4=B8=AA=E5=95=86=E6=9C=BA=E5=AD=97=E5=85=B8=E5=88=86?= =?UTF-8?q?=E7=BB=84=E7=A7=8D=E5=AD=90=20(=E4=BA=A7=E5=93=81=E5=BD=95?= =?UTF-8?q?=E5=85=A5=2020260825)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 新增分组 (契约 code 对齐 PRD §10/票 09/票 01): - opp_source 商机来源 (线索转入/线下挖掘/客户转介绍) - opp_type 商机类型 (新建/改造/升级) - bid_form 招标形式 (ZC政采/GC工程/BC EPC总分包/YB邀标) - result_tag 跟进结果标签 (8 项) - design_config 设计配置 (6 项, 方案卡字段选项集) - pause_reason 暂缓原因 (6 项) - pool_reason 进入公海原因 (5 项) - project_role 商机项目角色 (6 项, OpportunityTeam.projectRole) - close_reason 关闭原因 (6 项) item code = 分组前缀_序号 (同构 OPP_STAGE_0x, 避免中文项英译歧义), 47 项 分组 7→16, item 97→144; 种子测试断言同步更新, 45 全绿 --- .../crm/dict/config/DictDataInitializer.java | 67 ++++++++++++++++++- .../dict/config/DictDataInitializerTest.java | 26 +++---- 2 files changed, 80 insertions(+), 13 deletions(-) diff --git a/crm-dict/src/main/java/com/crm/dict/config/DictDataInitializer.java b/crm-dict/src/main/java/com/crm/dict/config/DictDataInitializer.java index 44490c0..734b44c 100644 --- a/crm-dict/src/main/java/com/crm/dict/config/DictDataInitializer.java +++ b/crm-dict/src/main/java/com/crm/dict/config/DictDataInitializer.java @@ -156,7 +156,72 @@ public class DictDataInitializer implements CommandLineRunner { new ItemSeed("OPP_STAGE_02", "关系摸排", "OPP_STAGE_02", 2, null, false), new ItemSeed("OPP_STAGE_03", "资料采集", "OPP_STAGE_03", 3, null, false), new ItemSeed("OPP_STAGE_04", "方案卡", "OPP_STAGE_04", 4, null, false), - new ItemSeed("OPP_STAGE_05", "已转项目", "OPP_STAGE_05", 5, null, false)))); + new ItemSeed("OPP_STAGE_05", "已转项目", "OPP_STAGE_05", 5, null, false))), + new GroupSeed("opp_source", "商机来源", 8, "渠道归因(含「线索转入」,商机模块票 03;产品录入 20260825)", + List.of( + new ItemSeed("opp_source_01", "线索转入", "opp_source_01", 1, null, false), + new ItemSeed("opp_source_02", "线下挖掘", "opp_source_02", 2, null, false), + new ItemSeed("opp_source_03", "客户转介绍", "opp_source_03", 3, null, false))), + new GroupSeed("opp_type", "商机类型", 9, "商机类型(票 03;产品录入 20260825)", + List.of( + new ItemSeed("opp_type_01", "新建", "opp_type_01", 1, null, false), + new ItemSeed("opp_type_02", "改造", "opp_type_02", 2, null, false), + new ItemSeed("opp_type_03", "升级", "opp_type_03", 3, null, false))), + new GroupSeed("bid_form", "招标形式", 10, "招标形式(票 03;产品录入 20260825)", + List.of( + new ItemSeed("bid_form_01", "ZC(政采)", "bid_form_01", 1, null, false), + new ItemSeed("bid_form_02", "GC(工程)", "bid_form_02", 2, null, false), + new ItemSeed("bid_form_03", "BC(EPC总分包)", "bid_form_03", 3, null, false), + new ItemSeed("bid_form_04", "YB(邀标)", "bid_form_04", 4, null, false))), + new GroupSeed("result_tag", "跟进结果标签", 11, "跟进记录结果标签(票 09 follow.result_tag;产品录入 20260825)", + List.of( + new ItemSeed("result_tag_01", "暂无进展", "result_tag_01", 1, null, false), + new ItemSeed("result_tag_02", "需求确认", "result_tag_02", 2, null, false), + new ItemSeed("result_tag_03", "方案推进", "result_tag_03", 3, null, false), + new ItemSeed("result_tag_04", "资料采集", "result_tag_04", 4, null, false), + new ItemSeed("result_tag_05", "关系维护", "result_tag_05", 5, null, false), + new ItemSeed("result_tag_06", "预算沟通", "result_tag_06", 6, null, false), + new ItemSeed("result_tag_07", "勘探现场", "result_tag_07", 7, null, false), + new ItemSeed("result_tag_08", "方案深化", "result_tag_08", 8, null, false))), + new GroupSeed("design_config", "设计配置", 12, "方案卡设计配置字段选项集(票 07 方案卡字段样例;产品录入 20260825)", + List.of( + new ItemSeed("design_config_01", "按清单配置", "design_config_01", 1, null, false), + new ItemSeed("design_config_02", "标准配置", "design_config_02", 2, null, false), + new ItemSeed("design_config_03", "高配", "design_config_03", 3, null, false), + new ItemSeed("design_config_04", "中配", "design_config_04", 4, null, false), + new ItemSeed("design_config_05", "低配", "design_config_05", 5, null, false), + new ItemSeed("design_config_06", "定制", "design_config_06", 6, null, false))), + new GroupSeed("pause_reason", "暂缓原因", 13, "商机暂缓原因(票 04 PauseCmd;产品录入 20260825)", + List.of( + new ItemSeed("pause_reason_01", "客户项目延期", "pause_reason_01", 1, null, false), + new ItemSeed("pause_reason_02", "预算冻结", "pause_reason_02", 2, null, false), + new ItemSeed("pause_reason_03", "招标暂停", "pause_reason_03", 3, null, false), + new ItemSeed("pause_reason_04", "政策与其他不可抗力因素", "pause_reason_04", 4, null, false), + new ItemSeed("pause_reason_05", "个人原因", "pause_reason_05", 5, null, false), + new ItemSeed("pause_reason_99", "其他", "pause_reason_99", 6, null, false))), + new GroupSeed("pool_reason", "进入公海原因", 14, "抛公海/超期回收原因(票 04/10 pool_reason;产品录入 20260825)", + List.of( + new ItemSeed("pool_reason_01", "长期未有效跟进", "pool_reason_01", 1, null, false), + new ItemSeed("pool_reason_02", "客户暂无预算", "pool_reason_02", 2, null, false), + new ItemSeed("pool_reason_03", "项目暂停或延期", "pool_reason_03", 3, null, false), + new ItemSeed("pool_reason_04", "介入时机不成熟", "pool_reason_04", 4, null, false), + new ItemSeed("pool_reason_99", "其他", "pool_reason_99", 5, null, false))), + new GroupSeed("project_role", "商机项目角色", 15, "团队成员项目角色纯回显标签(票 01;OpportunityTeam.projectRole;产品录入 20260825)", + List.of( + new ItemSeed("project_role_01", "商机负责人", "project_role_01", 1, null, false), + new ItemSeed("project_role_02", "现场专员", "project_role_02", 2, null, false), + new ItemSeed("project_role_03", "报备专员", "project_role_03", 3, null, false), + new ItemSeed("project_role_04", "报价专员", "project_role_04", 4, null, false), + new ItemSeed("project_role_05", "方案工程师", "project_role_05", 5, null, false), + new ItemSeed("project_role_06", "协同负责人", "project_role_06", 6, null, false))), + new GroupSeed("close_reason", "关闭原因", 16, "商机关闭原因(票 04 CloseCmd;选「其他」时补充说明必填;产品录入 20260825)", + List.of( + new ItemSeed("close_reason_01", "项目取消", "close_reason_01", 1, null, false), + new ItemSeed("close_reason_02", "客户放弃", "close_reason_02", 2, null, false), + new ItemSeed("close_reason_03", "竞争失败", "close_reason_03", 3, null, false), + new ItemSeed("close_reason_04", "重复商机", "close_reason_04", 4, null, false), + new ItemSeed("close_reason_05", "无效商机", "close_reason_05", 5, null, false), + new ItemSeed("close_reason_99", "其他", "close_reason_99", 6, null, false)))); @Override public void run(String... args) { diff --git a/crm-dict/src/test/java/com/crm/dict/config/DictDataInitializerTest.java b/crm-dict/src/test/java/com/crm/dict/config/DictDataInitializerTest.java index 16b8e10..441671b 100644 --- a/crm-dict/src/test/java/com/crm/dict/config/DictDataInitializerTest.java +++ b/crm-dict/src/test/java/com/crm/dict/config/DictDataInitializerTest.java @@ -31,24 +31,26 @@ class DictDataInitializerTest extends AbstractDictH2Test { } @Test - @DisplayName("首装:7 个内置分组 + 97 个内置项全部建库,ref_count 预建,种子默认项生效") + @DisplayName("首装:16 个内置分组 + 144 个内置项全部建库,ref_count 预建,种子默认项生效") void firstRun_createsBuiltinDictsWithDefaults() { initializer.run(); List groups = dictGroupMapper.selectList(null); - assertThat(groups).hasSize(7); + assertThat(groups).hasSize(16); assertThat(groups).extracting(DictGroup::getCode) .containsExactlyInAnyOrder("lead_source", "customer_status", "industry", - "brand", "product", "scene", "opp_stage"); + "brand", "product", "scene", "opp_stage", + "opp_source", "opp_type", "bid_form", "result_tag", "design_config", + "pause_reason", "pool_reason", "project_role", "close_reason"); assertThat(groups).allMatch(g -> Boolean.TRUE.equals(g.getBuiltin())); assertThat(groups).allMatch(g -> g.getStatus() == DictConstants.STATUS_ENABLED); List items = dictItemMapper.selectList(null); - assertThat(items).hasSize(97); // 38 渠道 + 3 客户状态 + 8 行业 + 9 品牌 + 16 需求产品 + 18 需求场景 + 5 商机阶段 + assertThat(items).hasSize(144); // 97 (旧) + 商机来源3 + 类型3 + 招标形式4 + 结果标签8 + 设计配置6 + 暂缓原因6 + 公海原因5 + 项目角色6 + 关闭原因6 assertThat(items).allMatch(i -> Boolean.TRUE.equals(i.getBuiltin())); // 引用计数行预建(与新建接口同口径) - assertThat(dictRefCountMapper.selectCount(null)).isEqualTo(97); + assertThat(dictRefCountMapper.selectCount(null)).isEqualTo(144); DictGroup statusGroup = groups.stream() .filter(g -> g.getCode().equals("customer_status")).findFirst().orElseThrow(); DictItem potential = dictItemMapper.selectList(null).stream() @@ -69,9 +71,9 @@ class DictDataInitializerTest extends AbstractDictH2Test { initializer.run(); initializer.run(); - assertThat(dictGroupMapper.selectCount(null)).isEqualTo(7); - assertThat(dictItemMapper.selectCount(null)).isEqualTo(97); - assertThat(dictRefCountMapper.selectCount(null)).isEqualTo(97); + assertThat(dictGroupMapper.selectCount(null)).isEqualTo(16); + assertThat(dictItemMapper.selectCount(null)).isEqualTo(144); + assertThat(dictRefCountMapper.selectCount(null)).isEqualTo(144); assertThat(dictGroupDefaultMapper.selectCount(null)).isEqualTo(1); } @@ -194,12 +196,12 @@ class DictDataInitializerTest extends AbstractDictH2Test { assertThat(dictGroupMapper.selectById(userGroup.getId()).getBuiltin()).isFalse(); assertThat(dictItemMapper.selectList(null).stream() .filter(i -> i.getGroupId().equals(userGroup.getId()))).isEmpty(); - // 种子其他分组不受影响(继续正常导入):customer_status + industry + brand + product + scene + opp_stage - assertThat(dictGroupMapper.selectCount(null)).isEqualTo(7); // lead_source(用户) + 其余 6 个内置分组 + // 种子其他分组不受影响(继续正常导入):customer_status + industry + brand + product + scene + opp_stage + 8 商机分组 + assertThat(dictGroupMapper.selectCount(null)).isEqualTo(16); // lead_source(用户) + 其余 15 个内置分组 assertThat(dictGroupMapper.selectList(null).stream() .filter(g -> g.getCode().equals("industry"))).hasSize(1); - assertThat(dictItemMapper.selectCount(null)).isEqualTo(59); // 97 - 渠道 38(用户分组整组跳过) - assertThat(dictRefCountMapper.selectCount(null)).isEqualTo(59); + assertThat(dictItemMapper.selectCount(null)).isEqualTo(106); // 144 - 渠道 38(用户分组整组跳过) + assertThat(dictRefCountMapper.selectCount(null)).isEqualTo(106); assertThat(dictGroupDefaultMapper.selectCount(null)).isEqualTo(1); // customer_status 默认项 }