README.md 12.2 KB

数据库规范与存量库实测结论

本文档是 parking-cloud 的数据库事实记录。所有结论都来自对老仓库 zteits-clouds 的实测, 不是设计意图的复述——老项目 CLAUDE.md 里关于「通用字段」的描述与存量库的实际结构不符, 若按其执行会导致新代码查询不到数据。凡有冲突,以本文档为准。

1. 库与归属

库 内容 数据源 落地阶段
parking_cloud 停车六库合并后的唯一库(表前缀 sys_ / park_ / pay_ / order_ / bill_ / charge_) primary P0 建库,P2 灌表
政务库(zteits_ptms_chifeng + zteits-gov) 政务平台自有数据 @DS("gov") P6(是否并为一库待 P6a 确认)

老库(实际六个:zt_sys / zt_park / zt_order / zt_order_n / zt_bill / zt_charge) 保留只读作为回滚路径,直到 P8 全面下线。

⚠️ 注意没有 zt_pay 这个库——这是历史命名造成的错觉(env 里 LEGACY_DB_PAY=zt_order, pay 域的活库其实叫 zt_order,order 域的活库叫 zt_order_n,见 <code>legacy-db-actual.md</code> §1)。 别照着"库名=业务域"去连库,会连错。

2. ⚠️ 通用字段:存量库的实测结论

统计口径:对老仓库 zteits-clouds 下 1311 个存量 mapper XML 做全量词频统计 (XML 里出现的列名即该列真正被业务使用过,比 DDL 全集更可靠——doc/sql/DDL.sql 只是变更日志, 只有 1 张表,不能作为全库结构依据)。

字段 出现次数 结论
del_flag 0 存量库没有逻辑删除列。新代码不要假设它存在
data_state 3619 通用状态列,几乎每张业务表都有;语义按表定义(作废/有效/审核态等)
create_date 2550 审计字段风格 A
modfiy_date 1695 审计字段风格 A(注意拼写是 modfiy,不是 modify)
modify_date 511 少部分表用了正确拼写,与上一行并存
create_time 1187 审计字段风格 B(与风格 A 并存)
update_time 28 风格 B(极少)
create_by / update_by 8 / 8 基本不存在
create_emp_id 1161 风格 A 的「操作人」列
modfiy_empid 956 风格 A 的「修改人」列(同样拼写为 modfiy)

由此确定的三条硬约束

  1. 不配置全局逻辑删除。mybatis-plus.global-config.db-config 里不能出现 logic-delete-field: delFlag;BaseDO 也不带 @TableLogic。 配置了它会让 MyBatis-Plus 给每条 SQL 拼上 WHERE del_flag = 0,存量表直接报 Unknown column 'del_flag'。软删除的语义在存量体系里由 data_state 表达。
  2. 审计字段不统一,BaseDO 只服务于新表(新表统一用 create_time / update_time / create_by / update_by)。 存量实体是纯 POJO,列名靠各自 XML 的 resultMap 映射,不要给它们套 BaseDO、 也不要用 @TableField 补全——否则会引入存量 SQL 里不存在的列。
  3. data_state 是业务状态列,不是通用布尔位。不要做「按位与」之类的通用处理, 每张表的取值含义必须回读该表的 XML / 前端页面确认。

