Files
leaudit-platform-backend/docs/合同模板搜索合同起草/合同模板上传与地区隔离改造方案.md

471 lines
11 KiB
Markdown
Raw Permalink 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.
# 合同模板上传与地区隔离改造方案
## 1. 背景
当前合同模板模块已具备以下只读能力:
- `/api/v3/contract-templates/categories`
- `/api/v3/contract-templates`
- `/api/v3/contract-templates/search`
- `/api/v3/contract-templates/{id}`
当前实现可以支撑模板分类、列表、搜索、详情展示,但还不具备正式的模板管理上传能力。现阶段新增需求包括:
-`/contract-template/list` 页面支持上传合同模板
- 新模板必须存入 `leaudit_platform` 主库,不再依赖旧项目库
- 模板数据需要支持地区区分
- 模板数据需要支持完整审计字段
- 模板数据需要支持软删除
- 前端与后端需统一按当前 LeAudit/FastAPI 风格实现
本方案仅覆盖“合同模板管理与上传”,不扩展“合同起草”独立业务模块。
## 2. 现状问题
### 2.1 数据表不足
当前 `contract_templates` 仅包含:
- `template_code`
- `title`
- `category_id`
- `description`
- `file_path`
- `file_format`
- `pdf_file_path`
- `is_featured`
- `created_at`
- `updated_at`
存在以下问题:
- 没有 `region`,无法做地区隔离
- 没有 `created_by / updated_by`,无法追踪操作者
- 没有 `deleted_at`,无法软删除
- 没有 `original_file_name / mime_type / file_size`,无法完整表达上传文件元数据
- 当前唯一约束仅为 `template_code` 全局唯一,不适合多地区模板管理
### 2.2 读接口缺少统一数据范围控制
当前合同模板读接口没有套用平台现有“按用户地区可见”的规则,无法满足:
- 省级管理员查看全部或指定地区
- 地市管理员仅查看本地区及公共模板
- 非管理用户限制可见范围
### 2.3 OSS 路径未带地区
当前 `BuildContractTemplateKey()` 只按:
- 分类
- 模板编码
- 文件角色
生成路径,未带 `region`,多地区下会出现路径命名冲突与后期归档困难。
### 2.4 权限不足
当前仅具备读权限:
- `contract_template:list:read`
- `contract_template:search:read`
- `contract_template:detail:read`
尚未具备:
- 上传创建权限
- 编辑权限
- 删除权限
## 3. 设计目标
本次改造目标如下:
1. 为合同模板模块补齐上传能力
2. 按地区隔离模板数据
3. 支持审计字段与软删除
4. 保持与现有 `govdoc` / `document` 模块相同的权限和地区控制风格
5. 新上传模板统一走新 OSS 路径规范
6. 不影响现有搜索、列表、详情页面的继续使用
## 4. 数据模型设计
### 4.1 `contract_categories`
分类暂不做地区化,保留为全局字典表,但补齐审计与软删除字段。
建议字段:
- `id BIGSERIAL/SERIAL PRIMARY KEY`
- `name VARCHAR(100) NOT NULL`
- `icon VARCHAR(100) NULL`
- `description TEXT NULL`
- `sort_order INTEGER NOT NULL DEFAULT 0`
- `created_by BIGINT NULL`
- `created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()`
- `updated_by BIGINT NULL`
- `updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()`
- `deleted_at TIMESTAMPTZ NULL`
索引建议:
- `UNIQUE INDEX uq_contract_categories_name_active ON contract_categories(name) WHERE deleted_at IS NULL`
- `INDEX idx_contract_categories_sort_active ON contract_categories(sort_order) WHERE deleted_at IS NULL`
### 4.2 `contract_templates`
模板表补齐地区、上传元数据、审计字段、软删除字段。
建议字段:
- `id BIGSERIAL PRIMARY KEY`
- `template_code VARCHAR(50) NOT NULL`
- `title VARCHAR(200) NOT NULL`
- `category_id INTEGER NOT NULL REFERENCES contract_categories(id)`
- `region VARCHAR(50) NOT NULL DEFAULT '省级'`
- `description TEXT NULL`
- `file_path VARCHAR(500) NULL`
- `pdf_file_path VARCHAR(500) NULL`
- `file_format VARCHAR(10) NOT NULL`
- `original_file_name VARCHAR(500) NOT NULL DEFAULT ''`
- `mime_type VARCHAR(200) NULL`
- `file_size BIGINT NOT NULL DEFAULT 0`
- `pdf_file_size BIGINT NULL`
- `is_featured BOOLEAN NOT NULL DEFAULT FALSE`
- `created_by BIGINT NULL`
- `created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()`
- `updated_by BIGINT NULL`
- `updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()`
- `deleted_at TIMESTAMPTZ NULL`
索引建议:
- `UNIQUE INDEX uq_contract_templates_region_code_active ON contract_templates(region, template_code) WHERE deleted_at IS NULL`
- `INDEX idx_contract_templates_region_active ON contract_templates(region) WHERE deleted_at IS NULL`
- `INDEX idx_contract_templates_category_active ON contract_templates(category_id) WHERE deleted_at IS NULL`
- `INDEX idx_contract_templates_updated_active ON contract_templates(updated_at DESC) WHERE deleted_at IS NULL`
### 4.3 软删除策略
所有列表、搜索、详情查询统一增加:
- `deleted_at IS NULL`
删除接口只做:
- `deleted_at = NOW()`
- `updated_at = NOW()`
- `updated_by = 当前用户`
本期不执行物理删除 OSS 对象,避免误删无法恢复。
## 5. 地区隔离策略
### 5.1 用户上下文来源
复用现有用户上下文判断逻辑,基于:
- `sso_users.area`
- 用户角色 `super_admin / provincial_admin / admin / 普通用户`
参考现有实现:
- `documentServiceImpl._getCurrentUserContext`
- `documentServiceImpl._buildDocumentScopeFilters`
- `govdocServiceImpl._resolve_upload_region`
### 5.2 模板可见性规则
采用“省级公共模板 + 地区私有模板”的一层可见性模型:
- `region = '省级'`:全局公共模板
- `region = 用户地区`:当前地区私有模板
读取规则:
- `super_admin / provincial_admin`
- 默认可查看全部地区
- 支持显式传 `region` 做筛选
- `admin`
- 默认可查看 `('省级', 自己地区)` 模板
- 若传入其他地区参数,返回空或直接拒绝
- 普通用户
- 默认可查看 `('省级', 自己地区)` 模板
- 不允许跨地区筛选
### 5.3 上传地区规则
上传接口中,`region` 使用以下规则解析:
- `super_admin / provincial_admin`
- 可选择上传到任意地区,含 `省级`
- `admin`
- 只能上传到自己地区
- 普通用户
- 默认不开放上传权限
### 5.4 为什么本期不做“地区模板覆盖省级模板”
本期不做同 `template_code` 的覆盖优先级逻辑,原因:
- 列表/搜索会出现同编码多条模板
- 详情页需要增加“实际命中版本”优先级
- 后续还会影响起草、下载、预览引用
本期先做“地区隔离 + 独立模板记录”,后续若业务明确需要,再做二期“本地覆盖省级模板”。
## 6. OSS 存储设计
### 6.1 新路径规范
建议合同模板 OSS key 改为:
`contract-templates/{region}/{category}/{template_code}/{role}__{filename}`
例如:
- `contract-templates/省级/房屋租赁/HT-RL-001/source__房屋租赁合同.docx`
- `contract-templates/梅州/房屋租赁/HT-RL-001/preview__房屋租赁合同.pdf`
### 6.2 文件角色
- 主模板文件:`source`
- 预览 PDF 文件:`preview`
### 6.3 老数据策略
老数据短期不强制迁移到新地区路径:
- 已有历史数据继续可读
- 新上传统一走新路径
后续若需要统一整洁,再单独执行历史迁移脚本,将存量模板迁到 `contract-templates/省级/...`
## 7. 接口设计
### 7.1 新增接口
#### 7.1.1 上传模板
- `POST /api/v3/contract-templates`
- 权限:`contract_template:create`
- Content-Type`multipart/form-data`
表单字段:
- `title: str`
- `template_code: str`
- `category_id: int`
- `region: str`
- `description: str | None`
- `is_featured: bool`
- `file: UploadFile`
- `pdf_file: UploadFile | None`
返回:
- 模板基础信息
- 文件路径
- 地区信息
- 审计时间
#### 7.1.2 更新模板
- `PUT /api/v3/contract-templates/{id}`
- 权限:`contract_template:update`
本期先可只支持元数据更新,文件替换可选一起补。
#### 7.1.3 删除模板
- `DELETE /api/v3/contract-templates/{id}`
- 权限:`contract_template:delete`
行为:
- 软删除,不物理删 OSS
### 7.2 既有接口增强
#### 7.2.1 分类接口
- 过滤 `deleted_at IS NULL`
- 返回 `template_count` 时只统计当前用户可见地区且未删除模板
#### 7.2.2 列表接口
新增可选参数:
- `region`
逻辑:
- 仅返回当前用户可见模板
- 默认按 `updated_at DESC`
#### 7.2.3 搜索接口
新增可选参数:
- `region`
逻辑:
- 仅搜索当前用户可见模板
#### 7.2.4 详情接口
逻辑:
- 校验模板存在
- 校验模板未删除
- 校验当前用户对模板所在地区可见
## 8. 权限设计
新增权限:
- `contract_template:create`
- `contract_template:update`
- `contract_template:delete`
保留权限:
- `contract_template:list:read`
- `contract_template:search:read`
- `contract_template:detail:read`
角色建议:
- `super_admin`
- 全部读写删
- `provincial_admin`
- 全部读写删
- `admin`
-
- 上传
- 编辑本地区模板
- 删除本地区模板
- 普通用户
- 仅按需开放读权限
## 9. 前端设计
### 9.1 列表页入口
`/contract-template/list` 页面右上角增加:
- “上传模板”按钮
仅有 `contract_template:create` 权限时展示。
### 9.2 上传弹窗字段
- 模板标题
- 模板编码
- 模板分类
- 所属地区
- 模板简介
- 是否推荐
- 模板主文件
- 预览 PDF 文件(可选)
### 9.3 前端交互规则
- `admin` 用户地区默认锁定为自己地区
- `provincial_admin` 可选择地区
- 上传成功后:
- 提示成功
- 关闭弹窗
- 刷新当前列表
- 上传失败时:
- 表单级错误走 toast
- 403 需给出明确无权限提示
### 9.4 API 封装
`lib/api/contract-template/index.ts` 增加:
- `createContractTemplate`
- `updateContractTemplate`
- `deleteContractTemplate`
上传使用 `FormData` 直接请求后端,不走多余代理语义。
## 10. 数据迁移方案
### 10.1 老表扩展
通过增量 SQL
- 新增缺失列
- 回填默认值
- 修正索引
- 增加 `updated_at` 触发器
### 10.2 老数据回填
建议回填:
- `region = '省级'`
- `original_file_name = ''` 或按旧路径推导
- `file_size = 0`
- `deleted_at = NULL`
### 10.3 回滚策略
若上传接口上线后发现问题:
- 可先关闭前端上传入口
- 已有新字段与索引保持兼容,不影响只读能力
## 11. 开发步骤
### 阶段 1:基础设施
1. 扩展合同模板 SQL
2. 扩展 RBAC seed
3. 更新 OSS 路径工具
### 阶段 2:后端接口
1. 增加 DTO/VO
2. 扩展 Service 接口
3. 实现上传、更新、删除
4. 为列表、搜索、详情补地区过滤
### 阶段 3:前端页面
1. 扩展 API 客户端
2. 在列表页增加上传按钮和弹窗
3. 接地区选择与权限控制
### 阶段 4:验证
1. 省级管理员上传省级模板
2. 地市管理员上传本地区模板
3. 非本地区参数校验
4. 403 提示
5. 列表、搜索、详情联调
## 12. 本期明确不做
- 合同起草模块重构
- 模板占位符自动解析
- docx 自动转 pdf
- 地区模板覆盖省级模板优先级
- 模板版本管理
- OSS 历史模板统一搬迁
## 13. 推荐本期交付范围
建议本期落地以下完整闭环:
1. 合同模板表完成地区化、审计化、软删除改造
2. 合同模板后端新增上传接口
3. 合同模板列表页新增上传弹窗
4. 合同模板读接口支持地区可见性控制
5. 权限种子补齐
这套范围足以让合同模板模块从“只读展示”升级为“可管理上传的地区化模板库”。