fs-lawrisk/docs/PRD_UNIT_PERMISSION_OPTIMIZ...

581 lines
16 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.

# 许可管理单位权限优化PRD
## 1. 需求背景
### 1.1 当前问题
- 现有权限体系:市局账号 vs 区局子管理员通过grade区分
- 现状:市局账号查看全部许可,区局子管理员仅能查看本区许可
- 局限:
- 缺乏市级单位(局、委、办)和区级单位的层级管理
- 上传许可文件时无法指定绑定的具体单位
- 浏览方式低效(仅按行政区筛选)
### 1.2 优化目标
1. **单位层级管理**:引入市级单位和区级单位的概念
2. **权限细分**
- 市局账号:保留全部可见权限
- 市级单位:可见自身 + 下属区级单位
- 区局子管理员:可见下属所有单位
- 区级单位:仅可见自身
3. **文件绑定优化**:上传许可可绑定到具体单位
4. **浏览效率提升**:从单一"选择区域"改为多维度"过滤器筛选"
### 1.3 保留内容
- ✅ 一市五区的行政区划不变
- ✅ 现有账户保留(作为市局和区局子管理员)
- ✅ 现有API保持兼容
## 2. 功能需求
### 2.1 单位管理功能
#### 2.1.1 单位级别定义
| 单位类型 | 说明 | 示例 | 可视范围 |
|---------|------|------|----------|
| admin | 市局管理员 | FSSJSJ市级管理员 | 全部许可 |
| municipal | 市级单位 | 市监局、卫健局、交通局等 | 自身 + 下属区级单位 |
| district | 区局子管理员 | 禅城区、南海区等管理员 | 下属所有单位 |
| unit | 区级单位 | 禅城区市场监督管理局等 | 仅自身 |
#### 2.1.2 单位创建流程
1. **创建市级单位**
- 指定单位名称、编码
- 设置单位级别为 `municipal`
- 可绑定多个区级行政区
- 自动生成管理员账号
2. **创建区级单位**
- 指定单位名称、编码
- 设置单位级别为 `unit`
- 绑定到具体行政区(禅城区、南海区等)
- 关联到市级单位作为上级
### 2.2 权限控制功能
#### 2.2.1 可见性规则
```python
# 权限判断逻辑
def get_visible_permits(user, filters):
dept = user.service_department
unit_level = dept.unit_level
if user.role == 'admin' or user.grade >= 90:
# 市局:全部许可
return query_all_permits(filters)
elif unit_level == 'municipal':
# 市级单位:自身 + 下属区级单位
subordinate_ids = get_subordinate_departments(dept.id, level='unit')
return query_permits_by_departments([dept.id] + subordinate_ids, filters)
elif unit_level == 'district':
# 区局子管理员:下属所有单位
return query_permits_by_region(dept.region_id, filters)
elif unit_level == 'unit':
# 区级单位:仅自身
return query_permits_by_departments([dept.id], filters)
else:
# 未知级别:无权限
return []
```
#### 2.2.2 权限矩阵
| 用户类型 | 自身许可 | 下属单位许可 | 其他区许可 | 其他市级单位许可 |
|---------|---------|-------------|----------|----------------|
| 市局管理员 | ✅ | ✅ | ✅ | ✅ |
| 市级单位用户 | ✅ | ✅ | ❌ | ❌ |
| 区局子管理员 | ✅ | ✅ | ❌ | ❌ |
| 区级单位用户 | ✅ | ❌ | ❌ | ❌ |
### 2.3 文件上传绑定功能
#### 2.3.1 绑定规则
| 上传者类型 | 默认绑定 | 可修改性 | 可见用户 |
|----------|---------|---------|----------|
| 市局管理员 | 市局自身 | 可选择任意单位 | 所有用户 |
| 市级单位用户 | 市级单位自身 | 可选择下属区级单位 | 市局 + 相关单位 |
| 区局子管理员 | 所属区 | 可修改 | 市局 + 相关区局 |
| 区级单位用户 | 自身单位 | 不可修改 | 市局 + 上级区局 |
#### 2.3.2 绑定流程
```python
def handle_permit_upload(file_data, uploader_user, bound_department_id=None):
# 1. 确定绑定单位
if not bound_department_id:
bound_department_id = get_default_binding(uploader_user)
# 2. 保存文件
file_id = save_permit_file(file_data)
# 3. 记录绑定关系
bind_permit_to_department(
file_id=file_id,
bound_department_id=bound_department_id,
uploader_department_id=uploader_user.service_department_id,
uploader_user_id=uploader_user.id
)
# 4. 返回可见用户列表
return get_visible_users(bound_department_id, uploader_user)
```
### 2.4 筛选浏览功能
#### 2.4.1 筛选维度
1. **市级单位**:下拉选择(全部/具体单位)
2. **区级单位**:下拉选择(全部/具体单位)
3. **行政区**:下拉选择(全部/禅城区/南海区/.../市级)
4. **许可名称**:文本搜索
#### 2.4.2 交互方式
- 市级单位和区级单位联动选择
- 支持搜索过滤
- 重置按钮清空所有筛选
- 显示已筛选结果数量
## 3. 技术方案
### 3.1 数据库设计
#### 3.1.1 现有表调整
**service_departments 表新增字段**
```sql
ALTER TABLE service_departments
ADD COLUMN unit_level VARCHAR(20) DEFAULT 'unit' CHECK (unit_level IN ('admin', 'municipal', 'district', 'unit')),
ADD COLUMN allowed_regions TEXT; -- 市级单位可访问的行政区,多个用逗号分隔
```
**permit_sources 表新增字段**
```sql
ALTER TABLE permit_sources
ADD COLUMN uploader_department_id UUID REFERENCES service_departments(id),
ADD COLUMN bound_department_id UUID REFERENCES service_departments(id);
```
#### 3.1.2 新建索引
```sql
CREATE INDEX idx_service_dept_unit_level ON service_departments(unit_level);
CREATE INDEX idx_service_dept_parent_level ON service_departments(parent_id, unit_level);
CREATE INDEX idx_permit_sources_bound_dept ON permit_sources(bound_department_id);
CREATE INDEX idx_permit_sources_uploader ON permit_sources(uploader_department_id);
```
#### 3.1.3 数据迁移脚本
```sql
-- 迁移现有数据
UPDATE service_departments SET unit_level = 'admin' WHERE grade >= 90;
UPDATE service_departments SET unit_level = 'district' WHERE parent_id IS NULL AND grade < 90;
-- 为现有表添加约束
ALTER TABLE service_departments ALTER COLUMN unit_level SET NOT NULL;
```
### 3.2 后端API设计
#### 3.2.1 新增API
**获取单位列表(支持层级)**
```
GET /admin/departments/tree
Response: {
"success": true,
"data": {
"municipal_units": [
{
"id": "uuid-1",
"name": "市监局",
"code": "AMR_FS",
"units": [
{"id": "uuid-2", "name": "禅城区市监局", "region": "禅城区"},
{"id": "uuid-3", "name": "南海区市监局", "region": "南海区"}
]
}
]
}
}
```
**筛选查询许可**
```
POST /admin/permits/filter
Request: {
"municipal_dept_id": "uuid-1",
"district_dept_id": "uuid-2",
"region": "禅城区",
"search_text": "电影院",
"page": 1,
"page_size": 20
}
Response: {
"success": true,
"data": {
"permits": [...],
"pagination": {
"total": 100,
"page": 1,
"page_size": 20
}
}
}
```
**上传许可文件(新增绑定参数)**
```
POST /admin/permit-import/upload
Request: form-data
- file: [Excel文件]
- bound_department_id: "uuid-2" # 可选,不传则自动绑定
Response: {
"success": true,
"data": {
"session_id": "uuid",
"bound_to": {
"department_id": "uuid-2",
"department_name": "禅城区市监局",
"visible_to": ["admin_users", "related_users"]
}
}
}
```
#### 3.2.2 修改现有API
**获取许可列表(向后兼容)**
```
GET /getPermits?region=禅城区
```
- 保持现有参数不变
- 新增根据单位级别过滤的逻辑
### 3.3 前端设计
#### 3.3.1 筛选器UI
```html
<div class="filter-panel">
<div class="filter-row">
<div class="filter-item">
<label>市级单位:</label>
<select id="municipalDept">
<option value="">全部</option>
<option value="dept_1">市监局</option>
<option value="dept_2">卫健局</option>
<option value="dept_3">交通局</option>
</select>
</div>
<div class="filter-item">
<label>区级单位:</label>
<select id="districtDept" disabled>
<option value="">请先选择市级单位</option>
</select>
</div>
<div class="filter-item">
<label>行政区:</label>
<select id="region">
<option value="">全部</option>
<option value="禅城区">禅城区</option>
<option value="南海区">南海区</option>
<option value="顺德区">顺德区</option>
<option value="三水区">三水区</option>
<option value="高明区">高明区</option>
<option value="市级">市级</option>
</select>
</div>
</div>
<div class="filter-row">
<div class="filter-item search-item">
<label>搜索许可:</label>
<input type="text" id="searchText" placeholder="输入许可名称...">
<button class="btn btn-primary" onclick="applyFilters()">筛选</button>
<button class="btn btn-secondary" onclick="resetFilters()">重置</button>
</div>
</div>
<div class="filter-result-info">
<span id="resultCount">共找到 0 条许可</span>
</div>
</div>
```
#### 3.3.2 联动逻辑
```javascript
// 市级单位选择变化时,动态加载区级单位
document.getElementById('municipalDept').addEventListener('change', function() {
const municipalId = this.value;
const districtSelect = document.getElementById('districtDept');
if (!municipalId) {
districtSelect.innerHTML = '<option value="">请先选择市级单位</option>';
districtSelect.disabled = true;
return;
}
// 加载该市级单位下的区级单位
fetch(`/api/admin/departments/children?parent_id=${municipalId}`)
.then(res => res.json())
.then(data => {
districtSelect.innerHTML = '<option value="">全部</option>';
data.units.forEach(unit => {
districtSelect.innerHTML += `<option value="${unit.id}">${unit.name}</option>`;
});
districtSelect.disabled = false;
});
});
// 应用筛选
function applyFilters() {
const filters = {
municipal_dept_id: document.getElementById('municipalDept').value,
district_dept_id: document.getElementById('districtDept').value,
region: document.getElementById('region').value,
search_text: document.getElementById('searchText').value
};
// 调用API获取数据
fetch('/api/admin/permits/filter', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify(filters)
}).then(res => res.json())
.then(data => {
renderPermits(data.permits);
updateResultCount(data.pagination.total);
});
}
```
#### 3.3.3 许可列表展示
```html
<div class="permit-list">
<div class="permit-item" v-for="permit in permits">
<div class="permit-header">
<h3>{{ permit.name }}</h3>
<span class="permit-dept">{{ permit.department_name }}</span>
<span class="permit-region">{{ permit.region_name }}</span>
</div>
<div class="permit-meta">
<span class="permit-theme">{{ permit.theme_name }}</span>
<span class="permit-risk-count">风险 {{ permit.risk_count }} 条</span>
</div>
<div class="permit-actions">
<button @click="viewPermit(permit.id)">查看详情</button>
<button @click="downloadFile(permit.file_id)" v-if="permit.file_id">下载文件</button>
</div>
</div>
</div>
```
## 4. 实施计划
### 4.1 阶段一后端开发预计3-5天
#### 4.1.1 数据库迁移
- [ ] 编写并执行ALTER TABLE语句
- [ ] 创建索引
- [ ] 执行数据迁移脚本
- [ ] 验证迁移结果
**负责人**:后端开发
**交付物**
- 迁移脚本文件
- 数据迁移报告
#### 4.1.2 权限逻辑调整
- [ ] 修改 `licensing_repo.py` 中的 `list_permits_for_region` 函数
- [ ] 新增 `get_subordinate_departments` 函数
- [ ] 新增 `get_visible_permits` 统一接口
- [ ] 编写单元测试
**负责人**:后端开发
**交付物**
- 权限控制代码
- 单元测试用例
- API文档
#### 4.1.3 新增API开发
- [ ] `/admin/departments/tree` - 获取单位树
- [ ] `/admin/departments/children` - 获取子单位
- [ ] `/admin/permits/filter` - 筛选查询
- [ ] 修改 `/admin/permit-import/upload` 支持绑定单位
**负责人**:后端开发
**交付物**
- API接口代码
- API文档
- Postman测试集合
#### 4.1.4 现有API兼容性测试
- [ ] 测试 `/getPermits` 接口
- [ ] 测试所有 `/admin/*` 接口
- [ ] 确保向后兼容
**负责人**:后端开发 + 测试
**交付物**
- 测试报告
- 性能测试结果
### 4.2 阶段二前端开发预计3-4天
#### 4.2.1 筛选器组件开发
- [ ] 设计筛选器UI样式
- [ ] 实现下拉联动逻辑
- [ ] 实现搜索和重置功能
- [ ] 响应式适配
**负责人**:前端开发
**交付物**
- HTML/CSS/JS代码
- 组件文档
#### 4.2.2 API集成
- [ ] 集成新的筛选API
- [ ] 实现数据加载和分页
- [ ] 优化加载性能
- [ ] 错误处理
**负责人**:前端开发
**交付物**
- 集成了新API的前端页面
- 前端测试报告
#### 4.2.3 文件上传绑定UI
- [ ] 添加绑定单位选择器
- [ ] 默认绑定逻辑提示
- [ ] 可见用户列表展示
**负责人**:前端开发
**交付物**
- 文件上传界面
- 用户体验测试报告
#### 4.2.4 整体测试
- [ ] 功能测试
- [ ] 兼容性测试
- [ ] 性能测试
- [ ] 修复BUG
**负责人**:前端开发 + 测试
**交付物**
- 测试报告
- 修复清单
### 4.3 阶段三联调测试预计2-3天
#### 4.3.1 单元测试
- [ ] 后端API测试
- [ ] 前端功能测试
- [ ] 数据库操作测试
#### 4.3.2 集成测试
- [ ] 端到端流程测试
- [ ] 权限验证测试
- [ ] 文件上传下载测试
#### 4.3.3 性能测试
- [ ] 并发查询测试
- [ ] 大量数据查询测试
- [ ] 优化性能瓶颈
#### 4.3.4 用户验收测试
- [ ] 业务场景测试
- [ ] 界面易用性测试
- [ ] 需求确认
**负责人**:全体开发 + 产品 + 测试
**交付物**
- 集成测试报告
- 性能测试报告
- UAT测试报告
### 4.4 阶段四上线部署预计1天
#### 4.4.1 生产环境部署
- [ ] 数据库迁移
- [ ] 后端服务部署
- [ ] 前端页面部署
- [ ] 配置检查
#### 4.4.2 上线验证
- [ ] 功能验证
- [ ] 性能验证
- [ ] 监控检查
**负责人**:运维 + 开发
**交付物**
- 上线检查清单
- 回滚预案
## 5. 风险评估
### 5.1 技术风险
| 风险项 | 影响程度 | 应对措施 |
|--------|---------|---------|
| 数据库迁移失败 | 高 | 提前备份,准备回滚脚本 |
| 权限逻辑复杂导致BUG | 中 | 详细单元测试,逐步发布 |
| 查询性能下降 | 中 | 添加索引必要时优化SQL |
| API变更影响现有功能 | 中 | 保持向后兼容新老API并行 |
### 5.2 业务风险
| 风险项 | 影响程度 | 应对措施 |
|--------|---------|---------|
| 权限设置错误导致数据泄露 | 高 | 严格测试,灰度发布 |
| 用户操作复杂,学习成本高 | 低 | 提供培训,更新文档 |
| 现有工作流程被破坏 | 中 | 保持核心功能不变,新增功能可选 |
### 5.3 应对预案
1. **数据安全**每次部署前创建checkpoint备份
2. **功能回滚**保留旧版本API至少3个月
3. **灰度发布**:新功能先开放给测试用户
4. **应急响应**建立问题反馈群24小时内响应
## 6. 成功标准
### 6.1 功能标准
- [ ] 单位层级管理功能完整可用
- [ ] 权限控制准确无误
- [ ] 文件上传绑定符合预期
- [ ] 筛选浏览高效便捷
### 6.2 性能标准
- [ ] API响应时间 < 1秒
- [ ] 页面加载时间 < 3秒
- [ ] 支持同时在线用户数 > 100
- [ ] 单位数量支持 > 100
### 6.3 用户体验标准
- [ ] 操作步骤减少 30%
- [ ] 筛选精度提升 50%
- [ ] 学习成本 < 30分钟
- [ ] 用户满意度 > 85%
## 7. 项目里程碑
| 里程碑 | 预计完成时间 | 交付物 |
|--------|-------------|--------|
| 需求评审通过 | T+0 | 本PRD文档 |
| 后端开发完成 | T+5 | 后端代码 + 测试 |
| 前端开发完成 | T+9 | 前端代码 + 测试 |
| 集成测试通过 | T+12 | 测试报告 |
| 生产环境上线 | T+13 | 上线系统 |
---
**编写人员**[产品经理] + [技术负责人]
**评审人员**[项目经理] + [开发团队] + [测试团队]
**最后更新**2025-11-19