Pārlūkot izejas kodu

docs(sql): add QC inspection item template table and menu.

Co-authored-by: Cursor <cursoragent@cursor.com>
zlwu 2 mēneši atpakaļ
vecāks
revīzija
15af9894bc

+ 322 - 0
docs/sql/alter_biz_qc_inspection_item_template.sql

@@ -0,0 +1,322 @@
+-- 质检:检测项目模版(建表 + C 菜单 + 4 F 按钮 + 角色授权)
+-- 幂等:可重复执行
+-- 授权:已有【质检基础信息】QCB 下任一有效子菜单,或已有质检工单菜单的角色
+--       → 授予 TOP + QCB + 本 C/F
+
+SET @tenant_no = CONVERT('zkxw' USING utf8mb4) COLLATE utf8mb4_unicode_ci;
+SET @top_id = CONVERT('TOP00000000000000000000000000003' USING utf8mb4) COLLATE utf8mb4_unicode_ci;
+SET @qcb_id = CONVERT('QCB00000000000000000000000000001' USING utf8mb4) COLLATE utf8mb4_unicode_ci;
+SET @wo_menu = CONVERT('70000000000000000000000000000007' USING utf8mb4) COLLATE utf8mb4_unicode_ci;
+SET @menu_c = CONVERT('95300000000000000000000000000001' USING utf8mb4) COLLATE utf8mb4_unicode_ci;
+SET @menu_f_list = CONVERT('95300000000000000000000000000002' USING utf8mb4) COLLATE utf8mb4_unicode_ci;
+SET @menu_f_add = CONVERT('95300000000000000000000000000003' USING utf8mb4) COLLATE utf8mb4_unicode_ci;
+SET @menu_f_edit = CONVERT('95300000000000000000000000000004' USING utf8mb4) COLLATE utf8mb4_unicode_ci;
+SET @menu_f_remove = CONVERT('95300000000000000000000000000005' USING utf8mb4) COLLATE utf8mb4_unicode_ci;
+
+-- 0) 建表
+CREATE TABLE IF NOT EXISTS `biz_qc_inspection_item_template` (
+  `id` varchar(64) NOT NULL COMMENT '主键ID',
+  `template_name` varchar(200) NOT NULL COMMENT '模版名称',
+  `lab_item_ids` varchar(2000) NOT NULL DEFAULT '' COMMENT '实验室项目ID逗号分隔',
+  `third_party_item_ids` varchar(2000) NOT NULL DEFAULT '' COMMENT '第三方项目ID逗号分隔',
+  `status` char(1) NOT NULL DEFAULT '1' COMMENT '1启用 0停用',
+  `tenant_no` varchar(64) NOT NULL DEFAULT 'zkxw' COMMENT '租户编号',
+  `order_num` int(11) DEFAULT NULL COMMENT '排序号',
+  `remark` varchar(512) DEFAULT NULL COMMENT '备注',
+  `create_by` varchar(64) DEFAULT NULL COMMENT '创建人id',
+  `create_dept_id` varchar(64) NOT NULL DEFAULT '' COMMENT '创建人部门ID',
+  `create_time` datetime NOT NULL COMMENT '创建时间',
+  `update_by` varchar(64) DEFAULT NULL COMMENT '更新人id',
+  `update_time` datetime NOT NULL COMMENT '更新时间',
+  `del_flag` tinyint(4) NOT NULL DEFAULT '0' COMMENT '删除标记',
+  PRIMARY KEY (`id`) USING BTREE,
+  KEY `idx_qc_iit_tenant_status` (`tenant_no`,`del_flag`,`status`) USING BTREE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='质检检测项目模版';
+
+-- 1) C 菜单
+INSERT INTO sys_menu (
+  id, menu_name, parent_id, menu_type, path, component, perms,
+  is_frame, visible, status, tenant_no, order_num, remark,
+  create_by, create_dept_id, create_time, update_by, update_time, del_flag
+)
+SELECT
+  @menu_c,
+  '检测项目模版',
+  @qcb_id,
+  'C',
+  'inspection-item-template',
+  '/quality-inspection/inspection-item-template/index',
+  'biz:qcInspectionItemTemplate:list',
+  1, '1', '1', @tenant_no, 5,
+  '质检基础信息:检测项目模版主数据',
+  NULL, '', NOW(), NULL, NOW(), 0
+FROM DUAL
+WHERE NOT EXISTS (
+  SELECT 1 FROM sys_menu m
+  WHERE m.id COLLATE utf8mb4_unicode_ci = @menu_c
+);
+
+UPDATE sys_menu
+SET
+  menu_name = '检测项目模版',
+  parent_id = @qcb_id,
+  menu_type = 'C',
+  path = 'inspection-item-template',
+  component = '/quality-inspection/inspection-item-template/index',
+  perms = 'biz:qcInspectionItemTemplate:list',
+  visible = '1',
+  status = '1',
+  order_num = 5,
+  remark = '质检基础信息:检测项目模版主数据',
+  del_flag = 0,
+  update_time = NOW()
+WHERE id COLLATE utf8mb4_unicode_ci = @menu_c;
+
+-- 2) F 按钮:list
+INSERT INTO sys_menu (
+  id, menu_name, parent_id, menu_type, path, component, perms,
+  is_frame, visible, status, tenant_no, order_num, remark,
+  create_by, create_dept_id, create_time, update_by, update_time, del_flag
+)
+SELECT
+  @menu_f_list,
+  '检测项目模版查询',
+  @menu_c,
+  'F',
+  '',
+  '',
+  'biz:qcInspectionItemTemplate:list',
+  1, '0', '1', @tenant_no, 1,
+  '检测项目模版列表查询',
+  NULL, '', NOW(), NULL, NOW(), 0
+FROM DUAL
+WHERE NOT EXISTS (
+  SELECT 1 FROM sys_menu m
+  WHERE m.id COLLATE utf8mb4_unicode_ci = @menu_f_list
+);
+
+UPDATE sys_menu
+SET
+  menu_name = '检测项目模版查询',
+  parent_id = @menu_c,
+  menu_type = 'F',
+  path = '',
+  component = '',
+  perms = 'biz:qcInspectionItemTemplate:list',
+  visible = '0',
+  status = '1',
+  order_num = 1,
+  remark = '检测项目模版列表查询',
+  del_flag = 0,
+  update_time = NOW()
+WHERE id COLLATE utf8mb4_unicode_ci = @menu_f_list;
+
+-- 2b) F 按钮:add
+INSERT INTO sys_menu (
+  id, menu_name, parent_id, menu_type, path, component, perms,
+  is_frame, visible, status, tenant_no, order_num, remark,
+  create_by, create_dept_id, create_time, update_by, update_time, del_flag
+)
+SELECT
+  @menu_f_add,
+  '检测项目模版新增',
+  @menu_c,
+  'F',
+  '',
+  '',
+  'biz:qcInspectionItemTemplate:add',
+  1, '0', '1', @tenant_no, 2,
+  '检测项目模版新增',
+  NULL, '', NOW(), NULL, NOW(), 0
+FROM DUAL
+WHERE NOT EXISTS (
+  SELECT 1 FROM sys_menu m
+  WHERE m.id COLLATE utf8mb4_unicode_ci = @menu_f_add
+);
+
+UPDATE sys_menu
+SET
+  menu_name = '检测项目模版新增',
+  parent_id = @menu_c,
+  menu_type = 'F',
+  path = '',
+  component = '',
+  perms = 'biz:qcInspectionItemTemplate:add',
+  visible = '0',
+  status = '1',
+  order_num = 2,
+  remark = '检测项目模版新增',
+  del_flag = 0,
+  update_time = NOW()
+WHERE id COLLATE utf8mb4_unicode_ci = @menu_f_add;
+
+-- 2c) F 按钮:edit
+INSERT INTO sys_menu (
+  id, menu_name, parent_id, menu_type, path, component, perms,
+  is_frame, visible, status, tenant_no, order_num, remark,
+  create_by, create_dept_id, create_time, update_by, update_time, del_flag
+)
+SELECT
+  @menu_f_edit,
+  '检测项目模版修改',
+  @menu_c,
+  'F',
+  '',
+  '',
+  'biz:qcInspectionItemTemplate:edit',
+  1, '0', '1', @tenant_no, 3,
+  '检测项目模版修改(含启停)',
+  NULL, '', NOW(), NULL, NOW(), 0
+FROM DUAL
+WHERE NOT EXISTS (
+  SELECT 1 FROM sys_menu m
+  WHERE m.id COLLATE utf8mb4_unicode_ci = @menu_f_edit
+);
+
+UPDATE sys_menu
+SET
+  menu_name = '检测项目模版修改',
+  parent_id = @menu_c,
+  menu_type = 'F',
+  path = '',
+  component = '',
+  perms = 'biz:qcInspectionItemTemplate:edit',
+  visible = '0',
+  status = '1',
+  order_num = 3,
+  remark = '检测项目模版修改(含启停)',
+  del_flag = 0,
+  update_time = NOW()
+WHERE id COLLATE utf8mb4_unicode_ci = @menu_f_edit;
+
+-- 2d) F 按钮:remove
+INSERT INTO sys_menu (
+  id, menu_name, parent_id, menu_type, path, component, perms,
+  is_frame, visible, status, tenant_no, order_num, remark,
+  create_by, create_dept_id, create_time, update_by, update_time, del_flag
+)
+SELECT
+  @menu_f_remove,
+  '检测项目模版删除',
+  @menu_c,
+  'F',
+  '',
+  '',
+  'biz:qcInspectionItemTemplate:remove',
+  1, '0', '1', @tenant_no, 4,
+  '检测项目模版删除',
+  NULL, '', NOW(), NULL, NOW(), 0
+FROM DUAL
+WHERE NOT EXISTS (
+  SELECT 1 FROM sys_menu m
+  WHERE m.id COLLATE utf8mb4_unicode_ci = @menu_f_remove
+);
+
+UPDATE sys_menu
+SET
+  menu_name = '检测项目模版删除',
+  parent_id = @menu_c,
+  menu_type = 'F',
+  path = '',
+  component = '',
+  perms = 'biz:qcInspectionItemTemplate:remove',
+  visible = '0',
+  status = '1',
+  order_num = 4,
+  remark = '检测项目模版删除',
+  del_flag = 0,
+  update_time = NOW()
+WHERE id COLLATE utf8mb4_unicode_ci = @menu_f_remove;
+
+-- 3) 目标角色:已有 QCB 下任一有效子菜单,或已有质检工单菜单
+-- MySQL 5.7:UPDATE 目标表不可在同句 FROM/EXISTS 中直接引用自身,须用派生表
+
+-- 3a) 恢复软删的 TOP / QCB / C / F 授权
+UPDATE sys_role_menu rm
+INNER JOIN sys_role r
+  ON r.id COLLATE utf8mb4_unicode_ci = rm.role_id COLLATE utf8mb4_unicode_ci
+ AND r.del_flag = 0
+ AND r.tenant_no COLLATE utf8mb4_unicode_ci = @tenant_no
+INNER JOIN (
+  SELECT DISTINCT rm2.role_id AS role_id
+  FROM sys_role_menu rm2
+  INNER JOIN sys_menu m2
+    ON m2.id COLLATE utf8mb4_unicode_ci = rm2.menu_id COLLATE utf8mb4_unicode_ci
+   AND m2.del_flag = 0
+   AND (
+     m2.parent_id COLLATE utf8mb4_unicode_ci = @qcb_id
+     OR m2.id COLLATE utf8mb4_unicode_ci = @wo_menu
+   )
+  WHERE rm2.tenant_no COLLATE utf8mb4_unicode_ci = @tenant_no
+    AND rm2.del_flag = 0
+) eligible
+  ON eligible.role_id COLLATE utf8mb4_unicode_ci = r.id COLLATE utf8mb4_unicode_ci
+SET
+  rm.del_flag = 0,
+  rm.update_time = NOW(),
+  rm.remark = 'grant qc inspection item template to QCB/WO roles'
+WHERE rm.tenant_no COLLATE utf8mb4_unicode_ci = @tenant_no
+  AND rm.del_flag = 1
+  AND rm.menu_id COLLATE utf8mb4_unicode_ci IN (
+    @top_id, @qcb_id, @menu_c,
+    @menu_f_list, @menu_f_add, @menu_f_edit, @menu_f_remove
+  );
+
+-- 3b) 插入缺失授权(TOP + QCB + C + 4 F)
+INSERT INTO sys_role_menu (
+  id, role_id, menu_id, tenant_no, order_num, remark,
+  create_by, create_dept_id, create_time, update_by, update_time, del_flag
+)
+SELECT
+  UPPER(MD5(CONCAT(src.role_id, '|', src.menu_id))),
+  src.role_id,
+  src.menu_id,
+  @tenant_no,
+  0,
+  'grant qc inspection item template to QCB/WO roles',
+  'admin',
+  '',
+  NOW(),
+  NULL,
+  NOW(),
+  0
+FROM (
+  SELECT
+    r.id AS role_id,
+    x.menu_id AS menu_id
+  FROM sys_role r
+  INNER JOIN (
+    SELECT @top_id AS menu_id
+    UNION ALL SELECT @qcb_id
+    UNION ALL SELECT @menu_c
+    UNION ALL SELECT @menu_f_list
+    UNION ALL SELECT @menu_f_add
+    UNION ALL SELECT @menu_f_edit
+    UNION ALL SELECT @menu_f_remove
+  ) x
+  INNER JOIN (
+    SELECT DISTINCT rm2.role_id AS role_id
+    FROM sys_role_menu rm2
+    INNER JOIN sys_menu m2
+      ON m2.id COLLATE utf8mb4_unicode_ci = rm2.menu_id COLLATE utf8mb4_unicode_ci
+     AND m2.del_flag = 0
+     AND (
+       m2.parent_id COLLATE utf8mb4_unicode_ci = @qcb_id
+       OR m2.id COLLATE utf8mb4_unicode_ci = @wo_menu
+     )
+    WHERE rm2.tenant_no COLLATE utf8mb4_unicode_ci = @tenant_no
+      AND rm2.del_flag = 0
+  ) eligible
+    ON eligible.role_id COLLATE utf8mb4_unicode_ci = r.id COLLATE utf8mb4_unicode_ci
+  INNER JOIN sys_menu m
+    ON m.id COLLATE utf8mb4_unicode_ci = x.menu_id
+   AND m.del_flag = 0
+  WHERE r.del_flag = 0
+    AND r.tenant_no COLLATE utf8mb4_unicode_ci = @tenant_no
+) src
+LEFT JOIN sys_role_menu existing
+  ON existing.role_id COLLATE utf8mb4_unicode_ci = src.role_id COLLATE utf8mb4_unicode_ci
+ AND existing.menu_id COLLATE utf8mb4_unicode_ci = src.menu_id COLLATE utf8mb4_unicode_ci
+ AND existing.tenant_no COLLATE utf8mb4_unicode_ci = @tenant_no
+ AND existing.del_flag = 0
+WHERE existing.id IS NULL;

