all in
This commit is contained in:
@@ -0,0 +1,54 @@
|
||||
-- ============================================================
|
||||
-- 添加文档列表子菜单路由
|
||||
-- 说明:为 /documents 父级菜单添加"文档列表"子菜单
|
||||
-- 生成时间: 2025-11-17
|
||||
-- ============================================================
|
||||
|
||||
-- 添加 /documents 索引路由作为子菜单
|
||||
INSERT INTO sys_routes (
|
||||
route_path,
|
||||
route_name,
|
||||
route_title,
|
||||
parent_id,
|
||||
icon,
|
||||
sort_order,
|
||||
is_hidden,
|
||||
is_cache,
|
||||
component,
|
||||
meta
|
||||
) VALUES (
|
||||
'/documents',
|
||||
'DocumentsList',
|
||||
'文档列表',
|
||||
2, -- parent_id = 2 (Documents 父级菜单)
|
||||
NULL,
|
||||
1,
|
||||
false,
|
||||
true,
|
||||
'views/documents/Index.vue',
|
||||
'{}'
|
||||
);
|
||||
|
||||
-- 调整文档上传的排序,让文档列表排在第一位
|
||||
UPDATE sys_routes
|
||||
SET sort_order = 2
|
||||
WHERE id = 32; -- /documents/upload
|
||||
|
||||
-- 验证结果
|
||||
SELECT id, route_path, route_name, route_title, parent_id, sort_order, is_hidden
|
||||
FROM sys_routes
|
||||
WHERE parent_id = 2 OR id = 2
|
||||
ORDER BY parent_id NULLS FIRST, sort_order;
|
||||
|
||||
-- ============================================================
|
||||
-- 说明
|
||||
-- ============================================================
|
||||
-- 现在的菜单结构:
|
||||
-- 文档管理 (/documents) [id=2]
|
||||
-- ├─ 文档列表 (/documents) [新增,sort_order=1]
|
||||
-- └─ 文档上传 (/documents/upload) [id=32,sort_order=2]
|
||||
--
|
||||
-- 注意:虽然父级和子菜单路径相同,但 Remix 会根据路由文件正确处理
|
||||
-- - documents.tsx: 父级布局
|
||||
-- - documents._index.tsx: 索引路由(文档列表)
|
||||
-- ============================================================
|
||||
@@ -0,0 +1,61 @@
|
||||
-- ============================================================
|
||||
-- 添加文档列表路由
|
||||
-- 说明:将文档列表作为"文档管理"的子菜单
|
||||
-- 生成时间: 2025-11-17
|
||||
-- ============================================================
|
||||
|
||||
-- 添加 /documents/list 路由(文档列表)
|
||||
INSERT INTO sys_routes (
|
||||
route_path,
|
||||
route_name,
|
||||
route_title,
|
||||
parent_id,
|
||||
icon,
|
||||
sort_order,
|
||||
is_hidden,
|
||||
is_cache,
|
||||
component,
|
||||
meta
|
||||
) VALUES (
|
||||
'/documents/list',
|
||||
'DocumentsList',
|
||||
'文档列表',
|
||||
2, -- parent_id = 2 (Documents 父级菜单)
|
||||
NULL,
|
||||
1,
|
||||
false,
|
||||
true,
|
||||
'views/documents/List.vue',
|
||||
'{}'
|
||||
);
|
||||
|
||||
-- 调整文档上传的排序,让文档列表排在第一位
|
||||
UPDATE sys_routes
|
||||
SET sort_order = 2
|
||||
WHERE id = 32; -- /documents/upload
|
||||
|
||||
-- 调整编辑文档的排序
|
||||
UPDATE sys_routes
|
||||
SET sort_order = 3
|
||||
WHERE id = 33; -- /documents/edit
|
||||
|
||||
-- 调整下载文档的排序
|
||||
UPDATE sys_routes
|
||||
SET sort_order = 4
|
||||
WHERE id = 34; -- /documents/download
|
||||
|
||||
-- 验证结果
|
||||
SELECT id, route_path, route_name, route_title, parent_id, sort_order, is_hidden
|
||||
FROM sys_routes
|
||||
WHERE parent_id = 2 OR id = 2
|
||||
ORDER BY parent_id NULLS FIRST, sort_order;
|
||||
|
||||
-- ============================================================
|
||||
-- 预期结果
|
||||
-- ============================================================
|
||||
-- 文档管理 (/documents) [id=2, parent_id=null]
|
||||
-- ├─ 文档列表 (/documents/list) [新增, sort_order=1]
|
||||
-- ├─ 文档上传 (/documents/upload) [id=32, sort_order=2]
|
||||
-- ├─ 编辑文档 (/documents/edit) [id=33, sort_order=3, is_hidden=true]
|
||||
-- └─ 下载文档 (/documents/download) [id=34, sort_order=4, is_hidden=true]
|
||||
-- ============================================================
|
||||
@@ -0,0 +1,109 @@
|
||||
-- 添加入口模块管理路由到 sys_routes 表
|
||||
-- 执行此脚本将在系统菜单中添加"入口模块管理"菜单项
|
||||
|
||||
-- 1. 插入父级菜单:入口模块管理
|
||||
INSERT INTO sys_routes (
|
||||
route_path,
|
||||
route_name,
|
||||
component,
|
||||
parent_id,
|
||||
route_title,
|
||||
icon,
|
||||
sort_order,
|
||||
is_hidden,
|
||||
is_cache,
|
||||
meta,
|
||||
status,
|
||||
created_at,
|
||||
updated_at
|
||||
) VALUES (
|
||||
'/entry-modules',
|
||||
'EntryModules',
|
||||
'views/entry-modules/Index.vue',
|
||||
NULL,
|
||||
'入口模块管理',
|
||||
'ri-apps-2-line',
|
||||
8,
|
||||
false,
|
||||
true,
|
||||
'{"requiresAuth": true, "requiresRole": ["admin", "developer", "provincial_admin"]}'::jsonb,
|
||||
0,
|
||||
NOW(),
|
||||
NOW()
|
||||
)
|
||||
ON CONFLICT (route_path) DO UPDATE SET
|
||||
route_title = EXCLUDED.route_title,
|
||||
icon = EXCLUDED.icon,
|
||||
sort_order = EXCLUDED.sort_order,
|
||||
updated_at = NOW();
|
||||
|
||||
-- 2. 插入子菜单:新建入口模块(如果需要在菜单中显示)
|
||||
-- 注意:获取父级菜单的ID
|
||||
WITH parent_route AS (
|
||||
SELECT id FROM sys_routes WHERE route_path = '/entry-modules'
|
||||
)
|
||||
INSERT INTO sys_routes (
|
||||
route_path,
|
||||
route_name,
|
||||
component,
|
||||
parent_id,
|
||||
route_title,
|
||||
icon,
|
||||
sort_order,
|
||||
is_hidden,
|
||||
is_cache,
|
||||
meta,
|
||||
status,
|
||||
created_at,
|
||||
updated_at
|
||||
)
|
||||
SELECT
|
||||
'/entry-modules/new',
|
||||
'EntryModulesNew',
|
||||
'views/entry-modules/New.vue',
|
||||
parent_route.id,
|
||||
'新建入口模块',
|
||||
'ri-add-circle-line',
|
||||
1,
|
||||
true, -- 隐藏,不在菜单中显示
|
||||
false,
|
||||
'{"requiresAuth": true, "requiresRole": ["admin", "developer", "provincial_admin"]}'::jsonb,
|
||||
0,
|
||||
NOW(),
|
||||
NOW()
|
||||
FROM parent_route
|
||||
ON CONFLICT (route_path) DO UPDATE SET
|
||||
parent_id = EXCLUDED.parent_id,
|
||||
updated_at = NOW();
|
||||
|
||||
-- 3. 查询确认插入结果
|
||||
SELECT
|
||||
id,
|
||||
route_path,
|
||||
route_name,
|
||||
route_title,
|
||||
parent_id,
|
||||
icon,
|
||||
sort_order,
|
||||
is_hidden
|
||||
FROM sys_routes
|
||||
WHERE route_path LIKE '/entry-modules%'
|
||||
ORDER BY sort_order;
|
||||
|
||||
-- 4. 为管理员角色分配权限(假设角色ID为1是admin)
|
||||
-- 获取新插入的路由ID
|
||||
WITH entry_routes AS (
|
||||
SELECT id FROM sys_routes WHERE route_path LIKE '/entry-modules%'
|
||||
)
|
||||
INSERT INTO role_route (role_id, route_id, created_at, updated_at)
|
||||
SELECT
|
||||
1, -- 假设 role_id = 1 是 admin 角色
|
||||
entry_routes.id,
|
||||
NOW(),
|
||||
NOW()
|
||||
FROM entry_routes
|
||||
ON CONFLICT (role_id, route_id) DO NOTHING;
|
||||
|
||||
-- 提示信息
|
||||
SELECT '✅ 入口模块管理路由已成功添加到系统菜单中!' AS message;
|
||||
SELECT '📌 请确保已为相应角色分配权限,路由才会在菜单中显示。' AS reminder;
|
||||
@@ -0,0 +1,43 @@
|
||||
-- ========================================
|
||||
-- 为评查点系统添加外键约束
|
||||
-- ========================================
|
||||
|
||||
-- 1️⃣ 为 evaluation_points 表添加外键约束
|
||||
-- evaluation_point_groups_id 引用 evaluation_point_groups.id
|
||||
ALTER TABLE evaluation_points
|
||||
ADD CONSTRAINT fk_evaluation_points_group
|
||||
FOREIGN KEY (evaluation_point_groups_id)
|
||||
REFERENCES evaluation_point_groups(id)
|
||||
ON DELETE SET NULL -- 删除分组时,将评查点的分组ID设为NULL
|
||||
ON UPDATE CASCADE; -- 更新分组ID时,级联更新
|
||||
|
||||
-- 2️⃣ 为 evaluation_point_groups 表添加自引用外键约束
|
||||
-- pid 引用同一个表的 id(父子关系)
|
||||
ALTER TABLE evaluation_point_groups
|
||||
ADD CONSTRAINT fk_evaluation_point_groups_parent
|
||||
FOREIGN KEY (pid)
|
||||
REFERENCES evaluation_point_groups(id)
|
||||
ON DELETE CASCADE -- 删除父分组时,级联删除子分组
|
||||
ON UPDATE CASCADE; -- 更新父分组ID时,级联更新
|
||||
|
||||
-- 添加注释
|
||||
COMMENT ON CONSTRAINT fk_evaluation_points_group ON evaluation_points IS '评查点所属分组外键约束';
|
||||
COMMENT ON CONSTRAINT fk_evaluation_point_groups_parent ON evaluation_point_groups IS '评查点分组父子关系自引用外键约束';
|
||||
|
||||
-- 验证外键约束是否创建成功
|
||||
SELECT
|
||||
tc.table_name,
|
||||
tc.constraint_name,
|
||||
kcu.column_name,
|
||||
ccu.table_name AS foreign_table_name,
|
||||
ccu.column_name AS foreign_column_name
|
||||
FROM information_schema.table_constraints AS tc
|
||||
JOIN information_schema.key_column_usage AS kcu
|
||||
ON tc.constraint_name = kcu.constraint_name
|
||||
AND tc.table_schema = kcu.table_schema
|
||||
JOIN information_schema.constraint_column_usage AS ccu
|
||||
ON ccu.constraint_name = tc.constraint_name
|
||||
AND ccu.table_schema = tc.table_schema
|
||||
WHERE tc.constraint_type = 'FOREIGN KEY'
|
||||
AND tc.table_name IN ('evaluation_points', 'evaluation_point_groups')
|
||||
ORDER BY tc.table_name, tc.constraint_name;
|
||||
@@ -0,0 +1,53 @@
|
||||
-- ========================================
|
||||
-- 简化版外键约束(不需要自引用)
|
||||
-- ========================================
|
||||
|
||||
-- 步骤 1: 将所有 pid=0 改为 NULL(标准做法)
|
||||
UPDATE evaluation_point_groups
|
||||
SET pid = NULL
|
||||
WHERE pid = 0;
|
||||
|
||||
-- 步骤 2: 为 evaluation_points 添加外键约束
|
||||
|
||||
-- 2.1 子分组外键:evaluation_point_groups_id → evaluation_point_groups.id
|
||||
ALTER TABLE evaluation_points
|
||||
ADD CONSTRAINT fk_evaluation_points_child_group
|
||||
FOREIGN KEY (evaluation_point_groups_id)
|
||||
REFERENCES evaluation_point_groups(id)
|
||||
ON DELETE SET NULL
|
||||
ON UPDATE CASCADE;
|
||||
|
||||
-- 2.2 父分组外键:evaluation_point_groups_pid → evaluation_point_groups.id
|
||||
ALTER TABLE evaluation_points
|
||||
ADD CONSTRAINT fk_evaluation_points_parent_group
|
||||
FOREIGN KEY (evaluation_point_groups_pid)
|
||||
REFERENCES evaluation_point_groups(id)
|
||||
ON DELETE SET NULL
|
||||
ON UPDATE CASCADE;
|
||||
|
||||
-- ⚠️ 注意:不再需要 evaluation_point_groups 表的自引用外键
|
||||
-- 因为 evaluation_points 已经直接存储了父级分组ID
|
||||
|
||||
-- 添加注释
|
||||
COMMENT ON CONSTRAINT fk_evaluation_points_child_group ON evaluation_points
|
||||
IS '评查点所属规则组外键约束(二级分组)';
|
||||
|
||||
COMMENT ON CONSTRAINT fk_evaluation_points_parent_group ON evaluation_points
|
||||
IS '评查点类型外键约束(父级分组)';
|
||||
|
||||
-- 步骤 3: 验证外键约束是否创建成功
|
||||
SELECT
|
||||
tc.table_name AS "表名",
|
||||
tc.constraint_name AS "约束名",
|
||||
kcu.column_name AS "列名",
|
||||
ccu.table_name AS "引用表名",
|
||||
ccu.column_name AS "引用列名"
|
||||
FROM information_schema.table_constraints AS tc
|
||||
JOIN information_schema.key_column_usage AS kcu
|
||||
ON tc.constraint_name = kcu.constraint_name
|
||||
JOIN information_schema.constraint_column_usage AS ccu
|
||||
ON ccu.constraint_name = tc.constraint_name
|
||||
WHERE tc.constraint_type = 'FOREIGN KEY'
|
||||
AND tc.table_name = 'evaluation_points'
|
||||
AND kcu.column_name IN ('evaluation_point_groups_id', 'evaluation_point_groups_pid')
|
||||
ORDER BY kcu.column_name;
|
||||
@@ -0,0 +1,78 @@
|
||||
#!/bin/bash
|
||||
|
||||
# ============================================================
|
||||
# 清理 app/routes 中未使用的路由文件
|
||||
# 生成时间: 2025-11-17
|
||||
# ============================================================
|
||||
|
||||
echo "🗑️ 开始清理未使用的路由文件..."
|
||||
|
||||
# 定义项目根目录
|
||||
PROJECT_ROOT="D:/remix_project/docreview"
|
||||
|
||||
# 定义要删除的文件列表
|
||||
FILES_TO_DELETE=(
|
||||
"app/routes/contract-search.tsx"
|
||||
"app/routes/contract-search._index.tsx"
|
||||
"app/routes/debug.tsx"
|
||||
)
|
||||
|
||||
# 定义要删除的目录
|
||||
DIRS_TO_DELETE=(
|
||||
"app/routes/examples"
|
||||
)
|
||||
|
||||
echo ""
|
||||
echo "📋 将删除以下文件:"
|
||||
for file in "${FILES_TO_DELETE[@]}"; do
|
||||
if [ -f "$PROJECT_ROOT/$file" ]; then
|
||||
echo " ✓ $file"
|
||||
else
|
||||
echo " ⚠️ $file (不存在,跳过)"
|
||||
fi
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo "📁 将删除以下目录:"
|
||||
for dir in "${DIRS_TO_DELETE[@]}"; do
|
||||
if [ -d "$PROJECT_ROOT/$dir" ]; then
|
||||
echo " ✓ $dir"
|
||||
else
|
||||
echo " ⚠️ $dir (不存在,跳过)"
|
||||
fi
|
||||
done
|
||||
|
||||
echo ""
|
||||
read -p "❓ 确认删除以上文件和目录吗?(y/n): " confirm
|
||||
|
||||
if [ "$confirm" != "y" ] && [ "$confirm" != "Y" ]; then
|
||||
echo "❌ 已取消删除操作"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "🔧 开始删除..."
|
||||
|
||||
# 删除文件
|
||||
for file in "${FILES_TO_DELETE[@]}"; do
|
||||
if [ -f "$PROJECT_ROOT/$file" ]; then
|
||||
rm "$PROJECT_ROOT/$file"
|
||||
echo " ✅ 已删除: $file"
|
||||
fi
|
||||
done
|
||||
|
||||
# 删除目录
|
||||
for dir in "${DIRS_TO_DELETE[@]}"; do
|
||||
if [ -d "$PROJECT_ROOT/$dir" ]; then
|
||||
rm -rf "$PROJECT_ROOT/$dir"
|
||||
echo " ✅ 已删除目录: $dir"
|
||||
fi
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo "✅ 清理完成!"
|
||||
echo ""
|
||||
echo "💡 提示:"
|
||||
echo " 1. 请运行 'npm run typecheck' 检查是否有引用这些文件的代码"
|
||||
echo " 2. 请运行 'git status' 查看变更"
|
||||
echo " 3. 如果一切正常,请提交这些更改"
|
||||
@@ -0,0 +1,45 @@
|
||||
-- 创建评查点视图,包含所属规则组和评查点类型信息
|
||||
-- 这个视图通过表连接自动关联 evaluation_point_groups 表(两次关联)
|
||||
|
||||
CREATE OR REPLACE VIEW evaluation_points_with_groups AS
|
||||
SELECT
|
||||
ep.id,
|
||||
ep.code,
|
||||
ep.name,
|
||||
ep.evaluation_point_groups_id,
|
||||
ep.risk,
|
||||
ep.description,
|
||||
ep.is_enabled,
|
||||
ep.references_laws,
|
||||
ep.extraction_config,
|
||||
ep.evaluation_config,
|
||||
ep.pass_message,
|
||||
ep.fail_message,
|
||||
ep.suggestion_message,
|
||||
ep.suggestion_message_type,
|
||||
ep.post_action,
|
||||
ep.action_config,
|
||||
ep.created_at,
|
||||
ep.updated_at,
|
||||
|
||||
-- 所属规则组(二级分组)
|
||||
child_group.id as group_id,
|
||||
child_group.name as group_name,
|
||||
child_group.pid as group_pid,
|
||||
|
||||
-- 评查点类型(父级分组)
|
||||
parent_group.id as type_id,
|
||||
parent_group.name as type_name
|
||||
|
||||
FROM evaluation_points ep
|
||||
|
||||
-- 第一次关联:获取二级分组(所属规则组)
|
||||
LEFT JOIN evaluation_point_groups child_group
|
||||
ON ep.evaluation_point_groups_id = child_group.id
|
||||
|
||||
-- 第二次关联:获取父级分组(评查点类型)
|
||||
LEFT JOIN evaluation_point_groups parent_group
|
||||
ON child_group.pid = parent_group.id;
|
||||
|
||||
-- 添加注释
|
||||
COMMENT ON VIEW evaluation_points_with_groups IS '评查点视图,包含所属规则组和评查点类型信息,通过两次表连接实现';
|
||||
@@ -0,0 +1,30 @@
|
||||
-- ========================================
|
||||
-- 修复重复的外键约束问题
|
||||
-- ========================================
|
||||
-- 问题:evaluation_point_groups_id 列有两个外键约束,导致 PostgREST 嵌套查询失败
|
||||
|
||||
-- 步骤 1: 删除旧的外键约束
|
||||
ALTER TABLE evaluation_points
|
||||
DROP CONSTRAINT IF EXISTS fk_evaluation_points_group;
|
||||
|
||||
-- 步骤 2: 验证删除结果(应该只剩下 2 个外键)
|
||||
SELECT
|
||||
tc.constraint_name AS "约束名",
|
||||
kcu.column_name AS "列名",
|
||||
ccu.table_name AS "引用表",
|
||||
ccu.column_name AS "引用列"
|
||||
FROM information_schema.table_constraints AS tc
|
||||
JOIN information_schema.key_column_usage AS kcu
|
||||
ON tc.constraint_name = kcu.constraint_name
|
||||
JOIN information_schema.constraint_column_usage AS ccu
|
||||
ON ccu.constraint_name = tc.constraint_name
|
||||
WHERE tc.constraint_type = 'FOREIGN KEY'
|
||||
AND tc.table_name = 'evaluation_points'
|
||||
AND kcu.column_name IN ('evaluation_point_groups_id', 'evaluation_point_groups_pid')
|
||||
ORDER BY kcu.column_name;
|
||||
|
||||
-- 预期结果(应该只有 2 个外键):
|
||||
-- 约束名 | 列名 | 引用表 | 引用列
|
||||
-- ------------------------------------------|-------------------------------|---------------------------|-------
|
||||
-- fk_evaluation_points_child_group | evaluation_point_groups_id | evaluation_point_groups | id
|
||||
-- fk_evaluation_points_parent_group | evaluation_point_groups_pid | evaluation_point_groups | id
|
||||
@@ -0,0 +1,55 @@
|
||||
-- ========================================
|
||||
-- 修复 pid=0 问题并添加外键约束
|
||||
-- ========================================
|
||||
|
||||
-- 步骤 1: 将所有 pid=0 的记录改为 NULL(表示顶级分组)
|
||||
UPDATE evaluation_point_groups
|
||||
SET pid = NULL
|
||||
WHERE pid = 0;
|
||||
|
||||
-- 验证修改结果
|
||||
SELECT COUNT(*) as "顶级分组数量(pid为NULL)"
|
||||
FROM evaluation_point_groups
|
||||
WHERE pid IS NULL;
|
||||
|
||||
-- 步骤 2: 添加外键约束
|
||||
|
||||
-- 2.1 为 evaluation_points 表添加外键约束
|
||||
ALTER TABLE evaluation_points
|
||||
ADD CONSTRAINT fk_evaluation_points_group
|
||||
FOREIGN KEY (evaluation_point_groups_id)
|
||||
REFERENCES evaluation_point_groups(id)
|
||||
ON DELETE SET NULL -- 删除分组时,将评查点的分组ID设为NULL
|
||||
ON UPDATE CASCADE; -- 更新分组ID时,级联更新
|
||||
|
||||
-- 2.2 为 evaluation_point_groups 表添加自引用外键约束
|
||||
-- pid 引用同一个表的 id(父子关系)
|
||||
-- NULL 值表示顶级分组,不受外键约束限制
|
||||
ALTER TABLE evaluation_point_groups
|
||||
ADD CONSTRAINT fk_evaluation_point_groups_parent
|
||||
FOREIGN KEY (pid)
|
||||
REFERENCES evaluation_point_groups(id)
|
||||
ON DELETE CASCADE -- 删除父分组时,级联删除子分组
|
||||
ON UPDATE CASCADE; -- 更新父分组ID时,级联更新
|
||||
|
||||
-- 添加注释
|
||||
COMMENT ON CONSTRAINT fk_evaluation_points_group ON evaluation_points IS '评查点所属分组外键约束';
|
||||
COMMENT ON CONSTRAINT fk_evaluation_point_groups_parent ON evaluation_point_groups IS '评查点分组父子关系自引用外键约束(pid=NULL表示顶级分组)';
|
||||
|
||||
-- 步骤 3: 验证外键约束是否创建成功
|
||||
SELECT
|
||||
tc.table_name AS "表名",
|
||||
tc.constraint_name AS "约束名",
|
||||
kcu.column_name AS "列名",
|
||||
ccu.table_name AS "引用表名",
|
||||
ccu.column_name AS "引用列名"
|
||||
FROM information_schema.table_constraints AS tc
|
||||
JOIN information_schema.key_column_usage AS kcu
|
||||
ON tc.constraint_name = kcu.constraint_name
|
||||
AND tc.table_schema = kcu.table_schema
|
||||
JOIN information_schema.constraint_column_usage AS ccu
|
||||
ON ccu.constraint_name = tc.constraint_name
|
||||
AND ccu.table_schema = tc.table_schema
|
||||
WHERE tc.constraint_type = 'FOREIGN KEY'
|
||||
AND tc.table_name IN ('evaluation_points', 'evaluation_point_groups')
|
||||
ORDER BY tc.table_name, tc.constraint_name;
|
||||
@@ -0,0 +1,200 @@
|
||||
-- ============================================================
|
||||
-- 角色路由权限初始化脚本
|
||||
-- 说明:为各角色分配路由权限(只分配一级菜单权限,子路由自动继承)
|
||||
-- 生成时间: 2025-11-17
|
||||
-- ============================================================
|
||||
|
||||
-- 清空现有权限(如果需要重新初始化)
|
||||
-- TRUNCATE TABLE role_route;
|
||||
|
||||
-- ============================================================
|
||||
-- 1. 超级管理员 (super_admin) - 拥有所有权限
|
||||
-- ============================================================
|
||||
|
||||
INSERT INTO role_route (role_id, route_id)
|
||||
SELECT 12, id FROM sys_routes WHERE parent_id IS NULL AND is_hidden = false;
|
||||
|
||||
-- ============================================================
|
||||
-- 2. 系统管理员 (admin) - 拥有所有权限
|
||||
-- ============================================================
|
||||
|
||||
INSERT INTO role_route (role_id, route_id)
|
||||
SELECT 1, id FROM sys_routes WHERE parent_id IS NULL AND is_hidden = false;
|
||||
|
||||
-- ============================================================
|
||||
-- 3. 系统管理员 (system_admin) - 拥有所有权限
|
||||
-- ============================================================
|
||||
|
||||
INSERT INTO role_route (role_id, route_id)
|
||||
SELECT 13, id FROM sys_routes WHERE parent_id IS NULL AND is_hidden = false;
|
||||
|
||||
-- ============================================================
|
||||
-- 4. 业务管理员 (business_admin) - 核心业务权限
|
||||
-- ============================================================
|
||||
|
||||
INSERT INTO role_route (role_id, route_id)
|
||||
SELECT 14, id FROM sys_routes
|
||||
WHERE parent_id IS NULL AND is_hidden = false
|
||||
AND route_path IN (
|
||||
'/home', -- 系统概览
|
||||
'/documents', -- 文档管理
|
||||
'/rules', -- 评查规则库
|
||||
'/contract-template', -- 合同模板
|
||||
'/cross-checking', -- 交叉评查
|
||||
'/chat-with-llm' -- AI法务助手
|
||||
);
|
||||
|
||||
-- ============================================================
|
||||
-- 5. 文档管理员 (document_admin) - 文档相关权限
|
||||
-- ============================================================
|
||||
|
||||
INSERT INTO role_route (role_id, route_id)
|
||||
SELECT 15, id FROM sys_routes
|
||||
WHERE parent_id IS NULL AND is_hidden = false
|
||||
AND route_path IN (
|
||||
'/home', -- 系统概览
|
||||
'/documents', -- 文档管理
|
||||
'/rules', -- 评查规则库
|
||||
'/contract-template' -- 合同模板
|
||||
);
|
||||
|
||||
-- ============================================================
|
||||
-- 6. 评查管理员 (evaluation_admin) - 评查相关权限
|
||||
-- ============================================================
|
||||
|
||||
INSERT INTO role_route (role_id, route_id)
|
||||
SELECT 16, id FROM sys_routes
|
||||
WHERE parent_id IS NULL AND is_hidden = false
|
||||
AND route_path IN (
|
||||
'/home', -- 系统概览
|
||||
'/documents', -- 文档管理
|
||||
'/rules', -- 评查规则库
|
||||
'/chat-with-llm' -- AI法务助手
|
||||
);
|
||||
|
||||
-- ============================================================
|
||||
-- 7. 交叉评查管理员 (crossreview_admin) - 交叉评查权限
|
||||
-- ============================================================
|
||||
|
||||
INSERT INTO role_route (role_id, route_id)
|
||||
SELECT 17, id FROM sys_routes
|
||||
WHERE parent_id IS NULL AND is_hidden = false
|
||||
AND route_path IN (
|
||||
'/home', -- 系统概览
|
||||
'/documents', -- 文档管理
|
||||
'/cross-checking' -- 交叉评查
|
||||
);
|
||||
|
||||
-- ============================================================
|
||||
-- 8. 评查员 (auditor) - 执行评查任务
|
||||
-- ============================================================
|
||||
|
||||
INSERT INTO role_route (role_id, route_id)
|
||||
SELECT 18, id FROM sys_routes
|
||||
WHERE parent_id IS NULL AND is_hidden = false
|
||||
AND route_path IN (
|
||||
'/home', -- 系统概览
|
||||
'/documents', -- 文档管理
|
||||
'/rules', -- 评查规则库
|
||||
'/cross-checking', -- 交叉评查
|
||||
'/chat-with-llm' -- AI法务助手
|
||||
);
|
||||
|
||||
-- ============================================================
|
||||
-- 9. 上传者 (uploader) - 仅上传文档
|
||||
-- ============================================================
|
||||
|
||||
INSERT INTO role_route (role_id, route_id)
|
||||
SELECT 19, id FROM sys_routes
|
||||
WHERE parent_id IS NULL AND is_hidden = false
|
||||
AND route_path IN (
|
||||
'/home', -- 系统概览
|
||||
'/documents' -- 文档管理
|
||||
);
|
||||
|
||||
-- ============================================================
|
||||
-- 10. 部门主管 (deptLeader) - 部门管理权限
|
||||
-- ============================================================
|
||||
|
||||
INSERT INTO role_route (role_id, route_id)
|
||||
SELECT 3, id FROM sys_routes
|
||||
WHERE parent_id IS NULL AND is_hidden = false
|
||||
AND route_path IN (
|
||||
'/home', -- 系统概览
|
||||
'/documents', -- 文档管理
|
||||
'/rules', -- 评查规则库
|
||||
'/contract-template', -- 合同模板
|
||||
'/cross-checking', -- 交叉评查
|
||||
'/chat-with-llm' -- AI法务助手
|
||||
);
|
||||
|
||||
-- ============================================================
|
||||
-- 11. 小组组长 (groupLeader) - 小组管理权限
|
||||
-- ============================================================
|
||||
|
||||
INSERT INTO role_route (role_id, route_id)
|
||||
SELECT 4, id FROM sys_routes
|
||||
WHERE parent_id IS NULL AND is_hidden = false
|
||||
AND route_path IN (
|
||||
'/home', -- 系统概览
|
||||
'/documents', -- 文档管理
|
||||
'/rules', -- 评查规则库
|
||||
'/cross-checking', -- 交叉评查
|
||||
'/chat-with-llm' -- AI法务助手
|
||||
);
|
||||
|
||||
-- ============================================================
|
||||
-- 12. 普通员工 (common) - 基本权限
|
||||
-- ============================================================
|
||||
|
||||
INSERT INTO role_route (role_id, route_id)
|
||||
SELECT 2, id FROM sys_routes
|
||||
WHERE parent_id IS NULL AND is_hidden = false
|
||||
AND route_path IN (
|
||||
'/home', -- 系统概览
|
||||
'/documents', -- 文档管理
|
||||
'/chat-with-llm' -- AI法务助手
|
||||
);
|
||||
|
||||
-- ============================================================
|
||||
-- 13. 访客 (guest) - 只读权限
|
||||
-- ============================================================
|
||||
|
||||
INSERT INTO role_route (role_id, route_id)
|
||||
SELECT 20, id FROM sys_routes
|
||||
WHERE parent_id IS NULL AND is_hidden = false
|
||||
AND route_path IN (
|
||||
'/home' -- 系统概览
|
||||
);
|
||||
|
||||
-- ============================================================
|
||||
-- 验证权限配置
|
||||
-- ============================================================
|
||||
|
||||
-- 查看各角色的路由权限数量
|
||||
SELECT
|
||||
r.role_name,
|
||||
r.role_key,
|
||||
COUNT(rr.route_id) as route_count
|
||||
FROM roles r
|
||||
LEFT JOIN role_route rr ON r.id = rr.role_id
|
||||
GROUP BY r.id, r.role_name, r.role_key
|
||||
ORDER BY route_count DESC;
|
||||
|
||||
-- 查看管理员角色的具体路由权限
|
||||
SELECT
|
||||
r.role_name,
|
||||
sr.route_path,
|
||||
sr.route_title,
|
||||
sr.sort_order
|
||||
FROM role_route rr
|
||||
JOIN roles r ON rr.role_id = r.id
|
||||
JOIN sys_routes sr ON rr.route_id = sr.id
|
||||
WHERE r.role_key IN ('admin', 'super_admin')
|
||||
ORDER BY r.role_name, sr.sort_order;
|
||||
|
||||
-- ============================================================
|
||||
-- 完成!
|
||||
-- ============================================================
|
||||
|
||||
-- 提示:执行此脚本后,请清除浏览器缓存并重新登录系统
|
||||
@@ -0,0 +1,82 @@
|
||||
-- 合同起草功能数据库迁移脚本
|
||||
-- 创建时间: 2025-01-04
|
||||
|
||||
-- 1. 创建起草合同表
|
||||
CREATE TABLE IF NOT EXISTS drafted_contracts (
|
||||
id SERIAL PRIMARY KEY,
|
||||
template_id INTEGER NOT NULL,
|
||||
file_path TEXT NOT NULL,
|
||||
title TEXT NOT NULL,
|
||||
placeholder_values JSONB DEFAULT '{}'::jsonb,
|
||||
status TEXT DEFAULT 'draft' CHECK (status IN ('draft', 'completed', 'archived')),
|
||||
created_by INTEGER,
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
|
||||
CONSTRAINT fk_template
|
||||
FOREIGN KEY (template_id)
|
||||
REFERENCES contract_templates(id)
|
||||
ON DELETE CASCADE,
|
||||
|
||||
CONSTRAINT fk_created_by
|
||||
FOREIGN KEY (created_by)
|
||||
REFERENCES auth.users(id)
|
||||
ON DELETE SET NULL
|
||||
);
|
||||
|
||||
-- 2. 创建索引优化查询性能
|
||||
CREATE INDEX idx_drafted_contracts_template_id ON drafted_contracts(template_id);
|
||||
CREATE INDEX idx_drafted_contracts_created_by ON drafted_contracts(created_by);
|
||||
CREATE INDEX idx_drafted_contracts_status ON drafted_contracts(status);
|
||||
CREATE INDEX idx_drafted_contracts_created_at ON drafted_contracts(created_at DESC);
|
||||
|
||||
-- 3. 创建更新时间触发器
|
||||
CREATE OR REPLACE FUNCTION update_drafted_contracts_updated_at()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
NEW.updated_at = NOW();
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
CREATE TRIGGER trigger_update_drafted_contracts_updated_at
|
||||
BEFORE UPDATE ON drafted_contracts
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION update_drafted_contracts_updated_at();
|
||||
|
||||
-- 4. 添加注释
|
||||
COMMENT ON TABLE drafted_contracts IS '起草的合同记录';
|
||||
COMMENT ON COLUMN drafted_contracts.id IS '主键ID';
|
||||
COMMENT ON COLUMN drafted_contracts.template_id IS '关联的合同模板ID';
|
||||
COMMENT ON COLUMN drafted_contracts.file_path IS '起草后的文件路径(MinIO)';
|
||||
COMMENT ON COLUMN drafted_contracts.title IS '合同标题';
|
||||
COMMENT ON COLUMN drafted_contracts.placeholder_values IS '占位符填充值(JSON格式)';
|
||||
COMMENT ON COLUMN drafted_contracts.status IS '状态:draft-草稿,completed-已完成,archived-已归档';
|
||||
COMMENT ON COLUMN drafted_contracts.created_by IS '创建人用户ID';
|
||||
COMMENT ON COLUMN drafted_contracts.created_at IS '创建时间';
|
||||
COMMENT ON COLUMN drafted_contracts.updated_at IS '更新时间';
|
||||
|
||||
-- 5. 为 contract_templates 表添加占位符配置字段
|
||||
ALTER TABLE contract_templates
|
||||
ADD COLUMN IF NOT EXISTS placeholder_schema JSONB DEFAULT NULL;
|
||||
|
||||
COMMENT ON COLUMN contract_templates.placeholder_schema IS '占位符配置Schema(JSON格式)';
|
||||
|
||||
-- 6. 示例:为测试模板添加占位符配置(可选,根据实际情况调整)
|
||||
-- UPDATE contract_templates
|
||||
-- SET placeholder_schema = '{
|
||||
-- "fields": [
|
||||
-- {"key": "甲方名称", "label": "甲方名称", "type": "text", "required": true, "group": "甲方信息"},
|
||||
-- {"key": "甲方地址", "label": "甲方地址", "type": "text", "required": true, "group": "甲方信息"},
|
||||
-- {"key": "甲方法定代表人", "label": "法定代表人", "type": "text", "required": true, "group": "甲方信息"},
|
||||
-- {"key": "甲方联系电话", "label": "联系电话", "type": "tel", "required": true, "group": "甲方信息"},
|
||||
-- {"key": "乙方名称", "label": "乙方名称", "type": "text", "required": true, "group": "乙方信息"},
|
||||
-- {"key": "乙方地址", "label": "乙方地址", "type": "text", "required": true, "group": "乙方信息"},
|
||||
-- {"key": "乙方法定代表人", "label": "法定代表人", "type": "text", "required": true, "group": "乙方信息"},
|
||||
-- {"key": "乙方联系电话", "label": "联系电话", "type": "tel", "required": true, "group": "乙方信息"},
|
||||
-- {"key": "合同金额", "label": "合同金额(元)", "type": "number", "required": true, "group": "合同条款"},
|
||||
-- {"key": "签订日期", "label": "签订日期", "type": "date", "required": true, "group": "合同条款"},
|
||||
-- {"key": "合同编号", "label": "合同编号", "type": "text", "required": false, "group": "基本信息"}
|
||||
-- ]
|
||||
-- }'::jsonb
|
||||
-- WHERE id = 1;
|
||||
@@ -0,0 +1,282 @@
|
||||
# 文档版本管理功能 - 部署指南(使用函数名前缀)
|
||||
|
||||
## 📋 方案说明
|
||||
|
||||
使用 **函数名前缀 `documents_`** 来区分版本管理函数,无需创建 schema。
|
||||
|
||||
**3个新函数**:
|
||||
- `documents_get_latest_documents_with_version_info` - 获取文档列表
|
||||
- `documents_count_latest_documents_with_filters` - 统计文档总数
|
||||
- `documents_get_document_history` - 获取历史版本
|
||||
|
||||
---
|
||||
|
||||
## 🚀 快速部署(2步)
|
||||
|
||||
### 步骤1:在 Navicat 中执行 SQL 脚本
|
||||
|
||||
#### 方法A:查询窗口
|
||||
1. 打开 Navicat,连接到 PostgreSQL 数据库
|
||||
2. 点击 **"查询"** → **"新建查询"**
|
||||
3. 打开 `add_document_version_management.sql` 文件
|
||||
4. 复制所有内容到查询窗口
|
||||
5. 点击 **"运行"** ▶️ 按钮
|
||||
|
||||
#### 方法B:运行 SQL 文件
|
||||
1. 右键点击数据库
|
||||
2. 选择 **"运行 SQL 文件"**
|
||||
3. 选择 `add_document_version_management.sql`
|
||||
4. 点击 **"开始"**
|
||||
|
||||
### 步骤2:重启应用
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
pm2 restart all
|
||||
```
|
||||
|
||||
**就这么简单!** ✅
|
||||
|
||||
---
|
||||
|
||||
## 🔍 验证函数是否创建成功
|
||||
|
||||
在 Navicat 中执行:
|
||||
|
||||
```sql
|
||||
-- 查看新创建的函数
|
||||
SELECT proname
|
||||
FROM pg_proc
|
||||
WHERE proname LIKE 'documents_%';
|
||||
```
|
||||
|
||||
**应该返回 3 个函数名**:
|
||||
```
|
||||
documents_get_latest_documents_with_version_info
|
||||
documents_count_latest_documents_with_filters
|
||||
documents_get_document_history
|
||||
```
|
||||
|
||||
### 在 Navicat 中查看函数
|
||||
|
||||
```
|
||||
你的数据库
|
||||
└─ public (schema)
|
||||
└─ 函数
|
||||
├─ documents_get_latest_documents_with_version_info(...)
|
||||
├─ documents_count_latest_documents_with_filters(...)
|
||||
└─ documents_get_document_history(...)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🧪 测试函数
|
||||
|
||||
在 Navicat 查询窗口执行:
|
||||
|
||||
```sql
|
||||
-- 测试1:获取文档列表(假设用户ID为1)
|
||||
SELECT * FROM documents_get_latest_documents_with_version_info(1, 1, 10);
|
||||
|
||||
-- 测试2:统计文档总数
|
||||
SELECT documents_count_latest_documents_with_filters(1);
|
||||
|
||||
-- 测试3:获取历史版本(如果有同名文档)
|
||||
SELECT * FROM documents_get_document_history('测试文档.pdf', 1, 123);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📁 在 Navicat 中操作函数
|
||||
|
||||
### 查看函数定义
|
||||
1. 在左侧树形结构中找到 `public` → `函数`
|
||||
2. 找到 `documents_get_latest_documents_with_version_info`
|
||||
3. 右键 → **"设计函数"** 或 **"查看 SQL"**
|
||||
|
||||
### 测试函数(图形界面)
|
||||
1. 右键点击函数
|
||||
2. 选择 **"执行函数"**
|
||||
3. 输入参数值(如 `p_user_id: 1, p_page: 1, p_page_size: 10`)
|
||||
4. 点击 **"运行"**
|
||||
|
||||
### 删除函数(如果需要重建)
|
||||
```sql
|
||||
DROP FUNCTION IF EXISTS documents_get_latest_documents_with_version_info;
|
||||
DROP FUNCTION IF EXISTS documents_count_latest_documents_with_filters;
|
||||
DROP FUNCTION IF EXISTS documents_get_document_history;
|
||||
|
||||
-- 然后重新执行 SQL 脚本
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ✅ 功能验证
|
||||
|
||||
### 1. 访问文档列表页面
|
||||
|
||||
打开浏览器:`http://your-domain/documents`
|
||||
|
||||
### 2. 检查显示效果
|
||||
|
||||
✅ **有历史版本的文档**:
|
||||
```
|
||||
[▶️] 📄 合同审查.pdf
|
||||
合同审查 v1 (共3个历史版本)
|
||||
问题数量:3 🟢 ↓ -2
|
||||
```
|
||||
|
||||
✅ **单版本文档**:
|
||||
```
|
||||
📄 新文档.pdf
|
||||
合同审查
|
||||
问题数量:4
|
||||
```
|
||||
|
||||
### 3. 测试展开功能
|
||||
|
||||
点击 [▶️] 图标,应该能看到:
|
||||
```
|
||||
[🔽] 📄 合同审查.pdf
|
||||
合同审查 v1 (共3个历史版本)
|
||||
问题数量:3 🟢 ↓ -2
|
||||
|
||||
├─ 🕒 v2 版本
|
||||
│ 问题数量:5 🔴 ↑ +2
|
||||
│
|
||||
├─ 🕒 v3 版本
|
||||
│ 问题数量:3 🟢 ↓ -1
|
||||
│
|
||||
└─ 🕒 v4 版本 (最早)
|
||||
问题数量:4
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ❌ 故障排查
|
||||
|
||||
### 问题1:函数未创建
|
||||
|
||||
**症状**:查询不到函数
|
||||
|
||||
**解决**:
|
||||
```sql
|
||||
-- 检查是否有错误
|
||||
-- 在 Navicat 的"消息"窗口查看错误信息
|
||||
|
||||
-- 重新执行 SQL 脚本
|
||||
```
|
||||
|
||||
### 问题2:前端报错
|
||||
|
||||
**症状**:浏览器控制台显示 `function does not exist`
|
||||
|
||||
**解决**:
|
||||
```bash
|
||||
# 1. 确认函数已创建
|
||||
# 在 Navicat 中执行:
|
||||
SELECT proname FROM pg_proc WHERE proname LIKE 'documents_%';
|
||||
|
||||
# 2. 确认前端代码已更新
|
||||
# 检查 app/api/files/documents.ts 中的函数名
|
||||
|
||||
# 3. 重新构建应用
|
||||
npm run build
|
||||
pm2 restart all
|
||||
```
|
||||
|
||||
### 问题3:数据不显示
|
||||
|
||||
**症状**:页面显示空白或加载失败
|
||||
|
||||
**检查清单**:
|
||||
- ✅ SQL 脚本是否执行成功
|
||||
- ✅ 函数是否创建在 `public` schema 中
|
||||
- ✅ 应用是否重启
|
||||
- ✅ 浏览器控制台是否有错误
|
||||
|
||||
**解决方案**:
|
||||
```bash
|
||||
# 1. 检查 PostgREST 日志
|
||||
journalctl -u postgrest -n 50
|
||||
|
||||
# 2. 检查应用日志
|
||||
pm2 logs
|
||||
|
||||
# 3. 清除浏览器缓存
|
||||
# 按 Ctrl + Shift + R (Windows) 或 Cmd + Shift + R (Mac)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📊 性能优化
|
||||
|
||||
SQL 脚本自动创建了 4 个索引来优化查询性能:
|
||||
|
||||
```sql
|
||||
-- 1. 用户+名称+时间索引
|
||||
idx_documents_user_name_created
|
||||
|
||||
-- 2. 名称+用户+时间索引
|
||||
idx_documents_name_user_created
|
||||
|
||||
-- 3. 创建时间索引
|
||||
idx_documents_created_at
|
||||
|
||||
-- 4. 文件状态索引
|
||||
idx_documents_status
|
||||
```
|
||||
|
||||
查看索引:
|
||||
```sql
|
||||
SELECT indexname, indexdef
|
||||
FROM pg_indexes
|
||||
WHERE tablename = 'documents';
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔐 权限配置(可选)
|
||||
|
||||
如果你使用了特定的 PostgREST 用户,需要授权:
|
||||
|
||||
```sql
|
||||
-- 查看你的 PostgREST 用户名
|
||||
SELECT usename FROM pg_user;
|
||||
|
||||
-- 授权函数(替换 'authenticator' 为实际用户名)
|
||||
GRANT EXECUTE ON FUNCTION documents_get_latest_documents_with_version_info TO authenticator;
|
||||
GRANT EXECUTE ON FUNCTION documents_count_latest_documents_with_filters TO authenticator;
|
||||
GRANT EXECUTE ON FUNCTION documents_get_document_history TO authenticator;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📚 相关文档
|
||||
|
||||
- **SQL 脚本**:`add_document_version_management.sql`
|
||||
- **完整技术文档**:`docs/文档版本管理设计方案.md`
|
||||
- **UI 原型**:`docs/UI设计原型.html`
|
||||
|
||||
---
|
||||
|
||||
## ✅ 部署检查清单
|
||||
|
||||
- [ ] SQL 脚本执行成功
|
||||
- [ ] 3个函数已创建
|
||||
- [ ] 应用已重新构建和重启
|
||||
- [ ] 文档列表页面正常显示
|
||||
- [ ] 展开/折叠功能正常
|
||||
- [ ] 问题数量差异正确显示(红色/绿色)
|
||||
|
||||
---
|
||||
|
||||
## 🎉 完成!
|
||||
|
||||
现在你可以:
|
||||
- ✅ 查看同名文档的版本历史
|
||||
- ✅ 展开/折叠历史版本
|
||||
- ✅ 对比问题数量变化
|
||||
- ✅ 享受高性能的版本管理功能
|
||||
|
||||
**部署完成,开始使用吧!** 🚀
|
||||
@@ -0,0 +1,340 @@
|
||||
# 文档版本管理 - 使用 documents Schema 部署指南
|
||||
|
||||
## 📋 文件说明
|
||||
|
||||
本次使用 **`documents` schema** 来组织所有文档版本管理相关的 RPC 函数。
|
||||
|
||||
**SQL 脚本位置**:`add_document_version_management_with_schema.sql`
|
||||
|
||||
---
|
||||
|
||||
## 🚀 快速部署(3步)
|
||||
|
||||
### 步骤1:在 Navicat 中执行 SQL 脚本
|
||||
|
||||
#### 方法A:通过查询窗口
|
||||
1. 打开 Navicat,连接到你的 PostgreSQL 数据库
|
||||
2. 点击 **"查询"** → **"新建查询"**
|
||||
3. 打开 `add_document_version_management_with_schema.sql` 文件
|
||||
4. 复制所有内容到查询窗口
|
||||
5. **⚠️ 重要:将所有的 `authenticator` 替换为你的实际 PostgREST 用户名**
|
||||
6. 点击 **"运行"** 按钮
|
||||
|
||||
#### 方法B:通过文件导入
|
||||
1. 在 Navicat 中右键点击数据库
|
||||
2. 选择 **"运行 SQL 文件"**
|
||||
3. 选择 `add_document_version_management_with_schema.sql`
|
||||
4. 点击 **"开始"**
|
||||
|
||||
### 步骤2:验证函数是否创建成功
|
||||
|
||||
在 Navicat 查询窗口执行:
|
||||
|
||||
```sql
|
||||
-- 查看 documents schema 中的所有函数
|
||||
SELECT proname, proargnames
|
||||
FROM pg_proc p
|
||||
JOIN pg_namespace n ON p.pronamespace = n.oid
|
||||
WHERE n.nspname = 'documents';
|
||||
```
|
||||
|
||||
**应该看到 3 个函数**:
|
||||
- `get_latest_documents_with_version_info`
|
||||
- `count_latest_documents_with_filters`
|
||||
- `get_document_history`
|
||||
|
||||
### 步骤3:测试函数(可选)
|
||||
|
||||
```sql
|
||||
-- 测试1:获取文档列表(假设用户ID为1)
|
||||
SELECT * FROM documents.get_latest_documents_with_version_info(1, 1, 10);
|
||||
|
||||
-- 测试2:统计文档数量
|
||||
SELECT documents.count_latest_documents_with_filters(1);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📁 Navicat 中查看函数
|
||||
|
||||
执行成功后,在 Navicat 左侧树形结构中:
|
||||
|
||||
```
|
||||
你的数据库
|
||||
└─ 模式 (Schemas)
|
||||
├─ public
|
||||
└─ documents ← 新创建的 schema
|
||||
├─ 表
|
||||
└─ 函数 ← 3个新函数在这里
|
||||
├─ get_latest_documents_with_version_info
|
||||
├─ count_latest_documents_with_filters
|
||||
└─ get_document_history
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ⚙️ PostgREST 配置
|
||||
|
||||
确保你的 PostgREST 配置文件包含 `documents` schema:
|
||||
|
||||
```conf
|
||||
# /etc/postgrest/config (或其他配置文件路径)
|
||||
|
||||
# 添加 documents 到 db-schemas
|
||||
db-schemas = "public, documents"
|
||||
|
||||
# 或者,如果之前没有配置,添加这一行
|
||||
db-schemas = "public, documents"
|
||||
```
|
||||
|
||||
修改配置后重启 PostgREST:
|
||||
```bash
|
||||
sudo systemctl restart postgrest
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔐 权限配置
|
||||
|
||||
### 查找你的 PostgREST 用户名
|
||||
|
||||
```sql
|
||||
-- 方法1:查看所有数据库用户
|
||||
SELECT usename FROM pg_user;
|
||||
|
||||
-- 方法2:查看当前连接用户
|
||||
SELECT current_user;
|
||||
```
|
||||
|
||||
**常见的 PostgREST 用户名**:
|
||||
- `authenticator`
|
||||
- `web_anon`
|
||||
- `postgres`
|
||||
- `api_user`
|
||||
|
||||
### 授权函数给用户
|
||||
|
||||
将下面的 `YOUR_POSTGREST_USER` 替换为你的实际用户名:
|
||||
|
||||
```sql
|
||||
-- 授权 schema 使用权限
|
||||
GRANT USAGE ON SCHEMA documents TO YOUR_POSTGREST_USER;
|
||||
|
||||
-- 授权函数执行权限
|
||||
GRANT EXECUTE ON FUNCTION documents.get_latest_documents_with_version_info TO YOUR_POSTGREST_USER;
|
||||
GRANT EXECUTE ON FUNCTION documents.count_latest_documents_with_filters TO YOUR_POSTGREST_USER;
|
||||
GRANT EXECUTE ON FUNCTION documents.get_document_history TO YOUR_POSTGREST_USER;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎯 前端代码已自动适配
|
||||
|
||||
前端代码已经修改为使用 `documents` schema:
|
||||
|
||||
```typescript
|
||||
// app/api/files/documents.ts
|
||||
|
||||
// ✅ 已修改为:
|
||||
postgrestPost('rpc/documents.get_latest_documents_with_version_info', ...)
|
||||
postgrestPost('rpc/documents.count_latest_documents_with_filters', ...)
|
||||
postgrestPost('rpc/documents.get_document_history', ...)
|
||||
```
|
||||
|
||||
**无需手动修改前端代码!**
|
||||
|
||||
---
|
||||
|
||||
## 🧪 完整测试流程
|
||||
|
||||
### 1. 在 Navicat 中测试 SQL
|
||||
|
||||
```sql
|
||||
-- 测试获取文档列表
|
||||
SELECT * FROM documents.get_latest_documents_with_version_info(
|
||||
p_user_id := 1,
|
||||
p_page := 1,
|
||||
p_page_size := 10
|
||||
);
|
||||
|
||||
-- 测试搜索功能
|
||||
SELECT * FROM documents.get_latest_documents_with_version_info(
|
||||
p_user_id := 1,
|
||||
p_page := 1,
|
||||
p_page_size := 10,
|
||||
p_search_name := '合同'
|
||||
);
|
||||
|
||||
-- 测试历史版本查询
|
||||
SELECT * FROM documents.get_document_history('测试文档.pdf', 1, 123);
|
||||
```
|
||||
|
||||
### 2. 测试 PostgREST API
|
||||
|
||||
```bash
|
||||
# 测试获取文档列表
|
||||
curl -X POST http://your-api-url/rpc/documents.get_latest_documents_with_version_info \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
|
||||
-d '{
|
||||
"p_user_id": 1,
|
||||
"p_page": 1,
|
||||
"p_page_size": 10
|
||||
}'
|
||||
```
|
||||
|
||||
### 3. 测试前端页面
|
||||
|
||||
1. 重启应用:
|
||||
```bash
|
||||
npm run build
|
||||
pm2 restart all
|
||||
```
|
||||
|
||||
2. 访问文档列表页面:`http://your-domain/documents`
|
||||
|
||||
3. 检查功能:
|
||||
- ✅ 文档列表正常显示
|
||||
- ✅ 有历史版本的文档显示展开图标
|
||||
- ✅ 点击展开图标能看到历史版本
|
||||
- ✅ 问题数量差异正确显示(红色/绿色)
|
||||
|
||||
---
|
||||
|
||||
## ❌ 故障排查
|
||||
|
||||
### 问题1:函数未创建成功
|
||||
|
||||
**症状**:执行查询时提示 `function does not exist`
|
||||
|
||||
**解决方案**:
|
||||
```sql
|
||||
-- 检查 documents schema 是否存在
|
||||
SELECT schema_name FROM information_schema.schemata WHERE schema_name = 'documents';
|
||||
|
||||
-- 如果不存在,手动创建
|
||||
CREATE SCHEMA IF NOT EXISTS documents;
|
||||
|
||||
-- 重新执行 SQL 脚本
|
||||
```
|
||||
|
||||
### 问题2:权限不足
|
||||
|
||||
**症状**:前端报错 `permission denied`
|
||||
|
||||
**解决方案**:
|
||||
```sql
|
||||
-- 检查当前用户权限
|
||||
SELECT grantee, privilege_type
|
||||
FROM information_schema.role_usage_grants
|
||||
WHERE object_schema = 'documents';
|
||||
|
||||
-- 重新授权(替换 YOUR_USER 为实际用户)
|
||||
GRANT USAGE ON SCHEMA documents TO YOUR_USER;
|
||||
GRANT EXECUTE ON ALL FUNCTIONS IN SCHEMA documents TO YOUR_USER;
|
||||
```
|
||||
|
||||
### 问题3:前端调用失败
|
||||
|
||||
**症状**:浏览器控制台报错 `404 Not Found`
|
||||
|
||||
**检查清单**:
|
||||
1. PostgREST 配置是否包含 `documents` schema
|
||||
2. PostgREST 是否已重启
|
||||
3. 前端代码是否已更新并重新构建
|
||||
|
||||
**解决方案**:
|
||||
```bash
|
||||
# 1. 检查 PostgREST 配置
|
||||
cat /etc/postgrest/config
|
||||
|
||||
# 2. 确保包含:
|
||||
# db-schemas = "public, documents"
|
||||
|
||||
# 3. 重启 PostgREST
|
||||
sudo systemctl restart postgrest
|
||||
|
||||
# 4. 重新构建前端
|
||||
npm run build
|
||||
pm2 restart all
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📊 性能优化
|
||||
|
||||
SQL 脚本已自动创建以下索引:
|
||||
|
||||
```sql
|
||||
-- 1. 用户+名称+时间索引
|
||||
CREATE INDEX idx_documents_user_name_created
|
||||
ON documents(user_id, name, created_at DESC);
|
||||
|
||||
-- 2. 名称+用户+时间索引
|
||||
CREATE INDEX idx_documents_name_user_created
|
||||
ON documents(name, user_id, created_at DESC);
|
||||
|
||||
-- 3. 创建时间索引
|
||||
CREATE INDEX idx_documents_created_at
|
||||
ON documents(created_at DESC);
|
||||
|
||||
-- 4. 文件状态索引
|
||||
CREATE INDEX idx_documents_status
|
||||
ON documents(status);
|
||||
```
|
||||
|
||||
查看索引是否创建成功:
|
||||
```sql
|
||||
SELECT indexname, indexdef
|
||||
FROM pg_indexes
|
||||
WHERE tablename = 'documents'
|
||||
AND schemaname = 'public';
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ✅ 部署检查清单
|
||||
|
||||
- [ ] SQL 脚本执行成功
|
||||
- [ ] 3个函数都已创建在 `documents` schema 中
|
||||
- [ ] 已授权给 PostgREST 用户
|
||||
- [ ] PostgREST 配置已更新
|
||||
- [ ] PostgREST 已重启
|
||||
- [ ] 前端应用已重新构建
|
||||
- [ ] 文档列表页面正常显示
|
||||
- [ ] 展开/折叠功能正常工作
|
||||
- [ ] 问题数量差异正确显示
|
||||
|
||||
---
|
||||
|
||||
## 🎉 完成!
|
||||
|
||||
部署完成后,你应该能看到:
|
||||
|
||||
```
|
||||
文档列表页面
|
||||
├─ [▶️] 📄 合同审查.pdf
|
||||
│ 合同审查 v1 (共3个历史版本)
|
||||
│ 问题数量:3 🟢 ↓ -2
|
||||
│
|
||||
├─ [▶️] 📄 许可证申请.docx
|
||||
│ 许可证审查 v1 (共2个历史版本)
|
||||
│ 问题数量:5 🔴 ↑ +1
|
||||
│
|
||||
└─ 📄 新文档.pdf
|
||||
合同审查
|
||||
问题数量:4
|
||||
```
|
||||
|
||||
点击展开图标后:
|
||||
```
|
||||
[🔽] 📄 合同审查.pdf
|
||||
合同审查 v1 (共3个历史版本)
|
||||
问题数量:3 🟢 ↓ -2
|
||||
|
||||
├─ 🕒 v2 版本 (问题:5 🔴 ↑ +2)
|
||||
├─ 🕒 v3 版本 (问题:3 🟢 ↓ -1)
|
||||
└─ 🕒 v4 版本 (最早) (问题:4)
|
||||
```
|
||||
|
||||
**功能完美运行!** 🚀
|
||||
@@ -0,0 +1,255 @@
|
||||
-- ============================================
|
||||
-- 文档版本管理 RPC 函数(第4版 - 显式类型转换)
|
||||
-- 功能:支持同名文档的版本管理和历史查询
|
||||
-- 修复:所有字段显式转换为函数返回类型
|
||||
-- 创建时间:2025-11-14
|
||||
-- ============================================
|
||||
|
||||
-- ====================
|
||||
-- 删除旧函数
|
||||
-- ====================
|
||||
DROP FUNCTION IF EXISTS documents_get_latest_documents_with_version_info(integer,integer,integer,text,text,integer[],integer,text,text,text);
|
||||
DROP FUNCTION IF EXISTS documents_count_latest_documents_with_filters(integer,text,text,integer[],integer,text,text,text);
|
||||
DROP FUNCTION IF EXISTS documents_get_document_history(text,integer,integer);
|
||||
|
||||
|
||||
-- ====================
|
||||
-- 1. 获取最新版本的文档列表(带版本信息)
|
||||
-- ====================
|
||||
CREATE FUNCTION documents_get_latest_documents_with_version_info(
|
||||
p_user_id integer,
|
||||
p_page integer DEFAULT 1,
|
||||
p_page_size integer DEFAULT 10,
|
||||
p_search_name text DEFAULT NULL,
|
||||
p_search_document_number text DEFAULT NULL,
|
||||
p_search_document_types integer[] DEFAULT NULL,
|
||||
p_search_audit_status integer DEFAULT NULL,
|
||||
p_search_file_status text DEFAULT NULL,
|
||||
p_search_date_from text DEFAULT NULL,
|
||||
p_search_date_to text DEFAULT NULL
|
||||
)
|
||||
RETURNS TABLE (
|
||||
id integer,
|
||||
name text,
|
||||
document_number text,
|
||||
type_id integer,
|
||||
type_name text,
|
||||
file_size integer,
|
||||
audit_status integer,
|
||||
status text,
|
||||
false_count bigint,
|
||||
created_at timestamp with time zone,
|
||||
updated_at timestamp with time zone,
|
||||
path text,
|
||||
is_test_document boolean,
|
||||
ocr_result jsonb,
|
||||
history_count bigint,
|
||||
previous_issues bigint
|
||||
) AS $$
|
||||
BEGIN
|
||||
RETURN QUERY
|
||||
WITH latest_docs AS (
|
||||
-- 使用 DISTINCT ON 获取每个文档名称的最新版本
|
||||
SELECT DISTINCT ON (d.name)
|
||||
d.id,
|
||||
d.name,
|
||||
d.user_id,
|
||||
d.created_at
|
||||
FROM documents d
|
||||
WHERE d.user_id = p_user_id
|
||||
-- 文档名称搜索
|
||||
AND (p_search_name IS NULL OR d.name ILIKE '%' || p_search_name || '%')
|
||||
-- 文档编号搜索
|
||||
AND (p_search_document_number IS NULL OR d.document_number ILIKE '%' || p_search_document_number || '%')
|
||||
-- 文档类型筛选
|
||||
AND (p_search_document_types IS NULL OR d.type_id = ANY(p_search_document_types))
|
||||
-- 审核状态筛选
|
||||
AND (p_search_audit_status IS NULL OR d.audit_status = p_search_audit_status)
|
||||
-- 文件状态筛选
|
||||
AND (p_search_file_status IS NULL OR d.status = p_search_file_status)
|
||||
-- 日期范围筛选
|
||||
AND (p_search_date_from IS NULL OR d.created_at >= p_search_date_from::timestamp)
|
||||
AND (p_search_date_to IS NULL OR d.created_at <= p_search_date_to::timestamp)
|
||||
ORDER BY d.name, d.created_at DESC
|
||||
)
|
||||
SELECT
|
||||
d.id::integer,
|
||||
d.name::text,
|
||||
d.document_number::text,
|
||||
d.type_id::integer,
|
||||
COALESCE(dt.name::text, '') as type_name,
|
||||
d.file_size::integer,
|
||||
d.audit_status::integer,
|
||||
d.status::text,
|
||||
-- 计算当前文档的问题数量(从 evaluation_results 表统计)
|
||||
COALESCE((
|
||||
SELECT COUNT(*)
|
||||
FROM evaluation_results er
|
||||
WHERE er.document_id = d.id
|
||||
AND (er.evaluated_results ->> 'result')::text = 'false'
|
||||
), 0)::bigint as false_count,
|
||||
d.created_at::timestamp with time zone,
|
||||
d.updated_at::timestamp with time zone,
|
||||
d.path::text,
|
||||
d.is_test_document::boolean,
|
||||
d.ocr_result::jsonb,
|
||||
-- 计算历史版本数量(不包含当前版本)
|
||||
COALESCE((
|
||||
SELECT COUNT(*)
|
||||
FROM documents d2
|
||||
WHERE d2.name = d.name
|
||||
AND d2.user_id = d.user_id
|
||||
AND d2.id != d.id
|
||||
), 0)::bigint as history_count,
|
||||
-- 获取上一个版本的问题数量
|
||||
COALESCE((
|
||||
SELECT COUNT(*)
|
||||
FROM evaluation_results er2
|
||||
WHERE er2.document_id = (
|
||||
SELECT d3.id
|
||||
FROM documents d3
|
||||
WHERE d3.name = d.name
|
||||
AND d3.user_id = d.user_id
|
||||
AND d3.created_at < d.created_at
|
||||
ORDER BY d3.created_at DESC
|
||||
LIMIT 1
|
||||
)
|
||||
AND (er2.evaluated_results ->> 'result')::text = 'false'
|
||||
), 0)::bigint as previous_issues
|
||||
FROM documents d
|
||||
INNER JOIN latest_docs ld ON d.id = ld.id
|
||||
LEFT JOIN document_types dt ON d.type_id = dt.id
|
||||
ORDER BY d.created_at DESC
|
||||
LIMIT p_page_size OFFSET (p_page - 1) * p_page_size;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
COMMENT ON FUNCTION documents_get_latest_documents_with_version_info IS '获取最新版本的文档列表,包含历史版本数量和上一个版本的问题数量(从 evaluation_results 表计算)';
|
||||
|
||||
|
||||
-- ====================
|
||||
-- 2. 统计最新版本文档的总数
|
||||
-- ====================
|
||||
CREATE FUNCTION documents_count_latest_documents_with_filters(
|
||||
p_user_id integer,
|
||||
p_search_name text DEFAULT NULL,
|
||||
p_search_document_number text DEFAULT NULL,
|
||||
p_search_document_types integer[] DEFAULT NULL,
|
||||
p_search_audit_status integer DEFAULT NULL,
|
||||
p_search_file_status text DEFAULT NULL,
|
||||
p_search_date_from text DEFAULT NULL,
|
||||
p_search_date_to text DEFAULT NULL
|
||||
)
|
||||
RETURNS integer AS $$
|
||||
DECLARE
|
||||
doc_count integer;
|
||||
BEGIN
|
||||
WITH latest_docs AS (
|
||||
SELECT DISTINCT ON (d.name)
|
||||
d.id
|
||||
FROM documents d
|
||||
WHERE d.user_id = p_user_id
|
||||
AND (p_search_name IS NULL OR d.name ILIKE '%' || p_search_name || '%')
|
||||
AND (p_search_document_number IS NULL OR d.document_number ILIKE '%' || p_search_document_number || '%')
|
||||
AND (p_search_document_types IS NULL OR d.type_id = ANY(p_search_document_types))
|
||||
AND (p_search_audit_status IS NULL OR d.audit_status = p_search_audit_status)
|
||||
AND (p_search_file_status IS NULL OR d.status = p_search_file_status)
|
||||
AND (p_search_date_from IS NULL OR d.created_at >= p_search_date_from::timestamp)
|
||||
AND (p_search_date_to IS NULL OR d.created_at <= p_search_date_to::timestamp)
|
||||
ORDER BY d.name, d.created_at DESC
|
||||
)
|
||||
SELECT COUNT(*)::integer INTO doc_count FROM latest_docs;
|
||||
|
||||
RETURN doc_count;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
COMMENT ON FUNCTION documents_count_latest_documents_with_filters IS '统计符合条件的最新版本文档总数(用于分页)';
|
||||
|
||||
|
||||
-- ====================
|
||||
-- 3. 获取文档的历史版本列表
|
||||
-- ====================
|
||||
CREATE FUNCTION documents_get_document_history(
|
||||
p_document_name text,
|
||||
p_user_id integer,
|
||||
p_exclude_id integer
|
||||
)
|
||||
RETURNS TABLE (
|
||||
id integer,
|
||||
name text,
|
||||
document_number text,
|
||||
type_id integer,
|
||||
type_name text,
|
||||
file_size integer,
|
||||
audit_status integer,
|
||||
status text,
|
||||
false_count bigint,
|
||||
created_at timestamp with time zone,
|
||||
updated_at timestamp with time zone,
|
||||
path text,
|
||||
is_test_document boolean,
|
||||
ocr_result jsonb
|
||||
) AS $$
|
||||
BEGIN
|
||||
RETURN QUERY
|
||||
SELECT
|
||||
d.id::integer,
|
||||
d.name::text,
|
||||
d.document_number::text,
|
||||
d.type_id::integer,
|
||||
COALESCE(dt.name::text, '') as type_name,
|
||||
d.file_size::integer,
|
||||
d.audit_status::integer,
|
||||
d.status::text,
|
||||
-- 计算每个历史版本的问题数量
|
||||
COALESCE((
|
||||
SELECT COUNT(*)
|
||||
FROM evaluation_results er
|
||||
WHERE er.document_id = d.id
|
||||
AND (er.evaluated_results ->> 'result')::text = 'false'
|
||||
), 0)::bigint as false_count,
|
||||
d.created_at::timestamp with time zone,
|
||||
d.updated_at::timestamp with time zone,
|
||||
d.path::text,
|
||||
d.is_test_document::boolean,
|
||||
d.ocr_result::jsonb
|
||||
FROM documents d
|
||||
LEFT JOIN document_types dt ON d.type_id = dt.id
|
||||
WHERE d.name = p_document_name
|
||||
AND d.user_id = p_user_id
|
||||
AND d.id != p_exclude_id
|
||||
ORDER BY d.created_at DESC;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
COMMENT ON FUNCTION documents_get_document_history IS '获取指定文档名称的所有历史版本(不包含指定的当前版本ID),从 evaluation_results 表计算问题数量';
|
||||
|
||||
|
||||
-- ====================
|
||||
-- 4. 创建索引优化查询性能
|
||||
-- ====================
|
||||
|
||||
-- 复合索引:user_id + name + created_at
|
||||
CREATE INDEX IF NOT EXISTS idx_documents_user_name_created
|
||||
ON documents(user_id, name, created_at DESC);
|
||||
|
||||
-- 复合索引:name + user_id + created_at
|
||||
CREATE INDEX IF NOT EXISTS idx_documents_name_user_created
|
||||
ON documents(name, user_id, created_at DESC);
|
||||
|
||||
-- 单列索引:created_at
|
||||
CREATE INDEX IF NOT EXISTS idx_documents_created_at
|
||||
ON documents(created_at DESC);
|
||||
|
||||
-- 单列索引:status
|
||||
CREATE INDEX IF NOT EXISTS idx_documents_status
|
||||
ON documents(status);
|
||||
|
||||
-- evaluation_results 的 document_id
|
||||
CREATE INDEX IF NOT EXISTS idx_evaluation_results_document_id
|
||||
ON evaluation_results(document_id);
|
||||
|
||||
-- evaluation_results 的 evaluated_results->>'result'
|
||||
CREATE INDEX IF NOT EXISTS idx_evaluation_results_result
|
||||
ON evaluation_results((evaluated_results ->> 'result'));
|
||||
@@ -0,0 +1,36 @@
|
||||
-- 入口模块地区管理 - 数据迁移脚本
|
||||
-- 将 areas 字段从字符串数组转换为对象数组,支持启用/禁用状态
|
||||
|
||||
-- 1. 添加备份列
|
||||
ALTER TABLE entry_modules ADD COLUMN IF NOT EXISTS areas_backup JSONB;
|
||||
|
||||
-- 2. 备份原数据
|
||||
UPDATE entry_modules SET areas_backup = areas WHERE areas IS NOT NULL;
|
||||
|
||||
-- 3. 转换数据格式
|
||||
UPDATE entry_modules
|
||||
SET areas = (
|
||||
SELECT jsonb_agg(
|
||||
jsonb_build_object(
|
||||
'area', area_name,
|
||||
'enabled', true,
|
||||
'sort_order', row_number
|
||||
) ORDER BY row_number
|
||||
)
|
||||
FROM (
|
||||
SELECT area_name, ROW_NUMBER() OVER () as row_number
|
||||
FROM jsonb_array_elements_text(areas) AS area_name
|
||||
) AS numbered_areas
|
||||
)
|
||||
WHERE areas IS NOT NULL AND jsonb_typeof(areas) = 'array';
|
||||
|
||||
-- 4. 添加注释
|
||||
COMMENT ON COLUMN entry_modules.areas IS '地区配置: [{"area": "地区名", "enabled": true/false, "sort_order": 排序号}]';
|
||||
|
||||
-- 5. 查看迁移结果
|
||||
SELECT id, name, areas_backup AS old_format, areas AS new_format
|
||||
FROM entry_modules WHERE areas IS NOT NULL;
|
||||
|
||||
-- 回滚(如需要):
|
||||
-- UPDATE entry_modules SET areas = areas_backup WHERE areas_backup IS NOT NULL;
|
||||
-- ALTER TABLE entry_modules DROP COLUMN areas_backup;
|
||||
@@ -0,0 +1,50 @@
|
||||
-- 入口模块地区管理 - 常用操作
|
||||
|
||||
-- 查看所有配置
|
||||
SELECT id, name, jsonb_pretty(areas) AS configs FROM entry_modules;
|
||||
|
||||
-- 禁用某个入口在某个地区(示例:禁用id=1在"云浮")
|
||||
UPDATE entry_modules
|
||||
SET areas = (
|
||||
SELECT jsonb_agg(CASE WHEN elem->>'area' = '云浮'
|
||||
THEN jsonb_set(elem, '{enabled}', 'false'::jsonb) ELSE elem END)
|
||||
FROM jsonb_array_elements(areas) AS elem
|
||||
), updated_at = NOW()
|
||||
WHERE id = 1;
|
||||
|
||||
-- 启用某个入口在某个地区(示例:启用id=1在"云浮")
|
||||
UPDATE entry_modules
|
||||
SET areas = (
|
||||
SELECT jsonb_agg(CASE WHEN elem->>'area' = '云浮'
|
||||
THEN jsonb_set(elem, '{enabled}', 'true'::jsonb) ELSE elem END)
|
||||
FROM jsonb_array_elements(areas) AS elem
|
||||
), updated_at = NOW()
|
||||
WHERE id = 1;
|
||||
|
||||
-- 添加新地区(示例:为id=1添加"揭阳")
|
||||
UPDATE entry_modules
|
||||
SET areas = areas || jsonb_build_array(
|
||||
jsonb_build_object('area', '揭阳', 'enabled', true, 'sort_order',
|
||||
(SELECT COALESCE(MAX((elem->>'sort_order')::int), 0) + 1
|
||||
FROM jsonb_array_elements(areas) AS elem))
|
||||
), updated_at = NOW()
|
||||
WHERE id = 1 AND NOT EXISTS (
|
||||
SELECT 1 FROM jsonb_array_elements(areas) AS elem WHERE elem->>'area' = '揭阳'
|
||||
);
|
||||
|
||||
-- 删除某个地区配置(示例:从id=1删除"云浮")
|
||||
UPDATE entry_modules
|
||||
SET areas = (SELECT jsonb_agg(elem) FROM jsonb_array_elements(areas) AS elem
|
||||
WHERE elem->>'area' != '云浮'
|
||||
), updated_at = NOW()
|
||||
WHERE id = 1;
|
||||
|
||||
-- 查看某个地区已启用的模块(示例:查看"梅州")
|
||||
SELECT id, name FROM entry_modules
|
||||
WHERE areas @> '[{"area": "梅州", "enabled": true}]'::jsonb;
|
||||
|
||||
-- 统计每个地区的已启用模块数量
|
||||
SELECT elem->>'area' AS area, COUNT(*) AS count
|
||||
FROM entry_modules, jsonb_array_elements(areas) AS elem
|
||||
WHERE (elem->>'enabled')::boolean = true
|
||||
GROUP BY elem->>'area';
|
||||
@@ -0,0 +1,11 @@
|
||||
-- 删除 /documents/download 路由记录
|
||||
-- 该路由已不再使用,项目统一使用 /api/pdf-proxy 进行文件下载
|
||||
|
||||
-- 删除路由记录
|
||||
DELETE FROM sys_routes WHERE id = 34 AND route_path = '/documents/download';
|
||||
|
||||
-- 验证删除结果
|
||||
SELECT id, route_path, route_name, route_title, parent_id, is_hidden
|
||||
FROM sys_routes
|
||||
WHERE route_path LIKE '/documents%'
|
||||
ORDER BY sort_order;
|
||||
@@ -0,0 +1,41 @@
|
||||
-- ========================================
|
||||
-- 重命名外键约束,使其与代码中的名称一致
|
||||
-- ========================================
|
||||
|
||||
-- 步骤 1: 删除旧的外键约束
|
||||
ALTER TABLE evaluation_points
|
||||
DROP CONSTRAINT IF EXISTS fk_evaluation_points_group;
|
||||
|
||||
-- 步骤 2: 创建新的外键约束,使用正确的名称
|
||||
ALTER TABLE evaluation_points
|
||||
ADD CONSTRAINT fk_evaluation_points_child_group
|
||||
FOREIGN KEY (evaluation_point_groups_id)
|
||||
REFERENCES evaluation_point_groups(id)
|
||||
ON DELETE SET NULL
|
||||
ON UPDATE CASCADE;
|
||||
|
||||
-- 添加注释
|
||||
COMMENT ON CONSTRAINT fk_evaluation_points_child_group ON evaluation_points
|
||||
IS '评查点所属规则组外键约束(子分组)';
|
||||
|
||||
-- 步骤 3: 验证外键约束(应该是正确的名称)
|
||||
SELECT
|
||||
tc.constraint_name AS "约束名",
|
||||
kcu.column_name AS "列名",
|
||||
ccu.table_name AS "引用表",
|
||||
ccu.column_name AS "引用列"
|
||||
FROM information_schema.table_constraints AS tc
|
||||
JOIN information_schema.key_column_usage AS kcu
|
||||
ON tc.constraint_name = kcu.constraint_name
|
||||
JOIN information_schema.constraint_column_usage AS ccu
|
||||
ON ccu.constraint_name = tc.constraint_name
|
||||
WHERE tc.constraint_type = 'FOREIGN KEY'
|
||||
AND tc.table_name = 'evaluation_points'
|
||||
AND kcu.column_name IN ('evaluation_point_groups_id', 'evaluation_point_groups_pid')
|
||||
ORDER BY kcu.column_name;
|
||||
|
||||
-- 预期结果:
|
||||
-- 约束名 | 列名 | 引用表 | 引用列
|
||||
-- ------------------------------------------|-------------------------------|---------------------------|-------
|
||||
-- fk_evaluation_points_child_group | evaluation_point_groups_id | evaluation_point_groups | id
|
||||
-- fk_evaluation_points_parent_group | evaluation_point_groups_pid | evaluation_point_groups | id
|
||||
@@ -0,0 +1,245 @@
|
||||
-- ============================================================
|
||||
-- 系统路由表 (sys_routes) 更新脚本
|
||||
-- 目的:根据实际 app/routes 目录和期望菜单结构更新路由配置
|
||||
-- 生成时间: 2025-11-17
|
||||
-- ============================================================
|
||||
|
||||
-- ============================================================
|
||||
-- 第 1 步:删除废弃的路由
|
||||
-- ============================================================
|
||||
|
||||
-- 删除废弃的一级菜单及其子路由
|
||||
DELETE FROM sys_routes WHERE id IN (3, 4, 5, 6);
|
||||
|
||||
-- ============================================================
|
||||
-- 第 2 步:调整现有路由的层级结构
|
||||
-- ============================================================
|
||||
|
||||
-- 2.1 将 rule-groups 改为 rules 的子级(评查点分组)
|
||||
UPDATE sys_routes
|
||||
SET parent_id = 41,
|
||||
sort_order = 1,
|
||||
route_title = '评查点分组'
|
||||
WHERE id = 43;
|
||||
|
||||
-- 2.2 将 prompts 改为 settings 的子级(稍后创建 settings 父级)
|
||||
-- 注意:先创建 settings 路由后再执行此更新
|
||||
-- UPDATE sys_routes SET parent_id = 53, sort_order = 2 WHERE id = 45;
|
||||
|
||||
-- 2.3 将 document-types 改为 settings 的子级
|
||||
-- UPDATE sys_routes SET parent_id = 53, sort_order = 1 WHERE id = 47;
|
||||
|
||||
-- 2.4 更新合同模板搜索的标题
|
||||
UPDATE sys_routes
|
||||
SET route_title = '智能搜索'
|
||||
WHERE id = 39;
|
||||
|
||||
-- ============================================================
|
||||
-- 第 3 步:添加新的一级菜单 - 系统设置
|
||||
-- ============================================================
|
||||
|
||||
INSERT INTO sys_routes (
|
||||
id,
|
||||
route_path,
|
||||
route_name,
|
||||
route_title,
|
||||
parent_id,
|
||||
icon,
|
||||
sort_order,
|
||||
is_hidden,
|
||||
is_cache,
|
||||
component,
|
||||
meta
|
||||
) VALUES (
|
||||
53,
|
||||
'/settings',
|
||||
'Settings',
|
||||
'系统设置',
|
||||
NULL,
|
||||
'el-icon-setting',
|
||||
20,
|
||||
false,
|
||||
true,
|
||||
'views/settings/Index.vue',
|
||||
'{}'
|
||||
);
|
||||
|
||||
-- 现在更新 prompts 和 document-types 的父级
|
||||
UPDATE sys_routes SET parent_id = 53, sort_order = 2, route_title = '提示词管理' WHERE id = 45;
|
||||
UPDATE sys_routes SET parent_id = 53, sort_order = 1, route_title = '文档类型' WHERE id = 47;
|
||||
|
||||
-- ============================================================
|
||||
-- 第 4 步:添加缺失的路由
|
||||
-- ============================================================
|
||||
|
||||
-- 4.1 添加 /rules/list - 评查点列表(作为 rules 的子级)
|
||||
INSERT INTO sys_routes (
|
||||
route_path,
|
||||
route_name,
|
||||
route_title,
|
||||
parent_id,
|
||||
icon,
|
||||
sort_order,
|
||||
is_hidden,
|
||||
is_cache,
|
||||
component,
|
||||
meta
|
||||
) VALUES (
|
||||
'/rules/list',
|
||||
'RulesList',
|
||||
'评查点列表',
|
||||
41,
|
||||
NULL,
|
||||
2,
|
||||
false,
|
||||
true,
|
||||
'views/rules/List.vue',
|
||||
'{}'
|
||||
);
|
||||
|
||||
-- 4.2 添加 /rules-files - 评查文件列表(作为 rules 的子级)
|
||||
INSERT INTO sys_routes (
|
||||
route_path,
|
||||
route_name,
|
||||
route_title,
|
||||
parent_id,
|
||||
icon,
|
||||
sort_order,
|
||||
is_hidden,
|
||||
is_cache,
|
||||
component,
|
||||
meta
|
||||
) VALUES (
|
||||
'/rules-files',
|
||||
'RulesFiles',
|
||||
'评查文件列表',
|
||||
41,
|
||||
NULL,
|
||||
3,
|
||||
false,
|
||||
true,
|
||||
'views/rules/Files.vue',
|
||||
'{}'
|
||||
);
|
||||
|
||||
-- 4.3 添加 /contract-template/list - 合同列表(作为 contract-template 的子级)
|
||||
INSERT INTO sys_routes (
|
||||
route_path,
|
||||
route_name,
|
||||
route_title,
|
||||
parent_id,
|
||||
icon,
|
||||
sort_order,
|
||||
is_hidden,
|
||||
is_cache,
|
||||
component,
|
||||
meta
|
||||
) VALUES (
|
||||
'/contract-template/list',
|
||||
'ContractTemplateList',
|
||||
'合同列表',
|
||||
38,
|
||||
NULL,
|
||||
2,
|
||||
false,
|
||||
true,
|
||||
'views/contract-template/List.vue',
|
||||
'{}'
|
||||
);
|
||||
|
||||
-- 4.4 添加 /reviews - 评查详情页(隐藏路由,不在侧边栏显示)
|
||||
INSERT INTO sys_routes (
|
||||
route_path,
|
||||
route_name,
|
||||
route_title,
|
||||
parent_id,
|
||||
icon,
|
||||
sort_order,
|
||||
is_hidden,
|
||||
is_cache,
|
||||
component,
|
||||
meta
|
||||
) VALUES (
|
||||
'/reviews',
|
||||
'Reviews',
|
||||
'评查详情',
|
||||
NULL,
|
||||
NULL,
|
||||
99,
|
||||
true,
|
||||
true,
|
||||
'views/reviews/Index.vue',
|
||||
'{}'
|
||||
);
|
||||
|
||||
-- ============================================================
|
||||
-- 第 5 步:调整排序顺序,使菜单符合期望结构
|
||||
-- ============================================================
|
||||
|
||||
-- 期望的一级菜单顺序:
|
||||
-- 1. 系统概览
|
||||
-- 2. 文档管理
|
||||
-- 3. 评查规则库
|
||||
-- 4. 合同模板
|
||||
-- 5. 系统设置
|
||||
|
||||
UPDATE sys_routes SET sort_order = 1 WHERE id = 31; -- /home (系统概览)
|
||||
UPDATE sys_routes SET sort_order = 2 WHERE id = 2; -- /documents (文档管理)
|
||||
UPDATE sys_routes SET sort_order = 3 WHERE id = 41; -- /rules (评查规则库)
|
||||
UPDATE sys_routes SET sort_order = 4 WHERE id = 38; -- /contract-template (合同模板)
|
||||
UPDATE sys_routes SET sort_order = 5 WHERE id = 53; -- /settings (系统设置)
|
||||
|
||||
-- 其他菜单(交叉评查、AI助手等)排在后面
|
||||
UPDATE sys_routes SET sort_order = 6 WHERE id = 35; -- /cross-checking (交叉评查)
|
||||
UPDATE sys_routes SET sort_order = 7 WHERE id = 40; -- /chat-with-llm (AI法务助手)
|
||||
UPDATE sys_routes SET sort_order = 8 WHERE id = 51; -- /files (文件管理)
|
||||
UPDATE sys_routes SET sort_order = 9 WHERE id = 49; -- /config-lists (配置列表)
|
||||
|
||||
-- ============================================================
|
||||
-- 第 6 步:隐藏不需要在菜单中显示的路由
|
||||
-- ============================================================
|
||||
|
||||
-- 隐藏入口页(不在侧边栏显示)
|
||||
UPDATE sys_routes SET is_hidden = true WHERE id = 30; -- /
|
||||
|
||||
-- ============================================================
|
||||
-- 第 7 步:验证更新结果
|
||||
-- ============================================================
|
||||
|
||||
-- 查看更新后的一级菜单
|
||||
SELECT id, route_path, route_name, route_title, parent_id, icon, sort_order, is_hidden
|
||||
FROM sys_routes
|
||||
WHERE parent_id IS NULL
|
||||
ORDER BY sort_order;
|
||||
|
||||
-- 查看 /rules 的子菜单
|
||||
SELECT id, route_path, route_name, route_title, parent_id, icon, sort_order, is_hidden
|
||||
FROM sys_routes
|
||||
WHERE parent_id = 41
|
||||
ORDER BY sort_order;
|
||||
|
||||
-- 查看 /contract-template 的子菜单
|
||||
SELECT id, route_path, route_name, route_title, parent_id, icon, sort_order, is_hidden
|
||||
FROM sys_routes
|
||||
WHERE parent_id = 38
|
||||
ORDER BY sort_order;
|
||||
|
||||
-- 查看 /settings 的子菜单
|
||||
SELECT id, route_path, route_name, route_title, parent_id, icon, sort_order, is_hidden
|
||||
FROM sys_routes
|
||||
WHERE parent_id = 53
|
||||
ORDER BY sort_order;
|
||||
|
||||
-- ============================================================
|
||||
-- 第 8 步:清理 role_route 表中的废弃路由关联
|
||||
-- ============================================================
|
||||
|
||||
-- 删除指向已废弃路由的角色-路由关联
|
||||
DELETE FROM role_route
|
||||
WHERE route_id IN (3, 4, 5, 6);
|
||||
|
||||
-- ============================================================
|
||||
-- 完成!
|
||||
-- ============================================================
|
||||
|
||||
-- 提示:执行此脚本后,请重新登录系统以刷新路由权限
|
||||
@@ -0,0 +1,119 @@
|
||||
-- ============================================================
|
||||
-- 系统路由表 (sys_routes) 剩余更新脚本
|
||||
-- 说明:部分更新已完成(废弃路由删除、settings创建等)
|
||||
-- 此脚本仅包含剩余需要执行的步骤
|
||||
-- 生成时间: 2025-11-17
|
||||
-- ============================================================
|
||||
|
||||
-- ============================================================
|
||||
-- 第 1 步:添加缺失的路由
|
||||
-- ============================================================
|
||||
|
||||
-- 1.1 添加 /rules/list 列表页(作为 rules 的子级)
|
||||
INSERT INTO sys_routes (
|
||||
route_path, route_name, route_title,
|
||||
parent_id, icon, sort_order,
|
||||
is_hidden, is_cache, component, meta
|
||||
) VALUES (
|
||||
'/rules/list', 'RulesList', '评查点列表',
|
||||
41, NULL, 2,
|
||||
false, true, 'views/rules/List.vue', '{}'
|
||||
);
|
||||
|
||||
-- 1.2 添加 /rules-files(作为 rules 的子级)
|
||||
INSERT INTO sys_routes (
|
||||
route_path, route_name, route_title,
|
||||
parent_id, icon, sort_order,
|
||||
is_hidden, is_cache, component, meta
|
||||
) VALUES (
|
||||
'/rules-files', 'RulesFiles', '评查文件列表',
|
||||
41, NULL, 3,
|
||||
false, true, 'views/rules/Files.vue', '{}'
|
||||
);
|
||||
|
||||
-- 1.3 添加 /contract-template/list(作为 contract-template 的子级)
|
||||
INSERT INTO sys_routes (
|
||||
route_path, route_name, route_title,
|
||||
parent_id, icon, sort_order,
|
||||
is_hidden, is_cache, component, meta
|
||||
) VALUES (
|
||||
'/contract-template/list', 'ContractTemplateList', '合同列表',
|
||||
38, NULL, 2,
|
||||
false, true, 'views/contract-template/List.vue', '{}'
|
||||
);
|
||||
|
||||
-- 1.4 添加 /reviews 评查详情页(隐藏路由)
|
||||
INSERT INTO sys_routes (
|
||||
route_path, route_name, route_title,
|
||||
parent_id, icon, sort_order,
|
||||
is_hidden, is_cache, component, meta
|
||||
) VALUES (
|
||||
'/reviews', 'Reviews', '评查详情',
|
||||
NULL, NULL, 99,
|
||||
true, true, 'views/reviews/Index.vue', '{}'
|
||||
);
|
||||
|
||||
-- ============================================================
|
||||
-- 第 2 步:调整排序顺序
|
||||
-- ============================================================
|
||||
|
||||
-- 期望的一级菜单顺序:
|
||||
-- 1. 系统概览
|
||||
-- 2. 文档管理
|
||||
-- 3. 评查规则库
|
||||
-- 4. 合同模板
|
||||
-- 5. 系统设置
|
||||
-- 6. 交叉评查
|
||||
-- 7. AI法务助手
|
||||
-- 8. 文件管理
|
||||
-- 9. 配置列表
|
||||
|
||||
UPDATE sys_routes SET sort_order = 1 WHERE id = 31; -- /home (系统概览)
|
||||
UPDATE sys_routes SET sort_order = 2 WHERE id = 2; -- /documents (文档管理)
|
||||
UPDATE sys_routes SET sort_order = 3 WHERE id = 41; -- /rules (评查规则库)
|
||||
UPDATE sys_routes SET sort_order = 4 WHERE id = 38; -- /contract-template (合同模板)
|
||||
UPDATE sys_routes SET sort_order = 5 WHERE id = 53; -- /settings (系统设置)
|
||||
UPDATE sys_routes SET sort_order = 6 WHERE id = 35; -- /cross-checking (交叉评查)
|
||||
UPDATE sys_routes SET sort_order = 7 WHERE id = 40; -- /chat-with-llm (AI法务助手)
|
||||
UPDATE sys_routes SET sort_order = 8 WHERE id = 51; -- /files (文件管理)
|
||||
UPDATE sys_routes SET sort_order = 9 WHERE id = 49; -- /config-lists (配置列表)
|
||||
|
||||
-- ============================================================
|
||||
-- 第 3 步:隐藏入口页
|
||||
-- ============================================================
|
||||
|
||||
UPDATE sys_routes SET is_hidden = true WHERE id = 30; -- /
|
||||
|
||||
-- ============================================================
|
||||
-- 第 4 步:验证更新结果
|
||||
-- ============================================================
|
||||
|
||||
-- 查看更新后的一级菜单
|
||||
SELECT id, route_path, route_name, route_title, parent_id, icon, sort_order, is_hidden
|
||||
FROM sys_routes
|
||||
WHERE parent_id IS NULL
|
||||
ORDER BY sort_order;
|
||||
|
||||
-- 查看 /rules 的子菜单
|
||||
SELECT id, route_path, route_name, route_title, parent_id, sort_order, is_hidden
|
||||
FROM sys_routes
|
||||
WHERE parent_id = 41
|
||||
ORDER BY sort_order;
|
||||
|
||||
-- 查看 /contract-template 的子菜单
|
||||
SELECT id, route_path, route_name, route_title, parent_id, sort_order, is_hidden
|
||||
FROM sys_routes
|
||||
WHERE parent_id = 38
|
||||
ORDER BY sort_order;
|
||||
|
||||
-- 查看 /settings 的子菜单
|
||||
SELECT id, route_path, route_name, route_title, parent_id, sort_order, is_hidden
|
||||
FROM sys_routes
|
||||
WHERE parent_id = 53
|
||||
ORDER BY sort_order;
|
||||
|
||||
-- ============================================================
|
||||
-- 完成!
|
||||
-- ============================================================
|
||||
|
||||
-- 提示:执行此脚本后,请重新登录系统以刷新路由权限
|
||||
Reference in New Issue
Block a user