dispatch_refactor_migrate.sql 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463
  1. -- 历史迁移保留旧数字编码(3已解除/4不实事件/5待验收)。
  2. -- 完成本脚本后,部署新版应用前执行 migration_dispatch_event_status.sql。
  3. -- 已升级为新版数字编码的数据库不得重跑本历史迁移。
  4. -- =====================================================================
  5. -- 应急与指挥调度模块(dispatch)改造脚本 —— 存量数据搬迁
  6. -- 数据库:KingbaseES(PG 兼容模式),业务 schema = app_user
  7. --
  8. -- ⚠️ 使用前提:**只在有存量数据的环境执行**。
  9. -- 本脚本依赖旧表 / 旧列(dis_event.source、dis_event."level"、dis_event_report、
  10. -- dis_event_verify、dis_event_transfer_log、dis_event_track_log、
  11. -- dis_event_urge(_detail)、dis_event_plan、dis_plan_approval 等)。
  12. -- 全新库请勿执行 —— 所有块都用 to_regclass / information_schema 做了存在性判断,
  13. -- 缺失时自动跳过,但空跑一遍也没有意义。
  14. --
  15. -- 执行顺序:先 dispatch_refactor.sql(建表 + 加列 + 字典),再执行本脚本。
  16. --
  17. -- 幂等性:所有 INSERT 带 NOT EXISTS 去重;所有 UPDATE 带状态判断,可重复执行。
  18. -- 破坏性:无。本脚本不删除任何旧表、旧列、旧数据。
  19. --
  20. -- ⚠️ 执行注意(KingbaseES 客户端 · 事务挂起):
  21. -- 本脚本按「一 / 二 / 三 / 四」分成 4 个独立事务(每段 END $$; 后即 COMMIT;),
  22. -- 某段失败只回滚该段,前面已成功的段落保持落库。
  23. -- 但失败后当前连接仍处于 aborted 事务中,此后**所有**查询(包括只读 SELECT)
  24. -- 都会报「ERROR: 当前事务被终止, 事务块结束之前的查询被忽略」。
  25. -- 处理办法:先单独执行 ROLLBACK; 再排查失败语句;不要反复重跑。
  26. --
  27. -- ⚠️ 执行注意(客户端把注释行与语句拼在一起):
  28. -- 部分客户端在「选中运行」时会把紧邻的 `--` 注释行并入语句并吃掉一个减号,
  29. -- 报 `ERROR: 语法错误 在 "." 或附近的`。逐条执行时请**只选中 SQL 本体**。
  30. --
  31. -- ⚠️⚠️ 方言红线(本实例实测,务必遵守):**不要用 `||` 做字符串拼接**。
  32. -- 本实例的 KingbaseES 处于 MySQL 兼容语义,`||` 被解析为**逻辑或(boolean OR)**
  33. -- 而不是字符串连接符。实测:
  34. -- SELECT 'GJ' || '20261014' || '001'; -- 返回 boolean t (错)
  35. -- SELECT concat('GJ','20261014','001'); -- 返回 GJ20261014001(对)
  36. -- 后果:`SET grade_no = 'GJ' || to_char(...)` 会把 16 行全写成一个值 `'true'`,
  37. -- 第 2 行即撞唯一索引 uk_dis_event_grade_no,报
  38. -- ERROR: 重复键违反唯一约束 / DETAIL: 键值 "(grade_no)=(true)" 已经存在。
  39. -- 凡拼接一律改用 concat(a, b, c);需要定宽时配 lpad(CAST(x AS varchar), n, '0')。
  40. -- =====================================================================
  41. -- =====================================================================
  42. -- 零、前置体检(只读,先跑这里看一眼输出再往下)
  43. -- 作用:把「搬迁源还在不在」显式喊出来。
  44. -- 本脚本内部的搬迁块都用 to_regclass 做存在性判断,旧表/旧列不存在时**静默跳过**,
  45. -- 这是为了幂等;但代价是:若 drop 脚本被提前执行(顺序颠倒),
  46. -- 整个搬迁会一声不响地全部跳过,脚本却显示"成功"。
  47. -- 本段会在那种情况下直接抛 WARNING,避免数据悄悄丢。
  48. -- =====================================================================
  49. DO $$
  50. DECLARE
  51. n_old int;
  52. src text;
  53. BEGIN
  54. SELECT count(*), string_agg(t.n, ', ' ORDER BY t.n)
  55. INTO n_old, src
  56. FROM (VALUES ('dis_event_report'), ('dis_event_verify'), ('dis_event_transfer'),
  57. ('dis_event_transfer_log'), ('dis_event_track'), ('dis_event_track_log'),
  58. ('dis_event_urge'), ('dis_event_urge_detail'), ('dis_event_record'),
  59. ('dis_event_plan'), ('dis_plan_approval'), ('dis_plan_approval_step'),
  60. ('dis_plan_category')
  61. ) AS t(n)
  62. WHERE to_regclass(concat('app_user.', t.n)) IS NOT NULL;
  63. IF n_old = 0 THEN
  64. RAISE WARNING '搬迁源体检:未检测到任何旧表,本脚本将【不搬迁任何数据】(所有段都会跳过,但脚本仍显示成功)。';
  65. RAISE WARNING '可能原因:drop 脚本被提前执行,旧表已被删除 —— 被删的旧表正是本脚本唯一的数据来源。';
  66. RAISE WARNING '正确顺序是 建表 -> migrate(本脚本) -> 业务验证 -> drop;顺序颠倒会永久丢失存量数据。';
  67. ELSE
  68. RAISE NOTICE '搬迁源体检:检测到 % 张旧表(%),将执行搬迁。', n_old, src;
  69. END IF;
  70. IF to_regclass('app_user.dis_event_grade') IS NOT NULL
  71. AND EXISTS (SELECT 1 FROM information_schema.columns
  72. WHERE table_schema = 'app_user' AND table_name = 'dis_event_grade'
  73. AND column_name = 'category') THEN
  74. RAISE NOTICE 'detected: dis_event_grade 旧列 category / level 尚在,第 4.1 步可正常搬值。';
  75. ELSE
  76. RAISE NOTICE 'dis_event_grade 旧列 category / level 已不存在(或表不存在),第 4.1 步会跳过,event_category / event_level 将保持为空。';
  77. END IF;
  78. END $$;
  79. BEGIN;
  80. -- =====================================================================
  81. -- 一、dis_event 状态归一 + 旧列搬迁
  82. -- 旧 status(中文串):待受理/待核实/已核实/处置中/已解除/已关闭/不实事件
  83. -- 新 status(int2) :0 待核实 / 1 已核实 / 2 处置中 / 3 已解除 / 4 不实事件
  84. -- =====================================================================
  85. DO $$
  86. DECLARE
  87. has_source boolean;
  88. has_level boolean;
  89. has_category boolean;
  90. has_close_remark boolean;
  91. BEGIN
  92. SELECT EXISTS (SELECT 1 FROM information_schema.columns
  93. WHERE table_schema = 'app_user' AND table_name = 'dis_event' AND column_name = 'source')
  94. INTO has_source;
  95. SELECT EXISTS (SELECT 1 FROM information_schema.columns
  96. WHERE table_schema = 'app_user' AND table_name = 'dis_event' AND column_name = 'level')
  97. INTO has_level;
  98. SELECT EXISTS (SELECT 1 FROM information_schema.columns
  99. WHERE table_schema = 'app_user' AND table_name = 'dis_event' AND column_name = 'category')
  100. INTO has_category;
  101. SELECT EXISTS (SELECT 1 FROM information_schema.columns
  102. WHERE table_schema = 'app_user' AND table_name = 'dis_event' AND column_name = 'close_remark')
  103. INTO has_close_remark;
  104. -- 1.1 状态归一:只处理仍是中文串的行,重复执行不会二次改写
  105. UPDATE app_user.dis_event SET status = '0' WHERE status::text IN ('待受理', '待核实');
  106. UPDATE app_user.dis_event SET status = '1' WHERE status::text = '已核实';
  107. UPDATE app_user.dis_event SET status = '2' WHERE status::text = '处置中';
  108. UPDATE app_user.dis_event SET status = '3' WHERE status::text IN ('已解除', '已关闭');
  109. UPDATE app_user.dis_event SET status = '4' WHERE status::text = '不实事件';
  110. -- 兜底:任何未识别的历史状态归 0(待核实),避免后续类型转换失败
  111. UPDATE app_user.dis_event SET status = '0' WHERE status::text !~ '^[0-9]+$';
  112. -- 1.1b 列类型收敛:varchar → int2
  113. -- 必须在状态归一之后执行,否则中文值无法直接转型。
  114. -- 已是 int2 的库跳过。
  115. IF EXISTS (SELECT 1 FROM information_schema.columns
  116. WHERE table_schema = 'app_user' AND table_name = 'dis_event'
  117. AND column_name = 'status' AND data_type <> 'smallint') THEN
  118. EXECUTE 'ALTER TABLE app_user.dis_event ALTER COLUMN status TYPE int2 USING status::int2';
  119. END IF;
  120. -- 1.2 旧列 → 新列(仅当新列为空)
  121. IF has_source THEN
  122. EXECUTE 'UPDATE app_user.dis_event SET event_source = source WHERE event_source IS NULL AND source IS NOT NULL';
  123. EXECUTE $q$UPDATE app_user.dis_event SET event_source = '1' WHERE event_source = '巡查上报'$q$;
  124. EXECUTE $q$UPDATE app_user.dis_event SET event_source = '2' WHERE event_source = '呼叫中心'$q$;
  125. EXECUTE $q$UPDATE app_user.dis_event SET event_source = '3' WHERE event_source = '视频监控'$q$;
  126. EXECUTE $q$UPDATE app_user.dis_event SET event_source = '4' WHERE event_source = '上级下达'$q$;
  127. END IF;
  128. IF has_level THEN
  129. EXECUTE 'UPDATE app_user.dis_event SET event_level = "level" WHERE event_level IS NULL AND "level" IS NOT NULL';
  130. -- 旧 4 级 → 新 3 级口径映射;执行前请按业务确认,以下为默认映射
  131. EXECUTE $q$UPDATE app_user.dis_event SET event_level = '1' WHERE event_level IN ('特别重大', '重大', '一级', 'Ⅰ级', '紧急')$q$;
  132. EXECUTE $q$UPDATE app_user.dis_event SET event_level = '2' WHERE event_level IN ('较大', '二级', 'Ⅱ级')$q$;
  133. EXECUTE $q$UPDATE app_user.dis_event SET event_level = '3' WHERE event_level IN ('一般', '三级', '四级', 'Ⅲ级', 'Ⅳ级')$q$;
  134. END IF;
  135. IF has_category THEN
  136. EXECUTE 'UPDATE app_user.dis_event SET event_category = category WHERE event_category IS NULL AND category IS NOT NULL';
  137. EXECUTE $q$UPDATE app_user.dis_event SET event_category = '1' WHERE event_category = '管道泄漏'$q$;
  138. EXECUTE $q$UPDATE app_user.dis_event SET event_category = '2' WHERE event_category IN ('路面积水', '积水')$q$;
  139. EXECUTE $q$UPDATE app_user.dis_event SET event_category = '3' WHERE event_category IN ('设施损坏', '塌陷')$q$;
  140. EXECUTE $q$UPDATE app_user.dis_event SET event_category = '4' WHERE event_category IN ('管道堵塞', '堵塞')$q$;
  141. END IF;
  142. -- 1.3 解除说明兜底
  143. IF has_close_remark THEN
  144. EXECUTE 'UPDATE app_user.dis_event SET release_note = close_remark WHERE release_note IS NULL AND close_remark IS NOT NULL';
  145. END IF;
  146. -- 1.4 状态为终态但缺少时间列的兜底
  147. UPDATE app_user.dis_event SET dispose_time = COALESCE(dispose_time, report_time)
  148. WHERE status IN (2, 3) AND dispose_time IS NULL;
  149. UPDATE app_user.dis_event SET release_time = COALESCE(release_time, update_time, report_time)
  150. WHERE status = 3 AND release_time IS NULL;
  151. END $$;
  152. -- 分段提交:第一段成功即落库;后续段落失败时不会把本段一起回滚。
  153. COMMIT;
  154. BEGIN;
  155. -- =====================================================================
  156. -- 二、各子表 → dis_event_log
  157. -- 旧子表保留不动;待新结构验证通过后由 dispatch_refactor_drop.sql 处理。
  158. -- 对应关系:
  159. -- dis_event_report → oper_type 0 接报登记
  160. -- dis_event_verify → oper_type 1/2 初判核实 / 不实核销
  161. -- dis_event_transfer_log → oper_type 3/4 电话转接 / 上报报送
  162. -- dis_event_track_log → oper_type 6 处置跟踪
  163. -- dis_event_urge_detail → oper_type 8 工作催办
  164. -- 由状态轨迹推导 → oper_type 5 开始处置 / 7 事件解除
  165. -- =====================================================================
  166. DO $$
  167. BEGIN
  168. -- 2.1 接报登记
  169. IF to_regclass('app_user.dis_event_report') IS NOT NULL THEN
  170. INSERT INTO app_user.dis_event_log
  171. (event_id, event_no, oper_type, oper_desc, status_to, oper_time, oper_user_name)
  172. SELECT e.id, r.event_no, 0, '接报登记(历史数据迁移)', 0, r.report_time, r.reporter
  173. FROM app_user.dis_event_report r
  174. JOIN app_user.dis_event e ON e.event_no = r.event_no
  175. WHERE r.event_no IS NOT NULL
  176. AND NOT EXISTS (SELECT 1 FROM app_user.dis_event_log l
  177. WHERE l.event_no = r.event_no AND l.oper_type = 0);
  178. END IF;
  179. -- 2.2 初判核实 / 不实核销
  180. IF to_regclass('app_user.dis_event_verify') IS NOT NULL THEN
  181. INSERT INTO app_user.dis_event_log
  182. (event_id, event_no, oper_type, oper_desc, status_from, status_to, oper_time, oper_user_name)
  183. SELECT e.id, v.event_no,
  184. CASE WHEN v.is_real = '不实' THEN 2 ELSE 1 END,
  185. COALESCE(v.opinion, '初判核实(历史数据迁移)'),
  186. 0, CASE WHEN v.is_real = '不实' THEN 4 ELSE 1 END,
  187. v.verify_time, v.verifier
  188. FROM app_user.dis_event_verify v
  189. JOIN app_user.dis_event e ON e.event_no = v.event_no
  190. WHERE v.is_real IS NOT NULL
  191. AND NOT EXISTS (SELECT 1 FROM app_user.dis_event_log l
  192. WHERE l.event_no = v.event_no AND l.oper_type IN (1, 2));
  193. END IF;
  194. -- 2.3 电话转接 / 上报报送
  195. IF to_regclass('app_user.dis_event_transfer_log') IS NOT NULL THEN
  196. INSERT INTO app_user.dis_event_log
  197. (event_id, event_no, oper_type, oper_desc, targets, oper_time)
  198. SELECT e.id, l.event_no,
  199. CASE WHEN l.action_type = '报送' THEN 4 ELSE 3 END,
  200. COALESCE(l.content, '转接报送(历史数据迁移)'),
  201. l.action_target, l.action_time
  202. FROM app_user.dis_event_transfer_log l
  203. JOIN app_user.dis_event e ON e.event_no = l.event_no
  204. WHERE l.event_no IS NOT NULL
  205. AND NOT EXISTS (SELECT 1 FROM app_user.dis_event_log x
  206. WHERE x.event_no = l.event_no AND x.oper_type IN (3, 4)
  207. AND COALESCE(x.oper_time, TIMESTAMP '1970-01-01') = l.action_time);
  208. END IF;
  209. -- 2.4 处置跟踪
  210. IF to_regclass('app_user.dis_event_track_log') IS NOT NULL THEN
  211. INSERT INTO app_user.dis_event_log
  212. (event_id, event_no, oper_type, oper_desc, trend, need_upgrade, oper_time)
  213. SELECT e.id, l.event_no, 6,
  214. COALESCE(l.content, '处置跟踪(历史数据迁移)'),
  215. l.assessment,
  216. CASE WHEN l.need_upgrade = '是' THEN 1 ELSE 0 END,
  217. l.action_time
  218. FROM app_user.dis_event_track_log l
  219. JOIN app_user.dis_event e ON e.event_no = l.event_no
  220. WHERE l.event_no IS NOT NULL
  221. AND NOT EXISTS (SELECT 1 FROM app_user.dis_event_log x
  222. WHERE x.event_no = l.event_no AND x.oper_type = 6
  223. AND COALESCE(x.oper_time, TIMESTAMP '1970-01-01') = l.action_time);
  224. END IF;
  225. -- 2.5 工作催办
  226. IF to_regclass('app_user.dis_event_urge_detail') IS NOT NULL
  227. AND to_regclass('app_user.dis_event_urge') IS NOT NULL THEN
  228. INSERT INTO app_user.dis_event_log
  229. (event_id, event_no, oper_type, oper_desc, targets, receiver_dept, receiver_phone,
  230. method, urge_result, oper_time)
  231. SELECT e.id, d.event_no, 8,
  232. COALESCE(d.content, '工作催办(历史数据迁移)'),
  233. u.receiver, u.receiver_dept, u.receiver_phone,
  234. d.method,
  235. CASE d.result WHEN '已反馈' THEN '1' WHEN '未反馈' THEN '0' ELSE NULL END,
  236. d.urge_time
  237. FROM app_user.dis_event_urge_detail d
  238. JOIN app_user.dis_event_urge u ON u.id = d.urge_id
  239. JOIN app_user.dis_event e ON e.event_no = d.event_no
  240. WHERE d.event_no IS NOT NULL
  241. AND NOT EXISTS (SELECT 1 FROM app_user.dis_event_log x
  242. WHERE x.event_no = d.event_no AND x.oper_type = 8
  243. AND COALESCE(x.oper_time, TIMESTAMP '1970-01-01') = d.urge_time);
  244. END IF;
  245. -- 2.6 由状态轨迹推导「开始处置」
  246. -- 旧模型没有「开始处置时间」列,取最早一条处置跟踪日志的时间作代理;
  247. -- 无跟踪日志时回退到解除时间 / 接报时间。
  248. IF to_regclass('app_user.dis_event_track') IS NOT NULL
  249. AND to_regclass('app_user.dis_event_track_log') IS NOT NULL THEN
  250. INSERT INTO app_user.dis_event_log
  251. (event_id, event_no, oper_type, oper_desc, status_from, status_to, oper_time)
  252. SELECT e.id, e.event_no, 5, '开始处置(历史数据迁移)', 1, 2,
  253. COALESCE(m.first_track, e.release_time, e.report_time)
  254. FROM app_user.dis_event e
  255. LEFT JOIN (
  256. SELECT t.rel_event_id, MIN(l.action_time) AS first_track
  257. FROM app_user.dis_event_track t
  258. JOIN app_user.dis_event_track_log l ON l.track_id = t.id
  259. GROUP BY t.rel_event_id
  260. ) m ON m.rel_event_id = e.id
  261. WHERE e.status IN (2, 3)
  262. AND NOT EXISTS (SELECT 1 FROM app_user.dis_event_log x
  263. WHERE x.event_id = e.id AND x.oper_type = 5);
  264. END IF;
  265. -- 2.7 由状态轨迹推导「事件解除」
  266. INSERT INTO app_user.dis_event_log
  267. (event_id, event_no, oper_type, oper_desc, status_from, status_to, oper_time)
  268. SELECT e.id, e.event_no, 7,
  269. COALESCE(e.release_note, '事件解除(历史数据迁移)'), 2, 3, e.release_time
  270. FROM app_user.dis_event e
  271. WHERE e.status = 3 AND e.release_time IS NOT NULL
  272. AND NOT EXISTS (SELECT 1 FROM app_user.dis_event_log x
  273. WHERE x.event_id = e.id AND x.oper_type = 7);
  274. END $$;
  275. -- 分段提交:第二段成功即落库。
  276. COMMIT;
  277. BEGIN;
  278. -- =====================================================================
  279. -- 三、预案域:dis_event_plan + dis_plan_approval → dis_plan + dis_plan_version
  280. -- =====================================================================
  281. DO $$
  282. BEGIN
  283. IF to_regclass('app_user.dis_event_plan') IS NOT NULL THEN
  284. -- 3.1 主表搬迁;plan_no 用业务序号格式回填(YA + yyyyMMdd + 4 位补零序列)
  285. INSERT INTO app_user.dis_plan
  286. (plan_no, plan_name, plan_type, plan_level, compile_unit, publish_unit, publish_date,
  287. is_template, status, version, content, create_by, create_time)
  288. -- 拼接必须用 concat():本实例 `||` 是 boolean OR,详见文件头「方言红线」
  289. SELECT concat('YA', to_char(CURRENT_TIMESTAMP, 'YYYYMMDD'),
  290. lpad(CAST(t.rn AS varchar), 4, '0')),
  291. t.plan_name, t.plan_type, t.plan_level, t.compile_unit, t.publish_unit, t.publish_date,
  292. 0, 0, t.version, t.plan_content, t.create_by, t.create_time
  293. FROM (SELECT p.*, row_number() OVER (ORDER BY p.id) AS rn
  294. FROM app_user.dis_event_plan p
  295. WHERE NOT EXISTS (SELECT 1 FROM app_user.dis_plan d WHERE d.plan_name = p.plan_name)) t;
  296. -- 3.2 已有正文且带版本号的历史预案,补一条「当前生效」版本快照
  297. INSERT INTO app_user.dis_plan_version
  298. (plan_id, plan_no, version, content, change_note, editor, edit_time, ver_status)
  299. SELECT d.id, d.plan_no, COALESCE(NULLIF(d.version, ''), 'v1.0'), d.content,
  300. '历史数据迁移', d.create_by, d.create_time, 1
  301. FROM app_user.dis_plan d
  302. WHERE d.status = 2
  303. AND NOT EXISTS (SELECT 1 FROM app_user.dis_plan_version v WHERE v.plan_id = d.id);
  304. END IF;
  305. -- 3.3 审批表状态合并:编辑中/审批中/终审中/已发布/待修正 → 0/1/1/2/3
  306. IF to_regclass('app_user.dis_plan_approval') IS NOT NULL THEN
  307. UPDATE app_user.dis_plan d
  308. SET status = CASE ap.status
  309. WHEN '已发布' THEN 2
  310. WHEN '审批中' THEN 1
  311. WHEN '终审中' THEN 1
  312. WHEN '待修正' THEN 3
  313. ELSE d.status END,
  314. approve_time = ap.approve_time,
  315. approve_note = COALESCE(d.approve_note, ap.audit_record),
  316. approve_result = CASE ap.status
  317. WHEN '已发布' THEN '通过'
  318. WHEN '待修正' THEN '不通过'
  319. ELSE d.approve_result END
  320. FROM app_user.dis_plan_approval ap
  321. WHERE d.plan_name = ap.plan_name
  322. AND d.status = 0;
  323. -- 3.4 审批轨迹补预案日志
  324. INSERT INTO app_user.dis_plan_log
  325. (plan_id, plan_no, oper_type, oper_desc, version, status_to, oper_user_name, oper_time)
  326. SELECT d.id, d.plan_no,
  327. CASE ap.status WHEN '已发布' THEN 2 WHEN '待修正' THEN 3 ELSE 1 END,
  328. COALESCE(ap.audit_record, '历史审批记录迁移'),
  329. ap.version,
  330. CASE ap.status WHEN '已发布' THEN 2 WHEN '待修正' THEN 3 ELSE 1 END,
  331. ap.reviewers, ap.approve_time
  332. FROM app_user.dis_plan_approval ap
  333. JOIN app_user.dis_plan d ON d.plan_name = ap.plan_name
  334. WHERE NOT EXISTS (SELECT 1 FROM app_user.dis_plan_log l WHERE l.plan_id = d.id);
  335. END IF;
  336. -- 3.5 预案等级旧 4 级口径 → 新 3 级口径(执行前请按业务确认)
  337. UPDATE app_user.dis_plan SET plan_level = '1'
  338. WHERE plan_level IN ('一级', 'Ⅰ级', '特别重大', '重大', '紧急');
  339. UPDATE app_user.dis_plan SET plan_level = '2'
  340. WHERE plan_level IN ('二级', 'Ⅱ级', '较大');
  341. UPDATE app_user.dis_plan SET plan_level = '3'
  342. WHERE plan_level IN ('三级', '四级', 'Ⅲ级', 'Ⅳ级', '一般');
  343. -- 3.6 预案类型中文 → 字典编码
  344. UPDATE app_user.dis_plan SET plan_type = '1' WHERE plan_type = '综合';
  345. UPDATE app_user.dis_plan SET plan_type = '2' WHERE plan_type = '专项';
  346. UPDATE app_user.dis_plan SET plan_type = '3' WHERE plan_type = '现场处置';
  347. END $$;
  348. -- 分段提交:第三段成功即落库。
  349. COMMIT;
  350. BEGIN;
  351. -- =====================================================================
  352. -- 四、事件分级:状态改数字 + 补业务编号
  353. -- =====================================================================
  354. DO $$
  355. BEGIN
  356. -- 4.1 先把旧列 category / level 搬到新列(必须早于下面的口径映射)
  357. IF EXISTS (SELECT 1 FROM information_schema.columns
  358. WHERE table_schema = 'app_user' AND table_name = 'dis_event_grade' AND column_name = 'category') THEN
  359. EXECUTE 'UPDATE app_user.dis_event_grade SET event_category = category WHERE event_category IS NULL AND category IS NOT NULL';
  360. END IF;
  361. IF EXISTS (SELECT 1 FROM information_schema.columns
  362. WHERE table_schema = 'app_user' AND table_name = 'dis_event_grade' AND column_name = 'level') THEN
  363. EXECUTE 'UPDATE app_user.dis_event_grade SET event_level = "level" WHERE event_level IS NULL AND "level" IS NOT NULL';
  364. END IF;
  365. -- 4.2 状态下沉:启用/停用 → 0/1
  366. UPDATE app_user.dis_event_grade SET status = '0' WHERE status::text IN ('启用', '正常');
  367. UPDATE app_user.dis_event_grade SET status = '1' WHERE status::text IN ('停用', '禁用');
  368. UPDATE app_user.dis_event_grade SET status = '0' WHERE status::text !~ '^[0-9]+$';
  369. IF EXISTS (SELECT 1 FROM information_schema.columns
  370. WHERE table_schema = 'app_user' AND table_name = 'dis_event_grade'
  371. AND column_name = 'status' AND data_type <> 'smallint') THEN
  372. EXECUTE 'ALTER TABLE app_user.dis_event_grade ALTER COLUMN status TYPE int2 USING status::int2';
  373. END IF;
  374. -- 4.3 等级口径:旧 4 级 → 新 3 级
  375. UPDATE app_user.dis_event_grade SET event_level = '1'
  376. WHERE event_level IN ('一级', 'Ⅰ级', '特别重大', '重大', '紧急');
  377. UPDATE app_user.dis_event_grade SET event_level = '2'
  378. WHERE event_level IN ('二级', 'Ⅱ级', '较大');
  379. UPDATE app_user.dis_event_grade SET event_level = '3'
  380. WHERE event_level IN ('三级', '四级', 'Ⅲ级', 'Ⅳ级', '一般');
  381. -- 4.4 类别口径
  382. UPDATE app_user.dis_event_grade SET event_category = '1'
  383. WHERE event_category IN ('管网泄漏', '管道泄漏');
  384. UPDATE app_user.dis_event_grade SET event_category = '2'
  385. WHERE event_category IN ('路面积水', '积水');
  386. UPDATE app_user.dis_event_grade SET event_category = '3'
  387. WHERE event_category IN ('设施损坏', '塌陷');
  388. UPDATE app_user.dis_event_grade SET event_category = '4'
  389. WHERE event_category IN ('管道堵塞', '堵塞');
  390. -- 4.5 业务编号回填:GJ + yyyyMMdd + 3 位序号(按 id 顺序)
  391. -- 防御一:只补 grade_no IS NULL 的行,可重复执行。
  392. -- 防御二:运行期取号(DispatchNumberSupport.nextGradeNo = GJ+yyyyMMdd+3位随机)
  393. -- 可能已经占用了同样的编号,这里逐个比对,撞号的候选值直接跳过
  394. -- (该行保持 NULL,由页面人工补号),避免 uk_dis_event_grade_no 报重复键。
  395. -- 注意:CURRENT_TIMESTAMP 在单个事务内是常量,同批回填的日期段一致。
  396. -- 注意:拼接必须用 concat()。本实例 `||` 被解析为 boolean OR,
  397. -- 写成 'GJ' || to_char(...) || lpad(...) 会让整列都变成 'true'(详见文件头)。
  398. UPDATE app_user.dis_event_grade g
  399. SET grade_no = c.new_no
  400. FROM (SELECT t.id,
  401. concat('GJ', to_char(CURRENT_TIMESTAMP, 'YYYYMMDD'),
  402. lpad(CAST(t.rn AS varchar), 3, '0')) AS new_no
  403. FROM (SELECT id, row_number() OVER (ORDER BY id) AS rn
  404. FROM app_user.dis_event_grade
  405. WHERE grade_no IS NULL) t) c
  406. WHERE g.id = c.id
  407. AND NOT EXISTS (SELECT 1 FROM app_user.dis_event_grade x
  408. WHERE x.grade_no = c.new_no);
  409. END $$;
  410. COMMIT;
  411. -- =====================================================================
  412. -- 五、迁移结果自检
  413. -- =====================================================================
  414. -- 1) 事件状态分布:只应是 0~4
  415. -- SELECT status, count(*) FROM app_user.dis_event GROUP BY status ORDER BY status;
  416. -- 2) 事件主表编号唯一性
  417. -- SELECT event_no, count(*) FROM app_user.dis_event GROUP BY event_no HAVING count(*) > 1;
  418. -- 3) 日志迁移覆盖率:有事件但无日志的异常数据
  419. -- SELECT e.id, e.event_no FROM app_user.dis_event e
  420. -- WHERE NOT EXISTS (SELECT 1 FROM app_user.dis_event_log l WHERE l.event_id = e.id);
  421. -- 4) 各操作类型日志条数
  422. -- SELECT oper_type, count(*) FROM app_user.dis_event_log GROUP BY oper_type ORDER BY oper_type;
  423. -- 5) 预案:有主表但无版本快照的已通过预案
  424. -- SELECT d.id, d.plan_no, d.status FROM app_user.dis_plan d
  425. -- WHERE d.status = 2 AND NOT EXISTS (SELECT 1 FROM app_user.dis_plan_version v WHERE v.plan_id = d.id);
  426. -- 6) 分级标准编号回填情况
  427. -- SELECT count(*) AS total, count(grade_no) AS with_no FROM app_user.dis_event_grade;