dispatch_refactor_migrate.sql 26 KB

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