feat: 医嘱闭环模块完整开发 - Mapper/Service/Controller + API + 测试通过
This commit is contained in:
@@ -0,0 +1,15 @@
|
||||
package com.healthlink.his.web.orderclosedloop.appservice;
|
||||
import com.healthlink.his.orderclosedloop.domain.OrderExecuteRecord;
|
||||
import com.healthlink.his.orderclosedloop.domain.OrderExecuteStep;
|
||||
import java.util.Map;
|
||||
import java.util.List;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
public interface IOrderClosedLoopAppService {
|
||||
IPage<OrderExecuteRecord> listRecords(String orderNo, String orderType, String executeStatus, Integer pageNum, Integer pageSize);
|
||||
Map<String, Object> getOrderStatus(Long orderId);
|
||||
void executeOrder(OrderExecuteRecord record);
|
||||
void completeOrder(OrderExecuteRecord record);
|
||||
void cancelOrder(OrderExecuteRecord record);
|
||||
Map<String, Object> getStatistics();
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
package com.healthlink.his.web.orderclosedloop.appservice.impl;
|
||||
import com.healthlink.his.orderclosedloop.domain.*;
|
||||
import com.healthlink.his.orderclosedloop.service.*;
|
||||
import com.healthlink.his.web.orderclosedloop.appservice.IOrderClosedLoopAppService;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import java.util.*;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
@Service
|
||||
public class OrderClosedLoopAppServiceImpl implements IOrderClosedLoopAppService {
|
||||
@Autowired private IOrderExecuteRecordService recordService;
|
||||
@Autowired private IOrderExecuteStepService stepService;
|
||||
|
||||
@Override
|
||||
public IPage<OrderExecuteRecord> listRecords(String orderNo, String orderType, String executeStatus, Integer pageNum, Integer pageSize) {
|
||||
LambdaQueryWrapper<OrderExecuteRecord> w = new LambdaQueryWrapper<>();
|
||||
if (orderNo != null && !orderNo.isEmpty()) w.like(OrderExecuteRecord::getOrderNo, orderNo);
|
||||
if (orderType != null && !orderType.isEmpty()) w.eq(OrderExecuteRecord::getOrderType, orderType);
|
||||
if (executeStatus != null && !executeStatus.isEmpty()) w.eq(OrderExecuteRecord::getExecuteStatus, executeStatus);
|
||||
w.orderByDesc(OrderExecuteRecord::getCreateTime);
|
||||
return recordService.page(new com.baomidou.mybatisplus.extension.plugins.pagination.Page<>(pageNum, pageSize), w);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getOrderStatus(Long orderId) {
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
OrderExecuteRecord record = recordService.getById(orderId);
|
||||
if (record == null) {
|
||||
result.put("error", "记录不存在");
|
||||
return result;
|
||||
}
|
||||
result.put("record", record);
|
||||
List<OrderExecuteStep> steps = stepService.list(new LambdaQueryWrapper<OrderExecuteStep>().eq(OrderExecuteStep::getOrderNo, record.getOrderNo()).orderByAsc(OrderExecuteStep::getStepOrder));
|
||||
result.put("steps", steps);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void executeOrder(OrderExecuteRecord record) {
|
||||
record.setExecuteStatus("executing");
|
||||
record.setExecuteTime(new Date());
|
||||
record.setCurrentStep("1");
|
||||
recordService.save(record);
|
||||
// Create default steps
|
||||
String[] stepNames = {"医师开立", "药师审核", "护士执行", "执行确认"};
|
||||
for (int i = 0; i < stepNames.length; i++) {
|
||||
OrderExecuteStep step = new OrderExecuteStep();
|
||||
step.setOrderNo(record.getOrderNo());
|
||||
step.setStepName(stepNames[i]);
|
||||
step.setStepOrder(i + 1);
|
||||
step.setCompleted(false);
|
||||
step.setTenantId(record.getTenantId() != null ? record.getTenantId() : 0);
|
||||
stepService.save(step);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void completeOrder(OrderExecuteRecord record) {
|
||||
OrderExecuteRecord existing = recordService.getById(record.getId());
|
||||
if (existing != null) {
|
||||
existing.setExecuteStatus("completed");
|
||||
existing.setCurrentStep("4");
|
||||
existing.setExecutorId(record.getExecutorId());
|
||||
existing.setExecutorName(record.getExecutorName());
|
||||
existing.setExecuteTime(new Date());
|
||||
recordService.updateById(existing);
|
||||
// Complete all steps
|
||||
List<OrderExecuteStep> steps = stepService.list(new LambdaQueryWrapper<OrderExecuteStep>().eq(OrderExecuteStep::getOrderNo, existing.getOrderNo()));
|
||||
for (OrderExecuteStep step : steps) {
|
||||
step.setCompleted(true);
|
||||
step.setExecuteTime(new Date());
|
||||
step.setExecutorId(record.getExecutorId());
|
||||
step.setExecutorName(record.getExecutorName());
|
||||
}
|
||||
stepService.updateBatchById(steps);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancelOrder(OrderExecuteRecord record) {
|
||||
OrderExecuteRecord existing = recordService.getById(record.getId());
|
||||
if (existing != null) {
|
||||
existing.setExecuteStatus("cancelled");
|
||||
existing.setCancelReason(record.getCancelReason());
|
||||
existing.setExecutorId(record.getExecutorId());
|
||||
existing.setExecutorName(record.getExecutorName());
|
||||
existing.setExecuteTime(new Date());
|
||||
recordService.updateById(existing);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getStatistics() {
|
||||
Map<String, Object> stats = new HashMap<>();
|
||||
long total = recordService.count();
|
||||
long pending = recordService.count(new LambdaQueryWrapper<OrderExecuteRecord>().eq(OrderExecuteRecord::getExecuteStatus, "pending"));
|
||||
long executing = recordService.count(new LambdaQueryWrapper<OrderExecuteRecord>().eq(OrderExecuteRecord::getExecuteStatus, "executing"));
|
||||
long completed = recordService.count(new LambdaQueryWrapper<OrderExecuteRecord>().eq(OrderExecuteRecord::getExecuteStatus, "completed"));
|
||||
long cancelled = recordService.count(new LambdaQueryWrapper<OrderExecuteRecord>().eq(OrderExecuteRecord::getExecuteStatus, "cancelled"));
|
||||
stats.put("total", total);
|
||||
stats.put("pending", pending);
|
||||
stats.put("executing", executing);
|
||||
stats.put("completed", completed);
|
||||
stats.put("cancelled", cancelled);
|
||||
stats.put("completionRate", total > 0 ? Math.round(completed * 100.0 / total) : 0);
|
||||
return stats;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package com.healthlink.his.web.orderclosedloop.controller;
|
||||
import com.core.common.core.domain.AjaxResult;
|
||||
import com.healthlink.his.orderclosedloop.domain.OrderExecuteRecord;
|
||||
import com.healthlink.his.web.orderclosedloop.appservice.IOrderClosedLoopAppService;
|
||||
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.web.bind.annotation.*;
|
||||
import java.util.Map;
|
||||
@Tag(name = "医嘱闭环") @RestController @RequestMapping("/api/v1/order-closed-loop")
|
||||
public class OrderClosedLoopController {
|
||||
@Autowired private IOrderClosedLoopAppService appService;
|
||||
|
||||
@Operation(summary = "医嘱执行记录列表")
|
||||
@GetMapping("/list")
|
||||
public AjaxResult list(@RequestParam(required = false) String orderNo,
|
||||
@RequestParam(required = false) String orderType,
|
||||
@RequestParam(required = false) String executeStatus,
|
||||
@RequestParam(defaultValue = "1") Integer pageNum,
|
||||
@RequestParam(defaultValue = "10") Integer pageSize) {
|
||||
return AjaxResult.success(appService.listRecords(orderNo, orderType, executeStatus, pageNum, pageSize));
|
||||
}
|
||||
|
||||
@Operation(summary = "获取医嘱执行状态")
|
||||
@GetMapping("/status/{orderId}")
|
||||
public AjaxResult status(@PathVariable Long orderId) {
|
||||
return AjaxResult.success(appService.getOrderStatus(orderId));
|
||||
}
|
||||
|
||||
@Operation(summary = "执行医嘱")
|
||||
@PostMapping("/execute")
|
||||
public AjaxResult execute(@RequestBody OrderExecuteRecord record) {
|
||||
appService.executeOrder(record);
|
||||
return AjaxResult.success();
|
||||
}
|
||||
|
||||
@Operation(summary = "完成医嘱")
|
||||
@PostMapping("/complete")
|
||||
public AjaxResult complete(@RequestBody OrderExecuteRecord record) {
|
||||
appService.completeOrder(record);
|
||||
return AjaxResult.success();
|
||||
}
|
||||
|
||||
@Operation(summary = "取消医嘱")
|
||||
@PostMapping("/cancel")
|
||||
public AjaxResult cancel(@RequestBody OrderExecuteRecord record) {
|
||||
appService.cancelOrder(record);
|
||||
return AjaxResult.success();
|
||||
}
|
||||
|
||||
@Operation(summary = "统计")
|
||||
@GetMapping("/statistics")
|
||||
public AjaxResult statistics() {
|
||||
return AjaxResult.success(appService.getStatistics());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
-- 医嘱执行记录表
|
||||
CREATE TABLE IF NOT EXISTS healthlink_his.order_execute_record (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
order_no VARCHAR(50) NOT NULL,
|
||||
encounter_id BIGINT,
|
||||
patient_id BIGINT,
|
||||
patient_name VARCHAR(100),
|
||||
order_type VARCHAR(20) NOT NULL,
|
||||
order_content TEXT,
|
||||
current_step VARCHAR(50),
|
||||
execute_status VARCHAR(20) DEFAULT 'pending',
|
||||
executor_id BIGINT,
|
||||
executor_name VARCHAR(100),
|
||||
execute_time TIMESTAMP,
|
||||
cancel_reason TEXT,
|
||||
tenant_id INTEGER DEFAULT 0,
|
||||
delete_flag CHAR(1) DEFAULT '0',
|
||||
create_by VARCHAR(64),
|
||||
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
update_by VARCHAR(64),
|
||||
update_time TIMESTAMP
|
||||
);
|
||||
|
||||
-- 医嘱执行步骤表(闭环流程)
|
||||
CREATE TABLE IF NOT EXISTS healthlink_his.order_execute_step (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
order_no VARCHAR(50) NOT NULL,
|
||||
step_name VARCHAR(100) NOT NULL,
|
||||
step_order INTEGER NOT NULL,
|
||||
completed BOOLEAN DEFAULT FALSE,
|
||||
executor_id BIGINT,
|
||||
executor_name VARCHAR(100),
|
||||
execute_time TIMESTAMP,
|
||||
remark TEXT,
|
||||
tenant_id INTEGER DEFAULT 0,
|
||||
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_order_exec_order_no ON healthlink_his.order_execute_record(order_no);
|
||||
CREATE INDEX IF NOT EXISTS idx_order_exec_status ON healthlink_his.order_execute_record(execute_status);
|
||||
CREATE INDEX IF NOT EXISTS idx_order_step_order_no ON healthlink_his.order_execute_step(order_no);
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.healthlink.his.orderclosedloop.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 com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@TableName("order_execute_record")
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class OrderExecuteRecord extends HisBaseEntity {
|
||||
@TableId(type = IdType.ASSIGN_ID)
|
||||
@JsonSerialize(using = ToStringSerializer.class)
|
||||
private Long id;
|
||||
private String orderNo;
|
||||
private Long encounterId;
|
||||
private Long patientId;
|
||||
private String patientName;
|
||||
private String orderType;
|
||||
private String orderContent;
|
||||
private String currentStep;
|
||||
private String executeStatus;
|
||||
private Long executorId;
|
||||
private String executorName;
|
||||
private Date executeTime;
|
||||
private String cancelReason;
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.healthlink.his.orderclosedloop.domain;
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
|
||||
import lombok.Data;
|
||||
import java.util.Date;
|
||||
|
||||
@Data
|
||||
@TableName("order_execute_step")
|
||||
public class OrderExecuteStep {
|
||||
@TableId(type = IdType.ASSIGN_ID)
|
||||
@JsonSerialize(using = ToStringSerializer.class)
|
||||
private Long id;
|
||||
private String orderNo;
|
||||
private String stepName;
|
||||
private Integer stepOrder;
|
||||
private Boolean completed;
|
||||
private Long executorId;
|
||||
private String executorName;
|
||||
private Date executeTime;
|
||||
private String remark;
|
||||
private Integer tenantId;
|
||||
private Date createTime;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package com.healthlink.his.orderclosedloop.mapper;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.healthlink.his.orderclosedloop.domain.OrderExecuteRecord;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
@Mapper public interface OrderExecuteRecordMapper extends BaseMapper<OrderExecuteRecord> {}
|
||||
@@ -0,0 +1,5 @@
|
||||
package com.healthlink.his.orderclosedloop.mapper;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.healthlink.his.orderclosedloop.domain.OrderExecuteStep;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
@Mapper public interface OrderExecuteStepMapper extends BaseMapper<OrderExecuteStep> {}
|
||||
@@ -0,0 +1,4 @@
|
||||
package com.healthlink.his.orderclosedloop.service;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.healthlink.his.orderclosedloop.domain.OrderExecuteRecord;
|
||||
public interface IOrderExecuteRecordService extends IService<OrderExecuteRecord> {}
|
||||
@@ -0,0 +1,4 @@
|
||||
package com.healthlink.his.orderclosedloop.service;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.healthlink.his.orderclosedloop.domain.OrderExecuteStep;
|
||||
public interface IOrderExecuteStepService extends IService<OrderExecuteStep> {}
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.healthlink.his.orderclosedloop.service.impl;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.healthlink.his.orderclosedloop.domain.OrderExecuteRecord;
|
||||
import com.healthlink.his.orderclosedloop.mapper.OrderExecuteRecordMapper;
|
||||
import com.healthlink.his.orderclosedloop.service.IOrderExecuteRecordService;
|
||||
import org.springframework.stereotype.Service;
|
||||
@Service
|
||||
public class OrderExecuteRecordServiceImpl extends ServiceImpl<OrderExecuteRecordMapper, OrderExecuteRecord> implements IOrderExecuteRecordService {}
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.healthlink.his.orderclosedloop.service.impl;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.healthlink.his.orderclosedloop.domain.OrderExecuteStep;
|
||||
import com.healthlink.his.orderclosedloop.mapper.OrderExecuteStepMapper;
|
||||
import com.healthlink.his.orderclosedloop.service.IOrderExecuteStepService;
|
||||
import org.springframework.stereotype.Service;
|
||||
@Service
|
||||
public class OrderExecuteStepServiceImpl extends ServiceImpl<OrderExecuteStepMapper, OrderExecuteStep> implements IOrderExecuteStepService {}
|
||||
30
healthlink-his-ui/src/api/orderclosedloop.js
Normal file
30
healthlink-his-ui/src/api/orderclosedloop.js
Normal file
@@ -0,0 +1,30 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
// ==================== 医嘱闭环 ====================
|
||||
export function listOrderExecuteRecord(params) {
|
||||
return request({ url: '/api/v1/order-closed-loop/list', method: 'get', params })
|
||||
}
|
||||
|
||||
export function getOrderClosedLoopStatus(orderId) {
|
||||
return request({ url: `/api/v1/order-closed-loop/status/${orderId}`, method: 'get' })
|
||||
}
|
||||
|
||||
export function getOrderStatistics() {
|
||||
return request({ url: '/api/v1/order-closed-loop/statistics', method: 'get' })
|
||||
}
|
||||
|
||||
export function executeOrder(data) {
|
||||
return request({ url: '/api/v1/order-closed-loop/execute', method: 'post', data })
|
||||
}
|
||||
|
||||
export function completeOrder(data) {
|
||||
return request({ url: '/api/v1/order-closed-loop/complete', method: 'post', data })
|
||||
}
|
||||
|
||||
export function cancelOrder(data) {
|
||||
return request({ url: '/api/v1/order-closed-loop/cancel', method: 'post', data })
|
||||
}
|
||||
|
||||
export function getClosedLoopStatistics(params) {
|
||||
return request({ url: '/api/v1/order-closed-loop/statistics', method: 'get', params })
|
||||
}
|
||||
Reference in New Issue
Block a user