6.9 KiB
6.9 KiB
预约处方查询权限控制
修改说明
为 getByAppointment 接口添加了权限检查,确保用户只能查询自己有权限查看的处方。
修改内容
1. Controller 层修改
文件: server/app/adminapi/controller/tcm/PrescriptionController.php
修改前:
public function getByAppointment()
{
$appointmentId = (int)($this->request->get('appointment_id') ?? 0);
if (!$appointmentId) {
return $this->fail('预约ID不能为空');
}
$prescription = PrescriptionLogic::getByAppointment($appointmentId);
return $this->data($prescription ?? []);
}
修改后:
public function getByAppointment()
{
$appointmentId = (int)($this->request->get('appointment_id') ?? 0);
if (!$appointmentId) {
return $this->fail('预约ID不能为空');
}
$prescription = PrescriptionLogic::getByAppointment($appointmentId, (int)$this->adminId, $this->adminInfo);
if ($prescription === null) {
$msg = PrescriptionLogic::getError();
return $this->fail($msg !== '' ? $msg : '未找到处方或无权限查看');
}
return $this->data($prescription);
}
改进点:
- 传入当前登录用户的
adminId和adminInfo - 处理权限检查失败的情况,返回明确的错误信息
- 区分"未找到处方"和"无权限查看"两种情况
2. Logic 层修改
文件: server/app/adminapi/logic/tcm/PrescriptionLogic.php
修改前:
/**
* 根据预约ID获取处方
*/
public static function getByAppointment(int $appointmentId): ?array
{
$row = Prescription::where('appointment_id', $appointmentId)
->whereNull('delete_time')
->order('id', 'desc')
->find();
return $row ? $row->toArray() : null;
}
修改后:
/**
* 根据预约ID获取处方(带权限检查)
*/
public static function getByAppointment(int $appointmentId, int $viewerAdminId, array $viewerAdminInfo): ?array
{
self::$error = '';
$row = Prescription::where('appointment_id', $appointmentId)
->whereNull('delete_time')
->order('id', 'desc')
->find();
if (!$row) {
return null;
}
// 权限检查:只返回当前用户有权限查看的处方
if (!self::canViewPrescription($row, $viewerAdminId, $viewerAdminInfo)) {
self::setError('无权限查看此处方');
return null;
}
return $row->toArray();
}
改进点:
- 添加
viewerAdminId和viewerAdminInfo参数 - 使用
canViewPrescription()方法进行权限检查 - 设置错误信息,便于前端显示
权限规则
根据 canViewPrescription() 方法,以下用户可以查看处方:
- 超级管理员 - 可以查看所有处方
- 处方创建人 - 可以查看自己创建的处方
- 医助 - 可以查看自己协助的处方
- 共享处方 - 所有人可以查看标记为共享的处方
- 可见角色 - 处方指定的可见角色可以查看
- 有开方权限的医生 - 拥有
tcm.diagnosis/kaifang权限的医生可以查看所有处方(只读)
使用场景
这个接口主要用于以下场景:
- 查看病历按钮 - 在预约列表中点击"查看病历"时调用
- 处方历史查询 - 根据预约ID查询患者的历史处方
- 医生协作 - 其他医生查看患者的处方记录(需要有开方权限)
前端调用示例
// admin/src/views/tcm/appointment/list.vue
const handleViewCase = async (row: any) => {
if (!row.id) {
feedback.msgWarning('预约信息不完整')
return
}
try {
const prescription = await prescriptionGetByAppointment({ appointment_id: row.id })
if (prescription?.id) {
prescriptionRef.value?.openById(prescription.id)
} else {
feedback.msgWarning('该预约暂无病历记录,请先开方')
}
} catch (error: any) {
// 权限不足或其他错误
feedback.msgError(error?.msg || '获取病历失败')
}
}
错误处理
可能的错误信息
- "预约ID不能为空" - 未传入
appointment_id参数 - "未找到处方或无权限查看" - 处方不存在或当前用户无权限查看
- "无权限查看此处方" - 处方存在但当前用户无权限查看
前端处理建议
try {
const prescription = await prescriptionGetByAppointment({ appointment_id: row.id })
// 成功获取处方
} catch (error: any) {
if (error?.msg?.includes('无权限')) {
feedback.msgWarning('您没有权限查看此处方')
} else if (error?.msg?.includes('未找到')) {
feedback.msgWarning('该预约暂无病历记录')
} else {
feedback.msgError(error?.msg || '获取病历失败')
}
}
测试建议
1. 权限测试
测试用例 1: 创建人查看自己的处方
- 医生A创建处方
- 医生A查看该处方
- 预期:成功返回处方数据
测试用例 2: 其他医生查看处方(有开方权限)
- 医生A创建处方
- 医生B(有
tcm.diagnosis/kaifang权限)查看该处方 - 预期:成功返回处方数据(只读)
测试用例 3: 其他医生查看处方(无开方权限)
- 医生A创建处方
- 医生C(无
tcm.diagnosis/kaifang权限)查看该处方 - 预期:返回"无权限查看此处方"错误
测试用例 4: 医助查看协助的处方
- 医生A创建处方,医助B协助
- 医助B查看该处方
- 预期:成功返回处方数据
测试用例 5: 查看共享处方
- 医生A创建处方并标记为共享
- 任何用户查看该处方
- 预期:成功返回处方数据
2. 边界测试
测试用例 6: 预约不存在
- 传入不存在的
appointment_id - 预期:返回"未找到处方或无权限查看"
测试用例 7: 预约存在但无处方
- 传入存在的
appointment_id,但该预约没有创建处方 - 预期:返回"未找到处方或无权限查看"
测试用例 8: 处方已删除
- 传入已删除处方的
appointment_id - 预期:返回"未找到处方或无权限查看"
安全性说明
- 防止越权访问 - 用户只能查看自己有权限的处方,防止查看其他医生的私有处方
- 数据隔离 - 通过权限检查实现数据隔离,保护患者隐私
- 审计追踪 - 所有查询操作都会记录当前用户信息,便于审计
- 错误信息脱敏 - 对于无权限的情况,不暴露处方是否存在的详细信息
相关文档
PRESCRIPTION_VIEW_PERMISSION_FIX.md- 处方查看权限修复文档BUG_FIXES_SUMMARY.md- Bug修复总结文档
总结
通过添加权限检查,确保了:
- ✅ 用户只能查询自己有权限查看的处方
- ✅ 防止越权访问其他医生的私有处方
- ✅ 支持医生协作(有开方权限的医生可以查看所有处方)
- ✅ 保护患者隐私和数据安全
- ✅ 提供明确的错误信息,便于前端处理