143 lines
4.5 KiB
Markdown
143 lines
4.5 KiB
Markdown
# 内部成本字段权限控制
|
||
|
||
## 需求说明
|
||
|
||
在业务订单列表中添加内部成本字段的显示,但只有指定角色的用户才能看到该字段。
|
||
|
||
## 权限配置
|
||
|
||
文件:`server/config/project.php`
|
||
|
||
```php
|
||
// 处方业务订单 internal_cost 等财务字段:以下角色 ID + root 可见
|
||
'prescription_order_finance_roles' => [0, 3, 6],
|
||
```
|
||
|
||
- 角色 0:超级管理员
|
||
- 角色 3:管理员
|
||
- 角色 6:药师
|
||
|
||
## 后端权限控制
|
||
|
||
文件:`server/app/adminapi/logic/tcm/PrescriptionOrderLogic.php`
|
||
|
||
后端已经实现了权限控制逻辑:
|
||
|
||
```php
|
||
public static function canViewInternalCost(array $adminInfo): bool
|
||
{
|
||
if (!empty($adminInfo['root']) && (int) $adminInfo['root'] === 1) {
|
||
return true;
|
||
}
|
||
$allow = Config::get('project.prescription_order_finance_roles', [0, 3]);
|
||
$allow = array_map('intval', is_array($allow) ? $allow : []);
|
||
$myRoles = array_map('intval', $adminInfo['role_id'] ?? []);
|
||
|
||
return count(array_intersect($myRoles, $allow)) > 0;
|
||
}
|
||
|
||
public static function maskInternalCostIfNeeded(array &$row, array $adminInfo): void
|
||
{
|
||
if (!self::canViewInternalCost($adminInfo)) {
|
||
unset($row['internal_cost']);
|
||
}
|
||
}
|
||
```
|
||
|
||
后端在返回数据前会自动移除 `internal_cost` 字段,如果用户没有权限。
|
||
|
||
## 前端权限控制
|
||
|
||
文件:`admin/src/views/consumer/prescription/order_list.vue`
|
||
|
||
### 1. 添加权限检查函数
|
||
|
||
```typescript
|
||
import useUserStore from '@/stores/modules/user'
|
||
|
||
const userStore = useUserStore()
|
||
|
||
/** 与 server/config/project.php prescription_order_finance_roles 保持一致 */
|
||
const FINANCE_ROLE_IDS = [0, 3, 6]
|
||
|
||
/** 判断当前用户是否有查看财务字段(内部成本)的权限 */
|
||
const canViewFinanceFields = () => {
|
||
const u = userStore.userInfo
|
||
if (!u) return false
|
||
if (Number(u.root) === 1) return true
|
||
const ids = Array.isArray(u.role_ids) ? u.role_ids.map((n: unknown) => Number(n)) : []
|
||
return FINANCE_ROLE_IDS.some((rid) => ids.includes(rid))
|
||
}
|
||
```
|
||
|
||
### 2. 在列表中添加权限控制
|
||
|
||
**表格列(只有有权限的用户才能看到整列):**
|
||
|
||
```vue
|
||
<el-table-column v-if="canViewFinanceFields()" label="内部成本" width="92">
|
||
<template #default="{ row }">
|
||
{{ row.internal_cost != null && row.internal_cost !== '' ? `¥${row.internal_cost}` : '—' }}
|
||
</template>
|
||
</el-table-column>
|
||
```
|
||
|
||
**金额信息弹出框(只有有权限的用户才能看到内部成本行):**
|
||
|
||
```vue
|
||
<div v-if="canViewFinanceFields() && row.internal_cost != null && row.internal_cost !== ''" class="flex justify-between">
|
||
<span class="text-gray-500">内部成本:</span>
|
||
<span class="font-medium text-orange-600">¥{{ formatMoney(row.internal_cost) }}</span>
|
||
</div>
|
||
```
|
||
|
||
## 权限控制层级
|
||
|
||
### 后端(第一层防护)
|
||
|
||
1. 在 `PrescriptionOrderLogic::detail()` 和列表方法中调用 `maskInternalCostIfNeeded()`
|
||
2. 如果用户没有权限,直接从返回数据中移除 `internal_cost` 字段
|
||
3. 前端收到的数据中不包含该字段
|
||
|
||
### 前端(第二层防护)
|
||
|
||
1. 使用 `v-if="canViewFinanceFields()"` 控制整个表格列的显示
|
||
2. 在弹出框中也使用 `canViewFinanceFields()` 控制内部成本行的显示
|
||
3. 即使后端数据泄露,前端也不会显示
|
||
|
||
## 双重防护的优势
|
||
|
||
1. **后端防护**:确保数据安全,无权限用户无法获取敏感数据
|
||
2. **前端防护**:优化用户体验,无权限用户不会看到空白或无意义的列
|
||
3. **配置同步**:前端 `FINANCE_ROLE_IDS` 与后端配置保持一致
|
||
|
||
## 测试验证
|
||
|
||
### 有权限的用户(角色 0, 3, 6)
|
||
|
||
1. 登录系统
|
||
2. 进入"消费者处方订单"列表
|
||
3. 可以看到"内部成本"列
|
||
4. 点击金额信息图标,弹出框中显示内部成本
|
||
|
||
### 无权限的用户(其他角色)
|
||
|
||
1. 登录系统
|
||
2. 进入"消费者处方订单"列表
|
||
3. 看不到"内部成本"列
|
||
4. 点击金额信息图标,弹出框中不显示内部成本
|
||
|
||
## 注意事项
|
||
|
||
1. **配置同步**:修改后端配置后,需要同步修改前端 `FINANCE_ROLE_IDS` 常量
|
||
2. **缓存清除**:修改配置后需要运行 `php think clear` 清除缓存
|
||
3. **角色分配**:确保用户的角色ID正确分配在数据库中
|
||
4. **超级管理员**:`root=1` 的用户始终有权限查看所有字段
|
||
|
||
## 相关文件
|
||
|
||
- `server/config/project.php` - 权限配置
|
||
- `server/app/adminapi/logic/tcm/PrescriptionOrderLogic.php` - 后端权限逻辑
|
||
- `admin/src/views/consumer/prescription/order_list.vue` - 前端显示控制
|
||
- `admin/src/stores/modules/user.ts` - 用户信息存储
|