Files
KCGL/db_migrations/deploy_production.sql
yueli 3c0954598c perf: 综合安全加固 — RBAC严格映射+异步邮件+字段权限白名单+前端对齐+导入模板
本次提交包含本会话所有修改的最终统一提交

## 权限系统重构
- permission_service.py: 添加入库/采购操作元素 + ensure_default_permissions
- field_permissions.py: 严格1-to-1 Default Deny 字段映射(StockBuy/Semi/Product/MaterialBase)
- decorators.py: _expand_operation_perms 双向粒度桥接 + prevent_double_submit
- deploy_production.sql: 修复 sys_element 别名码(qty_inbound→in_quantity)

## 采购模块
- purchase.py: 权限驱动可见性 + inbound_purchase独立权限 + 价格字段过滤
- purchase_service.py: 异步邮件 + 三阶段批量模糊匹配防N+1
- purchase/index.vue: canApprove严格操作权限 + upload重复修复

## 导出/入
- base_service.py: export_excel 流式写入防OOM + get_latest_specs 优化
- import_service.py + import_api.py: Excel批量导入(模板+预览+执行)
- ImportDialog.vue: 三步骤导入弹窗

## 异步邮件
- email_service.py: send_email_async (守护线程)
- inventory_task.py: send_email→send_email_async

## 前端对齐
- product/semi/buy.vue: 列对齐in_quantity/stock_quantity/available_quantity + localStorage缓存V2
- buyOdoo.vue: 排序修复 + 导入按钮 + 移除点击展开加载
- BomManage.vue: 懒加载分组 + 导入按钮
- list.vue: 导入按钮
- Selection.vue + borrow/apply: BOM匹配修复 + 导入按钮
- outbound/create.vue: 出库类型必选
- AppMain.vue: 移除transition白屏修复
- material_base.ts, outbound.ts, bom.ts, stock.ts: 新增API函数
2026-07-17 13:07:12 +08:00

265 lines
22 KiB
PL/PgSQL
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