3. 命名与放置

  • 表名蛇形 + 模块前缀:sys_org、park_lot、pay_order
  • 新表(P1 起新建的表,如 system_users、infra_file)沿用 yudao 风格表名, 与存量表并存;存量表不改名(改名会牵动 1311 个 XML 的 namespace/列名)
  • 存量 mapper XML 落位:parking-module-*/src/main/resources/mybatis/{mapper,smapper}/**
  • 新代码不写 XML,用 MyBatis-Plus

4. P2 前置:表清单与冲突排查

4.1 已产出(静态抽取,不依赖连库)

<code>table-inventory.md</code> + <code>table-inventory.csv</code>, 由 <code>tools/extract_table_inventory.py</code> 生成 (扫全仓 mapper XML 的 FROM/JOIN/INTO/UPDATE 与实体的 @Table/@TableName)。

口径提醒:它只覆盖代码真正引用过的表。doc/sql/*.sql 是 ALTER 变更日志、不含建表语句, 不能当作全库结构来源(同 §2 的说明)。

三条主要结论:

结论 数据 对 P2 的意义
表总量与归属 400 张,387 张单模块归属 归属清晰,可据此切模块边界
跨模块直连的表只有 13 张 见报告 §2 这 13 张是合库后唯一会从"侥幸隔离"变成"真能写别人表"的地方,必须逐张定唯一归属模块,其余模块改走 parking-module-api
t_* 前缀并非"无主" 41 张里 39 张属 zteits-bill 前缀与模块不是一一对应(td_ 跨 pay/order/business 三家),归属要按表定,不能按前缀猜
⚠️ 400 不是全量 还有 5 族运行期拼后缀的分表 见下面「分表族」

⚠️ 分表族:静态数不出真实表名

📍 本节的处数/族数已被后续实测修正,别照抄:静态抽取得到"5 处 / 5 族", 但连库实测后的权威结论是 2 个族、共 228 张物理表——tp_p_eqp_inout_record{yyyyMM} (115 张)与 tp_p_eqp_log{yyyyMM}(113 张);pay_order{yyyy} 不是分表族。 更关键的是判据换了:不是"代码里出现过 {yyyyMM} 这种占位",而是 代码里有没有 "前缀" 这个字面量(运行期分表必然要拼字符串)—— 见 <code>legacy-db-actual.md</code> §6。 本节保留的是当时静态抽取看到的东西,可作为"静态判据为什么会数错"的样例。

有 5 处 SQL 的表名后缀是调用方在运行时拼的(报告 §5):tp_p_eqp_inout_record{yyyyMM} (4 个变量名,都是按月)与 pay_order{yyyy}(按年)。

这意味着:

  1. 真实物理表远多于 400 张(每月一张)。将来对生产库 SHOW TABLES 时会看到一批 "代码里搜不到"的表——它们正是这里拼出来的,不要误判为垃圾表
  2. 建表/清理脚本不在本仓库(应该在定时任务或运维脚本里),合库时必须连着这些分表族一起搬
  3. 迁移改造时不能把 ${...} 写死成某个月,动态拼名逻辑要原样保留
  4. 顺带查出一个存量缺陷:ParkingLotUseStatisticDaoImpl:169 用当前时间拼表名, 查历史区间时实际只命中本月那张表,跨月查询会静默出错。迁移时要么修、要么显式记录

4.2 实测结论(已用只读账号直连)

见 <code>legacy-db-actual.md</code>。核心结论:

待验证项 实测
六库同一实例,INSERT INTO ... SELECT 直接搬 ✅ 成立:六库全在张北 renniting-prod-net 一台实例上
但库名与业务域是否对应 ⚠️ 错位:zt_order 装的是 pay 域(79/86 命中),order 域在 zt_order_n;且 zt_order 里混着一份冻结于 2018 年的订单归档
重名表"预期集中在字典/配置/日志类" ⚠️ 19 组,且包含 parking_lot(4 库)、sys_org(4 库)、park_in_out_record、td_c_cust_person 等核心业务表;12 组结构不一致
库小,可直接搬 ⚠️ 709 张表 / ~19 亿行 / ~747 GB,其中 16 亿行在 zt_park

另实测确认:字符集 666/709 非 utf8mb4(含 30 张 utf8_bin + 1 张 latin1)、 分表族 228 张物理表 / 2 个族(到 2027-03)、40 张代码引用但表不存在。

迁移源已确定为张北实例(按"代码引用的表在哪个库存在"的命中矩阵判定, 87~100% 命中)。剩余需定夺的事项见该文档 §9.2。

合库时的逐表处置见 <code>merge-conflict-plan.md</code>: 19 组重名表每张保留哪一份、哪些副本可丢弃、哪些必须先查调用方。

废弃/备份表的逐张确认见 <code>../docs/废弃表逐张确认.md</code> (由 <code>tools/audit_junk_tables.py</code> 生成,可随时重跑)。 61 张嫌疑表逐张给了精确行数 + 代码引用 + 建表时间;处置结论见 <code>legacy-db-actual.md</code> §8.3 / §10.1。

合库怎么动手见 <code>migration/README.md</code>: 该目录的产物(排除清单 / 并集补行 / 快速校验 99 / 逐表对账 98 / 抽样校验 97) 全部从判定表 import 现算,不存在第二份人工清单——重名表副本来自 tools/check_subset.py 的 CHECKS,拍板不搬的表来自 tools/audit_junk_tables.py 的 DO_NOT_MIGRATE (7 张 = 空表 4 + 有数据的备份 3,后者约 133 GB、由业务方拍板,两层证据强度不同); 改了任一处必须重跑 tools/gen_migration_sql.py。它已在本地用 zt_sys 彩排通过 (见该文档 §4.1)。

⚠️ 该目录有两条互斥的路,别拿错尺子(详见其 §0.1):

主张 走哪条
全量迁移(生产切换) 源库多少行、目标库就多少行 ②③ → ④⑤⑥(98 / 99)
抽样开发库(SAMPLE=N,本机开发) 每张表都在、列结构一致、每张表都有样例行 ②③ → ⑦(97_verify_sample.sql)

98 / 99 / 20 的判据是"行数相等",跑到抽样镜像上必然全红——三份文件头 都加了告示。反过来说,97 也不能用在生产切换上(它允许每表只有 500 行)。 当前 parking_cloud 里是本机开发用的抽样镜像(SAMPLE=500,679 张表), 所以现在能跑、也该跑的是 97。

5. P1 新建表的脚本与执行顺序

本地从零起库:

mysql -h127.0.0.1 -uroot < 00_create_database.sql   # 建 parking_cloud(utf8mb4_general_ci)
mysql -h127.0.0.1 -uroot parking_cloud < 01_system_schema.sql   # 13 张 system_* 表
mysql -h127.0.0.1 -uroot parking_cloud < 02_system_seed.sql     # 种子数据
mysql -h127.0.0.1 -uroot parking_cloud < 03_legacy_schema.sql   # 679 张存量表结构(生成物)
# 可选:让列表页有数据可看。内含真实生产数据抽样,见 sql/DEPLOY.md §4
mysql -h127.0.0.1 -uroot parking_cloud < 04_legacy_sample.sql

03 / 04 是生成物(由 tools/gen_deploy_sql.sh 从本地镜像库导出), 用途是「拿一份能装到别的服务器上的部署包」,完整说明与验收证据见 <code>DEPLOY.md</code>。 生成物不要手改——改了下次重跑就没了。

本地 parking_cloud 已于 2026-10-10 用这套脚本重建(692 张表、0 处 0900_ai_ci), 所以现在本地库与部署包是同一个状态:本地开发怎么跑,别的服务器上就怎么跑。 重建前本地镜像有 663 / 692 张表的排序规则是 0900_ai_ci(跨表 JOIN 会报 Illegal mix of collations),已一并修掉。

两个脚本都是幂等的(CREATE TABLE IF NOT EXISTS / INSERT IGNORE + 显式主键),可重复执行。

01_system_schema.sql 建的是 新表(system_*,沿用 yudao 风格表名),与 P2 才灌入的存量表并存, 存量表不改名。新表统一带 del_flag(与存量库相反,见 §2——存量表没有这一列)。

三个容易误判的点

点 事实
组织表叫什么 system_org,不是 yudao 的 system_dept。语义就是「部门」,用户的 orgId 与登录信息里的 deptId 是同一个值
新建角色的数据范围 system_role.data_scope 默认 5(仅本人),刻意取最小值,避免新建角色静默拿到全量数据。1 全部 / 2 自定义 / 3 本组织 / 4 本组织及以下 / 5 仅本人
登录日志的取值 log_type:1 账号登录 / 2 社交登录 / 3 自己登出 / 4 被删除登出;result:0 成功 / 10 密码错 / 11 验证码错 / 12 账号禁用。user_id 在登录失败时为空

6. 连接配置

  • 本地:src/main/resources/application-local.yaml(Druid,root 无密码,见该文件的注释)
  • 环境配置一律走环境变量(DB_HOST / DB_PORT / DB_NAME / DB_USERNAME / DB_PASSWORD), 严禁把生产密码写进配置文件后提交(项目红线第 11 条)