feat(critical-value): 危急值处理记录闭环
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
package com.healthlink.his.web.criticalvalue.appservice;
|
||||
import com.healthlink.his.criticalvalue.domain.CriticalValue;
|
||||
import com.healthlink.his.criticalvalue.domain.CriticalValueHandleRecord;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
public interface ICriticalValueAppService {
|
||||
@@ -10,4 +11,7 @@ public interface ICriticalValueAppService {
|
||||
List<CriticalValue> getPendingList();
|
||||
List<CriticalValue> getOverdueList();
|
||||
Map<String, Object> getStatistics(String startDate, String endDate);
|
||||
List<CriticalValue> getPendingHandleList();
|
||||
void handleCriticalValue(CriticalValueHandleRecord record);
|
||||
List<CriticalValueHandleRecord> getHandleHistory(Long criticalValueId);
|
||||
}
|
||||
|
||||
@@ -1,14 +1,18 @@
|
||||
package com.healthlink.his.web.criticalvalue.appservice.impl;
|
||||
import com.healthlink.his.criticalvalue.domain.CriticalValue;
|
||||
import com.healthlink.his.criticalvalue.domain.CriticalValueHandleRecord;
|
||||
import com.healthlink.his.criticalvalue.service.ICriticalValueHandleRecordService;
|
||||
import com.healthlink.his.criticalvalue.service.ICriticalValueService;
|
||||
import com.healthlink.his.web.criticalvalue.appservice.ICriticalValueAppService;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import java.util.*;
|
||||
@Service
|
||||
public class CriticalValueAppServiceImpl implements ICriticalValueAppService {
|
||||
@Autowired private ICriticalValueService criticalValueService;
|
||||
@Autowired private ICriticalValueHandleRecordService handleRecordService;
|
||||
|
||||
@Override
|
||||
public CriticalValue reportCriticalValue(CriticalValue cv) {
|
||||
@@ -72,4 +76,38 @@ public class CriticalValueAppServiceImpl implements ICriticalValueAppService {
|
||||
result.put("confirmRate", total > 0 ? Math.round(closed * 100.0 / total) : 0);
|
||||
return result;
|
||||
}
|
||||
@Override
|
||||
public List<CriticalValue> getPendingHandleList() {
|
||||
return criticalValueService.list(new LambdaQueryWrapper<CriticalValue>()
|
||||
.in(CriticalValue::getStatus, "PENDING", "RECEIVED")
|
||||
.eq(CriticalValue::getDeleteFlag, "0")
|
||||
.orderByDesc(CriticalValue::getReportTime));
|
||||
}
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void handleCriticalValue(CriticalValueHandleRecord record) {
|
||||
record.setHandleTime(new Date());
|
||||
handleRecordService.save(record);
|
||||
if ("HANDLE".equals(record.getHandleType())) {
|
||||
CriticalValue cv = criticalValueService.getById(record.getCriticalValueId());
|
||||
cv.setHandlerId(record.getHandlerId());
|
||||
cv.setHandlerName(record.getHandlerName());
|
||||
cv.setHandleTime(new Date());
|
||||
cv.setHandleResult(record.getHandleResult());
|
||||
cv.setStatus("PROCESSING");
|
||||
criticalValueService.updateById(cv);
|
||||
} else if ("CONFIRM".equals(record.getHandleType())) {
|
||||
CriticalValue cv = criticalValueService.getById(record.getCriticalValueId());
|
||||
cv.setCloseTime(new Date());
|
||||
cv.setStatus("CLOSED");
|
||||
criticalValueService.updateById(cv);
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public List<CriticalValueHandleRecord> getHandleHistory(Long criticalValueId) {
|
||||
return handleRecordService.list(new LambdaQueryWrapper<CriticalValueHandleRecord>()
|
||||
.eq(CriticalValueHandleRecord::getCriticalValueId, criticalValueId)
|
||||
.eq(CriticalValueHandleRecord::getDeleteFlag, "0")
|
||||
.orderByDesc(CriticalValueHandleRecord::getHandleTime));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
package com.healthlink.his.web.criticalvalue.controller;
|
||||
import com.core.common.core.domain.AjaxResult;
|
||||
import com.healthlink.his.criticalvalue.domain.CriticalValue;
|
||||
import com.healthlink.his.criticalvalue.domain.CriticalValueHandleRecord;
|
||||
import com.healthlink.his.web.criticalvalue.appservice.ICriticalValueAppService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
@Tag(name = "危急值管理")
|
||||
@RestController
|
||||
@@ -42,4 +44,21 @@ public class CriticalValueController {
|
||||
@GetMapping("/statistics")
|
||||
public AjaxResult statistics(@RequestParam(required = false) String startDate, @RequestParam(required = false) String endDate) {
|
||||
return AjaxResult.success(criticalValueAppService.getStatistics(startDate, endDate)); }
|
||||
|
||||
@Operation(summary = "待处理危急值列表")
|
||||
@PreAuthorize("@ss.hasPermi('inpatient:criticalvalue:list')")
|
||||
@GetMapping("/pending-handle")
|
||||
public AjaxResult pendingHandle() { return AjaxResult.success(criticalValueAppService.getPendingHandleList()); }
|
||||
|
||||
@Operation(summary = "处理危急值")
|
||||
@PreAuthorize("@ss.hasPermi('inpatient:criticalvalue:edit')")
|
||||
@PostMapping("/handle")
|
||||
public AjaxResult handle(@RequestBody CriticalValueHandleRecord record) {
|
||||
criticalValueAppService.handleCriticalValue(record); return AjaxResult.success(); }
|
||||
|
||||
@Operation(summary = "处理历史")
|
||||
@PreAuthorize("@ss.hasPermi('inpatient:criticalvalue:list')")
|
||||
@GetMapping("/history")
|
||||
public AjaxResult history(@RequestParam Long criticalValueId) {
|
||||
return AjaxResult.success(criticalValueAppService.getHandleHistory(criticalValueId)); }
|
||||
}
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
CREATE TABLE critical_value_handle_record (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
critical_value_id BIGINT NOT NULL,
|
||||
encounter_id BIGINT NOT NULL,
|
||||
patient_id BIGINT NOT NULL,
|
||||
handle_type VARCHAR(20) NOT NULL,
|
||||
handler_id BIGINT NOT NULL,
|
||||
handler_name VARCHAR(50),
|
||||
handle_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
handle_result TEXT,
|
||||
review_result TEXT,
|
||||
doctor_id BIGINT,
|
||||
doctor_name VARCHAR(50),
|
||||
confirm_time TIMESTAMP,
|
||||
tenant_id BIGINT DEFAULT 0,
|
||||
delete_flag CHAR(1) DEFAULT '0',
|
||||
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
create_by VARCHAR(64)
|
||||
);
|
||||
COMMENT ON TABLE critical_value_handle_record IS '危急值处理记录';
|
||||
COMMENT ON COLUMN critical_value_handle_record.handle_type IS '处理类型: HANDLE-处理 REVIEW-复查 CONFIRM-确认';
|
||||
CREATE INDEX idx_cv_handle_cv_id ON critical_value_handle_record(critical_value_id);
|
||||
CREATE INDEX idx_cv_handle_patient ON critical_value_handle_record(patient_id);
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.healthlink.his.criticalvalue.domain;
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.core.common.core.domain.HisBaseEntity;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.experimental.Accessors;
|
||||
import java.util.Date;
|
||||
@Data
|
||||
@TableName("critical_value_handle_record")
|
||||
@Accessors(chain = true)
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
public class CriticalValueHandleRecord extends HisBaseEntity {
|
||||
@TableId(type = IdType.ASSIGN_ID)
|
||||
private Long id;
|
||||
private Long criticalValueId;
|
||||
private Long encounterId;
|
||||
private Long patientId;
|
||||
private String handleType;
|
||||
private Long handlerId;
|
||||
private String handlerName;
|
||||
private Date handleTime;
|
||||
private String handleResult;
|
||||
private String reviewResult;
|
||||
private Long doctorId;
|
||||
private String doctorName;
|
||||
private Date confirmTime;
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package com.healthlink.his.criticalvalue.mapper;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.healthlink.his.criticalvalue.domain.CriticalValueHandleRecord;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
@Mapper
|
||||
public interface CriticalValueHandleRecordMapper extends BaseMapper<CriticalValueHandleRecord> {}
|
||||
@@ -0,0 +1,4 @@
|
||||
package com.healthlink.his.criticalvalue.service;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.healthlink.his.criticalvalue.domain.CriticalValueHandleRecord;
|
||||
public interface ICriticalValueHandleRecordService extends IService<CriticalValueHandleRecord> {}
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.healthlink.his.criticalvalue.service.impl;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.healthlink.his.criticalvalue.domain.CriticalValueHandleRecord;
|
||||
import com.healthlink.his.criticalvalue.mapper.CriticalValueHandleRecordMapper;
|
||||
import com.healthlink.his.criticalvalue.service.ICriticalValueHandleRecordService;
|
||||
import org.springframework.stereotype.Service;
|
||||
@Service
|
||||
public class CriticalValueHandleRecordServiceImpl extends ServiceImpl<CriticalValueHandleRecordMapper, CriticalValueHandleRecord> implements ICriticalValueHandleRecordService {}
|
||||
@@ -4,3 +4,6 @@ export function confirmValue(id, params) { return request({ url: '/api/v1/critic
|
||||
export function closeValue(id) { return request({ url: '/api/v1/critical-value/close/' + id, method: 'put' }) }
|
||||
export function getStatistics() { return request({ url: '/api/v1/critical-value/statistics', method: 'get' }) }
|
||||
export function getOverdueList() { return request({ url: '/api/v1/critical-value/overdue', method: 'get' }) }
|
||||
export function getPendingHandleList() { return request({ url: '/api/v1/critical-value/pending-handle', method: 'get' }) }
|
||||
export function handleCriticalValue(data) { return request({ url: '/api/v1/critical-value/handle', method: 'post', data: data }) }
|
||||
export function getHandleHistory(criticalValueId) { return request({ url: '/api/v1/critical-value/history', method: 'get', params: { criticalValueId } }) }
|
||||
|
||||
@@ -0,0 +1,207 @@
|
||||
<template>
|
||||
<div class="critical-value-handle-container">
|
||||
<el-card v-loading="loading">
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span>危急值处理</span>
|
||||
<el-button type="primary" size="small" @click="handleQuery">刷新</el-button>
|
||||
</div>
|
||||
</template>
|
||||
<el-table :data="tableData" border style="width: 100%">
|
||||
<el-table-column type="index" label="序号" width="60" align="center" />
|
||||
<el-table-column prop="patientName" label="患者姓名" width="120" />
|
||||
<el-table-column prop="itemName" label="危急项目" min-width="160" show-overflow-tooltip />
|
||||
<el-table-column prop="resultValue" label="结果" width="100" align="center" />
|
||||
<el-table-column prop="referenceRange" label="参考范围" width="120" />
|
||||
<el-table-column prop="labDepartment" label="检验科室" width="120" />
|
||||
<el-table-column prop="reportTime" label="报告时间" width="170" />
|
||||
<el-table-column prop="status" label="状态" width="100" align="center">
|
||||
<template #default="scope">
|
||||
<el-tag v-if="scope.row.status === 'PENDING'" type="danger">待处理</el-tag>
|
||||
<el-tag v-else-if="scope.row.status === 'RECEIVED'" type="warning">已接收</el-tag>
|
||||
<el-tag v-else-if="scope.row.status === 'PROCESSING'" type="primary">处理中</el-tag>
|
||||
<el-tag v-else-if="scope.row.status === 'CLOSED'" type="success">已关闭</el-tag>
|
||||
<el-tag v-else type="info">{{ scope.row.status }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="200" align="center" fixed="right">
|
||||
<template #default="scope">
|
||||
<el-button v-if="scope.row.status !== 'CLOSED'" link type="primary" @click="handleProcess(scope.row)">处理</el-button>
|
||||
<el-button link type="info" @click="handleHistory(scope.row)">历史</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<el-empty v-if="!loading && tableData.length === 0" description="暂无待处理危急值" />
|
||||
</el-card>
|
||||
|
||||
<el-dialog v-model="processDialogVisible" title="危急值处理" width="600px" destroy-on-close :close-on-click-modal="false">
|
||||
<el-form ref="processFormRef" :model="processForm" :rules="processRules" label-width="100px">
|
||||
<el-form-item label="患者">
|
||||
<el-input :model-value="currentRow?.patientName" disabled />
|
||||
</el-form-item>
|
||||
<el-form-item label="危急项目">
|
||||
<el-input :model-value="currentRow?.itemName" disabled />
|
||||
</el-form-item>
|
||||
<el-form-item label="结果">
|
||||
<el-input :model-value="currentRow?.resultValue" disabled />
|
||||
</el-form-item>
|
||||
<el-form-item label="处理类型" prop="handleType">
|
||||
<el-select v-model="processForm.handleType" placeholder="请选择" style="width: 100%">
|
||||
<el-option label="处理" value="HANDLE" />
|
||||
<el-option label="复查" value="REVIEW" />
|
||||
<el-option label="确认关闭" value="CONFIRM" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="处理人" prop="handlerName">
|
||||
<el-input v-model="processForm.handlerName" placeholder="请输入处理人姓名" />
|
||||
</el-form-item>
|
||||
<el-form-item v-if="processForm.handleType === 'HANDLE'" label="处理结果" prop="handleResult">
|
||||
<el-input v-model="processForm.handleResult" type="textarea" :rows="3" placeholder="请输入处理结果" />
|
||||
</el-form-item>
|
||||
<el-form-item v-if="processForm.handleType === 'REVIEW'" label="复查结果" prop="reviewResult">
|
||||
<el-input v-model="processForm.reviewResult" type="textarea" :rows="3" placeholder="请输入复查结果" />
|
||||
</el-form-item>
|
||||
<el-form-item v-if="processForm.handleType === 'CONFIRM'" label="审核医生" prop="doctorName">
|
||||
<el-input v-model="processForm.doctorName" placeholder="请输入审核医生姓名" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="processDialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" :loading="submitLoading" @click="submitProcess">确认</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog v-model="historyDialogVisible" title="处理历史" width="700px" destroy-on-close>
|
||||
<el-table :data="historyData" border style="width: 100%" size="small">
|
||||
<el-table-column type="index" label="序号" width="60" align="center" />
|
||||
<el-table-column prop="handleType" label="类型" width="100" align="center">
|
||||
<template #default="scope">
|
||||
<el-tag v-if="scope.row.handleType === 'HANDLE'" type="primary">处理</el-tag>
|
||||
<el-tag v-else-if="scope.row.handleType === 'REVIEW'" type="warning">复查</el-tag>
|
||||
<el-tag v-else-if="scope.row.handleType === 'CONFIRM'" type="success">确认</el-tag>
|
||||
<el-tag v-else type="info">{{ scope.row.handleType }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="handlerName" label="处理人" width="100" />
|
||||
<el-table-column prop="handleResult" label="处理结果" min-width="150" show-overflow-tooltip />
|
||||
<el-table-column prop="reviewResult" label="复查结果" min-width="150" show-overflow-tooltip />
|
||||
<el-table-column prop="doctorName" label="审核医生" width="100" />
|
||||
<el-table-column prop="handleTime" label="处理时间" width="170" />
|
||||
</el-table>
|
||||
<el-empty v-if="historyData.length === 0" description="暂无处理记录" />
|
||||
<template #footer>
|
||||
<el-button @click="historyDialogVisible = false">关闭</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted } from 'vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { getPendingHandleList, handleCriticalValue, getHandleHistory } from '@/api/criticalvalue'
|
||||
|
||||
const loading = ref(false)
|
||||
const submitLoading = ref(false)
|
||||
const tableData = ref([])
|
||||
|
||||
const processDialogVisible = ref(false)
|
||||
const historyDialogVisible = ref(false)
|
||||
|
||||
const processFormRef = ref(null)
|
||||
const currentRow = ref(null)
|
||||
|
||||
const processForm = reactive({
|
||||
handleType: 'HANDLE',
|
||||
handlerName: '',
|
||||
handleResult: '',
|
||||
reviewResult: '',
|
||||
doctorName: ''
|
||||
})
|
||||
const processRules = {
|
||||
handleType: [{ required: true, message: '请选择处理类型', trigger: 'change' }],
|
||||
handlerName: [{ required: true, message: '请输入处理人', trigger: 'blur' }],
|
||||
handleResult: [{ required: true, message: '请输入处理结果', trigger: 'blur' }],
|
||||
reviewResult: [{ required: true, message: '请输入复查结果', trigger: 'blur' }],
|
||||
doctorName: [{ required: true, message: '请输入审核医生', trigger: 'blur' }]
|
||||
}
|
||||
|
||||
const historyData = ref([])
|
||||
|
||||
function handleQuery() {
|
||||
loading.value = true
|
||||
getPendingHandleList().then(res => {
|
||||
tableData.value = res.data || []
|
||||
}).catch(() => {
|
||||
ElMessage.error('查询失败')
|
||||
}).finally(() => {
|
||||
loading.value = false
|
||||
})
|
||||
}
|
||||
|
||||
function handleProcess(row) {
|
||||
currentRow.value = row
|
||||
processForm.handleType = 'HANDLE'
|
||||
processForm.handlerName = ''
|
||||
processForm.handleResult = ''
|
||||
processForm.reviewResult = ''
|
||||
processForm.doctorName = ''
|
||||
processDialogVisible.value = true
|
||||
}
|
||||
|
||||
function submitProcess() {
|
||||
processFormRef.value?.validate(valid => {
|
||||
if (!valid) return
|
||||
submitLoading.value = true
|
||||
const payload = {
|
||||
criticalValueId: currentRow.value.id,
|
||||
encounterId: currentRow.value.encounterId,
|
||||
patientId: currentRow.value.patientId,
|
||||
handleType: processForm.handleType,
|
||||
handlerId: null,
|
||||
handlerName: processForm.handlerName,
|
||||
handleResult: processForm.handleResult,
|
||||
reviewResult: processForm.reviewResult,
|
||||
doctorId: null,
|
||||
doctorName: processForm.doctorName
|
||||
}
|
||||
handleCriticalValue(payload).then(res => {
|
||||
if (res.code === 200) {
|
||||
ElMessage.success('处理成功')
|
||||
processDialogVisible.value = false
|
||||
handleQuery()
|
||||
} else {
|
||||
ElMessage.error(res.msg || '处理失败')
|
||||
}
|
||||
}).catch(() => {
|
||||
ElMessage.error('处理失败')
|
||||
}).finally(() => {
|
||||
submitLoading.value = false
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
function handleHistory(row) {
|
||||
getHandleHistory(row.id).then(res => {
|
||||
historyData.value = res.data || []
|
||||
historyDialogVisible.value = true
|
||||
}).catch(() => {
|
||||
ElMessage.error('查询历史失败')
|
||||
})
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
handleQuery()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.critical-value-handle-container {
|
||||
padding: 12px;
|
||||
}
|
||||
.card-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user