Browse Source

feat: 字典种子对齐蓝湖原型——lead_source 改渠道 38 项并新增 brand/product/scene(frontend-integration 票据 02)

master
luoweijian 3 weeks ago
parent
commit
ea212fa8b6
  1. 101
      crm-dict/src/main/java/com/crm/dict/config/DictDataInitializer.java
  2. 39
      crm-dict/src/test/java/com/crm/dict/config/DictDataInitializerTest.java
  3. 10
      crm-lead/src/main/java/com/crm/lead/constant/LeadConstants.java
  4. 8
      crm-lead/src/main/java/com/crm/lead/domain/entity/Lead.java

101
crm-dict/src/main/java/com/crm/dict/config/DictDataInitializer.java

@ -40,17 +40,51 @@ public class DictDataInitializer implements CommandLineRunner {
private final DictGroupDefaultMapper dictGroupDefaultMapper;
private final DictRefCountMapper dictRefCountMapper;
/*-------- 首批内置字典种子(Excel 替换口,见类注释)--------*/
/*-------- 首批内置字典种子Excel 替换口见类注释
* 渠道/品牌/需求产品/需求场景选项取自蓝湖原型 A2-1-2-2 新增线索表单frontend-integration 票据 02
* 原型渠道清单中的占位文本列表项1不入库--------*/
private static final List<GroupSeed> SEED_GROUPS = List.of(
new GroupSeed("lead_source", "客户来源", 1, "客户首次触达渠道",
new GroupSeed("lead_source", "渠道", 1, "线索来源渠道(对齐蓝湖原型)",
List.of(
new ItemSeed("ad", "广告投放", "ad", 1, null, false),
new ItemSeed("referral", "转介绍", "referral", 2, null, false),
new ItemSeed("exhibition", "展会活动", "exhibition", 3, null, false),
new ItemSeed("website", "官网咨询", "website", 4, null, false),
new ItemSeed("offline", "线下拜访", "offline", 5, null, false),
new ItemSeed("other", "其他", "other", 6, null, false))),
new ItemSeed("exhibition", "展会", "exhibition", 1, null, false),
new ItemSeed("digital_av", "数字音视", "digital_av", 2, null, false),
new ItemSeed("jd", "京东", "jd", 3, null, false),
new ItemSeed("netease_news", "网易新闻", "netease_news", 4, null, false),
new ItemSeed("tencent_sogou", "腾讯搜狗", "tencent_sogou", 5, null, false),
new ItemSeed("zhihu", "知乎", "zhihu", 6, null, false),
new ItemSeed("edu_procurement", "教育采购", "edu_procurement", 7, null, false),
new ItemSeed("baidu_20146", "百度20146", "baidu_20146", 8, null, false),
new ItemSeed("edu_equipment", "教育装备采购网", "edu_equipment", 9, null, false),
new ItemSeed("1688", "1688", "1688", 10, null, false),
new ItemSeed("accessory_cable", "配件线材", "accessory_cable", 11, null, false),
new ItemSeed("ifeng", "凤凰网", "ifeng", 12, null, false),
new ItemSeed("junqiao", "军桥", "junqiao", 13, null, false),
new ItemSeed("soft_article", "软文", "soft_article", 14, null, false),
new ItemSeed("baidu_broadcast", "百度广播", "baidu_broadcast", 15, null, false),
new ItemSeed("baidu_pro_sound", "百度专业扩声", "baidu_pro_sound", 16, null, false),
new ItemSeed("baidu_edu", "百度教育", "baidu_edu", 17, null, false),
new ItemSeed("search_360", "360搜索", "search_360", 18, null, false),
new ItemSeed("shunqi", "顺企网", "shunqi", 19, null, false),
new ItemSeed("security_net", "安防网", "security_net", 20, null, false),
new ItemSeed("huicong", "慧聪", "huicong", 21, null, false),
new ItemSeed("baidu_video_meeting", "百度视频会议", "baidu_video_meeting", 22, null, false),
new ItemSeed("wechat_moments", "微信朋友圈", "wechat_moments", 23, null, false),
new ItemSeed("message", "留言", "message", 24, null, false),
new ItemSeed("phone", "电话", "phone", 25, null, false),
new ItemSeed("kuaishou", "快手", "kuaishou", 26, null, false),
new ItemSeed("video_account", "视频号", "video_account", 27, null, false),
new ItemSeed("official_account", "公众号", "official_account", 28, null, false),
new ItemSeed("douyin", "抖音号", "douyin", 29, null, false),
new ItemSeed("xiaoke_ai", "销氪AI", "xiaoke_ai", 30, null, false),
new ItemSeed("baidu_meeting", "百度会议", "baidu_meeting", 31, null, false),
new ItemSeed("baiying_ai", "百应AI", "baiying_ai", 32, null, false),
new ItemSeed("xiaohongshu", "小红书", "xiaohongshu", 33, null, false),
new ItemSeed("baidu_lighting", "百度亮化", "baidu_lighting", 34, null, false),
new ItemSeed("short_video", "短视频", "short_video", 35, null, false),
new ItemSeed("baidu_aicaigou", "百度爱采购", "baidu_aicaigou", 36, null, false),
new ItemSeed("media", "媒体", "media", 37, null, false),
new ItemSeed("toutiao", "头条", "toutiao", 38, null, false))),
new GroupSeed("customer_status", "客户状态", 2, "客户生命周期状态",
List.of(
new ItemSeed("potential", "潜在客户", "potential", 1, null, true),
@ -65,7 +99,56 @@ public class DictDataInitializer implements CommandLineRunner {
new ItemSeed("education", "教育", "education", 5, null, false),
new ItemSeed("retail", "零售", "retail", 6, null, false),
new ItemSeed("real_estate", "房地产", "real_estate", 7, null, false),
new ItemSeed("other", "其他", "other", 8, null, false))));
new ItemSeed("other", "其他", "other", 8, null, false))),
new GroupSeed("brand", "品牌", 4, "线索品牌(对齐蓝湖原型)",
List.of(
new ItemSeed("itc", "itc", "itc", 1, null, false),
new ItemSeed("shichang", "世畅", "shichang", 2, null, false),
new ItemSeed("lisheng", "丽声", "lisheng", 3, null, false),
new ItemSeed("crx", "CRX", "crx", 4, null, false),
new ItemSeed("three_a", "3A", "three_a", 5, null, false),
new ItemSeed("beihangxing", "北航星", "beihangxing", 6, null, false),
new ItemSeed("tekong", "特控", "tekong", 7, null, false),
new ItemSeed("sooann", "SOOANN", "sooann", 8, null, false),
new ItemSeed("jinye_chengxing", "金业诚兴", "jinye_chengxing", 9, null, false))),
new GroupSeed("product", "需求产品", 5, "线索需求产品(对齐蓝湖原型)",
List.of(
new ItemSeed("led_display", "LED显示屏", "led_display", 1, null, false),
new ItemSeed("conference_system", "会议系统", "conference_system", 2, null, false),
new ItemSeed("paperless_meeting", "无纸化会议", "paperless_meeting", 3, null, false),
new ItemSeed("stage_lighting", "舞台灯光", "stage_lighting", 4, null, false),
new ItemSeed("tourism_lighting", "文旅亮化", "tourism_lighting", 5, null, false),
new ItemSeed("record_broadcast", "录播系统", "record_broadcast", 6, null, false),
new ItemSeed("broadcast_system", "广播系统", "broadcast_system", 7, null, false),
new ItemSeed("conference_machine", "会议一体机", "conference_machine", 8, null, false),
new ItemSeed("video_conference", "视频会议", "video_conference", 9, null, false),
new ItemSeed("sound_system", "扩声系统", "sound_system", 10, null, false),
new ItemSeed("teaching_system", "教学系统", "teaching_system", 11, null, false),
new ItemSeed("command_center", "指挥中心", "command_center", 12, null, false),
new ItemSeed("medical_system", "医疗系统", "medical_system", 13, null, false),
new ItemSeed("info_distribution", "信发系统", "info_distribution", 14, null, false),
new ItemSeed("software_platform", "软件平台", "software_platform", 15, null, false),
new ItemSeed("security_surveillance", "安防监控", "security_surveillance", 16, null, false))),
new GroupSeed("scene", "需求场景", 6, "线索需求场景(对齐蓝湖原型)",
List.of(
new ItemSeed("meeting_room", "会议室", "meeting_room", 1, null, false),
new ItemSeed("auditorium", "报告厅", "auditorium", 2, null, false),
new ItemSeed("command_center", "指挥中心", "command_center", 3, null, false),
new ItemSeed("sports_venue", "文体场馆", "sports_venue", 4, null, false),
new ItemSeed("theater", "剧场剧院", "theater", 5, null, false),
new ItemSeed("bar_ktv", "酒吧KTV", "bar_ktv", 6, null, false),
new ItemSeed("higher_vocational", "高职教", "higher_vocational", 7, null, false),
new ItemSeed("basic_edu", "普教", "basic_edu", 8, null, false),
new ItemSeed("justice", "公检法司", "justice", 9, null, false),
new ItemSeed("medical", "医疗", "medical", 10, null, false),
new ItemSeed("military", "国防部队", "military", 11, null, false),
new ItemSeed("government", "政府单位", "government", 12, null, false),
new ItemSeed("transportation", "交通", "transportation", 13, null, false),
new ItemSeed("finance", "金融", "finance", 14, null, false),
new ItemSeed("energy", "能源", "energy", 15, null, false),
new ItemSeed("park", "园区", "park", 16, null, false),
new ItemSeed("cultural_tourism", "文旅", "cultural_tourism", 17, null, false),
new ItemSeed("exhibition_hall", "展厅", "exhibition_hall", 18, null, false))));
@Override
public void run(String... args) {

39
crm-dict/src/test/java/com/crm/dict/config/DictDataInitializerTest.java

@ -31,23 +31,24 @@ class DictDataInitializerTest extends AbstractDictH2Test {
}
@Test
@DisplayName("首装:3 个内置分组 + 17 个内置项全部建库,ref_count 预建,种子默认项生效")
@DisplayName("首装:6 个内置分组 + 92 个内置项全部建库,ref_count 预建,种子默认项生效")
void firstRun_createsBuiltinDictsWithDefaults() {
initializer.run();
List<DictGroup> groups = dictGroupMapper.selectList(null);
assertThat(groups).hasSize(3);
assertThat(groups).hasSize(6);
assertThat(groups).extracting(DictGroup::getCode)
.containsExactlyInAnyOrder("lead_source", "customer_status", "industry");
.containsExactlyInAnyOrder("lead_source", "customer_status", "industry",
"brand", "product", "scene");
assertThat(groups).allMatch(g -> Boolean.TRUE.equals(g.getBuiltin()));
assertThat(groups).allMatch(g -> g.getStatus() == DictConstants.STATUS_ENABLED);
List<DictItem> items = dictItemMapper.selectList(null);
assertThat(items).hasSize(17); // 6 + 3 + 8
assertThat(items).hasSize(92); // 38 渠道 + 3 客户状态 + 8 行业 + 9 品牌 + 16 需求产品 + 18 需求场景
assertThat(items).allMatch(i -> Boolean.TRUE.equals(i.getBuiltin()));
// 引用计数行预建(与新建接口同口径)
assertThat(dictRefCountMapper.selectCount(null)).isEqualTo(17);
assertThat(dictRefCountMapper.selectCount(null)).isEqualTo(92);
DictGroup statusGroup = groups.stream()
.filter(g -> g.getCode().equals("customer_status")).findFirst().orElseThrow();
DictItem potential = dictItemMapper.selectList(null).stream()
@ -68,9 +69,9 @@ class DictDataInitializerTest extends AbstractDictH2Test {
initializer.run();
initializer.run();
assertThat(dictGroupMapper.selectCount(null)).isEqualTo(3);
assertThat(dictItemMapper.selectCount(null)).isEqualTo(17);
assertThat(dictRefCountMapper.selectCount(null)).isEqualTo(17);
assertThat(dictGroupMapper.selectCount(null)).isEqualTo(6);
assertThat(dictItemMapper.selectCount(null)).isEqualTo(92);
assertThat(dictRefCountMapper.selectCount(null)).isEqualTo(92);
assertThat(dictGroupDefaultMapper.selectCount(null)).isEqualTo(1);
}
@ -82,12 +83,12 @@ class DictDataInitializerTest extends AbstractDictH2Test {
// 管理员本地化:改内置项名 + 手建用户项(builtin=false)
DictGroup leadSource = dictGroupMapper.selectList(null).stream()
.filter(g -> g.getCode().equals("lead_source")).findFirst().orElseThrow();
DictItem adItem = dictItemMapper.selectList(null).stream()
.filter(i -> i.getGroupId().equals(leadSource.getId()) && i.getCode().equals("ad"))
DictItem exhibitionItem = dictItemMapper.selectList(null).stream()
.filter(i -> i.getGroupId().equals(leadSource.getId()) && i.getCode().equals("exhibition"))
.findFirst().orElseThrow();
adItem.setName("广告投放-本地改名");
adItem.setValue("LOCAL_VALUE"); // 管理员本地化改值
dictItemMapper.updateById(adItem);
exhibitionItem.setName("展会-本地改名");
exhibitionItem.setValue("LOCAL_VALUE"); // 管理员本地化改值
dictItemMapper.updateById(exhibitionItem);
DictItem userItem = new DictItem();
userItem.setGroupId(leadSource.getId());
@ -102,8 +103,8 @@ class DictDataInitializerTest extends AbstractDictH2Test {
initializer.run();
// 内置项名被种子刷新(种子的"改"语义),但字典值属业务契约不被覆盖
DictItem refreshed = dictItemMapper.selectById(adItem.getId());
assertThat(refreshed.getName()).isEqualTo("广告投放");
DictItem refreshed = dictItemMapper.selectById(exhibitionItem.getId());
assertThat(refreshed.getName()).isEqualTo("展会");
assertThat(refreshed.getValue()).isEqualTo("LOCAL_VALUE");
// 用户项原样保留:名称/值/内置标记均未动
DictItem untouched = dictItemMapper.selectList(null).stream()
@ -193,12 +194,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
assertThat(dictGroupMapper.selectCount(null)).isEqualTo(3); // lead_source(用户) + customer_status + industry
// 种子其他分组不受影响(继续正常导入):customer_status + industry + brand + product + scene
assertThat(dictGroupMapper.selectCount(null)).isEqualTo(6); // lead_source(用户) + 其余 5 个内置分组
assertThat(dictGroupMapper.selectList(null).stream()
.filter(g -> g.getCode().equals("industry"))).hasSize(1);
assertThat(dictItemMapper.selectCount(null)).isEqualTo(11); // customer_status 3 + industry 8
assertThat(dictRefCountMapper.selectCount(null)).isEqualTo(11);
assertThat(dictItemMapper.selectCount(null)).isEqualTo(54); // 92 - 渠道 38(用户分组整组跳过)
assertThat(dictRefCountMapper.selectCount(null)).isEqualTo(54);
assertThat(dictGroupDefaultMapper.selectCount(null)).isEqualTo(1); // customer_status 默认项
}

10
crm-lead/src/main/java/com/crm/lead/constant/LeadConstants.java

@ -26,12 +26,12 @@ public interface LeadConstants {
int FEEDBACK_RECORD_DRAFT = 1; // 草稿
int FEEDBACK_RECORD_SUBMITTED = 2; // 已提交
/*-------- 字典分组 code(对齐 crm-dict)---------*/
/*-------- 字典分组 code(对齐 crm-dict 内置种子,frontend-integration 票据 02)---------*/
String DICT_GROUP_CHANNEL = "lead.channel";
String DICT_GROUP_BRAND = "lead.brand";
String DICT_GROUP_PRODUCT = "lead.product";
String DICT_GROUP_SCENE = "lead.scene";
String DICT_GROUP_LEAD_SOURCE = "lead_source";
String DICT_GROUP_BRAND = "brand";
String DICT_GROUP_PRODUCT = "product";
String DICT_GROUP_SCENE = "scene";
/*-------- 列偏好 scopeKey预留尚未接线---------
* 线索四视图列偏好的作用范围键每菜单独立 crm-preference/CONTEXT.md scope

8
crm-lead/src/main/java/com/crm/lead/domain/entity/Lead.java

@ -64,19 +64,19 @@ public class Lead extends BaseEntity {
// -------- 业务来源(字典 code)--------
@Comment("渠道字典 code(lead.channel)")
@Comment("渠道字典 code(lead_source)")
@Column(columnDefinition = "varchar(64) not null comment '渠道字典code'")
private String channelCode;
@Comment("品牌字典 code(lead.brand)")
@Comment("品牌字典 code(brand)")
@Column(columnDefinition = "varchar(64) not null comment '品牌字典code'")
private String brandCode;
@Comment("需求产品字典 code(lead.product)")
@Comment("需求产品字典 code(product)")
@Column(columnDefinition = "varchar(64) not null comment '需求产品字典code'")
private String productCode;
@Comment("需求场景字典 code(lead.scene)")
@Comment("需求场景字典 code(scene)")
@Column(columnDefinition = "varchar(64) comment '需求场景字典code'")
private String sceneCode;

Loading…
Cancel
Save