feat: add contract template v3 api and legacy oss migration
This commit is contained in:
@@ -46,6 +46,11 @@ psql -h <host> -U <user> -d <db_name> -v ON_ERROR_STOP=1 -f scripts/创建sql/<f
|
||||
3. `schema_v2_add_evaluation_tables.sql`
|
||||
4. `seed_home_entry_modules.sql`
|
||||
|
||||
### 合同模板模块上线
|
||||
|
||||
1. `schema_contract_templates.sql`
|
||||
2. `seed_contract_templates_rbac.sql`
|
||||
|
||||
### 系统使用统计上线
|
||||
|
||||
1. `schema_add_usage_stats.sql`
|
||||
@@ -221,6 +226,91 @@ ORDER BY permission_key;
|
||||
1. `schema_v3_add_cross_review_phase1.sql`
|
||||
2. `seed_cross_review_phase1_permissions.sql`
|
||||
|
||||
### 七、合同模板
|
||||
|
||||
- `schema_contract_templates.sql`
|
||||
- 用途:在主库创建合同模板分类表和模板表
|
||||
- 主要内容:新增 `contract_categories`、`contract_templates`、补索引和注释
|
||||
- 执行时机:上线合同模板新后端接口前必跑
|
||||
|
||||
- `seed_contract_templates_rbac.sql`
|
||||
- 用途:补齐合同模板只读权限点
|
||||
- 主要内容:新增 `contract_template:list:read`、`contract_template:search:read`、`contract_template:detail:read`
|
||||
- 依赖:`sys_routes` 中已经存在 `/contract-template/list` 和 `/contract-template/search`
|
||||
|
||||
- `migrate_legacy_contract_templates.py`
|
||||
- 用途:把老库 `docauditai` 的合同模板分类、模板记录和旧 bucket 文件迁入主库 `leaudit_platform`
|
||||
- 主要内容:
|
||||
- 读取老库 `public.contract_categories`、`public.contract_templates`
|
||||
- 从旧 bucket `docauditai` 读取 `contract-template/...` 对象
|
||||
- 复制到新 bucket `leaudit` 的 `contract-templates/...` 相对路径
|
||||
- 回写主库 `contract_categories`、`contract_templates.file_path`、`contract_templates.pdf_file_path`
|
||||
- 适用场景:主库已完成建表与权限初始化,但仍是 demo 数据或空数据时
|
||||
- 注意:
|
||||
- 脚本会重置主库 `contract_categories` / `contract_templates` 当前数据并按老库正式数据重建
|
||||
- 当前已知会自动修正 1 条老脏数据:
|
||||
- `contract_templates.id=3`
|
||||
- 标题:`房屋租赁合同(我方承租)`
|
||||
- 老 `file_path` 误指向“我方出租”docx,迁移时会自动改成“我方承租”docx
|
||||
|
||||
#### 推荐顺序
|
||||
|
||||
1. `schema_contract_templates.sql`
|
||||
2. `seed_contract_templates_rbac.sql`
|
||||
3. `python scripts/migrate_legacy_contract_templates.py`
|
||||
4. `python scripts/migrate_legacy_contract_templates.py --apply`
|
||||
|
||||
#### 标准执行命令
|
||||
|
||||
```bash
|
||||
psql -h <host> -U <user> -d <db_name> -v ON_ERROR_STOP=1 -f scripts/创建sql/schema_contract_templates.sql
|
||||
psql -h <host> -U <user> -d <db_name> -v ON_ERROR_STOP=1 -f scripts/创建sql/seed_contract_templates_rbac.sql
|
||||
|
||||
# 先 dry-run,看旧路径 -> 新路径映射
|
||||
python scripts/migrate_legacy_contract_templates.py
|
||||
|
||||
# 确认无误后正式执行:复制 OSS 文件 + 回写主库
|
||||
python scripts/migrate_legacy_contract_templates.py --apply
|
||||
```
|
||||
|
||||
#### 执行后验收
|
||||
|
||||
```sql
|
||||
SELECT to_regclass('public.contract_categories');
|
||||
SELECT to_regclass('public.contract_templates');
|
||||
|
||||
SELECT permission_key
|
||||
FROM permissions
|
||||
WHERE permission_key LIKE 'contract_template:%'
|
||||
ORDER BY permission_key;
|
||||
|
||||
SELECT r.role_key, p.permission_key, rp.grant_type, rp.data_scope
|
||||
FROM role_permissions rp
|
||||
JOIN roles r ON r.id = rp.role_id
|
||||
JOIN permissions p ON p.id = rp.permission_id
|
||||
WHERE p.permission_key LIKE 'contract_template:%'
|
||||
ORDER BY r.role_key, p.permission_key;
|
||||
|
||||
SELECT COUNT(*) AS category_count FROM public.contract_categories;
|
||||
SELECT COUNT(*) AS template_count FROM public.contract_templates;
|
||||
|
||||
SELECT id, template_code, title, file_path, pdf_file_path
|
||||
FROM public.contract_templates
|
||||
ORDER BY id
|
||||
LIMIT 10;
|
||||
```
|
||||
|
||||
#### 当前基线验收结果
|
||||
|
||||
- 主库 `leaudit_platform`
|
||||
- `contract_categories = 9`
|
||||
- `contract_templates = 27`
|
||||
- 新 bucket `leaudit`
|
||||
- `contract-templates/...` 对象总数 = `54`
|
||||
- 新路径样例
|
||||
- `contract-templates/买卖合同/mmht/source__买卖合同范本.docx`
|
||||
- `contract-templates/买卖合同/mmht/preview__买卖合同范本.pdf`
|
||||
|
||||
### 七、RAG
|
||||
|
||||
- `schema_add_rag_chat.sql`
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
BEGIN;
|
||||
|
||||
-- ============================================================================
|
||||
-- LeAudit Platform Contract Template Schema
|
||||
-- 目标:
|
||||
-- 1. 在主库 leaudit_platform 创建合同模板分类表
|
||||
-- 2. 在主库 leaudit_platform 创建合同模板表
|
||||
-- 3. 补齐索引与基础约束
|
||||
-- 说明:
|
||||
-- - 本脚本不依赖旧库 docauditai
|
||||
-- - 幂等脚本,可重复执行
|
||||
-- ============================================================================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS public.contract_categories (
|
||||
id SERIAL PRIMARY KEY,
|
||||
name VARCHAR(100) NOT NULL,
|
||||
icon VARCHAR(100) NULL,
|
||||
description TEXT NULL,
|
||||
sort_order INTEGER NOT NULL DEFAULT 0,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS idx_contract_categories_name
|
||||
ON public.contract_categories(name);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS public.contract_templates (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
template_code VARCHAR(50) NOT NULL,
|
||||
title VARCHAR(200) NOT NULL,
|
||||
category_id INTEGER NOT NULL REFERENCES public.contract_categories(id) ON DELETE RESTRICT,
|
||||
description TEXT NULL,
|
||||
file_path VARCHAR(500) NULL,
|
||||
file_format VARCHAR(10) NOT NULL,
|
||||
is_featured BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
pdf_file_path VARCHAR(500) NULL
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS idx_contract_templates_code
|
||||
ON public.contract_templates(template_code);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_contract_templates_category_id
|
||||
ON public.contract_templates(category_id);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_contract_templates_updated_at
|
||||
ON public.contract_templates(updated_at DESC);
|
||||
|
||||
COMMENT ON TABLE public.contract_categories IS '合同模板分类表';
|
||||
COMMENT ON COLUMN public.contract_categories.name IS '分类名称';
|
||||
COMMENT ON COLUMN public.contract_categories.icon IS '分类图标';
|
||||
COMMENT ON COLUMN public.contract_categories.description IS '分类描述';
|
||||
COMMENT ON COLUMN public.contract_categories.sort_order IS '排序值';
|
||||
|
||||
COMMENT ON TABLE public.contract_templates IS '合同模板主表';
|
||||
COMMENT ON COLUMN public.contract_templates.template_code IS '模板编码';
|
||||
COMMENT ON COLUMN public.contract_templates.title IS '模板标题';
|
||||
COMMENT ON COLUMN public.contract_templates.category_id IS '所属分类ID';
|
||||
COMMENT ON COLUMN public.contract_templates.description IS '模板描述';
|
||||
COMMENT ON COLUMN public.contract_templates.file_path IS '源模板文件路径';
|
||||
COMMENT ON COLUMN public.contract_templates.file_format IS '文件格式';
|
||||
COMMENT ON COLUMN public.contract_templates.is_featured IS '是否推荐模板';
|
||||
COMMENT ON COLUMN public.contract_templates.pdf_file_path IS 'PDF预览文件路径';
|
||||
|
||||
COMMIT;
|
||||
@@ -0,0 +1,140 @@
|
||||
BEGIN;
|
||||
|
||||
-- ============================================================================
|
||||
-- LeAudit Platform Contract Template RBAC Seed
|
||||
-- 目标:
|
||||
-- 1. 补齐合同模板读权限
|
||||
-- 2. 给 super_admin / provincial_admin / admin 分配模板读权限
|
||||
-- 说明:
|
||||
-- - 依赖 user_rbac_schema_patch.sql
|
||||
-- - 依赖合同模板前端路由已存在于 sys_routes
|
||||
-- - 幂等脚本,可重复执行
|
||||
-- ============================================================================
|
||||
|
||||
WITH route_map AS (
|
||||
SELECT id, route_path
|
||||
FROM sys_routes
|
||||
WHERE deleted_at IS NULL
|
||||
AND route_path IN ('/contract-template/list', '/contract-template/search')
|
||||
)
|
||||
INSERT INTO permissions (
|
||||
permission_key,
|
||||
module,
|
||||
resource,
|
||||
action,
|
||||
description,
|
||||
display_name,
|
||||
permission_type,
|
||||
is_system,
|
||||
metadata,
|
||||
created_at,
|
||||
updated_at,
|
||||
created_by,
|
||||
updated_by,
|
||||
parent_id,
|
||||
sort_order,
|
||||
route_id,
|
||||
api_path,
|
||||
api_method,
|
||||
related_routes
|
||||
)
|
||||
SELECT
|
||||
seed.permission_key,
|
||||
seed.module,
|
||||
seed.resource,
|
||||
seed.action,
|
||||
seed.description,
|
||||
seed.display_name,
|
||||
'API',
|
||||
TRUE,
|
||||
NULL::jsonb,
|
||||
NOW(),
|
||||
NOW(),
|
||||
NULL::bigint,
|
||||
NULL::bigint,
|
||||
NULL::bigint,
|
||||
seed.sort_order,
|
||||
route_map.id,
|
||||
seed.api_path,
|
||||
seed.api_method,
|
||||
NULL::bigint[]
|
||||
FROM (
|
||||
VALUES
|
||||
('contract_template:list:read', 'contract_template', 'list', 'read', '查看合同模板列表', '查看合同模板列表', '/contract-template/list', 310, '/api/v3/contract-templates', 'GET'),
|
||||
('contract_template:search:read', 'contract_template', 'search', 'read', '搜索合同模板', '搜索合同模板', '/contract-template/search', 311, '/api/v3/contract-templates/search','GET'),
|
||||
('contract_template:detail:read', 'contract_template', 'detail', 'read', '查看合同模板详情', '查看合同模板详情', '/contract-template/list', 312, '/api/v3/contract-templates/{id}', 'GET')
|
||||
) AS seed(
|
||||
permission_key,
|
||||
module,
|
||||
resource,
|
||||
action,
|
||||
description,
|
||||
display_name,
|
||||
route_path,
|
||||
sort_order,
|
||||
api_path,
|
||||
api_method
|
||||
)
|
||||
JOIN route_map ON route_map.route_path = seed.route_path
|
||||
ON CONFLICT (permission_key) DO UPDATE SET
|
||||
module = EXCLUDED.module,
|
||||
resource = EXCLUDED.resource,
|
||||
action = EXCLUDED.action,
|
||||
description = EXCLUDED.description,
|
||||
display_name = EXCLUDED.display_name,
|
||||
permission_type = EXCLUDED.permission_type,
|
||||
is_system = EXCLUDED.is_system,
|
||||
route_id = EXCLUDED.route_id,
|
||||
api_path = EXCLUDED.api_path,
|
||||
api_method = EXCLUDED.api_method,
|
||||
sort_order = EXCLUDED.sort_order,
|
||||
updated_at = NOW();
|
||||
|
||||
WITH role_map AS (
|
||||
SELECT id, role_key
|
||||
FROM roles
|
||||
WHERE role_key IN ('super_admin', 'provincial_admin', 'admin')
|
||||
),
|
||||
perm_map AS (
|
||||
SELECT id, permission_key
|
||||
FROM permissions
|
||||
WHERE permission_key LIKE 'contract_template:%'
|
||||
),
|
||||
seed(role_key, permission_key, grant_type, data_scope) AS (
|
||||
VALUES
|
||||
('super_admin', 'contract_template:list:read', 'GRANT', 'ALL'),
|
||||
('super_admin', 'contract_template:search:read', 'GRANT', 'ALL'),
|
||||
('super_admin', 'contract_template:detail:read', 'GRANT', 'ALL'),
|
||||
|
||||
('provincial_admin', 'contract_template:list:read', 'GRANT', 'ALL'),
|
||||
('provincial_admin', 'contract_template:search:read', 'GRANT', 'ALL'),
|
||||
('provincial_admin', 'contract_template:detail:read', 'GRANT', 'ALL'),
|
||||
|
||||
('admin', 'contract_template:list:read', 'GRANT', 'DEPT'),
|
||||
('admin', 'contract_template:search:read', 'GRANT', 'DEPT'),
|
||||
('admin', 'contract_template:detail:read', 'GRANT', 'DEPT')
|
||||
)
|
||||
INSERT INTO role_permissions (
|
||||
role_id,
|
||||
permission_id,
|
||||
grant_type,
|
||||
data_scope,
|
||||
created_at,
|
||||
updated_at
|
||||
)
|
||||
SELECT
|
||||
role_map.id,
|
||||
perm_map.id,
|
||||
seed.grant_type,
|
||||
seed.data_scope,
|
||||
NOW(),
|
||||
NOW()
|
||||
FROM seed
|
||||
JOIN role_map ON role_map.role_key = seed.role_key
|
||||
JOIN perm_map ON perm_map.permission_key = seed.permission_key
|
||||
ON CONFLICT (role_id, permission_id) DO UPDATE SET
|
||||
grant_type = EXCLUDED.grant_type,
|
||||
data_scope = EXCLUDED.data_scope,
|
||||
updated_at = NOW();
|
||||
|
||||
COMMIT;
|
||||
Reference in New Issue
Block a user