You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

5.4 KiB

09 — 字典项两级树形能力(DictItem 加 parent_id)

Type: grilling → task(本票先 grill 拍板不变式,再落地)

Status: needs-triage

Blocked by: 04

Problem Statement

现有 DictItem扁平模型group_id + code + name + value + sort_no + status,无 parent_id)。ADR-0015 spec 开篇即写「两级结构(分组 → 字典项)」——即层级止步于「分组含一批平铺项」,字典项自身之间没有父子关系

商机模块(20260825 产品拍板)要求「行业」字典是多级树形:一级 9 个大类(政府机关 / 政法国防 / 教育 / 医疗健康 / 金融 / 交通 / 能源与制造 / 文化旅游 / 园区与基建),每个大类下挂若干二级细分(如 政府机关 → 人大政协 / 应急局 / 烟草局 / …)。且明确:

  • 全系统通用:客户、商机共用同一套 industry(不单建 industry_code)——顺带解掉此前挂起的「路 A/B」之争,选路 A 的反面:复用并覆盖现有 industry
  • 商机只选到一级:商机主表 industry_code 字段存一级 code。
  • 客户侧是否选到二级 = 待拍板(见 Q6)。

现有 industry 分组种子是 8 项旧口径(制造业 / 信息技术 / 金融 / 医疗健康 / 教育 / 零售 / 房地产 / 其他),与新树完全不同口径,覆盖会造成客户存量数据 code 悬空(见 Q5)。

为什么必须先 grill(不能直接种)

DictItemparent_idcrm-dict 基础模型扩展,影响面远超「种一批值」:

  • 实体 + DDL(现有行 parent_id=null=顶层)
  • DictItemDTO(加 parentId / 可能 children 树形出参)
  • DictItemServiceImpl 校验不变式(见下)
  • DictQueryService 新增树形查询 + Caffeine 缓存结构调整
  • 现有 45 个 crm-dict 测试 + 全系统扁平消费方(lead / customer / opportunity)回归
  • 前端字典管理页树形录入/展示契约

砸坏一个被全系统依赖的模块风险太高,故按仓库 issue-tracker 规矩先立票 grill。

待拍板不变式(grill 逐条)

  • Q1 层级上限:两级封顶?还是允许任意深度树?(商机只需两级,但字典是通用能力——限死两级更简单、更可控;开放多级更通用但校验/查询复杂。)
  • Q2 父子约束:二级项的 parent_id 必须指向同分组内的另一项;禁止跨组;禁止自引用;禁止循环。一级项 parent_id = null。是否要求「有子项的项不能自己再当别人的子项」(即强制恰好两层)?
  • Q3 停用/删除级联
    • 停用一级项时,其二级子项是否连带不可选(对齐分组停用→整组不可选的既有语义)?
    • 删除一级项:若其下有存活子项,拦截(提示先删子项)还是级联软删?(建议拦截,与「删分组需组内无项」的既有保守语义一致。)
  • Q4 树形查询 APIDictQueryService 加什么?listTree(groupCode) 返回嵌套?listChildren(groupCode, parentCode)getItem 扁平接口保持不变(向后兼容所有现有消费方)。缓存怎么建(现 Caffeine 按 groupCode→扁平 list,加树需另建 key 还是复用 list 内存组树)。
  • Q5 存量数据安全:现有 industry 8 项(manufacturing/it/finance/…)是否已有客户业务数据引用?覆盖成新树前必须确认——若有存量,旧 code 会悬空(客户列表行业列显示空)。需要 DBA/产品确认,或提供 code 映射迁移。
  • Q6 客户侧选到几级:商机存一级 code 已定。客户模块 industry 字段是选一级还是可选到二级?决定二级项是否只是「商机的分类维度」还是「客户可直接选的值」。
  • Q7 商机存一级的落库形态:商机 industry_code 存一级项 code(如 industry_gov)。二级仅作字典维护/展示层级,不进商机主表?确认。

实现范围(Q 拍板后)

  1. DictItemparentId(nullable,顶层为 null);DDL 自动加列。
  2. DictItemDTOparentId(+ 视 Q4 决定是否加 children)。
  3. DictItemServiceImpl.saveItem 增校验:父项存在且同组、层级上限(Q1)、无自引用/循环(Q2);doDeleteItem 增级联策略(Q3)。
  4. DictQueryService 增树形查询(Q4)+ 缓存。
  5. 种子:industry 分组按新树重种(9 一级 + 全部二级),处理存量覆盖(Q5)。
  6. 测试:树形 CRUD / 父子约束 / 级联 / 树形查询;回归现有 45 扁平测试。

附:产品给定的行业全树(20260825,待 Q 拍板后种)

一级(9)→ 二级:

  • 政府机关:人大政协 / 应急局 / 烟草局 / 疾控中心 / 大数据中心 / 税务局 / 教育局 / 民政局 / 人社局 / 财政局 / 国土局 / 水利局 / 林业局 / 商务局 / 文体旅局 / 气象局 / 退役军人事务部 / 融媒体中心 / 人防办
  • 政法国防:公安 / 检察院 / 法院 / 司法厅 / 监狱 / 海关-边检站 / 武警 / 特警 / 消防
  • 教育:普教 / 职校 / 党校
  • 医疗健康:医院 / 康养中心
  • 金融:银行 / 保险公司
  • 交通:轨道交通 / 机场 / 高铁 / 高速公路
  • 能源与制造:石化-能源-矿业 / 发电站-核电站 / 电力实训基地 / 工厂
  • 文化旅游:文旅 / 酒店 / 展馆-博物馆
  • 园区与基建:智慧园区 / 集团大楼 / 商业中心 / 智算中心 / 低空飞行基地