+ 613 - 0
待同步到生产的sql脚本

@@ -0,0 +1,613 @@
+-- 员工微信服务号 openid / unionid 绑定字段(OA 审批通知)
+-- MySQL 5.7:唯一索引允许多个 NULL
+
+ALTER TABLE `sys_user`
+  ADD COLUMN `wx_mp_openid` varchar(64) DEFAULT NULL COMMENT '微信服务号 openid' AFTER `wx_openid`,
+  ADD COLUMN `wx_unionid` varchar(64) DEFAULT NULL COMMENT '微信开放平台 unionid' AFTER `wx_mp_openid`,
+  ADD COLUMN `wx_mp_bind_time` datetime DEFAULT NULL COMMENT '服务号绑定时间' AFTER `wx_unionid`;
+
+ALTER TABLE `sys_user`
+  ADD UNIQUE INDEX `uk_sys_user_wx_mp_openid` (`wx_mp_openid`);
+
+ALTER TABLE `sys_user`
+  ADD INDEX `idx_sys_user_wx_unionid` (`wx_unionid`);
+
+
+  -- 将「开启微信通知」(OA007) 授予全部未删除角色
+  -- 前置:已执行 docs/sql/mobile_menu_oa_wechat_notify.sql(菜单须已存在)
+  -- 幂等:可重复执行;已有有效授权跳过;软删则恢复
+  -- 说明:
+  --   1) 用户变量显式 utf8mb4_unicode_ci,避免 collation 混用报错 1267
+  --   2) 主键用 MD5(role_id|menu_id),避免 INSERT…SELECT 中 UUID() 只算一次导致重复主键
+  
+  SET @menu_id = CONVERT('MINI000000000000000000000000OA007' USING utf8mb4) COLLATE utf8mb4_unicode_ci;
+  SET @tenant_no = CONVERT('zkxw' USING utf8mb4) COLLATE utf8mb4_unicode_ci;
+  
+  -- 1) 恢复历史上已软删的授权(按 role_id + menu_id)
+  UPDATE sys_role_menu rm
+  INNER JOIN sys_role r
+    ON r.id COLLATE utf8mb4_unicode_ci = rm.role_id COLLATE utf8mb4_unicode_ci
+   AND r.del_flag = 0
+   AND r.tenant_no COLLATE utf8mb4_unicode_ci = @tenant_no
+  SET
+    rm.del_flag = 0,
+    rm.update_time = NOW(),
+    rm.remark = 'grant wechat-notify to all roles'
+  WHERE rm.menu_id COLLATE utf8mb4_unicode_ci = @menu_id
+    AND rm.tenant_no COLLATE utf8mb4_unicode_ci = @tenant_no
+    AND rm.del_flag = 1;
+  
+  -- 2) 为尚未关联该菜单的角色新增授权
+  INSERT INTO sys_role_menu (
+    id, role_id, menu_id, tenant_no, order_num, remark,
+    create_by, create_dept_id, create_time, update_by, update_time, del_flag
+  )
+  SELECT
+    UPPER(MD5(CONCAT(r.id, '|', @menu_id))),
+    r.id,
+    @menu_id,
+    @tenant_no,
+    0,
+    'grant wechat-notify to all roles',
+    'admin',
+    '',
+    NOW(),
+    NULL,
+    NOW(),
+    0
+  FROM sys_role r
+  WHERE r.del_flag = 0
+    AND r.tenant_no COLLATE utf8mb4_unicode_ci = @tenant_no
+    AND EXISTS (
+      SELECT 1 FROM sys_menu m
+      WHERE m.id COLLATE utf8mb4_unicode_ci = @menu_id
+        AND m.del_flag = 0
+    )
+    AND NOT EXISTS (
+      SELECT 1 FROM sys_role_menu rm
+      WHERE rm.role_id COLLATE utf8mb4_unicode_ci = r.id COLLATE utf8mb4_unicode_ci
+        AND rm.menu_id COLLATE utf8mb4_unicode_ci = @menu_id
+        AND rm.tenant_no COLLATE utf8mb4_unicode_ci = @tenant_no
+        AND rm.del_flag = 0
+    );
+
+    
+    -- 小程序「发起审批」菜单种子(挂在「移动端菜单」根下)
+-- 执行前请确认移动端根存在;角色授权请在 Web「角色管理」勾选本菜单。
+
+SET @mini_root = (
+  SELECT id FROM sys_menu
+  WHERE del_flag = 0 AND parent_id = '0' AND menu_name LIKE '%移动端%'
+  LIMIT 1
+);
+
+INSERT INTO sys_menu (
+  id, menu_name, parent_id, menu_type, path, component, perms,
+  is_frame, visible, status, tenant_no, order_num, remark,
+  create_by, create_dept_id, create_time, update_by, update_time, del_flag
+)
+SELECT
+  'MINI000000000000000000000000OA001',
+  '发起审批',
+  @mini_root,
+  'C',
+  '/pages/oa/start/index',
+  NULL,
+  'mini:oa:start',
+  1,
+  '1',
+  '1',
+  'zkxw',
+  10,
+  '小程序发起审批',
+  'admin',
+  '',
+  NOW(),
+  NULL,
+  NOW(),
+  0
+FROM DUAL
+WHERE @mini_root IS NOT NULL
+  AND NOT EXISTS (
+    SELECT 1 FROM sys_menu WHERE id = 'MINI000000000000000000000000OA001' AND del_flag = 0
+  );
+
+  -- 小程序 OA 五中心菜单种子(挂在「移动端菜单」根下)
+-- 前置:docs/sql/mobile_menu_oa_start.sql(发起审批 OA001)可已执行
+-- 角色授权请在 Web「角色管理」勾选本菜单。
+
+SET @mini_root = (
+  SELECT id FROM sys_menu
+  WHERE del_flag = 0 AND parent_id = '0' AND menu_name LIKE '%移动端%'
+  LIMIT 1
+);
+
+-- 草稿箱
+INSERT INTO sys_menu (
+  id, menu_name, parent_id, menu_type, path, component, perms,
+  is_frame, visible, status, tenant_no, order_num, remark,
+  create_by, create_dept_id, create_time, update_by, update_time, del_flag
+)
+SELECT
+  'MINI000000000000000000000000OA002',
+  '草稿箱',
+  @mini_root,
+  'C',
+  '/pages/oa/drafts/index',
+  NULL,
+  'mini:oa:drafts',
+  1,
+  '1',
+  '1',
+  'zkxw',
+  20,
+  '小程序草稿箱',
+  'admin',
+  '',
+  NOW(),
+  NULL,
+  NOW(),
+  0
+FROM DUAL
+WHERE @mini_root IS NOT NULL
+  AND NOT EXISTS (
+    SELECT 1 FROM sys_menu WHERE id = 'MINI000000000000000000000000OA002' AND del_flag = 0
+  );
+
+-- 待办中心
+INSERT INTO sys_menu (
+  id, menu_name, parent_id, menu_type, path, component, perms,
+  is_frame, visible, status, tenant_no, order_num, remark,
+  create_by, create_dept_id, create_time, update_by, update_time, del_flag
+)
+SELECT
+  'MINI000000000000000000000000OA003',
+  '待办中心',
+  @mini_root,
+  'C',
+  '/pages/oa/pending/index',
+  NULL,
+  'mini:oa:pending',
+  1,
+  '1',
+  '1',
+  'zkxw',
+  30,
+  '小程序待办中心',
+  'admin',
+  '',
+  NOW(),
+  NULL,
+  NOW(),
+  0
+FROM DUAL
+WHERE @mini_root IS NOT NULL
+  AND NOT EXISTS (
+    SELECT 1 FROM sys_menu WHERE id = 'MINI000000000000000000000000OA003' AND del_flag = 0
+  );
+
+-- 已办中心
+INSERT INTO sys_menu (
+  id, menu_name, parent_id, menu_type, path, component, perms,
+  is_frame, visible, status, tenant_no, order_num, remark,
+  create_by, create_dept_id, create_time, update_by, update_time, del_flag
+)
+SELECT
+  'MINI000000000000000000000000OA004',
+  '已办中心',
+  @mini_root,
+  'C',
+  '/pages/oa/done/index',
+  NULL,
+  'mini:oa:done',
+  1,
+  '1',
+  '1',
+  'zkxw',
+  40,
+  '小程序已办中心',
+  'admin',
+  '',
+  NOW(),
+  NULL,
+  NOW(),
+  0
+FROM DUAL
+WHERE @mini_root IS NOT NULL
+  AND NOT EXISTS (
+    SELECT 1 FROM sys_menu WHERE id = 'MINI000000000000000000000000OA004' AND del_flag = 0
+  );
+
+-- 我发起的
+INSERT INTO sys_menu (
+  id, menu_name, parent_id, menu_type, path, component, perms,
+  is_frame, visible, status, tenant_no, order_num, remark,
+  create_by, create_dept_id, create_time, update_by, update_time, del_flag
+)
+SELECT
+  'MINI000000000000000000000000OA005',
+  '我发起的',
+  @mini_root,
+  'C',
+  '/pages/oa/mine/index',
+  NULL,
+  'mini:oa:mine',
+  1,
+  '1',
+  '1',
+  'zkxw',
+  50,
+  '小程序我发起的',
+  'admin',
+  '',
+  NOW(),
+  NULL,
+  NOW(),
+  0
+FROM DUAL
+WHERE @mini_root IS NOT NULL
+  AND NOT EXISTS (
+    SELECT 1 FROM sys_menu WHERE id = 'MINI000000000000000000000000OA005' AND del_flag = 0
+  );
+
+-- 抄送我的
+INSERT INTO sys_menu (
+  id, menu_name, parent_id, menu_type, path, component, perms,
+  is_frame, visible, status, tenant_no, order_num, remark,
+  create_by, create_dept_id, create_time, update_by, update_time, del_flag
+)
+SELECT
+  'MINI000000000000000000000000OA006',
+  '抄送我的',
+  @mini_root,
+  'C',
+  '/pages/oa/cc/index',
+  NULL,
+  'mini:oa:cc',
+  1,
+  '1',
+  '1',
+  'zkxw',
+  60,
+  '小程序抄送我的',
+  'admin',
+  '',
+  NOW(),
+  NULL,
+  NOW(),
+  0
+FROM DUAL
+WHERE @mini_root IS NOT NULL
+  AND NOT EXISTS (
+    SELECT 1 FROM sys_menu WHERE id = 'MINI000000000000000000000000OA006' AND del_flag = 0
+  );
+
+  -- 小程序「开启微信通知」菜单(抄送我的之后)
+-- 前置:docs/sql/mobile_menu_oa_task_centers.sql(OA006)可已执行
+-- 角色授权请在 Web「角色管理」勾选本菜单。
+
+SET @mini_root = (
+  SELECT id FROM sys_menu
+  WHERE del_flag = 0 AND parent_id = '0' AND menu_name LIKE '%移动端%'
+  LIMIT 1
+);
+
+INSERT INTO sys_menu (
+  id, menu_name, parent_id, menu_type, path, component, perms,
+  is_frame, visible, status, tenant_no, order_num, remark,
+  create_by, create_dept_id, create_time, update_by, update_time, del_flag
+)
+SELECT
+  'MINI000000000000000000000000OA007',
+  '开启微信通知',
+  @mini_root,
+  'C',
+  '/pages/oa/wechat-notify/index',
+  NULL,
+  'mini:oa:wechat-notify',
+  1,
+  '1',
+  '1',
+  'zkxw',
+  70,
+  '小程序开启微信通知(关注+绑定)',
+  'admin',
+  '',
+  NOW(),
+  NULL,
+  NOW(),
+  0
+FROM DUAL
+WHERE @mini_root IS NOT NULL
+  AND NOT EXISTS (
+    SELECT 1 FROM sys_menu WHERE id = 'MINI000000000000000000000000OA007' AND del_flag = 0
+  );
+
+
+-- 将「移动端菜单」根及其全部子孙菜单,授予全部未删除角色
+-- 前置:移动端根菜单已存在(parent_id=0 且名称含「移动端」)
+-- 幂等:可重复执行;已有有效授权跳过;软删则恢复
+-- 说明:
+--   1) 用户变量显式 utf8mb4_unicode_ci,避免 collation 混用报错 1267
+--   2) 主键用 MD5(role_id|menu_id),避免 INSERT…SELECT 中 UUID() 只算一次导致重复主键
+--   3) MySQL 5.7 无递归 CTE,按最多 3 层子孙展开(根→一级→二级→三级)
+
+SET @tenant_no = CONVERT('zkxw' USING utf8mb4) COLLATE utf8mb4_unicode_ci;
+
+SET @mini_root = (
+  SELECT CONVERT(id USING utf8mb4) COLLATE utf8mb4_unicode_ci
+  FROM sys_menu
+  WHERE del_flag = 0
+    AND parent_id COLLATE utf8mb4_unicode_ci = CONVERT('0' USING utf8mb4) COLLATE utf8mb4_unicode_ci
+    AND menu_name LIKE '%移动端%'
+    AND tenant_no COLLATE utf8mb4_unicode_ci = @tenant_no
+  LIMIT 1
+);
+
+-- 1) 恢复历史上已软删的授权(菜单属于移动端树)
+UPDATE sys_role_menu rm
+INNER JOIN sys_role r
+  ON r.id COLLATE utf8mb4_unicode_ci = rm.role_id COLLATE utf8mb4_unicode_ci
+ AND r.del_flag = 0
+ AND r.tenant_no COLLATE utf8mb4_unicode_ci = @tenant_no
+INNER JOIN sys_menu m
+  ON m.id COLLATE utf8mb4_unicode_ci = rm.menu_id COLLATE utf8mb4_unicode_ci
+ AND m.del_flag = 0
+ AND m.tenant_no COLLATE utf8mb4_unicode_ci = @tenant_no
+ AND (
+   m.id COLLATE utf8mb4_unicode_ci = @mini_root
+   OR m.parent_id COLLATE utf8mb4_unicode_ci = @mini_root
+   OR EXISTS (
+     SELECT 1 FROM sys_menu p1
+     WHERE p1.del_flag = 0
+       AND p1.id COLLATE utf8mb4_unicode_ci = m.parent_id COLLATE utf8mb4_unicode_ci
+       AND p1.parent_id COLLATE utf8mb4_unicode_ci = @mini_root
+   )
+   OR EXISTS (
+     SELECT 1 FROM sys_menu p1
+     INNER JOIN sys_menu p2
+       ON p2.del_flag = 0
+      AND p2.id COLLATE utf8mb4_unicode_ci = p1.parent_id COLLATE utf8mb4_unicode_ci
+     WHERE p1.del_flag = 0
+       AND p1.id COLLATE utf8mb4_unicode_ci = m.parent_id COLLATE utf8mb4_unicode_ci
+       AND p2.parent_id COLLATE utf8mb4_unicode_ci = @mini_root
+   )
+ )
+SET
+  rm.del_flag = 0,
+  rm.update_time = NOW(),
+  rm.remark = 'grant all mobile menus to all roles'
+WHERE @mini_root IS NOT NULL
+  AND rm.tenant_no COLLATE utf8mb4_unicode_ci = @tenant_no
+  AND rm.del_flag = 1;
+
+-- 2) 为尚未关联的「角色 × 移动端菜单」新增授权
+INSERT INTO sys_role_menu (
+  id, role_id, menu_id, tenant_no, order_num, remark,
+  create_by, create_dept_id, create_time, update_by, update_time, del_flag
+)
+SELECT
+  UPPER(MD5(CONCAT(r.id, '|', m.id))),
+  r.id,
+  m.id,
+  @tenant_no,
+  0,
+  'grant all mobile menus to all roles',
+  'admin',
+  '',
+  NOW(),
+  NULL,
+  NOW(),
+  0
+FROM sys_role r
+INNER JOIN sys_menu m
+  ON m.del_flag = 0
+ AND m.tenant_no COLLATE utf8mb4_unicode_ci = @tenant_no
+ AND (
+   m.id COLLATE utf8mb4_unicode_ci = @mini_root
+   OR m.parent_id COLLATE utf8mb4_unicode_ci = @mini_root
+   OR EXISTS (
+     SELECT 1 FROM sys_menu p1
+     WHERE p1.del_flag = 0
+       AND p1.id COLLATE utf8mb4_unicode_ci = m.parent_id COLLATE utf8mb4_unicode_ci
+       AND p1.parent_id COLLATE utf8mb4_unicode_ci = @mini_root
+   )
+   OR EXISTS (
+     SELECT 1 FROM sys_menu p1
+     INNER JOIN sys_menu p2
+       ON p2.del_flag = 0
+      AND p2.id COLLATE utf8mb4_unicode_ci = p1.parent_id COLLATE utf8mb4_unicode_ci
+     WHERE p1.del_flag = 0
+       AND p1.id COLLATE utf8mb4_unicode_ci = m.parent_id COLLATE utf8mb4_unicode_ci
+       AND p2.parent_id COLLATE utf8mb4_unicode_ci = @mini_root
+   )
+ )
+WHERE @mini_root IS NOT NULL
+  AND r.del_flag = 0
+  AND r.tenant_no COLLATE utf8mb4_unicode_ci = @tenant_no
+  AND NOT EXISTS (
+    SELECT 1 FROM sys_role_menu rm
+    WHERE rm.role_id COLLATE utf8mb4_unicode_ci = r.id COLLATE utf8mb4_unicode_ci
+      AND rm.menu_id COLLATE utf8mb4_unicode_ci = m.id COLLATE utf8mb4_unicode_ci
+      AND rm.tenant_no COLLATE utf8mb4_unicode_ci = @tenant_no
+      AND rm.del_flag = 0
+  );
+
+
+
+commit;
+
+-- ===== 生产管理:预产及在途样本菜单(独立权限码)=====
+-- 权威脚本:docs/sql/alter_sys_menu_add_production_pre_transit_sample.sql
+
+-- 生产管理:预产及在途样本(独立页面 + 独立权限码)
+-- 幂等:可重复执行
+-- 授权:已有【生产管理】PRM 下任一有效子菜单的角色 → 授予本 C/F,并补授 PRM 祖先
+
+SET @tenant_no = CONVERT('zkxw' USING utf8mb4) COLLATE utf8mb4_unicode_ci;
+SET @prm_id = CONVERT('PRM00000000000000000000000000001' USING utf8mb4) COLLATE utf8mb4_unicode_ci;
+SET @menu_c = CONVERT('95200000000000000000000000000001' USING utf8mb4) COLLATE utf8mb4_unicode_ci;
+SET @menu_f = CONVERT('95200000000000000000000000000002' USING utf8mb4) COLLATE utf8mb4_unicode_ci;
+
+-- 1) C 菜单
+INSERT INTO sys_menu (
+  id, menu_name, parent_id, menu_type, path, component, perms,
+  is_frame, visible, status, tenant_no, order_num, remark,
+  create_by, create_dept_id, create_time, update_by, update_time, del_flag
+)
+SELECT
+  @menu_c,
+  '预产及在途样本',
+  @prm_id,
+  'C',
+  'pre-transit-sample',
+  '/production/pre-transit-sample/index',
+  'production:preTransitSample:list',
+  1, '1', '1', @tenant_no, 0,
+  '生产看板:在途样本与预产协议(协议单维度只读;与质检页代码独立)',
+  NULL, '', NOW(), NULL, NOW(), 0
+FROM DUAL
+WHERE NOT EXISTS (
+  SELECT 1 FROM sys_menu m
+  WHERE m.id COLLATE utf8mb4_unicode_ci = @menu_c
+);
+
+UPDATE sys_menu
+SET
+  menu_name = '预产及在途样本',
+  parent_id = @prm_id,
+  menu_type = 'C',
+  path = 'pre-transit-sample',
+  component = '/production/pre-transit-sample/index',
+  perms = 'production:preTransitSample:list',
+  visible = '1',
+  status = '1',
+  order_num = 0,
+  remark = '生产看板:在途样本与预产协议(协议单维度只读;与质检页代码独立)',
+  del_flag = 0,
+  update_time = NOW()
+WHERE id COLLATE utf8mb4_unicode_ci = @menu_c;
+
+-- 2) F 按钮
+INSERT INTO sys_menu (
+  id, menu_name, parent_id, menu_type, path, component, perms,
+  is_frame, visible, status, tenant_no, order_num, remark,
+  create_by, create_dept_id, create_time, update_by, update_time, del_flag
+)
+SELECT
+  @menu_f,
+  '预产及在途查询',
+  @menu_c,
+  'F',
+  '',
+  '',
+  'production:preTransitSample:list',
+  1, '0', '1', @tenant_no, 1,
+  '生产侧预产及在途样本列表查询',
+  NULL, '', NOW(), NULL, NOW(), 0
+FROM DUAL
+WHERE NOT EXISTS (
+  SELECT 1 FROM sys_menu m
+  WHERE m.id COLLATE utf8mb4_unicode_ci = @menu_f
+);
+
+UPDATE sys_menu
+SET
+  menu_name = '预产及在途查询',
+  parent_id = @menu_c,
+  menu_type = 'F',
+  path = '',
+  component = '',
+  perms = 'production:preTransitSample:list',
+  visible = '0',
+  status = '1',
+  order_num = 1,
+  remark = '生产侧预产及在途样本列表查询',
+  del_flag = 0,
+  update_time = NOW()
+WHERE id COLLATE utf8mb4_unicode_ci = @menu_f;
+
+-- 3) 目标角色:已有 PRM 下任一有效子菜单授权
+-- MySQL 5.7:UPDATE 目标表不可在同句 FROM/EXISTS 中直接引用自身,须用派生表
+
+-- 3a) 恢复软删的 PRM / C / F 授权
+UPDATE sys_role_menu rm
+INNER JOIN sys_role r
+  ON r.id COLLATE utf8mb4_unicode_ci = rm.role_id COLLATE utf8mb4_unicode_ci
+ AND r.del_flag = 0
+ AND r.tenant_no COLLATE utf8mb4_unicode_ci = @tenant_no
+INNER JOIN (
+  SELECT DISTINCT rm2.role_id AS role_id
+  FROM sys_role_menu rm2
+  INNER JOIN sys_menu m2
+    ON m2.id COLLATE utf8mb4_unicode_ci = rm2.menu_id COLLATE utf8mb4_unicode_ci
+   AND m2.del_flag = 0
+   AND m2.parent_id COLLATE utf8mb4_unicode_ci = @prm_id
+  WHERE rm2.tenant_no COLLATE utf8mb4_unicode_ci = @tenant_no
+    AND rm2.del_flag = 0
+) eligible
+  ON eligible.role_id COLLATE utf8mb4_unicode_ci = r.id COLLATE utf8mb4_unicode_ci
+SET
+  rm.del_flag = 0,
+  rm.update_time = NOW(),
+  rm.remark = 'grant production pre-transit to PRM roles'
+WHERE rm.tenant_no COLLATE utf8mb4_unicode_ci = @tenant_no
+  AND rm.del_flag = 1
+  AND rm.menu_id COLLATE utf8mb4_unicode_ci IN (@prm_id, @menu_c, @menu_f);
+
+-- 3b) 插入缺失授权(PRM + C + F)
+INSERT INTO sys_role_menu (
+  id, role_id, menu_id, tenant_no, order_num, remark,
+  create_by, create_dept_id, create_time, update_by, update_time, del_flag
+)
+SELECT
+  UPPER(MD5(CONCAT(src.role_id, '|', src.menu_id))),
+  src.role_id,
+  src.menu_id,
+  @tenant_no,
+  0,
+  'grant production pre-transit to PRM roles',
+  'admin',
+  '',
+  NOW(),
+  NULL,
+  NOW(),
+  0
+FROM (
+  SELECT
+    r.id AS role_id,
+    x.menu_id AS menu_id
+  FROM sys_role r
+  INNER JOIN (
+    SELECT @prm_id AS menu_id
+    UNION ALL SELECT @menu_c
+    UNION ALL SELECT @menu_f
+  ) x
+  INNER JOIN (
+    SELECT DISTINCT rm2.role_id AS role_id
+    FROM sys_role_menu rm2
+    INNER JOIN sys_menu m2
+      ON m2.id COLLATE utf8mb4_unicode_ci = rm2.menu_id COLLATE utf8mb4_unicode_ci
+     AND m2.del_flag = 0
+     AND m2.parent_id COLLATE utf8mb4_unicode_ci = @prm_id
+    WHERE rm2.tenant_no COLLATE utf8mb4_unicode_ci = @tenant_no
+      AND rm2.del_flag = 0
+  ) eligible
+    ON eligible.role_id COLLATE utf8mb4_unicode_ci = r.id COLLATE utf8mb4_unicode_ci
+  INNER JOIN sys_menu m
+    ON m.id COLLATE utf8mb4_unicode_ci = x.menu_id
+   AND m.del_flag = 0
+  WHERE r.del_flag = 0
+    AND r.tenant_no COLLATE utf8mb4_unicode_ci = @tenant_no
+) src
+LEFT JOIN sys_role_menu existing
+  ON existing.role_id COLLATE utf8mb4_unicode_ci = src.role_id COLLATE utf8mb4_unicode_ci
+ AND existing.menu_id COLLATE utf8mb4_unicode_ci = src.menu_id COLLATE utf8mb4_unicode_ci
+ AND existing.tenant_no COLLATE utf8mb4_unicode_ci = @tenant_no
+ AND existing.del_flag = 0
+WHERE existing.id IS NULL;
+
+
+
+-- ===== 质检:检测项目模版建表 + 菜单授权 =====
+-- 权威脚本:docs/sql/alter_biz_qc_inspection_item_template.sql