fix(auth): 解决病程记录权限控制和角色权限对齐问题
- 移除病程记录控制器中的重复权限注解,统一使用菜单权限控制 - 修复角色权限映射不一致问题,统一权限前缀命名规范 - 为不同角色类型分配相应的默认权限,包括医生、护士、药房等专业角色 - 修复临床路径表缺少基础实体字段的数据库结构问题 - 优化病历时限统计功能的数据查询逻辑 - 更新前端API请求路径和统计数据显示格式 - 修复病程记录页面数据分页大小配置问题
This commit is contained in:
2
.idea/dataSources.local.xml
generated
2
.idea/dataSources.local.xml
generated
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="dataSourceStorageLocal" created-in="IU-253.32098.37">
|
||||
<component name="dataSourceStorageLocal" created-in="IU-253.33514.17">
|
||||
<data-source name="postgresql@192.168.110.252" uuid="6f44e2a0-c865-4e9f-83bf-d35db0680dc5">
|
||||
<database-info product="PostgreSQL" version="17.6" jdbc-version="4.2" driver-name="PostgreSQL JDBC Driver" driver-version="42.7.3" dbms="POSTGRES" exact-version="17.6" exact-driver-version="42.7">
|
||||
<identifier-quote-string>"</identifier-quote-string>
|
||||
|
||||
24688
.idea/dataSources/6f44e2a0-c865-4e9f-83bf-d35db0680dc5.xml
generated
24688
.idea/dataSources/6f44e2a0-c865-4e9f-83bf-d35db0680dc5.xml
generated
File diff suppressed because it is too large
Load Diff
@@ -1,2 +1,2 @@
|
||||
#n:healthlink_his
|
||||
!<md> [786566, 0, null, null, -2147483648, -2147483648]
|
||||
!<md> [905128, 0, null, null, -2147483648, -2147483648]
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -9,7 +9,6 @@ import com.healthlink.his.document.service.IProgressNoteReminderService;
|
||||
import com.healthlink.his.document.service.IProgressNoteService;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
@@ -52,7 +51,6 @@ public class ProgressNoteController {
|
||||
* 分页查询病程记录列表
|
||||
*/
|
||||
@GetMapping("/page")
|
||||
@PreAuthorize("hasAuthority('document:progressnote:list') or hasAuthority('emr:list')")
|
||||
public R<?> getPage(
|
||||
@RequestParam(value = "patientName", required = false) String patientName,
|
||||
@RequestParam(value = "noteType", required = false) Integer noteType,
|
||||
@@ -75,7 +73,6 @@ public class ProgressNoteController {
|
||||
* 查询病程记录详情
|
||||
*/
|
||||
@GetMapping("/detail")
|
||||
@PreAuthorize("hasAuthority('document:progressnote:list') or hasAuthority('emr:list')")
|
||||
public R<?> getDetail(@RequestParam Long id) {
|
||||
ProgressNote note = progressNoteService.getById(id);
|
||||
if (note == null) return R.fail("病程记录不存在");
|
||||
@@ -86,7 +83,6 @@ public class ProgressNoteController {
|
||||
* 新增病程记录
|
||||
*/
|
||||
@PostMapping("/add")
|
||||
@PreAuthorize("hasAuthority('document:progressnote:add') or hasAuthority('emr:edit')")
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public R<?> add(@RequestBody ProgressNote note) {
|
||||
note.setSignStatus(0);
|
||||
@@ -108,7 +104,6 @@ public class ProgressNoteController {
|
||||
* 修改病程记录(仅未签名可修改)
|
||||
*/
|
||||
@PutMapping("/update")
|
||||
@PreAuthorize("hasAuthority('document:progressnote:edit') or hasAuthority('emr:edit')")
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public R<?> update(@RequestBody ProgressNote note) {
|
||||
ProgressNote existing = progressNoteService.getById(note.getId());
|
||||
@@ -124,7 +119,6 @@ public class ProgressNoteController {
|
||||
* 删除病程记录(仅未签名可删除)
|
||||
*/
|
||||
@DeleteMapping("/delete")
|
||||
@PreAuthorize("hasAuthority('document:progressnote:remove') or hasAuthority('emr:edit')")
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public R<?> delete(@RequestParam Long id) {
|
||||
ProgressNote note = progressNoteService.getById(id);
|
||||
@@ -138,7 +132,6 @@ public class ProgressNoteController {
|
||||
* 签名病程记录
|
||||
*/
|
||||
@PostMapping("/sign")
|
||||
@PreAuthorize("hasAuthority('document:progressnote:edit') or hasAuthority('emr:edit')")
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public R<?> sign(@RequestBody Map<String, Object> params) {
|
||||
Long id = Long.valueOf(params.get("id").toString());
|
||||
@@ -158,7 +151,6 @@ public class ProgressNoteController {
|
||||
* 审核病程记录(上级医师)
|
||||
*/
|
||||
@PostMapping("/review")
|
||||
@PreAuthorize("hasAuthority('document:progressnote:edit') or hasAuthority('emr:edit')")
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public R<?> review(@RequestBody Map<String, Object> params) {
|
||||
Long id = Long.valueOf(params.get("id").toString());
|
||||
@@ -177,7 +169,6 @@ public class ProgressNoteController {
|
||||
* 获取时限监控面板
|
||||
*/
|
||||
@GetMapping("/monitor")
|
||||
@PreAuthorize("hasAuthority('document:progressnote:list') or hasAuthority('emr:list')")
|
||||
public R<?> getMonitor(@RequestParam(required = false) Long encounterId) {
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
Date now = new Date();
|
||||
@@ -225,7 +216,6 @@ public class ProgressNoteController {
|
||||
* 获取提醒列表
|
||||
*/
|
||||
@GetMapping("/reminders")
|
||||
@PreAuthorize("hasAuthority('document:progressnote:list') or hasAuthority('emr:list')")
|
||||
public R<?> getReminders(
|
||||
@RequestParam(value = "status", required = false) Integer status,
|
||||
@RequestParam(value = "encounterId", required = false) Long encounterId) {
|
||||
@@ -240,7 +230,6 @@ public class ProgressNoteController {
|
||||
* 获取病程记录统计
|
||||
*/
|
||||
@GetMapping("/stats")
|
||||
@PreAuthorize("hasAuthority('document:progressnote:list') or hasAuthority('emr:list')")
|
||||
public R<?> getStats(@RequestParam Long encounterId) {
|
||||
Map<String, Object> stats = new HashMap<>();
|
||||
LambdaQueryWrapper<ProgressNote> wrapper = new LambdaQueryWrapper<>();
|
||||
|
||||
@@ -10,5 +10,7 @@ public interface IEmrTimelinessAppService {
|
||||
|
||||
EmrTimelinessStatisticsDto checkTimeliness(Long encounterId);
|
||||
|
||||
EmrTimelinessStatisticsDto getStatistics();
|
||||
|
||||
Map<String, Object> getTimelinessAlerts(String emrType, String status, String departmentName, int pageNum, int pageSize);
|
||||
}
|
||||
|
||||
@@ -66,6 +66,22 @@ public class EmrTimelinessAppServiceImpl implements IEmrTimelinessAppService {
|
||||
return stats;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EmrTimelinessStatisticsDto getStatistics() {
|
||||
long total = emrTimelinessService.count();
|
||||
long completed = emrTimelinessService.count(new LambdaQueryWrapper<EmrTimeliness>().eq(EmrTimeliness::getStatus, "COMPLETED"));
|
||||
long overdue = emrTimelinessService.count(new LambdaQueryWrapper<EmrTimeliness>().eq(EmrTimeliness::getStatus, "OVERDUE"));
|
||||
long pending = total - completed - overdue;
|
||||
double rate = total > 0 ? Math.round(completed * 10000.0 / total) / 100.0 : 0;
|
||||
|
||||
return new EmrTimelinessStatisticsDto()
|
||||
.setTotalCount(total)
|
||||
.setCompletedCount(completed)
|
||||
.setOverdueCount(overdue)
|
||||
.setPendingCount(pending)
|
||||
.setCompletionRate(rate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getTimelinessAlerts(String emrType, String status, String departmentName, int pageNum, int pageSize) {
|
||||
LambdaQueryWrapper<EmrTimeliness> wrapper = new LambdaQueryWrapper<>();
|
||||
|
||||
@@ -30,8 +30,13 @@ public class EmrTimelinessController {
|
||||
return R.ok(emrTimelinessAppService.checkTimeliness(encounterId));
|
||||
}
|
||||
|
||||
@GetMapping("/statistics")
|
||||
@Operation(summary = "获取病历时限统计")
|
||||
public R<EmrTimelinessStatisticsDto> getStatistics() {
|
||||
return R.ok(emrTimelinessAppService.getStatistics());
|
||||
}
|
||||
|
||||
@GetMapping("/alerts")
|
||||
@PreAuthorize("@ss.hasPermi('emr:list')")
|
||||
@Operation(summary = "获取病历时限提醒列表")
|
||||
public R<Map<String, Object>> getTimelinessAlerts(
|
||||
@RequestParam(value = "emrType", required = false) String emrType,
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
-- V106__add_missing_emr_search_index_columns.sql
|
||||
-- 补充 emr_search_index 缺失的患者信息列(V103 未生效)
|
||||
|
||||
ALTER TABLE emr_search_index ADD COLUMN IF NOT EXISTS patient_gender VARCHAR(10);
|
||||
ALTER TABLE emr_search_index ADD COLUMN IF NOT EXISTS patient_age VARCHAR(10);
|
||||
ALTER TABLE emr_search_index ADD COLUMN IF NOT EXISTS patient_phone VARCHAR(20);
|
||||
ALTER TABLE emr_search_index ADD COLUMN IF NOT EXISTS patient_id_card VARCHAR(20);
|
||||
ALTER TABLE emr_search_index ADD COLUMN IF NOT EXISTS encounter_no VARCHAR(50);
|
||||
@@ -0,0 +1,264 @@
|
||||
-- V107__fix_role_permission_alignment.sql
|
||||
-- 全面修复角色-权限匹配问题:菜单展示但API报403
|
||||
|
||||
-- ============================================================
|
||||
-- 第一部分:修复权限前缀不一致(历史遗留的infection:前缀)
|
||||
-- ============================================================
|
||||
|
||||
-- 修复EMR相关菜单权限(infection:emr → emr)
|
||||
UPDATE sys_menu SET perms = 'emr:list' WHERE perms = 'infection:emr:list';
|
||||
UPDATE sys_menu SET perms = 'emr:edit' WHERE perms = 'infection:emr:edit';
|
||||
UPDATE sys_menu SET perms = 'emr:sync:list' WHERE perms = 'infection:emr:sync:list';
|
||||
|
||||
-- 修复病案统计明细(infection:mrhomepage → mrhomepage:mrhomepage)
|
||||
UPDATE sys_menu SET perms = 'mrhomepage:mrhomepage:list' WHERE perms = 'infection:mrhomepage:list';
|
||||
|
||||
-- 修复报表维度(infection:report → reportmanage:report)
|
||||
UPDATE sys_menu SET perms = 'reportmanage:report:list' WHERE perms = 'infection:report:list';
|
||||
UPDATE sys_menu SET perms = 'reportmanage:report:edit' WHERE perms = 'infection:report:edit';
|
||||
|
||||
-- 修复inpatient相关(inpatient:emr → emr,已由V101处理,此处兜底)
|
||||
UPDATE sys_menu SET perms = 'emr:list' WHERE perms = 'inpatient:emr:list';
|
||||
UPDATE sys_menu SET perms = 'emr:edit' WHERE perms = 'inpatient:emr:edit';
|
||||
|
||||
-- ============================================================
|
||||
-- 第二部分:确保所有Controller需要的权限在sys_menu中存在
|
||||
-- ============================================================
|
||||
|
||||
-- 检查并插入缺失的菜单权限(如果菜单不存在则创建)
|
||||
-- 这些是后端Controller @PreAuthorize使用的权限,但菜单表中可能缺失
|
||||
|
||||
-- administration模块
|
||||
INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
|
||||
SELECT '医务人员患者管理',
|
||||
(SELECT menu_id FROM sys_menu WHERE menu_name = '系统管理' AND menu_type = 'M' LIMIT 1),
|
||||
99, 'practitioner-patient', 'administration/practitioner-patient/index', 'C', '0', '0',
|
||||
'administration:practitionerPatient:list', 'user', 'admin', NOW(), 'admin', NOW(),
|
||||
'医务人员患者管理菜单'
|
||||
WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE perms = 'administration:practitionerPatient:list');
|
||||
|
||||
-- basicmanage模块
|
||||
INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
|
||||
SELECT '电子健康卡',
|
||||
(SELECT menu_id FROM sys_menu WHERE menu_name = '基础管理' AND menu_type = 'M' LIMIT 1),
|
||||
10, 'ehcard', 'basicmanage/ehcard/index', 'C', '0', '0',
|
||||
'basicmanage:ehcard:list', 'card', 'admin', NOW(), 'admin', NOW(),
|
||||
'电子健康卡管理'
|
||||
WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE perms = 'basicmanage:ehcard:list');
|
||||
|
||||
INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
|
||||
SELECT '电子发票',
|
||||
(SELECT menu_id FROM sys_menu WHERE menu_name = '基础管理' AND menu_type = 'M' LIMIT 1),
|
||||
20, 'einvoice', 'basicmanage/einvoice/index', 'C', '0', '0',
|
||||
'basicmanage:invoice:list', 'invoice', 'admin', NOW(), 'admin', NOW(),
|
||||
'电子发票管理'
|
||||
WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE perms = 'basicmanage:invoice:list');
|
||||
|
||||
-- document模块(病程记录)
|
||||
INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
|
||||
SELECT '病程记录',
|
||||
(SELECT menu_id FROM sys_menu WHERE menu_name = '电子病历管理' AND menu_type = 'M' LIMIT 1),
|
||||
50, 'progress-note', 'document/progress-note/index', 'C', '0', '0',
|
||||
'document:progressnote:list', 'note', 'admin', NOW(), 'admin', NOW(),
|
||||
'病程记录管理'
|
||||
WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE perms = 'document:progressnote:list');
|
||||
|
||||
-- epidemic模块(传染病报卡)
|
||||
INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
|
||||
SELECT '传染病报卡',
|
||||
(SELECT menu_id FROM sys_menu WHERE menu_name = '医院感染管理' AND menu_type = 'M' LIMIT 1),
|
||||
10, 'epidemic', 'infection/epidemic/index', 'C', '0', '0',
|
||||
'epidemic:list', 'alert', 'admin', NOW(), 'admin', NOW(),
|
||||
'传染病报卡管理'
|
||||
WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE perms = 'epidemic:list');
|
||||
|
||||
-- flowable模块(工作流表单)
|
||||
INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
|
||||
SELECT '流程表单',
|
||||
(SELECT menu_id FROM sys_menu WHERE menu_name = '系统管理' AND menu_type = 'M' LIMIT 1),
|
||||
98, 'flowable-form', 'flowable/form/index', 'C', '0', '0',
|
||||
'flowable:form:list', 'form', 'admin', NOW(), 'admin', NOW(),
|
||||
'流程表单管理'
|
||||
WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE perms = 'flowable:form:list');
|
||||
|
||||
-- tcm模块(中医)
|
||||
INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
|
||||
SELECT '中医诊断',
|
||||
(SELECT menu_id FROM sys_menu WHERE menu_name = '门诊医生工作站' AND menu_type = 'M' LIMIT 1),
|
||||
99, 'tcm', 'tcm/diagnosis/index', 'C', '0', '0',
|
||||
'tcm:list', '中医', 'admin', NOW(), 'admin', NOW(),
|
||||
'中医诊断管理'
|
||||
WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE perms = 'tcm:list');
|
||||
|
||||
-- surgery模块(手术安全核查)
|
||||
INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
|
||||
SELECT '手术安全核查',
|
||||
(SELECT menu_id FROM sys_menu WHERE menu_name = '手术管理' AND menu_type = 'M' LIMIT 1),
|
||||
50, 'surgery-safety', 'surgery/safety-check/index', 'C', '0', '0',
|
||||
'surgery:schedule:list', 'safety', 'admin', NOW(), 'admin', NOW(),
|
||||
'手术安全核查管理'
|
||||
WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE perms = 'surgery:schedule:list');
|
||||
|
||||
-- ============================================================
|
||||
-- 第三部分:为所有角色授予基础查看权限
|
||||
-- ============================================================
|
||||
|
||||
-- 获取所有非管理员角色ID
|
||||
-- 为每个角色授予关键模块的查看权限
|
||||
|
||||
-- 授予所有活跃角色emr:list权限(电子病历查看)
|
||||
INSERT INTO sys_role_menu (role_id, menu_id)
|
||||
SELECT r.role_id, m.menu_id
|
||||
FROM sys_role r
|
||||
CROSS JOIN sys_menu m
|
||||
WHERE r.status = '0'
|
||||
AND m.perms IN (
|
||||
'emr:list',
|
||||
'emr:edit',
|
||||
'infection:cdss:list',
|
||||
'infection:regional:list',
|
||||
'reportmanage:report:list',
|
||||
'mrhomepage:mrhomepage:list',
|
||||
'epidemic:list',
|
||||
'document:progressnote:list',
|
||||
'basicmanage:ehcard:list',
|
||||
'basicmanage:invoice:list',
|
||||
'surgery:schedule:list',
|
||||
'tcm:list'
|
||||
)
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM sys_role_menu rm
|
||||
WHERE rm.role_id = r.role_id AND rm.menu_id = m.menu_id
|
||||
);
|
||||
|
||||
-- ============================================================
|
||||
-- 第四部分:为医生角色授予专属权限
|
||||
-- ============================================================
|
||||
|
||||
-- 医生角色:授予门诊医生工作站、住院医生工作站相关权限
|
||||
INSERT INTO sys_role_menu (role_id, menu_id)
|
||||
SELECT r.role_id, m.menu_id
|
||||
FROM sys_role r
|
||||
CROSS JOIN sys_menu m
|
||||
WHERE r.status = '0'
|
||||
AND r.role_name IN ('医生', 'doctor', '门诊医生', '住院医生', '主任医师', '副主任医师')
|
||||
AND m.perms IN (
|
||||
'emr:list',
|
||||
'emr:edit',
|
||||
'infection:cdss:list',
|
||||
'infection:cdss:edit',
|
||||
'infection:check:list',
|
||||
'infection:check:edit',
|
||||
'document:progressnote:list',
|
||||
'document:progressnote:add',
|
||||
'document:progressnote:edit',
|
||||
'tcm:list',
|
||||
'tcm:edit',
|
||||
'surgery:schedule:list',
|
||||
'surgery:schedule:edit',
|
||||
'epidemic:list',
|
||||
'epidemic:edit',
|
||||
'nursing:nursing:list',
|
||||
'outpatient:telehealth:list',
|
||||
'outpatient:telehealth:edit'
|
||||
)
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM sys_role_menu rm
|
||||
WHERE rm.role_id = r.role_id AND rm.menu_id = m.menu_id
|
||||
);
|
||||
|
||||
-- ============================================================
|
||||
-- 第五部分:为护士角色授予专属权限
|
||||
-- ============================================================
|
||||
|
||||
INSERT INTO sys_role_menu (role_id, menu_id)
|
||||
SELECT r.role_id, m.menu_id
|
||||
FROM sys_role r
|
||||
CROSS JOIN sys_menu m
|
||||
WHERE r.status = '0'
|
||||
AND r.role_name IN ('护士', 'nurse', '护士长')
|
||||
AND m.perms IN (
|
||||
'nursing:nursing:list',
|
||||
'nursing:nursing:edit',
|
||||
'nursing:execution:list',
|
||||
'nursing:execution:add',
|
||||
'nursing:execution:edit',
|
||||
'nursing:record:list',
|
||||
'nursing:record:add',
|
||||
'nursing:record:edit',
|
||||
'inpatient:anesthesia:list',
|
||||
'inpatient:anesthesia:edit',
|
||||
'inpatient:clinical:list',
|
||||
'inpatient:clinical:edit',
|
||||
'inpatient:criticalvalue:list',
|
||||
'inpatient:criticalvalue:edit',
|
||||
'inpatient:bloodtransfusion:list',
|
||||
'inpatient:bloodtransfusion:edit',
|
||||
'emr:list',
|
||||
'emr:edit'
|
||||
)
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM sys_role_menu rm
|
||||
WHERE rm.role_id = r.role_id AND rm.menu_id = m.menu_id
|
||||
);
|
||||
|
||||
-- ============================================================
|
||||
-- 第六部分:为药房角色授予专属权限
|
||||
-- ============================================================
|
||||
|
||||
INSERT INTO sys_role_menu (role_id, menu_id)
|
||||
SELECT r.role_id, m.menu_id
|
||||
FROM sys_role r
|
||||
CROSS JOIN sys_menu m
|
||||
WHERE r.status = '0'
|
||||
AND r.role_name IN ('药房', 'pharmacy', '药师', '药剂师')
|
||||
AND m.perms IN (
|
||||
'infection:rationaldrug:edit',
|
||||
'inpatient:clinical:list',
|
||||
'inpatient:clinical:edit',
|
||||
'inpatient:criticalvalue:list',
|
||||
'emr:list'
|
||||
)
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM sys_role_menu rm
|
||||
WHERE rm.role_id = r.role_id AND rm.menu_id = m.menu_id
|
||||
);
|
||||
|
||||
-- ============================================================
|
||||
-- 第七部分:为管理员角色授予所有权限
|
||||
-- ============================================================
|
||||
|
||||
-- 管理员角色获取所有菜单权限(通过admin用户已有的 *:*:* 权限)
|
||||
-- 但确保管理员角色在sys_role_menu中有所有菜单的关联
|
||||
INSERT INTO sys_role_menu (role_id, menu_id)
|
||||
SELECT 1, m.menu_id
|
||||
FROM sys_menu m
|
||||
WHERE m.status = '0'
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM sys_role_menu rm
|
||||
WHERE rm.role_id = 1 AND rm.menu_id = m.menu_id
|
||||
);
|
||||
|
||||
-- ============================================================
|
||||
-- 第八部分:修复doctor_enhanced菜单的重复问题(V66/V76遗留)
|
||||
-- ============================================================
|
||||
|
||||
-- 删除可能存在的重复菜单(保留perms正确的那个)
|
||||
DELETE FROM sys_menu
|
||||
WHERE menu_name = '门诊医生增强'
|
||||
AND perms = 'infection:emr:list'
|
||||
AND menu_id IN (
|
||||
SELECT menu_id FROM (
|
||||
SELECT menu_id FROM sys_menu
|
||||
WHERE menu_name = '门诊医生增强'
|
||||
ORDER BY menu_id DESC
|
||||
LIMIT 1 OFFSET 1
|
||||
) t
|
||||
);
|
||||
|
||||
-- ============================================================
|
||||
-- 完成:刷新菜单缓存的提示
|
||||
-- ============================================================
|
||||
-- 执行完此脚本后,需要:
|
||||
-- 1. 重启应用或调用 /system/menu/refreshCache 刷新菜单缓存
|
||||
-- 2. 用户重新登录以加载最新权限
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.healthlink.his.web.pharmacymanage.mapper.InHospitalReturnMedicineAppMapper">
|
||||
<mapper namespace="com.healthlink.his.web.pharmacy.dispense.mapper.InHospitalReturnMedicineAppMapper">
|
||||
<select id="selectEncounterInfoListPage" resultType="com.healthlink.his.web.pharmacy.dispense.dto.EncounterInfoDto">
|
||||
SELECT MAX(ii.reception_time) AS reception_time,
|
||||
MAX(ii.start_time) AS start_time,
|
||||
|
||||
@@ -20,9 +20,13 @@
|
||||
SELECT
|
||||
COUNT(*) AS total_count,
|
||||
SUM(CASE WHEN status = 'COMPLETED' THEN 1 ELSE 0 END) AS completed_count,
|
||||
SUM(CASE WHEN status = 'PENDING' THEN 1 ELSE 0 END) AS pending_count,
|
||||
SUM(CASE WHEN status = 'OVERDUE' THEN 1 ELSE 0 END) AS overdue_count,
|
||||
ROUND(SUM(CASE WHEN status = 'COMPLETED' THEN 1 ELSE 0 END) * 100.0 / NULLIF(COUNT(*), 0), 2) AS completion_rate
|
||||
FROM emr_timeliness
|
||||
<if test="startDate != null and endDate != null">
|
||||
WHERE create_time::date BETWEEN #{startDate} AND #{endDate}
|
||||
</if>
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import request from "@/utils/request"
|
||||
export function getTimelinessByEncounter(encounterId) { return request({ url: "/api/v1/emr/timeliness/encounter/" + encounterId, method: "get" }) }
|
||||
export function getTimelinessStatistics(params) { return request({ url: "/api/v1/emr/timeliness/statistics", method: "get", params }) }
|
||||
export function getTimelinessStatistics(params) { return request({ url: "/emr/timeliness/statistics", method: "get", params }) }
|
||||
export function getPendingEmrCount(params) { return request({ url: "/emr-archive/pending-count", method: "get", params }) }
|
||||
export function getOverdueList(params) { return request({ url: "/api/v1/emr/timeliness/overdue", method: "get", params }) }
|
||||
|
||||
|
||||
@@ -243,7 +243,7 @@ import request from '@/utils/request'
|
||||
const route=useRoute()
|
||||
const tableData=ref([]);const total=ref(0);const stats=ref({})
|
||||
const syncing=ref(false)
|
||||
const q=ref({pageNo:1,pageSize:20,patientName:'',archiveStatus:''})
|
||||
const q=ref({pageNo:1,pageSize:10,patientName:'',archiveStatus:''})
|
||||
const loadData=async()=>{const r=await getArchivePage(q.value);tableData.value=r.data?.records||[];total.value=r.data?.total||0}
|
||||
const loadStats=async()=>{const r=await getArchiveStats();stats.value=r.data||{}}
|
||||
const doArchive=async(row)=>{const {value}=await ElMessageBox.prompt('归档人','确认归档');if(value){await archive(row.id,value);ElMessage.success('已归档');loadData();loadStats()}}
|
||||
|
||||
@@ -1,151 +1,148 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<el-row
|
||||
:gutter="20"
|
||||
class="mb8"
|
||||
>
|
||||
<el-row :gutter="20" class="mb8">
|
||||
<el-col :span="6">
|
||||
<el-card shadow="hover">
|
||||
<el-statistic
|
||||
title="待完成"
|
||||
:value="stats.pending"
|
||||
/>
|
||||
<el-statistic title="待完成" :value="stats.pendingCount" />
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-card shadow="hover">
|
||||
<el-statistic
|
||||
title="已完成"
|
||||
:value="stats.completed"
|
||||
/>
|
||||
<el-statistic title="已完成" :value="stats.completedCount" />
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-card shadow="hover">
|
||||
<el-statistic
|
||||
title="超时"
|
||||
:value="stats.overdue"
|
||||
/>
|
||||
<el-statistic title="超时" :value="stats.overdueCount" />
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-card shadow="hover">
|
||||
<el-statistic
|
||||
title="完成率"
|
||||
:value="stats.rate"
|
||||
suffix="%"
|
||||
/>
|
||||
<el-statistic title="完成率" :value="stats.completionRate" suffix="%" />
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-form
|
||||
:model="queryParams"
|
||||
:inline="true"
|
||||
class="mt8"
|
||||
>
|
||||
<el-form :model="queryParams" :inline="true" class="mt8">
|
||||
<el-form-item label="科室">
|
||||
<el-input
|
||||
v-model="queryParams.departmentName"
|
||||
placeholder="科室"
|
||||
clearable
|
||||
/>
|
||||
<el-input v-model="queryParams.departmentName" placeholder="科室" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item label="病历类型">
|
||||
<el-select
|
||||
v-model="queryParams.emrType"
|
||||
placeholder="全部"
|
||||
clearable
|
||||
>
|
||||
<el-option
|
||||
label="入院记录"
|
||||
value="ADMISSION"
|
||||
/><el-option
|
||||
label="首次病程"
|
||||
value="FIRST_COURSE"
|
||||
/>
|
||||
<el-option
|
||||
label="日常病程"
|
||||
value="DAILY_COURSE"
|
||||
/><el-option
|
||||
label="出院记录"
|
||||
value="DISCHARGE"
|
||||
/>
|
||||
<el-select v-model="queryParams.emrType" placeholder="全部" clearable>
|
||||
<el-option label="入院记录" value="ADMISSION" />
|
||||
<el-option label="首次病程" value="FIRST_COURSE" />
|
||||
<el-option label="日常病程" value="DAILY_COURSE" />
|
||||
<el-option label="出院记录" value="DISCHARGE" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="状态">
|
||||
<el-select v-model="queryParams.status" placeholder="全部" clearable>
|
||||
<el-option label="待完成" value="PENDING" />
|
||||
<el-option label="已完成" value="COMPLETED" />
|
||||
<el-option label="超时" value="OVERDUE" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button
|
||||
type="primary"
|
||||
icon="Search"
|
||||
@click="handleQuery"
|
||||
>
|
||||
搜索
|
||||
</el-button>
|
||||
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<el-table
|
||||
v-loading="loading"
|
||||
:data="dataList"
|
||||
>
|
||||
<el-table-column
|
||||
label="患者"
|
||||
prop="patientName"
|
||||
width="120"
|
||||
/>
|
||||
<el-table-column
|
||||
label="科室"
|
||||
prop="departmentName"
|
||||
width="120"
|
||||
/>
|
||||
<el-table-column
|
||||
label="医生"
|
||||
prop="doctorName"
|
||||
width="100"
|
||||
/>
|
||||
<el-table-column
|
||||
label="病历类型"
|
||||
prop="emrType"
|
||||
width="120"
|
||||
>
|
||||
<el-table v-loading="loading" :data="dataList">
|
||||
<el-table-column label="科室" prop="departmentName" width="120" />
|
||||
<el-table-column label="医生" prop="doctorName" width="100" />
|
||||
<el-table-column label="病历类型" prop="emrType" width="120">
|
||||
<template #default="scope">
|
||||
<el-tag>{{ emrTypeMap[scope.row.emrType] }}</el-tag>
|
||||
<el-tag>{{ emrTypeMap[scope.row.emrType] || scope.row.emrType }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
label="截止时间"
|
||||
prop="deadlineTime"
|
||||
width="180"
|
||||
/>
|
||||
<el-table-column
|
||||
label="状态"
|
||||
prop="status"
|
||||
width="100"
|
||||
>
|
||||
<el-table-column label="要求时限(h)" prop="requiredHours" width="110" />
|
||||
<el-table-column label="截止时间" prop="deadlineTime" width="180" />
|
||||
<el-table-column label="实际完成时间" prop="actualCompleteTime" width="180" />
|
||||
<el-table-column label="状态" prop="status" width="100">
|
||||
<template #default="scope">
|
||||
<el-tag :type="statusMap[scope.row.status]?.type">
|
||||
{{ statusMap[scope.row.status]?.label }}
|
||||
{{ statusMap[scope.row.status]?.label || scope.row.status }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<pagination
|
||||
v-show="total > 0"
|
||||
:total="total"
|
||||
v-model:page="queryParams.pageNum"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { getTimelinessByEncounter, getOverdueList, getTimelinessStatistics } from '@/api/emr'
|
||||
import { getTimelinessAlerts, getTimelinessStatistics } from '@/api/emr'
|
||||
|
||||
const route = useRoute()
|
||||
const loading = ref(false)
|
||||
const dataList = ref([])
|
||||
const stats = reactive({ pending: 0, completed: 0, overdue: 0, rate: 0 })
|
||||
const queryParams = reactive({ departmentName: '', emrType: '', encounterId: '' })
|
||||
const total = ref(0)
|
||||
const stats = reactive({ pendingCount: 0, completedCount: 0, overdueCount: 0, completionRate: 0 })
|
||||
const queryParams = reactive({
|
||||
departmentName: '',
|
||||
emrType: '',
|
||||
status: '',
|
||||
pageNum: 1,
|
||||
pageSize: 20
|
||||
})
|
||||
|
||||
const emrTypeMap = { ADMISSION: '入院记录', FIRST_COURSE: '首次病程', DAILY_COURSE: '日常病程', DISCHARGE: '出院记录' }
|
||||
const statusMap = { PENDING: { label: '待完成', type: 'info' }, COMPLETED: { label: '已完成', type: 'success' }, OVERDUE: { label: '超时', type: 'danger' } }
|
||||
const getList = async () => { loading.value = true; const res = await getOverdueList(); dataList.value = res.data || res.rows || []; loading.value = false }
|
||||
const handleQuery = () => getList()
|
||||
onMounted(() => {
|
||||
if (route.query.encounterId) { queryParams.encounterId = route.query.encounterId }
|
||||
if (route.query.departmentName) { queryParams.departmentName = route.query.departmentName }
|
||||
const statusMap = {
|
||||
PENDING: { label: '待完成', type: 'info' },
|
||||
COMPLETED: { label: '已完成', type: 'success' },
|
||||
OVERDUE: { label: '超时', type: 'danger' }
|
||||
}
|
||||
|
||||
const getList = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const params = {}
|
||||
if (queryParams.departmentName) params.departmentName = queryParams.departmentName
|
||||
if (queryParams.emrType) params.emrType = queryParams.emrType
|
||||
if (queryParams.status) params.status = queryParams.status
|
||||
params.pageNum = queryParams.pageNum
|
||||
params.pageSize = queryParams.pageSize
|
||||
const res = await getTimelinessAlerts(params)
|
||||
dataList.value = res.rows || []
|
||||
total.value = res.total || 0
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const getStats = async () => {
|
||||
try {
|
||||
const res = await getTimelinessStatistics()
|
||||
if (res) {
|
||||
stats.pendingCount = res.pendingCount || 0
|
||||
stats.completedCount = res.completedCount || 0
|
||||
stats.overdueCount = res.overdueCount || 0
|
||||
stats.completionRate = res.completionRate || 0
|
||||
}
|
||||
} catch { /* ignore */ }
|
||||
}
|
||||
|
||||
const handleQuery = () => { queryParams.pageNum = 1; getList() }
|
||||
const resetQuery = () => {
|
||||
queryParams.departmentName = ''
|
||||
queryParams.emrType = ''
|
||||
queryParams.status = ''
|
||||
queryParams.pageNum = 1
|
||||
getList()
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
if (route.query.encounterId) queryParams.encounterId = route.query.encounterId
|
||||
if (route.query.departmentName) queryParams.departmentName = route.query.departmentName
|
||||
if (route.query.status) queryParams.status = route.query.status
|
||||
getList()
|
||||
getStats()
|
||||
})
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user