feat(sql): 迁移脚本补齐审批配置地基 + 采购计划/订单变更,并加订单审批测试
原 xgdl_review_audit_pending_changes.sql 只加了「审核人字段」和「审核按钮权限」,
缺少审批引擎本身:新环境只跑它,所有单据提交都会在
validateApprovalEnabledAndGetApprovers 处失败。
新增第四部分:
- system_approval_config / system_approval_config_user 建表
- 26 个业务类型种子(默认未启用)
- 审批配置页面菜单 + 查询/保存按钮;父级用 component/permission 锚点反查,
不硬编码菜单 ID
- erp_purchase_plan / erp_purchase_order 审核人四字段(原第一部分未覆盖这两张表)
- 两单的 audit 按钮权限
- 角色授权:本框架无超管绕过逻辑,:audit 与审批配置权限一律显式授予
super_admin 与 common
权限对账(grep 代码中的 ':audit' 与脚本比对)另发现 3 个代码已使用但脚本未建的菜单,
一并补入:aftersales:compensation、hrm:performance-appraise、mes:pd-product。
现代码 22 项 / 脚本 22 项,无缺漏无冗余。
开发库还实测出 mes:pd-product:audit、mes:pd-tech-confirm:audit 漏授 super_admin,
授权段的 NOT EXISTS 会顺带补齐。
脚本经 dry-run 验证:各插入语句算出的 parent_id 与开发库既有记录完全一致
(1 / 1065900 / 1065900 / 1065212 / 2666),幂等守卫在开发库上均为跳过、零新增。
测试:新增 scripts/test-purchase-order-api.js(接口级,24/24 通过)
与 scripts/test-purchase-order-ui.js(Playwright 端到端,自建种子数据)
注:本次提交同时带上了工作区里已有的既有加固(各菜单插入语句补
WHERE a.parent_id IS NOT NULL,防止锚点权限缺失时插入 parent_id=NULL 的孤立菜单),
该改动非本次所写,一并入库。
Co-Authored-By: Claude <noreply@anthropic.com>
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | /** |
| | | * éè´è®¢åç¶ææºå®¡æ¹ - æ¥å£çº§èªå¨åæµè¯ |
| | | * |
| | | * èªå»ºæµè¯åæ®ï¼æ¯æ¬¡æ°å»ºä¸å¼ ï¼ï¼ä¸ä¾èµå岿°æ®ç¶æï¼å¯é夿§è¡ã |
| | | * è¦çï¼æäº¤/å®¡æ ¸/驳å/éæ°æäº¤/éè¿ç宿´é¾è·¯ + å
¨é¨æå¡ç«¯æ ¡éªåæ¯ã |
| | | * |
| | | * ç¨æ³ï¼node scripts/test-purchase-order-api.js |
| | | * å¯éç¯å¢åéï¼TEST_USER / TEST_PASS / PRODUCT_ID / PRODUCT_UNIT_ID |
| | | */ |
| | | const BASE = process.env.TEST_BASE || 'http://127.0.0.1:48080/admin-api'; |
| | | const USER = process.env.TEST_USER || 'admin'; |
| | | const PASS = process.env.TEST_PASS || 'admin123'; |
| | | const BIZ_TYPE = 'erp_purchase_order_approve'; |
| | | // ç©æåèª mdm_itemï¼æµè¯åºä¸ id=57ãæµè¯ãï¼åä½ id=9ï¼ |
| | | const PRODUCT_ID = Number(process.env.PRODUCT_ID || 57); |
| | | const PRODUCT_UNIT_ID = Number(process.env.PRODUCT_UNIT_ID || 9); |
| | | |
| | | const results = []; |
| | | let TOKEN = ''; |
| | | |
| | | function ok(name, extra = '') { |
| | | results.push({ pass: true, name }); |
| | | console.log(` â
${name}${extra ? ' â ' + extra : ''}`); |
| | | } |
| | | function fail(name, extra = '') { |
| | | results.push({ pass: false, name, extra }); |
| | | console.log(` â ${name}${extra ? ' â ' + extra : ''}`); |
| | | } |
| | | |
| | | async function api(method, path, body) { |
| | | const res = await fetch(BASE + path, { |
| | | method, |
| | | headers: { |
| | | 'Content-Type': 'application/json', |
| | | 'tenant-id': '1', |
| | | ...(TOKEN ? { Authorization: `Bearer ${TOKEN}` } : {}), |
| | | }, |
| | | body: body ? JSON.stringify(body) : undefined, |
| | | }); |
| | | let json = null; |
| | | try { |
| | | json = await res.json(); |
| | | } catch { |
| | | /* é JSON */ |
| | | } |
| | | return { status: res.status, json }; |
| | | } |
| | | |
| | | async function login() { |
| | | console.log('\n[0] ç»å½'); |
| | | const { json } = await api('POST', '/system/auth/login', { username: USER, password: PASS }); |
| | | if (!json || json.code !== 0 || !json.data?.accessToken) { |
| | | fail('ç»å½å¤±è´¥', JSON.stringify(json)); |
| | | process.exit(1); |
| | | } |
| | | TOKEN = json.data.accessToken; |
| | | ok('ç»å½æå'); |
| | | } |
| | | |
| | | /** ç¡®ä¿ç®æ ä¸å¡ç±»åå·²å¯ç¨ä¸å®¡æ¹äººå« admin(id=1)ï¼è¿åé
置对象 */ |
| | | async function ensureConfig(userIds) { |
| | | const { json } = await api('GET', `/system/approval-config/page?pageNo=1&pageSize=100&bizType=${BIZ_TYPE}`); |
| | | const row = (json?.data?.list || []).find((x) => x.bizType === BIZ_TYPE); |
| | | if (!row) { |
| | | fail('æªæ¾å°å®¡æ¹é
ç½®ç§åæ°æ®', BIZ_TYPE); |
| | | return null; |
| | | } |
| | | const save = await api('POST', '/system/approval-config/save', { |
| | | id: row.id, |
| | | bizType: row.bizType, |
| | | bizName: row.bizName, |
| | | approvalEnabled: true, |
| | | remark: row.remark, |
| | | userIds, |
| | | }); |
| | | if (!save.json || save.json.code !== 0) { |
| | | fail('ä¿å审æ¹é
置失败', JSON.stringify(save.json)); |
| | | return null; |
| | | } |
| | | const again = await api('GET', `/system/approval-config/page?pageNo=1&pageSize=100&bizType=${BIZ_TYPE}`); |
| | | return (again.json?.data?.list || []).find((x) => x.bizType === BIZ_TYPE) || row; |
| | | } |
| | | |
| | | /** |
| | | * éè´è®¢åçä¾åºåæ ¡éªèµ° SRMï¼ErpSupplierServiceImpl.validateSupplier -> srmSupplierApiï¼ï¼ |
| | | * ä½ä¾åºåå¢å æ¹æ¥ä¸åç«¯ä¸æå读å erp_supplierï¼ä¸¤å¤æ°æ®æºä¸ä¸è´ã |
| | | * å æ¤æµè¯æ°æ®å¿
é¡»å»ºå¨ SRM ä¾§ï¼å¦åä¸åå¿
ç¶æ¥ãä¾åºåä¸åå¨ãã |
| | | */ |
| | | async function getSupplierId() { |
| | | const { json } = await api('GET', '/srm/supplier/simple-list'); |
| | | const list = json?.data || []; |
| | | if (list.length > 0) return list[0].id; |
| | | const seq = Date.now().toString().slice(-6); |
| | | const created = await api('POST', '/srm/supplier/create', { |
| | | code: `TESTSUP${seq}`, |
| | | name: `èªå¨åæµè¯ä¾åºå${seq}`, |
| | | status: 0, |
| | | remark: 'ç± test-purchase-order-api.js å建', |
| | | }); |
| | | if (created.json?.code !== 0) { |
| | | fail('å建 SRM ä¾åºå失败', JSON.stringify(created.json)); |
| | | return null; |
| | | } |
| | | return created.json.data; |
| | | } |
| | | |
| | | async function createOrder(supplierId) { |
| | | const body = { |
| | | supplierId, |
| | | orderTime: '2026-09-16 10:00:00', |
| | | discountPercent: 0, |
| | | depositPrice: 0, |
| | | remark: 'èªå¨åæµè¯éè´è®¢å', |
| | | items: [ |
| | | { |
| | | productId: PRODUCT_ID, |
| | | productUnitId: PRODUCT_UNIT_ID, |
| | | productPrice: 100, |
| | | count: 5, |
| | | taxPercent: 13, |
| | | remark: 'æµè¯é¡¹', |
| | | }, |
| | | ], |
| | | }; |
| | | const { json } = await api('POST', '/purchase/order/create', body); |
| | | return json; |
| | | } |
| | | |
| | | async function getOrder(id) { |
| | | const { json } = await api('GET', `/purchase/order/get?id=${id}`); |
| | | return json?.data; |
| | | } |
| | | |
| | | (async () => { |
| | | await login(); |
| | | |
| | | console.log('\n[1] 审æ¹é
ç½®'); |
| | | const cfg = await ensureConfig([1]); |
| | | if (!cfg) process.exit(1); |
| | | ok('éè´è®¢å审æ¹å·²å¯ç¨', `bizName=${cfg.bizName}`); |
| | | |
| | | const ap = await api('GET', `/purchase/order/approver-ids`); |
| | | if (ap.json?.code === 0 && (ap.json.data || []).includes(1)) ok('approver-ids å« admin(1)', JSON.stringify(ap.json.data)); |
| | | else fail('approver-ids å¼å¸¸', JSON.stringify(ap.json)); |
| | | |
| | | console.log('\n[2] æ°å»ºéè´è®¢å'); |
| | | const supplierId = await getSupplierId(); |
| | | if (!supplierId) { |
| | | console.log(' æ æ³åå¤ä¾åºåï¼ç»æ¢'); |
| | | process.exit(1); |
| | | } |
| | | ok('ä¾åºå就绪', `supplierId=${supplierId}`); |
| | | |
| | | const created = await createOrder(supplierId); |
| | | if (!created || created.code !== 0) { |
| | | fail('å建éè´è®¢å失败', JSON.stringify(created)); |
| | | process.exit(1); |
| | | } |
| | | const id = created.data; |
| | | ok('å建éè´è®¢åæå', `id=${id}`); |
| | | |
| | | const o0 = await getOrder(id); |
| | | if (o0?.status === 0) ok('æ°ååå§ç¶æä¸ºæªæäº¤(0)', `no=${o0.no}`); |
| | | else fail('æ°ååå§ç¶æä¸æ¯ 0', `status=${o0?.status}`); |
| | | |
| | | console.log('\n[3] BPM æ®çæ¥å£åºå·²ä¸çº¿'); |
| | | const legacy = await api('GET', '/purchase/order/approve-process-list'); |
| | | if (legacy.status === 404 || (legacy.json && legacy.json.code !== 0)) { |
| | | ok('approve-process-list 已移é¤', `HTTP ${legacy.status} code=${legacy.json?.code}`); |
| | | } else { |
| | | fail('approve-process-list ä»å¯ç¨', JSON.stringify(legacy.json).slice(0, 120)); |
| | | } |
| | | |
| | | console.log('\n[4] æªæäº¤ä¸è½å®¡æ ¸'); |
| | | const early = await api('PUT', '/purchase/order/audit', { id, pass: true, reviewRemark: 'æ¢è·' }); |
| | | if (early.json?.code === 1_030_101_016) ok('æ£ç¡®æç»ï¼é审æ¹ä¸ä¸å¯å®¡æ ¸', `code=${early.json.code}`); |
| | | else fail('æªæé¢ææç»', JSON.stringify(early.json)); |
| | | |
| | | console.log('\n[5] æäº¤ â 审æ¹ä¸'); |
| | | const sub = await api('PUT', `/purchase/order/submit?id=${id}`); |
| | | if (sub.json?.code === 0) ok('æäº¤æå', JSON.stringify(sub.json)); |
| | | else fail('æäº¤å¤±è´¥', JSON.stringify(sub.json)); |
| | | const o1 = await getOrder(id); |
| | | if (o1?.status === 10) ok('ç¶æå·²ç½®ä¸ºå®¡æ¹ä¸(10)'); |
| | | else fail('æäº¤åç¶æä¸å¯¹', `status=${o1?.status}`); |
| | | |
| | | console.log('\n[6] 审æ¹ä¸ä¸å¯ä¿®æ¹'); |
| | | const upd = await api('PUT', '/purchase/order/update', { |
| | | id, |
| | | supplierId, |
| | | orderTime: '2026-09-16 10:00:00', |
| | | remark: '审æ¹ä¸æ¹å', |
| | | items: [{ productId: PRODUCT_ID, productUnitId: PRODUCT_UNIT_ID, productPrice: 100, count: 5, taxPercent: 13 }], |
| | | }); |
| | | if (upd.json?.code === 1_030_101_019) ok('æ£ç¡®æç»ï¼å®¡æ¹ä¸ä¸å¯ä¿®æ¹', `code=${upd.json.code}`); |
| | | else fail('审æ¹ä¸å±
ç¶å¯ä¿®æ¹', JSON.stringify(upd.json)); |
| | | |
| | | console.log('\n[7] é审æ¹äººä¸å¯å®¡æ ¸'); |
| | | await ensureConfig([111]); // ä¸´æ¶æå®¡æ¹äººæ¹æãæµè¯ç¨æ·ã |
| | | const notAp = await api('PUT', '/purchase/order/audit', { id, pass: true, reviewRemark: 'è¶æ' }); |
| | | if (notAp.json?.code === 1_002_030_005) ok('æ£ç¡®æç»ï¼å½åç¨æ·ä¸æ¯å®¡æ¹äºº', `code=${notAp.json.code}`); |
| | | else fail('è¶æå®¡æ ¸æªè¢«æ¦æª', JSON.stringify(notAp.json)); |
| | | await ensureConfig([1]); // è¿å |
| | | const back = await api('GET', '/purchase/order/approver-ids'); |
| | | if ((back.json?.data || []).includes(1)) ok('审æ¹äººå·²è¿å为 admin'); |
| | | else fail('审æ¹äººè¿å失败', JSON.stringify(back.json?.data)); |
| | | |
| | | console.log('\n[8] 驳åå¿
须填åå '); |
| | | const noWhy = await api('PUT', '/purchase/order/audit', { id, pass: false }); |
| | | if (noWhy.json?.code === 1_030_101_017) ok('æ£ç¡®æç»ï¼ç¼ºé©³ååå ', `code=${noWhy.json.code}`); |
| | | else fail('缺åå æªè¢«æ¦æª', JSON.stringify(noWhy.json)); |
| | | |
| | | console.log('\n[9] å®¡æ ¸ä¸éè¿ â éå'); |
| | | const rej = await api('PUT', '/purchase/order/audit', { id, pass: false, reviewRemark: 'ä»·æ ¼åé«ï¼éåéè®®' }); |
| | | if (rej.json?.code === 0) ok('驳åæå', JSON.stringify(rej.json)); |
| | | else fail('驳å失败', JSON.stringify(rej.json)); |
| | | const o2 = await getOrder(id); |
| | | if (o2?.status === 30) ok('ç¶æä¸ºå®¡æ ¸ä¸éè¿(30)'); |
| | | else fail('驳ååç¶æä¸å¯¹', `status=${o2?.status}`); |
| | | if (o2?.reviewerName && o2?.reviewRemark === 'ä»·æ ¼åé«ï¼éåéè®®') ok('å®¡æ ¸äººä¸é©³ååå å·²è½åº', `${o2.reviewerName} / ${o2.reviewRemark}`); |
| | | else fail('å®¡æ ¸ä¿¡æ¯æªè½åº', JSON.stringify({ rn: o2?.reviewerName, rr: o2?.reviewRemark })); |
| | | |
| | | console.log('\n[10] éååå¯ä¿®æ¹å¹¶éæ°æäº¤'); |
| | | const upd2 = await api('PUT', '/purchase/order/update', { |
| | | id, |
| | | supplierId, |
| | | orderTime: '2026-09-16 10:00:00', |
| | | remark: 'å·²éä»·éè®®', |
| | | items: [{ productId: PRODUCT_ID, productUnitId: PRODUCT_UNIT_ID, productPrice: 80, count: 5, taxPercent: 13 }], |
| | | }); |
| | | if (upd2.json?.code === 0) ok('éååå¯ä¿®æ¹', JSON.stringify(upd2.json)); |
| | | else fail('éååä»ä¸å¯ä¿®æ¹', JSON.stringify(upd2.json)); |
| | | |
| | | const sub2 = await api('PUT', `/purchase/order/submit?id=${id}`); |
| | | if (sub2.json?.code === 0) ok('éæ°æäº¤æå'); |
| | | else fail('éæ°æäº¤å¤±è´¥', JSON.stringify(sub2.json)); |
| | | const o3 = await getOrder(id); |
| | | if (o3?.status === 10 && !o3?.reviewRemark && !o3?.reviewerName) ok('éæ°æäº¤å·²æ¸
空ä¸ä¸è½®å®¡æ ¸ä¿¡æ¯', `reviewRemark=${JSON.stringify(o3?.reviewRemark ?? null)}`); |
| | | else fail('éæåå®¡æ ¸ä¿¡æ¯æ®ç', JSON.stringify({ st: o3?.status, rn: o3?.reviewerName, rr: o3?.reviewRemark })); |
| | | |
| | | console.log('\n[11] å®¡æ ¸éè¿'); |
| | | const app = await api('PUT', '/purchase/order/audit', { id, pass: true, reviewRemark: 'åæéè´' }); |
| | | if (app.json?.code === 0) ok('å®¡æ ¸éè¿æå', JSON.stringify(app.json)); |
| | | else fail('å®¡æ ¸éè¿å¤±è´¥', JSON.stringify(app.json)); |
| | | const o4 = await getOrder(id); |
| | | if (o4?.status === 20) ok('ç¶æä¸ºå®¡æ ¸éè¿(20)', `å®¡æ ¸äºº=${o4.reviewerName}`); |
| | | else fail('éè¿åç¶æä¸å¯¹', `status=${o4?.status}`); |
| | | |
| | | console.log('\n[12] å·²å®¡æ ¸ä¸å¯ä¿®æ¹'); |
| | | const upd3 = await api('PUT', '/purchase/order/update', { |
| | | id, |
| | | supplierId, |
| | | orderTime: '2026-09-16 10:00:00', |
| | | remark: 'å®¡æ ¸ååæ¹', |
| | | items: [{ productId: PRODUCT_ID, productUnitId: PRODUCT_UNIT_ID, productPrice: 80, count: 5, taxPercent: 13 }], |
| | | }); |
| | | if (upd3.json?.code === 1_030_101_005) ok('æ£ç¡®æç»ï¼å·²å®¡æ ¸ä¸å¯ä¿®æ¹', `code=${upd3.json.code}`); |
| | | else fail('å·²å®¡æ ¸ä»å¯ä¿®æ¹', JSON.stringify(upd3.json)); |
| | | |
| | | console.log('\n[13] å
¨ç¨ä¸åºååºç° 500 ç³»ç»å¼å¸¸'); |
| | | const calls = [ |
| | | ['GET', `/purchase/order/page?pageNo=1&pageSize=10`], |
| | | ['GET', `/purchase/order/approver-ids`], |
| | | ]; |
| | | let bad500 = 0; |
| | | for (const [m, p] of calls) { |
| | | const r = await api(m, p); |
| | | if (r.json?.code === 500) { |
| | | bad500++; |
| | | console.log(` ${m} ${p} â 500`); |
| | | } |
| | | } |
| | | if (bad500 === 0) ok('å
³é®æ¥å£æ 500'); |
| | | else fail(`仿 ${bad500} 个æ¥å£è¿å 500`); |
| | | |
| | | console.log(`\næµè¯åæ®ï¼erp_purchase_order id=${id}ï¼${o0?.no}ï¼`); |
| | | const passed = results.filter((r) => r.pass).length; |
| | | console.log(`===== æ±æ»ï¼${passed}/${results.length} éè¿ =====`); |
| | | results.filter((r) => !r.pass).forEach((r) => console.log(` 失败: ${r.name} ${r.extra}`)); |
| | | process.exit(passed === results.length ? 0 : 1); |
| | | })().catch((e) => { |
| | | console.error('æ§è¡å¼å¸¸:', e.message); |
| | | process.exit(1); |
| | | }); |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | /** |
| | | * éè´è®¢åå®¡æ¹ - æµè§å¨ç«¯å°ç«¯æµè¯ |
| | | * |
| | | * åç½®æ°æ®ç¨ admin-api ç´æ¥åå¤ï¼ä¸æ¬¡è·å®çä¸ï¼1 å¼ æªæäº¤ + 1 å¼ å®¡æ¹ä¸ï¼ï¼ |
| | | * ä¹åå
¨é¨èµ°ç宿µè§å¨äº¤äºéªè¯æé®å¯è§æ§ãå¼¹çªæ ¡éªä¸ç¶ææµè½¬ã |
| | | * |
| | | * ç¨æ³ï¼node scripts/test-purchase-order-ui.js |
| | | */ |
| | | const { chromium } = require('playwright'); |
| | | const path = require('path'); |
| | | const fs = require('fs'); |
| | | |
| | | const API = process.env.TEST_API || 'http://127.0.0.1:48080/admin-api'; |
| | | const BASE = process.env.TEST_BASE || 'http://localhost:5666'; |
| | | const USER = process.env.TEST_USER || 'admin'; |
| | | const PASS = process.env.TEST_PASS || 'admin123'; |
| | | const BIZ_TYPE = 'erp_purchase_order_approve'; |
| | | const PRODUCT_ID = Number(process.env.PRODUCT_ID || 57); |
| | | const PRODUCT_UNIT_ID = Number(process.env.PRODUCT_UNIT_ID || 9); |
| | | |
| | | const SHOT_DIR = path.join(__dirname, 'shots'); |
| | | if (!fs.existsSync(SHOT_DIR)) fs.mkdirSync(SHOT_DIR, { recursive: true }); |
| | | |
| | | const results = []; |
| | | function ok(name, extra = '') { |
| | | results.push({ pass: true, name }); |
| | | console.log(` â
${name}${extra ? ' â ' + extra : ''}`); |
| | | } |
| | | function fail(name, extra = '') { |
| | | results.push({ pass: false, name, extra }); |
| | | console.log(` â ${name}${extra ? ' â ' + extra : ''}`); |
| | | } |
| | | |
| | | let TOKEN = ''; |
| | | async function api(method, p, body) { |
| | | const res = await fetch(API + p, { |
| | | method, |
| | | headers: { |
| | | 'Content-Type': 'application/json', |
| | | 'tenant-id': '1', |
| | | ...(TOKEN ? { Authorization: `Bearer ${TOKEN}` } : {}), |
| | | }, |
| | | body: body ? JSON.stringify(body) : undefined, |
| | | }); |
| | | try { |
| | | return await res.json(); |
| | | } catch { |
| | | return null; |
| | | } |
| | | } |
| | | |
| | | async function seed() { |
| | | console.log('\n[S] å夿µè¯æ°æ®ï¼æ¥å£ï¼'); |
| | | const lg = await api('POST', '/system/auth/login', { username: USER, password: PASS }); |
| | | if (!lg || lg.code !== 0) { |
| | | fail('æ¥å£ç»å½å¤±è´¥', JSON.stringify(lg)); |
| | | process.exit(1); |
| | | } |
| | | TOKEN = lg.data.accessToken; |
| | | ok('æ¥å£ç»å½æå'); |
| | | |
| | | const page = await api('GET', `/system/approval-config/page?pageNo=1&pageSize=100&bizType=${BIZ_TYPE}`); |
| | | const cfg = (page?.data?.list || []).find((x) => x.bizType === BIZ_TYPE); |
| | | if (!cfg) { |
| | | fail('缺å°å®¡æ¹é
ç½®ç§å'); |
| | | process.exit(1); |
| | | } |
| | | await api('POST', '/system/approval-config/save', { |
| | | id: cfg.id, |
| | | bizType: cfg.bizType, |
| | | bizName: cfg.bizName, |
| | | approvalEnabled: true, |
| | | remark: cfg.remark, |
| | | userIds: [1], |
| | | }); |
| | | ok('审æ¹å·²å¯ç¨ï¼å®¡æ¹äºº=admin'); |
| | | |
| | | // æ¯æ¬¡è·é½ç¨å¯ä¸å½åçä¾åºåï¼ä¾¿äºå¨å表é精确é宿¬æ¬¡ç§ååæ® |
| | | const seq = Date.now().toString().slice(-6); |
| | | const supplierName = `UIAUTOSUP${seq}`; |
| | | let supplierId = null; |
| | | const c = await api('POST', '/srm/supplier/create', { |
| | | code: `UIAUTOSUP${seq}`, |
| | | name: supplierName, |
| | | status: 0, |
| | | }); |
| | | supplierId = c?.data; |
| | | if (!supplierId) { |
| | | const sup = await api('GET', '/srm/supplier/simple-list'); |
| | | const hit = (sup?.data || []).find((x) => x.name === supplierName) || (sup?.data || [])[0]; |
| | | supplierId = hit?.id; |
| | | } |
| | | if (!supplierId) { |
| | | fail('æ æ³åå¤ä¾åºåï¼æ³¨æ ERP/SRM æ°æ®æºä¸ä¸è´é®é¢ï¼', JSON.stringify(c)); |
| | | process.exit(1); |
| | | } |
| | | ok('ä¾åºå就绪', `supplierId=${supplierId} name=${supplierName}`); |
| | | |
| | | const mk = async () => { |
| | | return api('POST', '/purchase/order/create', { |
| | | supplierId, |
| | | orderTime: '2026-09-16 10:00:00', |
| | | discountPercent: 0, |
| | | depositPrice: 0, |
| | | remark: supplierName, |
| | | items: [ |
| | | { |
| | | productId: PRODUCT_ID, |
| | | productUnitId: PRODUCT_UNIT_ID, |
| | | productPrice: 100, |
| | | count: 5, |
| | | taxPercent: 13, |
| | | }, |
| | | ], |
| | | }); |
| | | }; |
| | | |
| | | const a = await mk(); |
| | | const b = await mk(); |
| | | if (a?.code !== 0 || b?.code !== 0) { |
| | | fail('å建æµè¯è®¢å失败', JSON.stringify({ a, b })); |
| | | process.exit(1); |
| | | } |
| | | const draftId = a.data; |
| | | const approvingId = b.data; |
| | | const sub = await api('PUT', `/purchase/order/submit?id=${approvingId}`); |
| | | if (sub?.code !== 0) { |
| | | fail('é¢ç½®ã审æ¹ä¸ãåæ®å¤±è´¥', JSON.stringify(sub)); |
| | | process.exit(1); |
| | | } |
| | | ok('æ°æ®å°±ç»ª', `è稿 id=${draftId}ï¼å®¡æ¹ä¸ id=${approvingId}`); |
| | | return { approvingId, draftId, supplierName }; |
| | | } |
| | | |
| | | async function shot(page, name) { |
| | | const p = path.join(SHOT_DIR, `${name}.png`); |
| | | await page.screenshot({ path: p, fullPage: true }); |
| | | console.log(` ð¸ ${p}`); |
| | | } |
| | | |
| | | /** |
| | | * åæ°æ®è¡ä¸åºå®å³åè¡ï¼æç´¢å¼é
对ã |
| | | * 注æï¼æ¬é¡µãç¶æã忝 fixed:rightï¼ä¸æä½æé®åå¤åºå®å³å容å¨ï¼ |
| | | * å æ¤ main éä¸å«ç¶æï¼å¿
é¡»ä» fixed ææ¬éè¯»ç¶æã |
| | | */ |
| | | async function rowPairs(page) { |
| | | const dataRows = await page.locator('.vxe-table--main-wrapper .vxe-body--row').all(); |
| | | const fixedRows = await page.locator('.vxe-table--fixed-right-wrapper .vxe-body--row').all(); |
| | | const out = []; |
| | | const n = Math.min(dataRows.length, fixedRows.length); |
| | | for (let i = 0; i < n; i++) { |
| | | out.push({ |
| | | main: (await dataRows[i].innerText()).replace(/\s+/g, ' '), |
| | | fixed: (await fixedRows[i].innerText()).replace(/\s+/g, ' ').trim(), |
| | | actionRow: fixedRows[i], |
| | | }); |
| | | } |
| | | return out; |
| | | } |
| | | |
| | | (async () => { |
| | | const seeded = await seed(); |
| | | |
| | | const browser = await chromium.launch({ headless: true, channel: 'chrome' }); |
| | | const page = await browser.newPage({ viewport: { width: 1680, height: 950 } }); |
| | | |
| | | const apiErrors = []; |
| | | page.on('response', async (r) => { |
| | | if (!r.url().includes('/admin-api/')) return; |
| | | try { |
| | | const b = await r.json(); |
| | | if (b && typeof b.code === 'number' && b.code !== 0) { |
| | | apiErrors.push({ url: r.url().replace(BASE, ''), code: b.code, msg: b.msg }); |
| | | } |
| | | } catch { |
| | | /* ignore */ |
| | | } |
| | | }); |
| | | |
| | | try { |
| | | // 1. ç»å½ |
| | | console.log('\n[1] æµè§å¨ç»å½'); |
| | | await page.goto(`${BASE}/auth/login`, { waitUntil: 'networkidle', timeout: 60000 }); |
| | | await page.locator('input[placeholder="请è¾å
¥ç¨æ·å"]').first().fill(USER); |
| | | await page.locator('input[type="password"]').first().fill(PASS); |
| | | await page.locator('button[aria-label="login"]').first().click(); |
| | | await page.waitForURL((u) => !u.pathname.includes('/login'), { timeout: 30000 }); |
| | | ok('ç»å½æå'); |
| | | |
| | | // 2. 审æ¹é
置页åºè½çå°éè´è®¢åå®¡æ ¸ |
| | | console.log('\n[2] 审æ¹é
置页'); |
| | | await page.goto(`${BASE}/system/approval-config`, { waitUntil: 'networkidle', timeout: 60000 }); |
| | | await page.waitForTimeout(2500); |
| | | const cfgRow = page.locator('.vxe-body--row', { hasText: 'éè´è®¢åå®¡æ ¸' }); |
| | | if ((await cfgRow.count()) > 0) { |
| | | const t = (await cfgRow.first().innerText()).replace(/\s+/g, ' '); |
| | | if (t.includes('å·²å¯ç¨')) ok('éè´è®¢åå®¡æ¹æ¾ç¤ºå·²å¯ç¨', t.slice(0, 100)); |
| | | else fail('éè´è®¢åå®¡æ¹æªæ¾ç¤ºå·²å¯ç¨', t.slice(0, 100)); |
| | | if (t.includes('è¶
级管çå')) ok('审æ¹äººæ¾ç¤ºè¶
级管çå'); |
| | | else fail('审æ¹äººæªæ¾ç¤º', t.slice(0, 100)); |
| | | } else { |
| | | fail('é
置页æ¾ä¸å°ãéè´è®¢åå®¡æ ¸ãè¡'); |
| | | } |
| | | await shot(page, 'po-01-config'); |
| | | |
| | | // 3. éè´è®¢åå表æé®å¯è§æ§ |
| | | console.log('\n[3] éè´è®¢åå表æé®'); |
| | | await page.goto(`${BASE}/purchase/order`, { waitUntil: 'networkidle', timeout: 60000 }); |
| | | await page.waitForTimeout(3000); |
| | | await shot(page, 'po-02-list'); |
| | | |
| | | const pairs = await rowPairs(page); |
| | | if (pairs.length === 0) { |
| | | fail('åè¡¨æ æ°æ®è¡'); |
| | | throw new Error('empty grid'); |
| | | } |
| | | // æ¬æ¬¡ç§ååæ®é å¯ä¸ä¾åºååå®ä½ï¼å表ä¸å±ç¤ºå¤æ³¨åï¼ |
| | | const mine = pairs.filter((p) => p.main.includes(seeded.supplierName)); |
| | | if (mine.length < 2) { |
| | | fail('æªè½å¨å表ä¸å®ä½å°æ¬æ¬¡ç§å订å', `å½ä¸ ${mine.length} è¡ / å
± ${pairs.length} è¡`); |
| | | pairs.forEach((p) => console.log(' ROW:', p.main.slice(0, 60), '||', p.fixed)); |
| | | } else { |
| | | ok('æä¾åºååå®ä½å°æ¬æ¬¡ç§ååæ®', `${mine.length} è¡`); |
| | | } |
| | | const draft = mine.find((p) => p.fixed.includes('è稿')); |
| | | const approving = mine.find((p) => p.fixed.includes('审æ¹ä¸')); |
| | | if (draft) ok('èç¨¿åæ¸²æä¸ºãè稿ã', draft.fixed); |
| | | else fail('æªæ¾å°èç¨¿ç¶æçç§å订å', mine.map((p) => p.fixed).join(' | ')); |
| | | if (approving) ok('审æ¹ä¸å渲æä¸ºã审æ¹ä¸ã', approving.fixed); |
| | | else fail('æªæ¾å°å®¡æ¹ä¸ç¶æçç§å订å', mine.map((p) => p.fixed).join(' | ')); |
| | | |
| | | if (draft) { |
| | | const hasSubmit = /æäº¤/.test(draft.fixed); |
| | | const hasAudit = draft.fixed.includes('å®¡æ ¸'); |
| | | const hasEdit = /(ä¿®æ¹|ç¼è¾)/.test(draft.fixed); |
| | | if (hasSubmit && hasEdit && !hasAudit) ok('è稿è¡ï¼æãæäº¤ããä¿®æ¹ãï¼æ å®¡æ ¸æé®', draft.fixed); |
| | | else fail('èç¨¿è¡æé®ä¸ç¬¦', draft.fixed); |
| | | } |
| | | if (approving) { |
| | | const hasBoth = |
| | | approving.fixed.includes('å®¡æ ¸ä¸éè¿') && |
| | | /å®¡æ ¸(?!\s*ä¸éè¿)/.test(approving.fixed.replace('å®¡æ ¸ä¸éè¿', '')); |
| | | const noSubmitEdit = !/æäº¤/.test(approving.fixed) && !/(ä¿®æ¹|ç¼è¾)/.test(approving.fixed); |
| | | if (hasBoth && noSubmitEdit) ok('审æ¹ä¸è¡ï¼æãå®¡æ ¸ããå®¡æ ¸ä¸éè¿ãï¼æ æäº¤/ä¿®æ¹', approving.fixed); |
| | | else fail('审æ¹ä¸è¡æé®ä¸ç¬¦', approving.fixed); |
| | | } |
| | | |
| | | // 4. èµ°ä¸éå®¡æ ¸ä¸éè¿å¼¹çªï¼å«å¿
å¡«æ ¡éªï¼ |
| | | if (approving) { |
| | | console.log('\n[4] å®¡æ ¸ä¸éè¿å¼¹çª'); |
| | | await approving.actionRow.getByText('å®¡æ ¸ä¸éè¿', { exact: true }).first().click(); |
| | | await page.waitForTimeout(1200); |
| | | const dialog = page.locator('[role="dialog"]').last(); |
| | | if ((await dialog.count()) === 0) { |
| | | fail('æªå¼¹åºå®¡æ ¸å¼¹çª'); |
| | | } else { |
| | | const dlgText = (await dialog.innerText()).replace(/\s+/g, ' '); |
| | | ok('å¼¹çªå·²æå¼', dlgText.slice(0, 120)); |
| | | const btns = await dialog.locator('button').allInnerTexts(); |
| | | console.log(' å¼¹çªæé®:', JSON.stringify(btns.map((b) => b.trim()).filter(Boolean))); |
| | | |
| | | // 空åå ç´æ¥ç¡®è®¤ï¼åºè¢«å端å¿
å¡«æ¦ä½ï¼å¼¹çªä¸å
³ï¼ |
| | | const confirmBtn = dialog |
| | | .locator('button') |
| | | .filter({ hasText: /ç¡®\s*认|ç¡®\s*å®|æ\s*交|OK/ }) |
| | | .last(); |
| | | await confirmBtn.click(); |
| | | await page.waitForTimeout(1200); |
| | | const stillOpen = (await page.locator('[role="dialog"]').count()) > 0; |
| | | if (stillOpen) ok('æªå¡«åå æ¶è¢«å¿
å¡«æ ¡éªæ¦ä¸'); |
| | | else fail('æªå¡«åå ç«æäº¤æåï¼åç«¯æ ¡éªç¼ºå¤±ï¼'); |
| | | |
| | | // å¡«åå å确认 |
| | | await dialog.locator('textarea').first().fill('UIæµè¯ï¼åä»·åé«ï¼éåéè®®'); |
| | | const resp = page |
| | | .waitForResponse( |
| | | (r) => r.url().includes('/purchase/order/audit'), |
| | | { timeout: 15000 }, |
| | | ) |
| | | .catch(() => null); |
| | | await confirmBtn.click(); |
| | | const res = await resp; |
| | | if (!res) fail('æªæè· audit ååº'); |
| | | else { |
| | | const body = await res.json().catch(() => null); |
| | | if (body?.code === 0) ok('驳åæäº¤æå', JSON.stringify(body)); |
| | | else fail('驳å失败', JSON.stringify(body)); |
| | | } |
| | | await page.waitForTimeout(2000); |
| | | } |
| | | await shot(page, 'po-03-after-reject'); |
| | | |
| | | // 5. ç¶æåºå为审æ¹ä¸éè¿ï¼ä¸å¯éæ°ä¿®æ¹ |
| | | await page.reload({ waitUntil: 'networkidle' }); |
| | | await page.waitForTimeout(3000); |
| | | const pairs2 = await rowPairs(page); |
| | | const mine2 = pairs2.filter((p) => p.main.includes(seeded.supplierName)); |
| | | const rejected = mine2.find((p) => p.fixed.includes('审æ¹ä¸éè¿')); |
| | | if (rejected) { |
| | | ok('驳å忏²æä¸ºã审æ¹ä¸éè¿ã', rejected.fixed); |
| | | const canResubmit = /æäº¤/.test(rejected.fixed) && /(ä¿®æ¹|ç¼è¾)/.test(rejected.fixed); |
| | | const noAudit = !rejected.fixed.includes('å®¡æ ¸'); |
| | | if (canResubmit && noAudit) ok('驳ååå¯ãä¿®æ¹ãå¹¶å¯éæ°ãæäº¤ãï¼ä¸ä¸åæ¾ç¤ºå®¡æ ¸æé®', rejected.fixed); |
| | | else fail('驳ååå
¥å£ä¸ç¬¦', rejected.fixed); |
| | | } else { |
| | | fail('æªæ¾å°å®¡æ¹ä¸éè¿çç§å订å', mine2.map((p) => p.fixed).join(' | ')); |
| | | } |
| | | await shot(page, 'po-04-rejected'); |
| | | } |
| | | |
| | | console.log(`\nï¼ç§ååæ® idï¼è稿=${seeded.draftId} 审æ¹ä¸â驳å=${seeded.approvingId}ï¼`); |
| | | } catch (e) { |
| | | fail('æ§è¡å¼å¸¸', e.message); |
| | | await shot(page, 'po-99-crash').catch(() => {}); |
| | | } |
| | | |
| | | console.log('\n===== æè·å°çæ¥å£ä¸å¡é误 ====='); |
| | | const relevant = apiErrors.filter( |
| | | (e) => |
| | | !e.url.includes('/auth/login') && |
| | | !e.url.includes('simple-list') && |
| | | !e.url.includes('/dict') && |
| | | !e.url.includes('unread-count'), |
| | | ); |
| | | if (relevant.length === 0) console.log(' ï¼æ ï¼'); |
| | | relevant.forEach((e) => console.log(` ${e.url} â code=${e.code} msg=${e.msg}`)); |
| | | |
| | | await browser.close(); |
| | | const passed = results.filter((r) => r.pass).length; |
| | | console.log(`\n===== æ±æ»ï¼${passed}/${results.length} éè¿ =====`); |
| | | results.filter((r) => !r.pass).forEach((r) => console.log(` 失败: ${r.name} ${r.extra}`)); |
| | | process.exit(passed === results.length ? 0 : 1); |
| | | })(); |
| | |
| | | -- ç®æ åºï¼mom-xgdl |
| | | -- 说æï¼æ¬èæ¬å¹çï¼å¯é夿§è¡ï¼å·²åå¨çåæ®µ/èåä¼èªå¨è·³è¿ã |
| | | -- å¼ååºï¼192.168.197.128ï¼å·²å
¨é¨æ§è¡è¿ï¼æ¬èæ¬ç¨äºåå
¶å®ç¯å¢ï¼å¦æµè¯/ç产ï¼åæ¥ã |
| | | -- å
å®¹ç»æï¼ |
| | | -- 第ä¸é¨å 20 å¼ åæ®è¡¨çå®¡æ ¸äººååæ®µ |
| | | -- 第äºé¨å ä¸è¿°åæ®çãå®¡æ ¸ãæé®æéï¼system_menu type=3ï¼ |
| | | -- 第ä¸é¨å çµåä½ä¸è®¡åçæå·¥åç¼ç è§å说æï¼æ éæ§è¡ï¼ |
| | | -- 第åé¨å éç¨å®¡æ¹é
置表 + 26 个ä¸å¡ç±»å + 审æ¹é
置页èå |
| | | -- + éè´è®¡å/éè´è®¢åå®¡æ ¸åæ®µä¸æé® + è§è²ææ |
| | | -- |
| | | -- æ§è¡åç½®è¦æ±ï¼å¿
é¡»ç¨æ¯æ DELIMITER ä¸åå¨è¿ç¨ç客æ·ç«¯æ´ä½æ§è¡ |
| | | -- ï¼mysql CLIï¼mysql -h<host> -P<port> -u<user> -p mom-xgdl < æ¬æä»¶ï¼ã |
| | | -- 夿°å¾å½¢å/å¨çº¿ SQL æ§å¶å°æåå·æåè¯å¥ï¼ä¼æ DELIMITER $$ æ®µè½æåã |
| | | -- |
| | | -- 对åºä»£ç æäº¤ï¼ |
| | | -- refactor(crm/erp/hrm/mes): åæ®å®¡æ ¸æ¹ä¸ºå®¡æ¹é
ç½®ç¶ææº |
| | | -- fix(mes): çµåä½ä¸è®¡åçæå·¥åè¡¥å
ç¼ç è§ååå· |
| | | -- e347fd6e refactor(approval): éè´è®¡å/éè´è®¢å审æ¹ç± BPM æ¹ä¸ºè½»éçº§ç¶ææº |
| | | -- ============================================================================ |
| | | |
| | | SET NAMES utf8mb4; |
| | |
| | | SELECT t.name, t.perm, 3, 99, a.parent_id, '', '', NULL, NULL, 0, b'1', b'1', b'1', '1', NOW(), '1', NOW(), b'0' |
| | | FROM (SELECT 'éæ·ç³è¯·å®¡æ ¸' AS name, 'crm:cancellation:audit' AS perm, 'crm:cancellation:update' AS anchor) t |
| | | JOIN (SELECT MIN(x.parent_id) AS parent_id FROM system_menu x WHERE x.deleted = 0 AND x.permission = 'crm:cancellation:update') a ON 1 = 1 |
| | | WHERE NOT EXISTS (SELECT 1 FROM (SELECT id FROM system_menu WHERE deleted = 0 AND permission = 'crm:cancellation:audit') m); |
| | | WHERE a.parent_id IS NOT NULL AND NOT EXISTS (SELECT 1 FROM (SELECT id FROM system_menu WHERE deleted = 0 AND permission = 'crm:cancellation:audit') m); |
| | | |
| | | INSERT INTO system_menu (name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) |
| | | SELECT t.name, t.perm, 3, 99, a.parent_id, '', '', NULL, NULL, 0, b'1', b'1', b'1', '1', NOW(), '1', NOW(), b'0' |
| | | FROM (SELECT 'ååå®¡æ ¸' AS name, 'crm:contract:audit' AS perm) t |
| | | JOIN (SELECT MIN(x.parent_id) AS parent_id FROM system_menu x WHERE x.deleted = 0 AND x.permission = 'crm:contract:update') a ON 1 = 1 |
| | | WHERE NOT EXISTS (SELECT 1 FROM (SELECT id FROM system_menu WHERE deleted = 0 AND permission = 'crm:contract:audit') m); |
| | | WHERE a.parent_id IS NOT NULL AND NOT EXISTS (SELECT 1 FROM (SELECT id FROM system_menu WHERE deleted = 0 AND permission = 'crm:contract:audit') m); |
| | | |
| | | INSERT INTO system_menu (name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) |
| | | SELECT t.name, t.perm, 3, 99, a.parent_id, '', '', NULL, NULL, 0, b'1', b'1', b'1', '1', NOW(), '1', NOW(), b'0' |
| | | FROM (SELECT 'åç¥¨å®¡æ ¸' AS name, 'crm:invoice:audit' AS perm) t |
| | | JOIN (SELECT MIN(x.parent_id) AS parent_id FROM system_menu x WHERE x.deleted = 0 AND x.permission = 'crm:invoice:update') a ON 1 = 1 |
| | | WHERE NOT EXISTS (SELECT 1 FROM (SELECT id FROM system_menu WHERE deleted = 0 AND permission = 'crm:invoice:audit') m); |
| | | WHERE a.parent_id IS NOT NULL AND NOT EXISTS (SELECT 1 FROM (SELECT id FROM system_menu WHERE deleted = 0 AND permission = 'crm:invoice:audit') m); |
| | | |
| | | INSERT INTO system_menu (name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) |
| | | SELECT t.name, t.perm, 3, 99, a.parent_id, '', '', NULL, NULL, 0, b'1', b'1', b'1', '1', NOW(), '1', NOW(), b'0' |
| | | FROM (SELECT 'éè´¹åå®¡æ ¸' AS name, 'crm:refund:audit' AS perm) t |
| | | JOIN (SELECT MIN(x.parent_id) AS parent_id FROM system_menu x WHERE x.deleted = 0 AND x.permission = 'crm:refund:update') a ON 1 = 1 |
| | | WHERE NOT EXISTS (SELECT 1 FROM (SELECT id FROM system_menu WHERE deleted = 0 AND permission = 'crm:refund:audit') m); |
| | | WHERE a.parent_id IS NOT NULL AND NOT EXISTS (SELECT 1 FROM (SELECT id FROM system_menu WHERE deleted = 0 AND permission = 'crm:refund:audit') m); |
| | | |
| | | INSERT INTO system_menu (name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) |
| | | SELECT t.name, t.perm, 3, 99, a.parent_id, '', '', NULL, NULL, 0, b'1', b'1', b'1', '1', NOW(), '1', NOW(), b'0' |
| | | FROM (SELECT '仿¬¾åå®¡æ ¸' AS name, 'erp:finance-payment:audit' AS perm) t |
| | | JOIN (SELECT MIN(x.parent_id) AS parent_id FROM system_menu x WHERE x.deleted = 0 AND x.permission = 'erp:finance-payment:update') a ON 1 = 1 |
| | | WHERE NOT EXISTS (SELECT 1 FROM (SELECT id FROM system_menu WHERE deleted = 0 AND permission = 'erp:finance-payment:audit') m); |
| | | WHERE a.parent_id IS NOT NULL AND NOT EXISTS (SELECT 1 FROM (SELECT id FROM system_menu WHERE deleted = 0 AND permission = 'erp:finance-payment:audit') m); |
| | | |
| | | INSERT INTO system_menu (name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) |
| | | SELECT t.name, t.perm, 3, 99, a.parent_id, '', '', NULL, NULL, 0, b'1', b'1', b'1', '1', NOW(), '1', NOW(), b'0' |
| | | FROM (SELECT 'éè´ç³è¯·å®¡æ ¸' AS name, 'erp:purchase-request:audit' AS perm) t |
| | | JOIN (SELECT MIN(x.parent_id) AS parent_id FROM system_menu x WHERE x.deleted = 0 AND x.permission = 'erp:purchase-request:update') a ON 1 = 1 |
| | | WHERE NOT EXISTS (SELECT 1 FROM (SELECT id FROM system_menu WHERE deleted = 0 AND permission = 'erp:purchase-request:audit') m); |
| | | WHERE a.parent_id IS NOT NULL AND NOT EXISTS (SELECT 1 FROM (SELECT id FROM system_menu WHERE deleted = 0 AND permission = 'erp:purchase-request:audit') m); |
| | | |
| | | INSERT INTO system_menu (name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) |
| | | SELECT t.name, t.perm, 3, 99, a.parent_id, '', '', NULL, NULL, 0, b'1', b'1', b'1', '1', NOW(), '1', NOW(), b'0' |
| | | FROM (SELECT '请åç³è¯·å®¡æ ¸' AS name, 'hrm:leave:audit' AS perm) t |
| | | JOIN (SELECT MIN(x.parent_id) AS parent_id FROM system_menu x WHERE x.deleted = 0 AND x.permission = 'hrm:leave:update') a ON 1 = 1 |
| | | WHERE NOT EXISTS (SELECT 1 FROM (SELECT id FROM system_menu WHERE deleted = 0 AND permission = 'hrm:leave:audit') m); |
| | | WHERE a.parent_id IS NOT NULL AND NOT EXISTS (SELECT 1 FROM (SELECT id FROM system_menu WHERE deleted = 0 AND permission = 'hrm:leave:audit') m); |
| | | |
| | | INSERT INTO system_menu (name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) |
| | | SELECT t.name, t.perm, 3, 99, a.parent_id, '', '', NULL, NULL, 0, b'1', b'1', b'1', '1', NOW(), '1', NOW(), b'0' |
| | | FROM (SELECT '转æ£ç³è¯·å®¡æ ¸' AS name, 'hrm:regularize:audit' AS perm) t |
| | | JOIN (SELECT MIN(x.parent_id) AS parent_id FROM system_menu x WHERE x.deleted = 0 AND x.permission = 'hrm:regularize:update') a ON 1 = 1 |
| | | WHERE NOT EXISTS (SELECT 1 FROM (SELECT id FROM system_menu WHERE deleted = 0 AND permission = 'hrm:regularize:audit') m); |
| | | WHERE a.parent_id IS NOT NULL AND NOT EXISTS (SELECT 1 FROM (SELECT id FROM system_menu WHERE deleted = 0 AND permission = 'hrm:regularize:audit') m); |
| | | |
| | | INSERT INTO system_menu (name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) |
| | | SELECT t.name, t.perm, 3, 99, a.parent_id, '', '', NULL, NULL, 0, b'1', b'1', b'1', '1', NOW(), '1', NOW(), b'0' |
| | | FROM (SELECT '离èç³è¯·å®¡æ ¸' AS name, 'hrm:resignation:audit' AS perm) t |
| | | JOIN (SELECT MIN(x.parent_id) AS parent_id FROM system_menu x WHERE x.deleted = 0 AND x.permission = 'hrm:resignation:update') a ON 1 = 1 |
| | | WHERE NOT EXISTS (SELECT 1 FROM (SELECT id FROM system_menu WHERE deleted = 0 AND permission = 'hrm:resignation:audit') m); |
| | | WHERE a.parent_id IS NOT NULL AND NOT EXISTS (SELECT 1 FROM (SELECT id FROM system_menu WHERE deleted = 0 AND permission = 'hrm:resignation:audit') m); |
| | | |
| | | INSERT INTO system_menu (name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) |
| | | SELECT t.name, t.perm, 3, 99, a.parent_id, '', '', NULL, NULL, 0, b'1', b'1', b'1', '1', NOW(), '1', NOW(), b'0' |
| | | FROM (SELECT 'è°å¨ç³è¯·å®¡æ ¸' AS name, 'hrm:transfer:audit' AS perm) t |
| | | JOIN (SELECT MIN(x.parent_id) AS parent_id FROM system_menu x WHERE x.deleted = 0 AND x.permission = 'hrm:transfer:update') a ON 1 = 1 |
| | | WHERE NOT EXISTS (SELECT 1 FROM (SELECT id FROM system_menu WHERE deleted = 0 AND permission = 'hrm:transfer:audit') m); |
| | | WHERE a.parent_id IS NOT NULL AND NOT EXISTS (SELECT 1 FROM (SELECT id FROM system_menu WHERE deleted = 0 AND permission = 'hrm:transfer:audit') m); |
| | | |
| | | INSERT INTO system_menu (name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) |
| | | SELECT t.name, t.perm, 3, 99, a.parent_id, '', '', NULL, NULL, 0, b'1', b'1', b'1', '1', NOW(), '1', NOW(), b'0' |
| | | FROM (SELECT 'èå¤å¼å¸¸å®¡æ ¸' AS name, 'hrm:attendance-exception:audit' AS perm) t |
| | | JOIN (SELECT MIN(x.parent_id) AS parent_id FROM system_menu x WHERE x.deleted = 0 AND x.permission = 'hrm:attendance-exception:update') a ON 1 = 1 |
| | | WHERE NOT EXISTS (SELECT 1 FROM (SELECT id FROM system_menu WHERE deleted = 0 AND permission = 'hrm:attendance-exception:audit') m); |
| | | WHERE a.parent_id IS NOT NULL AND NOT EXISTS (SELECT 1 FROM (SELECT id FROM system_menu WHERE deleted = 0 AND permission = 'hrm:attendance-exception:audit') m); |
| | | |
| | | INSERT INTO system_menu (name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) |
| | | SELECT t.name, t.perm, 3, 99, a.parent_id, '', '', NULL, NULL, 0, b'1', b'1', b'1', '1', NOW(), '1', NOW(), b'0' |
| | | FROM (SELECT '项ç®å®¡æ ¸' AS name, 'mes:pd-project:audit' AS perm) t |
| | | JOIN (SELECT MIN(x.parent_id) AS parent_id FROM system_menu x WHERE x.deleted = 0 AND x.permission = 'mes:pd-project:update') a ON 1 = 1 |
| | | WHERE NOT EXISTS (SELECT 1 FROM (SELECT id FROM system_menu WHERE deleted = 0 AND permission = 'mes:pd-project:audit') m); |
| | | WHERE a.parent_id IS NOT NULL AND NOT EXISTS (SELECT 1 FROM (SELECT id FROM system_menu WHERE deleted = 0 AND permission = 'mes:pd-project:audit') m); |
| | | |
| | | INSERT INTO system_menu (name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) |
| | | SELECT t.name, t.perm, 3, 99, a.parent_id, '', '', NULL, NULL, 0, b'1', b'1', b'1', '1', NOW(), '1', NOW(), b'0' |
| | | FROM (SELECT 'ææ¯ç¡®è®¤å½å®¡æ ¸' AS name, 'mes:pd-tech-confirm:audit' AS perm) t |
| | | JOIN (SELECT MIN(x.parent_id) AS parent_id FROM system_menu x WHERE x.deleted = 0 AND x.permission = 'mes:pd-tech-confirm:update') a ON 1 = 1 |
| | | WHERE NOT EXISTS (SELECT 1 FROM (SELECT id FROM system_menu WHERE deleted = 0 AND permission = 'mes:pd-tech-confirm:audit') m); |
| | | WHERE a.parent_id IS NOT NULL AND NOT EXISTS (SELECT 1 FROM (SELECT id FROM system_menu WHERE deleted = 0 AND permission = 'mes:pd-tech-confirm:audit') m); |
| | | |
| | | INSERT INTO system_menu (name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) |
| | | SELECT t.name, t.perm, 3, 99, a.parent_id, '', '', NULL, NULL, 0, b'1', b'1', b'1', '1', NOW(), '1', NOW(), b'0' |
| | | FROM (SELECT 'éè´å
¥åºåå®¡æ ¸' AS name, 'mes:wm-item-receipt:audit' AS perm) t |
| | | JOIN (SELECT MIN(x.parent_id) AS parent_id FROM system_menu x WHERE x.deleted = 0 AND x.permission = 'mes:wm-item-receipt:update') a ON 1 = 1 |
| | | WHERE NOT EXISTS (SELECT 1 FROM (SELECT id FROM system_menu WHERE deleted = 0 AND permission = 'mes:wm-item-receipt:audit') m); |
| | | WHERE a.parent_id IS NOT NULL AND NOT EXISTS (SELECT 1 FROM (SELECT id FROM system_menu WHERE deleted = 0 AND permission = 'mes:wm-item-receipt:audit') m); |
| | | |
| | | INSERT INTO system_menu (name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) |
| | | SELECT t.name, t.perm, 3, 99, a.parent_id, '', '', NULL, NULL, 0, b'1', b'1', b'1', '1', NOW(), '1', NOW(), b'0' |
| | | FROM (SELECT 'æé¡¹åºåºåå®¡æ ¸' AS name, 'mes:wm-misc-issue:audit' AS perm) t |
| | | JOIN (SELECT MIN(x.parent_id) AS parent_id FROM system_menu x WHERE x.deleted = 0 AND x.permission = 'mes:wm-misc-issue:update') a ON 1 = 1 |
| | | WHERE NOT EXISTS (SELECT 1 FROM (SELECT id FROM system_menu WHERE deleted = 0 AND permission = 'mes:wm-misc-issue:audit') m); |
| | | WHERE a.parent_id IS NOT NULL AND NOT EXISTS (SELECT 1 FROM (SELECT id FROM system_menu WHERE deleted = 0 AND permission = 'mes:wm-misc-issue:audit') m); |
| | | |
| | | INSERT INTO system_menu (name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) |
| | | SELECT t.name, t.perm, 3, 99, a.parent_id, '', '', NULL, NULL, 0, b'1', b'1', b'1', '1', NOW(), '1', NOW(), b'0' |
| | | FROM (SELECT 'éè´éè´§åå®¡æ ¸' AS name, 'mes:wm-return-vendor:audit' AS perm) t |
| | | JOIN (SELECT MIN(x.parent_id) AS parent_id FROM system_menu x WHERE x.deleted = 0 AND x.permission = 'mes:wm-return-vendor:update') a ON 1 = 1 |
| | | WHERE NOT EXISTS (SELECT 1 FROM (SELECT id FROM system_menu WHERE deleted = 0 AND permission = 'mes:wm-return-vendor:audit') m); |
| | | WHERE a.parent_id IS NOT NULL AND NOT EXISTS (SELECT 1 FROM (SELECT id FROM system_menu WHERE deleted = 0 AND permission = 'mes:wm-return-vendor:audit') m); |
| | | |
| | | INSERT INTO system_menu (name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) |
| | | SELECT t.name, t.perm, 3, 99, a.parent_id, '', '', NULL, NULL, 0, b'1', b'1', b'1', '1', NOW(), '1', NOW(), b'0' |
| | | FROM (SELECT 'çç¹ä»»å¡å®¡æ ¸' AS name, 'mes:wm-stock-taking-task:audit' AS perm) t |
| | | JOIN (SELECT MIN(x.parent_id) AS parent_id FROM system_menu x WHERE x.deleted = 0 AND x.permission = 'mes:wm-stock-taking-task:update') a ON 1 = 1 |
| | | WHERE NOT EXISTS (SELECT 1 FROM (SELECT id FROM system_menu WHERE deleted = 0 AND permission = 'mes:wm-stock-taking-task:audit') m); |
| | | WHERE a.parent_id IS NOT NULL AND NOT EXISTS (SELECT 1 FROM (SELECT id FROM system_menu WHERE deleted = 0 AND permission = 'mes:wm-stock-taking-task:audit') m); |
| | | |
| | | INSERT INTO system_menu (name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) |
| | | SELECT t.name, t.perm, 3, 99, a.parent_id, '', '', NULL, NULL, 0, b'1', b'1', b'1', '1', NOW(), '1', NOW(), b'0' |
| | | FROM (SELECT '设å¤å
¥åºåå®¡æ ¸' AS name, 'mes:wm:equipment-receipt:audit' AS perm) t |
| | | JOIN (SELECT MIN(x.parent_id) AS parent_id FROM system_menu x WHERE x.deleted = 0 AND x.permission = 'mes:wm:equipment-receipt:update') a ON 1 = 1 |
| | | WHERE NOT EXISTS (SELECT 1 FROM (SELECT id FROM system_menu WHERE deleted = 0 AND permission = 'mes:wm:equipment-receipt:audit') m); |
| | | WHERE a.parent_id IS NOT NULL AND NOT EXISTS (SELECT 1 FROM (SELECT id FROM system_menu WHERE deleted = 0 AND permission = 'mes:wm:equipment-receipt:audit') m); |
| | | |
| | | INSERT INTO system_menu (name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) |
| | | SELECT t.name, t.perm, 3, 99, a.parent_id, '', '', NULL, NULL, 0, b'1', b'1', b'1', '1', NOW(), '1', NOW(), b'0' |
| | | FROM (SELECT 'æé¡¹å
¥åºåå®¡æ ¸' AS name, 'mes:wm:misc-receipt:audit' AS perm) t |
| | | JOIN (SELECT MIN(x.parent_id) AS parent_id FROM system_menu x WHERE x.deleted = 0 AND x.permission = 'mes:wm:misc-receipt:update') a ON 1 = 1 |
| | | WHERE NOT EXISTS (SELECT 1 FROM (SELECT id FROM system_menu WHERE deleted = 0 AND permission = 'mes:wm:misc-receipt:audit') m); |
| | | WHERE a.parent_id IS NOT NULL AND NOT EXISTS (SELECT 1 FROM (SELECT id FROM system_menu WHERE deleted = 0 AND permission = 'mes:wm:misc-receipt:audit') m); |
| | | |
| | | -- ============================================================================ |
| | | -- 第ä¸é¨åï¼çµåä½ä¸è®¡åçæå·¥å ââ å·¥åç¼ç åå·è§åï¼æ åæ´ï¼ä»
说æï¼ |
| | |
| | | -- ï¼æ³¨æï¼è¯¥èæ¬ä¼ TRUNC å¼é建è§å表ï¼ä»
éç¨äºé¦æ¬¡åå§åï¼å¿å¨å·²ææ°æ®çç¯å¢æ§è¡ï¼ã |
| | | -- ============================================================================ |
| | | |
| | | -- ============================================================================ |
| | | -- 第åé¨åï¼éç¨å®¡æ¹é
ç½®ï¼å®¡æ¹å¼æçå°åºï¼ |
| | | -- |
| | | -- åä¸é¨ååªå äºãå®¡æ ¸äººåæ®µãåãå®¡æ ¸æé®æéãï¼ä½æ²¡æå»ºå®¡æ¹é
置表æ¬èº«ã |
| | | -- åªè·åä¸é¨åçè¯ï¼ææåæ®çãæäº¤ãé½ä¼å¨ validateApprovalEnabledAndGetApprovers |
| | | -- å¤å¤±è´¥ï¼å®¡æ¹é
ç½®ä¸åå¨ / æªå¯ç¨ / æªé
置审æ¹äººï¼ãæ¬é¨åè¡¥é½ï¼ |
| | | -- 4.1 审æ¹é
置主表 + 审æ¹äººå表 |
| | | -- 4.2 26 个ä¸å¡ç±»åç§åï¼é»è®¤æªå¯ç¨ï¼ |
| | | -- 4.3 审æ¹é
置页é¢èå䏿¥è¯¢/ä¿åæé® |
| | | -- 4.4 éè´è®¡åãéè´è®¢åçå®¡æ ¸äººååæ®µ |
| | | -- 4.5 éè´è®¡åãéè´è®¢åçå®¡æ ¸æé®æé |
| | | -- 4.6 è§è²ææï¼æ¬æ¡æ¶æ ãè¶
管ç»è¿æéãé»è¾ï¼ä¸ææå admin ä¹çä¸å°æé®ï¼ |
| | | -- |
| | | -- 对åºä»£ç æäº¤ï¼e347fd6e refactor(approval): éè´è®¡å/éè´è®¢å审æ¹ç± BPM æ¹ä¸ºè½»éçº§ç¶ææº |
| | | -- ============================================================================ |
| | | |
| | | -- 4.1 审æ¹é
置主表 / 审æ¹äººå表 |
| | | CREATE TABLE IF NOT EXISTS system_approval_config ( |
| | | id bigint NOT NULL AUTO_INCREMENT COMMENT '主é®ID', |
| | | biz_type varchar(64) NOT NULL COMMENT 'ä¸å¡ç±»åç¼ç ï¼ä¸å模å审æ¹åç±»ç¼ç ä¸è´', |
| | | biz_name varchar(100) NOT NULL COMMENT 'ä¸å¡ç±»ååç§°', |
| | | approval_enabled bit(1) NOT NULL DEFAULT b'0' COMMENT 'æ¯å¦å¯ç¨å®¡æ¹', |
| | | remark varchar(500) NULL DEFAULT NULL COMMENT '夿³¨', |
| | | creator varchar(64) NULL DEFAULT '' COMMENT 'å建è
', |
| | | create_time datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'å建æ¶é´', |
| | | updater varchar(64) NULL DEFAULT '' COMMENT 'æ´æ°è
', |
| | | update_time datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'æ´æ°æ¶é´', |
| | | deleted bit(1) NOT NULL DEFAULT b'0' COMMENT 'æ¯å¦å é¤', |
| | | PRIMARY KEY (id) USING BTREE, |
| | | KEY idx_biz_type (biz_type) USING BTREE |
| | | ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'éç¨å®¡æ¹é
置表'; |
| | | |
| | | CREATE TABLE IF NOT EXISTS system_approval_config_user ( |
| | | id bigint NOT NULL AUTO_INCREMENT COMMENT '主é®ID', |
| | | config_id bigint NOT NULL COMMENT '审æ¹é
ç½®IDï¼å
³è system_approval_config.idï¼', |
| | | user_id bigint NOT NULL COMMENT '审æ¹äººç¨æ·IDï¼ç³»ç»ç¨æ·ï¼', |
| | | sort int NOT NULL DEFAULT 0 COMMENT 'æ¾ç¤ºé¡ºåº', |
| | | creator varchar(64) NULL DEFAULT '' COMMENT 'å建è
', |
| | | create_time datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'å建æ¶é´', |
| | | updater varchar(64) NULL DEFAULT '' COMMENT 'æ´æ°è
', |
| | | update_time datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'æ´æ°æ¶é´', |
| | | deleted bit(1) NOT NULL DEFAULT b'0' COMMENT 'æ¯å¦å é¤', |
| | | PRIMARY KEY (id) USING BTREE, |
| | | KEY idx_config_id (config_id) USING BTREE, |
| | | KEY idx_user_id (user_id) USING BTREE |
| | | ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'éç¨å®¡æ¹é
ç½®-审æ¹äººè¡¨'; |
| | | |
| | | -- 4.2 ä¸å¡ç±»åç§åï¼ç¼ç ä¸å模å *_APPROVE_* 常éä¸ä¸å¯¹åºï¼é»è®¤å
¨é¨æªå¯ç¨ |
| | | INSERT IGNORE INTO system_approval_config |
| | | (biz_type, biz_name, approval_enabled, remark, creator, create_time, updater, update_time, deleted) VALUES |
| | | ('purchase_plan_approve', 'éè´è®¡åå®¡æ ¸', b'0', 'ERP éè´è®¡å', '1', NOW(), '1', NOW(), b'0'), |
| | | ('purchase_request_approve', 'éè´ç³è¯·å®¡æ ¸', b'0', 'ERP éè´ç³è¯·', '1', NOW(), '1', NOW(), b'0'), |
| | | ('erp_purchase_order_approve', 'éè´è®¢åå®¡æ ¸', b'0', 'ERP éè´è®¢åï¼åè¯¯ç¨ purchase_request_approveï¼å·²æ¹ä¸ºç¬ç«ç¼ç ï¼', '1', NOW(), '1', NOW(), b'0'), |
| | | ('erp_purchase_invoice_approve', 'éè´æ¥ç¥¨å®¡æ ¸', b'0', 'ERP éè´æ¥ç¥¨', '1', NOW(), '1', NOW(), b'0'), |
| | | ('erp_finance_payment_approve', '仿¬¾åå®¡æ ¸', b'0', 'ERP 仿¬¾å', '1', NOW(), '1', NOW(), b'0'), |
| | | ('contract_approve', 'åå审æ¹', b'0', 'CRM åå', '1', NOW(), '1', NOW(), b'0'), |
| | | ('refund_approve', 'é费审æ¹', b'0', 'CRM 鿬¾', '1', NOW(), '1', NOW(), b'0'), |
| | | ('cancellation_approve', '鿷审æ¹', b'0', 'CRM éæ·', '1', NOW(), '1', NOW(), b'0'), |
| | | ('crm_invoice_approve', 'å¼ç¥¨å®¡æ¹', b'0', 'CRM å票', '1', NOW(), '1', NOW(), b'0'), |
| | | ('sale_quotation_approve', 'é宿¥ä»·å®¡æ¹', b'0', 'CRM é宿¥ä»·', '1', NOW(), '1', NOW(), b'0'), |
| | | ('hrm_leave_approve', '请å审æ¹', b'0', 'HRM 请åç³è¯·', '1', NOW(), '1', NOW(), b'0'), |
| | | ('hrm_regularize_approve', '转æ£å®¡æ¹', b'0', 'HRM 转æ£ç³è¯·', '1', NOW(), '1', NOW(), b'0'), |
| | | ('hrm_resignation_approve', '离è审æ¹', b'0', 'HRM 离èç³è¯·', '1', NOW(), '1', NOW(), b'0'), |
| | | ('hrm_transfer_approve', 'è°å¨å®¡æ¹', b'0', 'HRM è°å¨ç³è¯·', '1', NOW(), '1', NOW(), b'0'), |
| | | ('hrm_attendance_approve', 'èå¤å¼å¸¸å®¡æ¹', b'0', 'HRM èå¤å¼å¸¸', '1', NOW(), '1', NOW(), b'0'), |
| | | ('performance_appraise_approve', '绩æèæ ¸å®¡æ¹', b'0', 'HRM 绩æèæ ¸', '1', NOW(), '1', NOW(), b'0'), |
| | | ('mes_pd_project_approve', '产å项ç®å®¡æ¹', b'0', 'MES 产å项ç®', '1', NOW(), '1', NOW(), b'0'), |
| | | ('pd_tech_confirm_approve', 'ææ¯ç¡®è®¤å½å®¡æ¹', b'0', 'MES äº§åææ¯ç¡®è®¤å½', '1', NOW(), '1', NOW(), b'0'), |
| | | ('item_receipt_approve', 'éè´å
¥åºå®¡æ¹', b'0', 'MES éè´å
¥åºå', '1', NOW(), '1', NOW(), b'0'), |
| | | ('equipment_receipt_approve', '设å¤å
¥åºå®¡æ¹', b'0', 'MES 设å¤å
¥åºå', '1', NOW(), '1', NOW(), b'0'), |
| | | ('misc_issue_approve', 'å
¶ä»åºåºå®¡æ¹', b'0', 'MES å
¶ä»åºåºå', '1', NOW(), '1', NOW(), b'0'), |
| | | ('misc_receipt_approve', 'å
¶ä»å
¥åºå®¡æ¹', b'0', 'MES å
¶ä»å
¥åºå', '1', NOW(), '1', NOW(), b'0'), |
| | | ('return_vendor_approve', 'é货审æ¹', b'0', 'MES éè´§å', '1', NOW(), '1', NOW(), b'0'), |
| | | ('stock_taking_result_approve', 'çç¹ç»æå®¡æ¹', b'0', 'MES çç¹å', '1', NOW(), '1', NOW(), b'0'), |
| | | ('wms_approve', 'ä»åºåæ®å®¡æ ¸', b'0', 'MES ä»å¨éç¨å®¡æ¹', '1', NOW(), '1', NOW(), b'0'), |
| | | ('compensation_approve', 'èµä»å®¡æ¹', b'0', 'å®åèµå¿å', '1', NOW(), '1', NOW(), b'0'); |
| | | |
| | | -- 4.3 审æ¹é
置页é¢èå |
| | | -- ç¶çº§éå®ãç³»ç»ç®¡çãï¼å system/user/index ç parent_idï¼é¿å
硬ç¼ç ç®å½ ID |
| | | INSERT INTO system_menu (name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) |
| | | SELECT '审æ¹é
ç½®', '', 2, 8, a.parent_id, 'approval-config', 'ep:stamp', 'system/approvalConfig/index', 'SystemApprovalConfig', 0, b'1', b'1', b'1', '1', NOW(), '1', NOW(), b'0' |
| | | FROM (SELECT MIN(m.parent_id) AS parent_id FROM system_menu m WHERE m.deleted = 0 AND m.component = 'system/user/index') a |
| | | WHERE a.parent_id IS NOT NULL |
| | | AND NOT EXISTS (SELECT 1 FROM (SELECT id FROM system_menu WHERE deleted = 0 AND component = 'system/approvalConfig/index') x); |
| | | |
| | | -- 4.3.1 审æ¹é
ç½®æ¥è¯¢æé®ï¼ç¶çº§ = ä¸é¢ç页é¢èåï¼æ component åæ¥ï¼ |
| | | INSERT INTO system_menu (name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) |
| | | SELECT '审æ¹é
ç½®æ¥è¯¢', 'system:approval-config:query', 3, 1, a.parent_id, '', '', NULL, NULL, 0, b'1', b'1', b'1', '1', NOW(), '1', NOW(), b'0' |
| | | FROM (SELECT MIN(m.id) AS parent_id FROM system_menu m WHERE m.deleted = 0 AND m.component = 'system/approvalConfig/index') a |
| | | WHERE a.parent_id IS NOT NULL |
| | | AND NOT EXISTS (SELECT 1 FROM (SELECT id FROM system_menu WHERE deleted = 0 AND permission = 'system:approval-config:query') x); |
| | | |
| | | -- 4.3.2 审æ¹é
ç½®ä¿åæé® |
| | | INSERT INTO system_menu (name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) |
| | | SELECT '审æ¹é
ç½®ä¿å', 'system:approval-config:update', 3, 2, a.parent_id, '', '', NULL, NULL, 0, b'1', b'1', b'1', '1', NOW(), '1', NOW(), b'0' |
| | | FROM (SELECT MIN(m.id) AS parent_id FROM system_menu m WHERE m.deleted = 0 AND m.component = 'system/approvalConfig/index') a |
| | | WHERE a.parent_id IS NOT NULL |
| | | AND NOT EXISTS (SELECT 1 FROM (SELECT id FROM system_menu WHERE deleted = 0 AND permission = 'system:approval-config:update') x); |
| | | |
| | | -- 4.4 éè´è®¡å / éè´è®¢å å®¡æ ¸äººååæ®µ |
| | | -- ï¼ç¬¬ä¸é¨ååªè¦çäºå¦å¤ 20 å¼ è¡¨ï¼è¿ä¸¤å¼ æ¯éè´è®¡å/éè´è®¢åæ¹é æ¶è¡¥çï¼ |
| | | DROP TABLE IF EXISTS _tmp_xgdl_po_review_cols; |
| | | CREATE TABLE _tmp_xgdl_po_review_cols ( |
| | | tbl VARCHAR(64) NOT NULL, |
| | | col VARCHAR(64) NOT NULL, |
| | | coldef VARCHAR(255) NOT NULL |
| | | ); |
| | | |
| | | INSERT INTO _tmp_xgdl_po_review_cols (tbl, col, coldef) VALUES |
| | | ('erp_purchase_plan', 'reviewer_id', 'bigint COMMENT ''å®¡æ ¸äººç¼å·ï¼å®é
æ§è¡å®¡æ ¸çç¨æ·ï¼'''), |
| | | ('erp_purchase_plan', 'reviewer_name', 'varchar(64) COMMENT ''å®¡æ ¸äººå§åï¼åä½è®°å½ï¼é¿å
å表æ¥è¯¢åæ¥ç¨æ·ï¼'''), |
| | | ('erp_purchase_plan', 'review_time', 'datetime COMMENT ''å®¡æ ¸æ¶é´'''), |
| | | ('erp_purchase_plan', 'review_remark', 'varchar(500) COMMENT ''å®¡æ ¸æè§ï¼éè¿æ¶çæè§ / ä¸éè¿æ¶çåå ï¼'''), |
| | | ('erp_purchase_order', 'reviewer_id', 'bigint COMMENT ''å®¡æ ¸äººç¼å·ï¼å®é
æ§è¡å®¡æ ¸çç¨æ·ï¼'''), |
| | | ('erp_purchase_order', 'reviewer_name', 'varchar(64) COMMENT ''å®¡æ ¸äººå§åï¼åä½è®°å½ï¼é¿å
å表æ¥è¯¢åæ¥ç¨æ·ï¼'''), |
| | | ('erp_purchase_order', 'review_time', 'datetime COMMENT ''å®¡æ ¸æ¶é´'''), |
| | | ('erp_purchase_order', 'review_remark', 'varchar(500) COMMENT ''å®¡æ ¸æè§ï¼éè¿æ¶çæè§ / ä¸éè¿æ¶çåå ï¼'''); |
| | | |
| | | DROP PROCEDURE IF EXISTS xgdl_add_po_review_cols; |
| | | DELIMITER $$ |
| | | CREATE PROCEDURE xgdl_add_po_review_cols() |
| | | BEGIN |
| | | DECLARE v_done INT DEFAULT 0; |
| | | DECLARE v_tbl VARCHAR(64); |
| | | DECLARE v_col VARCHAR(64); |
| | | DECLARE v_def VARCHAR(255); |
| | | DECLARE cur CURSOR FOR SELECT tbl, col, coldef FROM _tmp_xgdl_po_review_cols; |
| | | DECLARE CONTINUE HANDLER FOR NOT FOUND SET v_done = 1; |
| | | |
| | | OPEN cur; |
| | | read_loop: LOOP |
| | | FETCH cur INTO v_tbl, v_col, v_def; |
| | | IF v_done = 1 THEN |
| | | LEAVE read_loop; |
| | | END IF; |
| | | IF EXISTS (SELECT 1 FROM information_schema.tables |
| | | WHERE table_schema = DATABASE() AND table_name = v_tbl) |
| | | AND NOT EXISTS (SELECT 1 FROM information_schema.columns |
| | | WHERE table_schema = DATABASE() AND table_name = v_tbl AND column_name = v_col) |
| | | THEN |
| | | SET @ddl = CONCAT('ALTER TABLE `', v_tbl, '` ADD COLUMN `', v_col, '` ', v_def); |
| | | PREPARE stmt FROM @ddl; |
| | | EXECUTE stmt; |
| | | DEALLOCATE PREPARE stmt; |
| | | END IF; |
| | | END LOOP; |
| | | CLOSE cur; |
| | | END$$ |
| | | DELIMITER ; |
| | | |
| | | CALL xgdl_add_po_review_cols(); |
| | | DROP PROCEDURE IF EXISTS xgdl_add_po_review_cols; |
| | | DROP TABLE IF EXISTS _tmp_xgdl_po_review_cols; |
| | | |
| | | -- 4.5 éè´è®¡å / éè´è®¢å å®¡æ ¸æé®æéï¼éç¹å第ä¸é¨åï¼åèªèµæºç :update æé®ï¼ |
| | | INSERT INTO system_menu (name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) |
| | | SELECT t.name, t.perm, 3, 99, a.parent_id, '', '', NULL, NULL, 0, b'1', b'1', b'1', '1', NOW(), '1', NOW(), b'0' |
| | | FROM (SELECT 'éè´è®¡åå®¡æ ¸' AS name, 'erp:purchase-plan:audit' AS perm) t |
| | | JOIN (SELECT MIN(x.parent_id) AS parent_id FROM system_menu x WHERE x.deleted = 0 AND x.permission = 'erp:purchase-plan:update') a ON 1 = 1 |
| | | WHERE a.parent_id IS NOT NULL AND NOT EXISTS (SELECT 1 FROM (SELECT id FROM system_menu WHERE deleted = 0 AND permission = 'erp:purchase-plan:audit') m); |
| | | |
| | | INSERT INTO system_menu (name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) |
| | | SELECT t.name, t.perm, 3, 99, a.parent_id, '', '', NULL, NULL, 0, b'1', b'1', b'1', '1', NOW(), '1', NOW(), b'0' |
| | | FROM (SELECT 'éè´è®¢åå®¡æ ¸' AS name, 'erp:purchase-order:audit' AS perm) t |
| | | JOIN (SELECT MIN(x.parent_id) AS parent_id FROM system_menu x WHERE x.deleted = 0 AND x.permission = 'erp:purchase-order:update') a ON 1 = 1 |
| | | WHERE a.parent_id IS NOT NULL AND NOT EXISTS (SELECT 1 FROM (SELECT id FROM system_menu WHERE deleted = 0 AND permission = 'erp:purchase-order:audit') m); |
| | | |
| | | -- 4.6 è¡¥æ¼ï¼ä»£ç é已使ç¨ãä½ç¬¬äºé¨åæªå建ç 3 ä¸ªå®¡æ ¸æé®æé |
| | | -- 对账æ¹å¼ï¼grep -rhoE "'[a-z]+:[a-z0-9-]+:audit'" --include=*.java |
| | | -- 缺失ä¼å¯¼è´å¯¹åºé¡µé¢çå®¡æ ¸æé®å¯¹ä»»ä½äººé½ä¸æ¾ç¤ºï¼å« adminï¼ã |
| | | INSERT INTO system_menu (name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) |
| | | SELECT t.name, t.perm, 3, 99, a.parent_id, '', '', NULL, NULL, 0, b'1', b'1', b'1', '1', NOW(), '1', NOW(), b'0' |
| | | FROM (SELECT 'æ
éèµä»å®¡æ ¸' AS name, 'aftersales:compensation:audit' AS perm) t |
| | | JOIN (SELECT MIN(x.parent_id) AS parent_id FROM system_menu x WHERE x.deleted = 0 AND x.permission = 'aftersales:compensation:update') a ON 1 = 1 |
| | | WHERE a.parent_id IS NOT NULL AND NOT EXISTS (SELECT 1 FROM (SELECT id FROM system_menu WHERE deleted = 0 AND permission = 'aftersales:compensation:audit') m); |
| | | |
| | | INSERT INTO system_menu (name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) |
| | | SELECT t.name, t.perm, 3, 99, a.parent_id, '', '', NULL, NULL, 0, b'1', b'1', b'1', '1', NOW(), '1', NOW(), b'0' |
| | | FROM (SELECT '绩æèæ ¸å®¡æ ¸' AS name, 'hrm:performance-appraise:audit' AS perm) t |
| | | JOIN (SELECT MIN(x.parent_id) AS parent_id FROM system_menu x WHERE x.deleted = 0 AND x.permission = 'hrm:performance-appraise:update') a ON 1 = 1 |
| | | WHERE a.parent_id IS NOT NULL AND NOT EXISTS (SELECT 1 FROM (SELECT id FROM system_menu WHERE deleted = 0 AND permission = 'hrm:performance-appraise:audit') m); |
| | | |
| | | INSERT INTO system_menu (name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) |
| | | SELECT t.name, t.perm, 3, 99, a.parent_id, '', '', NULL, NULL, 0, b'1', b'1', b'1', '1', NOW(), '1', NOW(), b'0' |
| | | FROM (SELECT '产å项ç®å®¡æ ¸' AS name, 'mes:pd-product:audit' AS perm) t |
| | | JOIN (SELECT MIN(x.parent_id) AS parent_id FROM system_menu x WHERE x.deleted = 0 AND x.permission = 'mes:pd-product:update') a ON 1 = 1 |
| | | WHERE a.parent_id IS NOT NULL AND NOT EXISTS (SELECT 1 FROM (SELECT id FROM system_menu WHERE deleted = 0 AND permission = 'mes:pd-product:audit') m); |
| | | |
| | | -- 4.7 è§è²ææ |
| | | -- éè¦ï¼æ¬æ¡æ¶ SecurityFrameworkServiceImpl 没æãè¶
级管çåç»è¿æéæ ¡éªãççè·¯é»è¾ï¼ |
| | | -- æéä¸å¾èµ° system_role_menuã䏿æçè¯è¿ admin é½çä¸å°å®¡æ¹é
ç½®èååå®¡æ ¸æé®ã |
| | | -- å¼ååºå®æµï¼å
¨é¨ :audit åå·²æäº super_admin(1) ä¸ common(2)ï¼ |
| | | -- ä»
mes:pd-product:audit ä¸ mes:pd-tech-confirm:audit æ¼æ super_admin ââ |
| | | -- ä¸é¢è¿æ¡è¯å¥ä¼é¡ºå¸¦æè¿ä¸¤ä¸ªç¼ºå£è¡¥é½ã |
| | | -- å
¶ä½è§è²è¯·å¨ãè§è²ç®¡çã页颿éå¾éã |
| | | INSERT INTO system_role_menu (role_id, menu_id, creator, create_time, updater, update_time, deleted) |
| | | SELECT r.id, m.id, '1', NOW(), '1', NOW(), b'0' |
| | | FROM system_role r |
| | | CROSS JOIN system_menu m |
| | | WHERE r.code IN ('super_admin', 'common') |
| | | AND r.deleted = 0 |
| | | AND m.deleted = 0 |
| | | AND ( m.component = 'system/approvalConfig/index' |
| | | OR m.permission IN ('system:approval-config:query', 'system:approval-config:update') |
| | | OR m.permission LIKE '%:audit' ) |
| | | AND NOT EXISTS ( |
| | | SELECT 1 FROM system_role_menu rm |
| | | WHERE rm.role_id = r.id AND rm.menu_id = m.id AND rm.deleted = 0 |
| | | ); |
| | | |
| | | -- 宿ã坿§è¡ä»¥ä¸è¯å¥æ ¡éªï¼ |
| | | -- SELECT table_name, column_name FROM information_schema.columns |
| | | -- WHERE table_schema = DATABASE() AND column_name = 'reviewer_id'; |
| | | -- SELECT permission FROM system_menu WHERE deleted = 0 AND permission LIKE '%:audit'; |
| | | -- SELECT COUNT(*) FROM system_approval_config WHERE deleted = 0; -- ææ 26 |
| | | -- SELECT id, name, permission, parent_id FROM system_menu -- å¿
é¡»å读ä¸é¡¹ï¼å¿åªçè¡æ° |
| | | -- WHERE deleted = 0 AND (component = 'system/approvalConfig/index' |
| | | -- OR permission LIKE 'system:approval-config:%' |
| | | -- OR permission IN ('erp:purchase-plan:audit','erp:purchase-order:audit')); |
| | | -- SELECT r.code, COUNT(*) FROM system_role_menu rm JOIN system_role r ON r.id = rm.role_id |
| | | -- JOIN system_menu m ON m.id = rm.menu_id |
| | | -- WHERE rm.deleted = 0 AND (m.component = 'system/approvalConfig/index' |
| | | -- OR m.permission LIKE 'system:approval-config:%' |
| | | -- OR m.permission IN ('erp:purchase-plan:audit','erp:purchase-order:audit')) |
| | | -- GROUP BY r.code; -- æææ¯è§è² 5 æ¡ |