581 lines
16 KiB
Markdown
581 lines
16 KiB
Markdown
# 许可管理单位权限优化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
|