-- ============================================================
-- 生产环境部署 SQL可直接在 DataGrip 中执行)
-- 涵盖: 表结构变更 + 权限码补全 + 默认分配
-- ============================================================
BEGIN;
-- ============================================================
-- 一、表结构变更
-- ============================================================
-- 1. 采购申请表新增 base_id关联物料基础信息
ALTER TABLE purchase_request ADD COLUMN IF NOT EXISTS base_id INTEGER;
-- 2. 采购申请表新增 tax_rate税率
ALTER TABLE purchase_request ADD COLUMN IF NOT EXISTS tax_rate NUMERIC(5,2) DEFAULT 0;
-- 3. 采购入库表新增 request_id关联采购申请单
ALTER TABLE stock_buy ADD COLUMN IF NOT EXISTS request_id INTEGER;
-- 4. 角色权限表新增 company_name支持公司级别权限隔离
ALTER TABLE sys_role_permission ADD COLUMN IF NOT EXISTS company_name VARCHAR(255);
-- ============================================================
-- 二、补全 sys_element 权限码(确保权限分配页面可见)
-- ============================================================
-- 2.1 采购入库字段权限14个缺失码
INSERT INTO sys_element (menu_code, name, code, element_type)
SELECT 'inbound_buy', '基础物料ID', 'inbound_buy:base_id', 'column' WHERE NOT EXISTS (SELECT 1 FROM sys_element WHERE code = 'inbound_buy:base_id');
INSERT INTO sys_element (menu_code, name, code, element_type)
SELECT 'inbound_buy', '全局打印ID', 'inbound_buy:global_print_id', 'column' WHERE NOT EXISTS (SELECT 1 FROM sys_element WHERE code = 'inbound_buy:global_print_id');
INSERT INTO sys_element (menu_code, name, code, element_type)
SELECT 'inbound_buy', '强制质检标记', 'inbound_buy:isInspectionRequired', 'column' WHERE NOT EXISTS (SELECT 1 FROM sys_element WHERE code = 'inbound_buy:isInspectionRequired');
INSERT INTO sys_element (menu_code, name, code, element_type)
SELECT 'inbound_buy', '入库数量', 'inbound_buy:in_quantity', 'column' WHERE NOT EXISTS (SELECT 1 FROM sys_element WHERE code = 'inbound_buy:in_quantity');
INSERT INTO sys_element (menu_code, name, code, element_type)
SELECT 'inbound_buy', '当前库存', 'inbound_buy:stock_quantity', 'column' WHERE NOT EXISTS (SELECT 1 FROM sys_element WHERE code = 'inbound_buy:stock_quantity');
INSERT INTO sys_element (menu_code, name, code, element_type)
SELECT 'inbound_buy', '当前可用', 'inbound_buy:available_quantity', 'column' WHERE NOT EXISTS (SELECT 1 FROM sys_element WHERE code = 'inbound_buy:available_quantity');
INSERT INTO sys_element (menu_code, name, code, element_type)
SELECT 'inbound_buy', '含税单价', 'inbound_buy:post_tax_unit_price', 'column' WHERE NOT EXISTS (SELECT 1 FROM sys_element WHERE code = 'inbound_buy:post_tax_unit_price');
INSERT INTO sys_element (menu_code, name, code, element_type)
SELECT 'inbound_buy', '采购邮箱', 'inbound_buy:purchaser_email', 'column' WHERE NOT EXISTS (SELECT 1 FROM sys_element WHERE code = 'inbound_buy:purchaser_email');
INSERT INTO sys_element (menu_code, name, code, element_type)
SELECT 'inbound_buy', '原始链接', 'inbound_buy:source_link', 'column' WHERE NOT EXISTS (SELECT 1 FROM sys_element WHERE code = 'inbound_buy:source_link');
INSERT INTO sys_element (menu_code, name, code, element_type)
SELECT 'inbound_buy', '详情链接', 'inbound_buy:detail_link', 'column' WHERE NOT EXISTS (SELECT 1 FROM sys_element WHERE code = 'inbound_buy:detail_link');
INSERT INTO sys_element (menu_code, name, code, element_type)
SELECT 'inbound_buy', '到货图片', 'inbound_buy:arrival_photo', 'column' WHERE NOT EXISTS (SELECT 1 FROM sys_element WHERE code = 'inbound_buy:arrival_photo');
INSERT INTO sys_element (menu_code, name, code, element_type)
SELECT 'inbound_buy', '检测报告', 'inbound_buy:inspection_report', 'column' WHERE NOT EXISTS (SELECT 1 FROM sys_element WHERE code = 'inbound_buy:inspection_report');
INSERT INTO sys_element (menu_code, name, code, element_type)
SELECT 'inbound_buy', '关联采购单ID', 'inbound_buy:request_id', 'column' WHERE NOT EXISTS (SELECT 1 FROM sys_element WHERE code = 'inbound_buy:request_id');
INSERT INTO sys_element (menu_code, name, code, element_type)
SELECT 'inbound_buy', '关联采购单号', 'inbound_buy:request_no', 'column' WHERE NOT EXISTS (SELECT 1 FROM sys_element WHERE code = 'inbound_buy:request_no');
-- 2.1b 入库半成品字段权限(对齐 field_to_perm
INSERT INTO sys_element (menu_code, name, code, element_type)
SELECT 'inbound_semi', '基础物料ID', 'inbound_semi:base_id', 'column' WHERE NOT EXISTS (SELECT 1 FROM sys_element WHERE code = 'inbound_semi:base_id');
INSERT INTO sys_element (menu_code, name, code, element_type)
SELECT 'inbound_semi', '批号', 'inbound_semi:batch_number', 'column' WHERE NOT EXISTS (SELECT 1 FROM sys_element WHERE code = 'inbound_semi:batch_number');
INSERT INTO sys_element (menu_code, name, code, element_type)
SELECT 'inbound_semi', '序列号', 'inbound_semi:serial_number', 'column' WHERE NOT EXISTS (SELECT 1 FROM sys_element WHERE code = 'inbound_semi:serial_number');
INSERT INTO sys_element (menu_code, name, code, element_type)
SELECT 'inbound_semi', 'BOM版本', 'inbound_semi:bom_version', 'column' WHERE NOT EXISTS (SELECT 1 FROM sys_element WHERE code = 'inbound_semi:bom_version');
INSERT INTO sys_element (menu_code, name, code, element_type)
SELECT 'inbound_semi', '人工成本', 'inbound_semi:manual_cost', 'column' WHERE NOT EXISTS (SELECT 1 FROM sys_element WHERE code = 'inbound_semi:manual_cost');
INSERT INTO sys_element (menu_code, name, code, element_type)
SELECT 'inbound_semi', '生产开始时间', 'inbound_semi:production_start_time', 'column' WHERE NOT EXISTS (SELECT 1 FROM sys_element WHERE code = 'inbound_semi:production_start_time');
INSERT INTO sys_element (menu_code, name, code, element_type)
SELECT 'inbound_semi', '生产结束时间', 'inbound_semi:production_end_time', 'column' WHERE NOT EXISTS (SELECT 1 FROM sys_element WHERE code = 'inbound_semi:production_end_time');
INSERT INTO sys_element (menu_code, name, code, element_type)
SELECT 'inbound_semi', '到货图片', 'inbound_semi:arrival_photo', 'column' WHERE NOT EXISTS (SELECT 1 FROM sys_element WHERE code = 'inbound_semi:arrival_photo');
INSERT INTO sys_element (menu_code, name, code, element_type)
SELECT 'inbound_semi', '质检报告链接', 'inbound_semi:quality_report_link', 'column' WHERE NOT EXISTS (SELECT 1 FROM sys_element WHERE code = 'inbound_semi:quality_report_link');
INSERT INTO sys_element (menu_code, name, code, element_type)
SELECT 'inbound_semi', '详情链接', 'inbound_semi:detail_link', 'column' WHERE NOT EXISTS (SELECT 1 FROM sys_element WHERE code = 'inbound_semi:detail_link');
-- 2.1c 入库成品字段权限(对齐 field_to_perm
INSERT INTO sys_element (menu_code, name, code, element_type)
SELECT 'inbound_product', '基础物料ID', 'inbound_product:base_id', 'column' WHERE NOT EXISTS (SELECT 1 FROM sys_element WHERE code = 'inbound_product:base_id');
INSERT INTO sys_element (menu_code, name, code, element_type)
SELECT 'inbound_product', '批号', 'inbound_product:batch_number', 'column' WHERE NOT EXISTS (SELECT 1 FROM sys_element WHERE code = 'inbound_product:batch_number');
INSERT INTO sys_element (menu_code, name, code, element_type)
SELECT 'inbound_product', '条形码', 'inbound_product:barcode', 'column' WHERE NOT EXISTS (SELECT 1 FROM sys_element WHERE code = 'inbound_product:barcode');
INSERT INTO sys_element (menu_code, name, code, element_type)
SELECT 'inbound_product', '序列号', 'inbound_product:serial_number', 'column' WHERE NOT EXISTS (SELECT 1 FROM sys_element WHERE code = 'inbound_product:serial_number');
INSERT INTO sys_element (menu_code, name, code, element_type)
SELECT 'inbound_product', 'BOM版本', 'inbound_product:bom_version', 'column' WHERE NOT EXISTS (SELECT 1 FROM sys_element WHERE code = 'inbound_product:bom_version');
INSERT INTO sys_element (menu_code, name, code, element_type)
SELECT 'inbound_product', '人工成本', 'inbound_product:manual_cost', 'column' WHERE NOT EXISTS (SELECT 1 FROM sys_element WHERE code = 'inbound_product:manual_cost');
INSERT INTO sys_element (menu_code, name, code, element_type)
SELECT 'inbound_product', '生产开始时间', 'inbound_product:production_start_time', 'column' WHERE NOT EXISTS (SELECT 1 FROM sys_element WHERE code = 'inbound_product:production_start_time');
INSERT INTO sys_element (menu_code, name, code, element_type)
SELECT 'inbound_product', '生产结束时间', 'inbound_product:production_end_time', 'column' WHERE NOT EXISTS (SELECT 1 FROM sys_element WHERE code = 'inbound_product:production_end_time');
INSERT INTO sys_element (menu_code, name, code, element_type)
SELECT 'inbound_product', '产品图片', 'inbound_product:product_photo', 'column' WHERE NOT EXISTS (SELECT 1 FROM sys_element WHERE code = 'inbound_product:product_photo');
INSERT INTO sys_element (menu_code, name, code, element_type)
SELECT 'inbound_product', '质检报告链接', 'inbound_product:quality_report_link', 'column' WHERE NOT EXISTS (SELECT 1 FROM sys_element WHERE code = 'inbound_product:quality_report_link');
INSERT INTO sys_element (menu_code, name, code, element_type)
SELECT 'inbound_product', '质检报告链接(入库)', 'inbound_product:inspection_report_link', 'column' WHERE NOT EXISTS (SELECT 1 FROM sys_element WHERE code = 'inbound_product:inspection_report_link');
INSERT INTO sys_element (menu_code, name, code, element_type)
SELECT 'inbound_product', '详情链接', 'inbound_product:detail_link', 'column' WHERE NOT EXISTS (SELECT 1 FROM sys_element WHERE code = 'inbound_product:detail_link');
INSERT INTO sys_element (menu_code, name, code, element_type)
SELECT 'inbound_product', '入库数量', 'inbound_product:in_quantity', 'column' WHERE NOT EXISTS (SELECT 1 FROM sys_element WHERE code = 'inbound_product:in_quantity');
INSERT INTO sys_element (menu_code, name, code, element_type)
SELECT 'inbound_product', '当前库存', 'inbound_product:stock_quantity', 'column' WHERE NOT EXISTS (SELECT 1 FROM sys_element WHERE code = 'inbound_product:stock_quantity');
INSERT INTO sys_element (menu_code, name, code, element_type)
SELECT 'inbound_product', '当前可用', 'inbound_product:available_quantity', 'column' WHERE NOT EXISTS (SELECT 1 FROM sys_element WHERE code = 'inbound_product:available_quantity');
-- 2.2 BOM 管理字段权限5个缺失码
INSERT INTO sys_element (menu_code, name, code, element_type)
SELECT 'bom_manage', '父件ID', 'bom_manage:parent_id', 'column' WHERE NOT EXISTS (SELECT 1 FROM sys_element WHERE code = 'bom_manage:parent_id');
INSERT INTO sys_element (menu_code, name, code, element_type)
SELECT 'bom_manage', '父件规格', 'bom_manage:parent_spec', 'column' WHERE NOT EXISTS (SELECT 1 FROM sys_element WHERE code = 'bom_manage:parent_spec');
INSERT INTO sys_element (menu_code, name, code, element_type)
SELECT 'bom_manage', '子件ID', 'bom_manage:child_id', 'column' WHERE NOT EXISTS (SELECT 1 FROM sys_element WHERE code = 'bom_manage:child_id');
INSERT INTO sys_element (menu_code, name, code, element_type)
SELECT 'bom_manage', '用量', 'bom_manage:dosage', 'column' WHERE NOT EXISTS (SELECT 1 FROM sys_element WHERE code = 'bom_manage:dosage');
INSERT INTO sys_element (menu_code, name, code, element_type)
SELECT 'bom_manage', '备注', 'bom_manage:remark', 'column' WHERE NOT EXISTS (SELECT 1 FROM sys_element WHERE code = 'bom_manage:remark');
-- 2.3 员工管理字段权限8个缺失码
INSERT INTO sys_element (menu_code, name, code, element_type)
SELECT 'system_user', '用户ID', 'system_user:id', 'column' WHERE NOT EXISTS (SELECT 1 FROM sys_element WHERE code = 'system_user:id');
INSERT INTO sys_element (menu_code, name, code, element_type)
SELECT 'system_user', '用户名', 'system_user:username', 'column' WHERE NOT EXISTS (SELECT 1 FROM sys_element WHERE code = 'system_user:username');
INSERT INTO sys_element (menu_code, name, code, element_type)
SELECT 'system_user', '账号ID', 'system_user:account_id', 'column' WHERE NOT EXISTS (SELECT 1 FROM sys_element WHERE code = 'system_user:account_id');
INSERT INTO sys_element (menu_code, name, code, element_type)
SELECT 'system_user', '邮箱', 'system_user:email', 'column' WHERE NOT EXISTS (SELECT 1 FROM sys_element WHERE code = 'system_user:email');
INSERT INTO sys_element (menu_code, name, code, element_type)
SELECT 'system_user', '部门', 'system_user:department', 'column' WHERE NOT EXISTS (SELECT 1 FROM sys_element WHERE code = 'system_user:department');
INSERT INTO sys_element (menu_code, name, code, element_type)
SELECT 'system_user', '角色', 'system_user:role', 'column' WHERE NOT EXISTS (SELECT 1 FROM sys_element WHERE code = 'system_user:role');
INSERT INTO sys_element (menu_code, name, code, element_type)
SELECT 'system_user', '状态', 'system_user:status', 'column' WHERE NOT EXISTS (SELECT 1 FROM sys_element WHERE code = 'system_user:status');
INSERT INTO sys_element (menu_code, name, code, element_type)
SELECT 'system_user', '创建时间', 'system_user:created_at', 'column' WHERE NOT EXISTS (SELECT 1 FROM sys_element WHERE code = 'system_user:created_at');
-- 2.4 系统管理操作权限2个
INSERT INTO sys_element (menu_code, name, code, element_type)
SELECT 'system_mgmt', '全局跨域访问', 'crossDomain', 'element' WHERE NOT EXISTS (SELECT 1 FROM sys_element WHERE code = 'crossDomain');
INSERT INTO sys_element (menu_code, name, code, element_type)
SELECT 'system_mgmt', '权限分配操作', 'system_permission:operation', 'element' WHERE NOT EXISTS (SELECT 1 FROM sys_element WHERE code = 'system_permission:operation');
-- ============================================================
-- 三、补全 sys_role_permission给各角色分配新权限码
-- ============================================================
-- 3.1 BOM 权限 → 分配给已有 BOM 权限的角色
INSERT INTO sys_role_permission (role_code, target_code, type, company_name)
SELECT rp.role_code, elem.code, 'element', rp.company_name
FROM (SELECT DISTINCT role_code, company_name FROM sys_role_permission WHERE target_code LIKE 'bom_manage:%') rp
CROSS JOIN (SELECT unnest(ARRAY['bom_manage:parent_id','bom_manage:parent_spec','bom_manage:child_id','bom_manage:dosage','bom_manage:remark','bom_manage:operation']) AS code) elem
WHERE NOT EXISTS (
SELECT 1 FROM sys_role_permission rp2
WHERE rp2.role_code = rp.role_code AND rp2.target_code = elem.code
AND COALESCE(rp2.company_name, '') = COALESCE(rp.company_name, '')
);
-- 3.2 员工管理字段权限 → 分配给 SUPER_ADMIN 和 SUPERVISOR
INSERT INTO sys_role_permission (role_code, target_code, type)
SELECT r.role, elem.code, 'element'
FROM (SELECT unnest(ARRAY['SUPER_ADMIN','SUPERVISOR']) AS role) r
CROSS JOIN (
SELECT unnest(ARRAY['system_user:id','system_user:username','system_user:account_id','system_user:email','system_user:department','system_user:role','system_user:status','system_user:created_at','system_user:operation']) AS code
) elem
WHERE NOT EXISTS (
SELECT 1 FROM sys_role_permission rp
WHERE rp.role_code = r.role AND rp.target_code = elem.code AND rp.type = 'element'
);
-- 3.3 crossDomain → 仅 SUPER_ADMIN 默认拥有(其他角色通过权限页手动勾选)
INSERT INTO sys_role_permission (role_code, target_code, type)
SELECT 'SUPER_ADMIN', 'crossDomain', 'element'
WHERE NOT EXISTS (SELECT 1 FROM sys_role_permission WHERE role_code = 'SUPER_ADMIN' AND target_code = 'crossDomain');
-- 3.4 system_permission:operation → 分配给 SUPER_ADMIN 和 SUPERVISOR
INSERT INTO sys_role_permission (role_code, target_code, type)
SELECT 'SUPER_ADMIN', 'system_permission:operation', 'element'
WHERE NOT EXISTS (SELECT 1 FROM sys_role_permission WHERE role_code = 'SUPER_ADMIN' AND target_code = 'system_permission:operation');
INSERT INTO sys_role_permission (role_code, target_code, type)
SELECT 'SUPERVISOR', 'system_permission:operation', 'element'
WHERE NOT EXISTS (SELECT 1 FROM sys_role_permission WHERE role_code = 'SUPERVISOR' AND target_code = 'system_permission:operation');
-- 3.5 system_mgmt / system_permission / system_audit → SUPERVISOR
INSERT INTO sys_role_permission (role_code, target_code, type)
SELECT 'SUPERVISOR', code, 'menu'
FROM sys_menu
WHERE code IN ('system_mgmt', 'system_permission', 'system_audit')
AND NOT EXISTS (SELECT 1 FROM sys_role_permission rp WHERE rp.role_code = 'SUPERVISOR' AND rp.target_code = sys_menu.code AND rp.type = 'menu');
-- ============================================================
-- 三补、借库管理菜单 + 权限码 + 角色分配
-- ============================================================
-- 借库选单 & 借库审批 菜单
INSERT INTO sys_menu (code, name, parent_id, is_visible)
SELECT 'op_borrow_apply', '借库选单', m.id, true
FROM sys_menu m WHERE m.code = 'operation_mgmt'
AND NOT EXISTS (SELECT 1 FROM sys_menu WHERE code = 'op_borrow_apply');
INSERT INTO sys_menu (code, name, parent_id, is_visible)
SELECT 'op_borrow_approval', '借库审批', m.id, true
FROM sys_menu m WHERE m.code = 'operation_mgmt'
AND NOT EXISTS (SELECT 1 FROM sys_menu WHERE code = 'op_borrow_approval');
-- 借库选单元素权限
INSERT INTO sys_element (menu_code, name, code, element_type) SELECT 'op_borrow_apply', '物品名称', 'op_borrow_apply:name', 'column' WHERE NOT EXISTS (SELECT 1 FROM sys_element WHERE code = 'op_borrow_apply:name');
INSERT INTO sys_element (menu_code, name, code, element_type) SELECT 'op_borrow_apply', '规格型号', 'op_borrow_apply:spec', 'column' WHERE NOT EXISTS (SELECT 1 FROM sys_element WHERE code = 'op_borrow_apply:spec');
INSERT INTO sys_element (menu_code, name, code, element_type) SELECT 'op_borrow_apply', '可用库存', 'op_borrow_apply:available_quantity', 'column' WHERE NOT EXISTS (SELECT 1 FROM sys_element WHERE code = 'op_borrow_apply:available_quantity');
INSERT INTO sys_element (menu_code, name, code, element_type) SELECT 'op_borrow_apply', '操作/编辑', 'op_borrow_apply:operation', 'column' WHERE NOT EXISTS (SELECT 1 FROM sys_element WHERE code = 'op_borrow_apply:operation');
-- 角色分配:有 operation_mgmt 的角色 → 获得借库选单/审批菜单 + 元素
INSERT INTO sys_role_permission (role_code, target_code, type)
SELECT DISTINCT rp.role_code, 'op_borrow_apply', 'menu'
FROM sys_role_permission rp WHERE rp.target_code = 'operation_mgmt'
AND NOT EXISTS (SELECT 1 FROM sys_role_permission WHERE role_code = rp.role_code AND target_code = 'op_borrow_apply');
INSERT INTO sys_role_permission (role_code, target_code, type)
SELECT DISTINCT rp.role_code, 'op_borrow_approval', 'menu'
FROM sys_role_permission rp WHERE rp.target_code = 'operation_mgmt'
AND NOT EXISTS (SELECT 1 FROM sys_role_permission WHERE role_code = rp.role_code AND target_code = 'op_borrow_approval');
INSERT INTO sys_role_permission (role_code, target_code, type)
SELECT rp.role_code, elem.code, 'element'
FROM (SELECT DISTINCT role_code FROM sys_role_permission WHERE target_code = 'operation_mgmt') rp
CROSS JOIN (SELECT unnest(ARRAY['op_borrow_apply:name','op_borrow_apply:spec','op_borrow_apply:available_quantity','op_borrow_apply:operation']) AS code) elem
WHERE NOT EXISTS (SELECT 1 FROM sys_role_permission rp2 WHERE rp2.role_code = rp.role_code AND rp2.target_code = elem.code);
-- 补借库管理菜单给 SUPERVISOR如缺
INSERT INTO sys_role_permission (role_code, target_code, type) SELECT 'SUPERVISOR', 'operation_mgmt', 'menu' WHERE NOT EXISTS (SELECT 1 FROM sys_role_permission WHERE role_code = 'SUPERVISOR' AND target_code = 'operation_mgmt');
INSERT INTO sys_role_permission (role_code, target_code, type) SELECT 'SUPERVISOR', 'op_borrow_apply', 'menu' WHERE NOT EXISTS (SELECT 1 FROM sys_role_permission WHERE role_code = 'SUPERVISOR' AND target_code = 'op_borrow_apply');
INSERT INTO sys_role_permission (role_code, target_code, type) SELECT 'SUPERVISOR', 'op_borrow_approval', 'menu' WHERE NOT EXISTS (SELECT 1 FROM sys_role_permission WHERE role_code = 'SUPERVISOR' AND target_code = 'op_borrow_approval');
INSERT INTO sys_role_permission (role_code, target_code, type) SELECT 'SUPERVISOR', 'op_borrow', 'menu' WHERE NOT EXISTS (SELECT 1 FROM sys_role_permission WHERE role_code = 'SUPERVISOR' AND target_code = 'op_borrow');
INSERT INTO sys_role_permission (role_code, target_code, type) SELECT 'SUPERVISOR', 'op_return', 'menu' WHERE NOT EXISTS (SELECT 1 FROM sys_role_permission WHERE role_code = 'SUPERVISOR' AND target_code = 'op_return');
INSERT INTO sys_role_permission (role_code, target_code, type) SELECT 'SUPERVISOR', 'op_records', 'menu' WHERE NOT EXISTS (SELECT 1 FROM sys_role_permission WHERE role_code = 'SUPERVISOR' AND target_code = 'op_records');
-- 出库管理菜单补 SUPERVISOR
INSERT INTO sys_role_permission (role_code, target_code, type) SELECT 'SUPERVISOR', 'outbound_mgmt', 'menu' WHERE NOT EXISTS (SELECT 1 FROM sys_role_permission WHERE role_code = 'SUPERVISOR' AND target_code = 'outbound_mgmt');
INSERT INTO sys_role_permission (role_code, target_code, type) SELECT 'SUPERVISOR', 'outbound_approval', 'menu' WHERE NOT EXISTS (SELECT 1 FROM sys_role_permission WHERE role_code = 'SUPERVISOR' AND target_code = 'outbound_approval');
INSERT INTO sys_role_permission (role_code, target_code, type) SELECT 'SUPERVISOR', elem.code, 'element' FROM (SELECT unnest(ARRAY['outbound_approval:request_no','outbound_approval:applicant','outbound_approval:approver','outbound_approval:status','outbound_approval:operation']) AS code) elem WHERE NOT EXISTS (SELECT 1 FROM sys_role_permission WHERE role_code = 'SUPERVISOR' AND target_code = elem.code);
-- ============================================================
-- 四、验证
-- ============================================================
SELECT '=== 验证 ===' AS info;
SELECT '表结构' AS item, column_name, data_type
FROM information_schema.columns
WHERE table_name IN ('purchase_request', 'stock_buy', 'sys_role_permission')
AND column_name IN ('base_id', 'request_id', 'company_name', 'tax_rate')
ORDER BY table_name, column_name;
SELECT '权限码总数' AS item, count(*) FROM sys_element;
SELECT '角色权限总数' AS item, count(*) FROM sys_role_permission;
COMMIT;