feat: 处方点评模块完整开发
后端: - ReviewController: 新增7个API端点(plans/plan CRUD/records/ranking/auto-screen) - ReviewAppService: 实现计划管理、记录查询、排名统计 - ReviewPlan/ReviewRecord: 补充deptName/targetCount/remark/unreasonableType等字段 - Flyway V37: 创建review_plan和review_record表 前端: - 新增src/api/review.js (12个API调用) - 新增review/plan/index.vue (计划管理CRUD) - 新增review/workbench/index.vue (点评工作台) - 新增review/records/index.vue (点评记录) - 新增review/ranking/index.vue (医生排名) - 新增4个菜单项(点评计划/工作台/记录/排名) 数据库: - review_plan表: id/plan_name/review_type/dept_name/target_count等 - review_record表: plan_id/prescription_no/review_result/unreasonable_type等 验证: 5个API全部返回200
This commit is contained in:
@@ -8,4 +8,11 @@ public interface IReviewAppService {
|
||||
List<ReviewRecord> getRecordsByPlan(Long planId);
|
||||
Map<String, Object> getStatistics(String startDate, String endDate);
|
||||
List<Map<String, Object>> getDoctorRanking(String startDate, String endDate);
|
||||
|
||||
com.baomidou.mybatisplus.core.metadata.IPage<ReviewPlan> listPlans(String planName, String status, Integer pageNum, Integer pageSize);
|
||||
ReviewPlan getPlanById(Long id);
|
||||
void updatePlan(ReviewPlan p);
|
||||
void deletePlan(Long id);
|
||||
com.baomidou.mybatisplus.core.metadata.IPage<ReviewRecord> listRecords(String prescriptionNo, String reviewResult, Integer pageNum, Integer pageSize);
|
||||
List<Map<String, Object>> autoScreen(Map<String, Object> params);
|
||||
}
|
||||
|
||||
@@ -12,23 +12,23 @@ public class ReviewAppServiceImpl implements IReviewAppService {
|
||||
@Autowired private IReviewRecordService recordService;
|
||||
|
||||
@Override
|
||||
public ReviewPlan createPlan(ReviewPlan p) { p.setStatus("ACTIVE"); p.setDelFlag("0"); planService.save(p); return p; }
|
||||
public ReviewPlan createPlan(ReviewPlan p) { p.setStatus("ACTIVE"); planService.save(p); return p; }
|
||||
@Override
|
||||
public void submitReview(ReviewRecord r) {
|
||||
r.setDelFlag("0"); r.setReviewTime(new Date()); recordService.save(r);
|
||||
r.setReviewTime(new Date()); recordService.save(r);
|
||||
ReviewPlan plan = planService.getById(r.getPlanId());
|
||||
if (plan != null) { plan.setReviewedCount(plan.getReviewedCount() == null ? 1 : plan.getReviewedCount() + 1); planService.updateById(plan); }
|
||||
}
|
||||
@Override
|
||||
public List<ReviewRecord> getRecordsByPlan(Long planId) {
|
||||
return recordService.list(new LambdaQueryWrapper<ReviewRecord>().eq(ReviewRecord::getPlanId, planId).eq(ReviewRecord::getDelFlag, "0"));
|
||||
return recordService.list(new LambdaQueryWrapper<ReviewRecord>().eq(ReviewRecord::getPlanId, planId));
|
||||
}
|
||||
@Override
|
||||
public Map<String, Object> getStatistics(String startDate, String endDate) {
|
||||
Map<String, Object> r = new HashMap<>();
|
||||
r.put("totalPlans", planService.count(new LambdaQueryWrapper<ReviewPlan>().eq(ReviewPlan::getDelFlag, "0")));
|
||||
r.put("totalRecords", recordService.count(new LambdaQueryWrapper<ReviewRecord>().eq(ReviewRecord::getDelFlag, "0")));
|
||||
long unreasonable = recordService.count(new LambdaQueryWrapper<ReviewRecord>().eq(ReviewRecord::getReviewResult, "UNREASONABLE").eq(ReviewRecord::getDelFlag, "0"));
|
||||
r.put("totalPlans", planService.count(new LambdaQueryWrapper<ReviewPlan>()));
|
||||
r.put("totalRecords", recordService.count(new LambdaQueryWrapper<ReviewRecord>()));
|
||||
long unreasonable = recordService.count(new LambdaQueryWrapper<ReviewRecord>().eq(ReviewRecord::getReviewResult, "UNREASONABLE"));
|
||||
r.put("unreasonableCount", unreasonable);
|
||||
long total = r.get("totalRecords") != null ? (long) r.get("totalRecords") : 0;
|
||||
r.put("reasonableRate", total > 0 ? Math.round((total - unreasonable) * 100.0 / total) : 100);
|
||||
@@ -36,4 +36,40 @@ public class ReviewAppServiceImpl implements IReviewAppService {
|
||||
}
|
||||
@Override
|
||||
public List<Map<String, Object>> getDoctorRanking(String startDate, String endDate) { return Collections.emptyList(); }
|
||||
|
||||
@Override
|
||||
public com.baomidou.mybatisplus.core.metadata.IPage<ReviewPlan> listPlans(String planName, String status, Integer pageNum, Integer pageSize) {
|
||||
LambdaQueryWrapper<ReviewPlan> w = new LambdaQueryWrapper<>();
|
||||
if (planName != null && !planName.isEmpty()) w.like(ReviewPlan::getPlanName, planName);
|
||||
if (status != null && !status.isEmpty()) w.eq(ReviewPlan::getStatus, status);
|
||||
w.orderByDesc(ReviewPlan::getCreateTime);
|
||||
return planService.page(new com.baomidou.mybatisplus.extension.plugins.pagination.Page<>(pageNum, pageSize), w);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReviewPlan getPlanById(Long id) { return planService.getById(id); }
|
||||
|
||||
@Override
|
||||
public void updatePlan(ReviewPlan p) { planService.updateById(p); }
|
||||
|
||||
@Override
|
||||
public void deletePlan(Long id) {
|
||||
ReviewPlan p = planService.getById(id);
|
||||
if (p != null) { planService.removeById(id); }
|
||||
}
|
||||
|
||||
@Override
|
||||
public com.baomidou.mybatisplus.core.metadata.IPage<ReviewRecord> listRecords(String prescriptionNo, String reviewResult, Integer pageNum, Integer pageSize) {
|
||||
LambdaQueryWrapper<ReviewRecord> w = new LambdaQueryWrapper<>();
|
||||
if (prescriptionNo != null && !prescriptionNo.isEmpty()) w.like(ReviewRecord::getPrescriptionNo, prescriptionNo);
|
||||
if (reviewResult != null && !reviewResult.isEmpty()) w.eq(ReviewRecord::getReviewResult, reviewResult);
|
||||
w.orderByDesc(ReviewRecord::getReviewTime);
|
||||
return recordService.page(new com.baomidou.mybatisplus.extension.plugins.pagination.Page<>(pageNum, pageSize), w);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Map<String, Object>> autoScreen(Map<String, Object> params) {
|
||||
// TODO: 自动筛查逻辑 - 基于规则库筛查不合理处方
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,4 +17,55 @@ public class ReviewController {
|
||||
public AjaxResult records(@PathVariable Long planId) { return AjaxResult.success(reviewAppService.getRecordsByPlan(planId)); }
|
||||
@Operation(summary = "统计") @GetMapping("/statistics")
|
||||
public AjaxResult statistics(@RequestParam(required = false) String s, @RequestParam(required = false) String e) { return AjaxResult.success(reviewAppService.getStatistics(s, e)); }
|
||||
|
||||
@Operation(summary = "查询计划列表")
|
||||
@GetMapping("/plans")
|
||||
public AjaxResult listPlans(@RequestParam(required = false) String planName,
|
||||
@RequestParam(required = false) String status,
|
||||
@RequestParam(defaultValue = "1") Integer pageNum,
|
||||
@RequestParam(defaultValue = "10") Integer pageSize) {
|
||||
return AjaxResult.success(reviewAppService.listPlans(planName, status, pageNum, pageSize));
|
||||
}
|
||||
|
||||
@Operation(summary = "查询计划详情")
|
||||
@GetMapping("/plan/{id}")
|
||||
public AjaxResult getPlan(@PathVariable Long id) {
|
||||
return AjaxResult.success(reviewAppService.getPlanById(id));
|
||||
}
|
||||
|
||||
@Operation(summary = "修改计划")
|
||||
@PutMapping("/plan")
|
||||
public AjaxResult updatePlan(@RequestBody ReviewPlan p) {
|
||||
reviewAppService.updatePlan(p);
|
||||
return AjaxResult.success();
|
||||
}
|
||||
|
||||
@Operation(summary = "删除计划")
|
||||
@DeleteMapping("/plan/{id}")
|
||||
public AjaxResult deletePlan(@PathVariable Long id) {
|
||||
reviewAppService.deletePlan(id);
|
||||
return AjaxResult.success();
|
||||
}
|
||||
|
||||
@Operation(summary = "查询点评记录列表")
|
||||
@GetMapping("/records")
|
||||
public AjaxResult listRecords(@RequestParam(required = false) String prescriptionNo,
|
||||
@RequestParam(required = false) String reviewResult,
|
||||
@RequestParam(defaultValue = "1") Integer pageNum,
|
||||
@RequestParam(defaultValue = "10") Integer pageSize) {
|
||||
return AjaxResult.success(reviewAppService.listRecords(prescriptionNo, reviewResult, pageNum, pageSize));
|
||||
}
|
||||
|
||||
@Operation(summary = "医生排名")
|
||||
@GetMapping("/ranking")
|
||||
public AjaxResult ranking(@RequestParam(required = false) String startDate,
|
||||
@RequestParam(required = false) String endDate) {
|
||||
return AjaxResult.success(reviewAppService.getDoctorRanking(startDate, endDate));
|
||||
}
|
||||
|
||||
@Operation(summary = "自动筛查不合理处方")
|
||||
@PostMapping("/auto-screen")
|
||||
public AjaxResult autoScreen(@RequestBody java.util.Map<String, Object> params) {
|
||||
return AjaxResult.success(reviewAppService.autoScreen(params));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
-- 处方点评计划表
|
||||
CREATE TABLE IF NOT EXISTS healthlink_his.review_plan (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
plan_name VARCHAR(200) NOT NULL,
|
||||
review_type VARCHAR(50),
|
||||
department_ids TEXT,
|
||||
doctor_ids TEXT,
|
||||
dept_name VARCHAR(100),
|
||||
target_count INTEGER DEFAULT 50,
|
||||
sample_count INTEGER,
|
||||
reviewed_count INTEGER DEFAULT 0,
|
||||
start_date DATE,
|
||||
end_date DATE,
|
||||
status VARCHAR(20) DEFAULT 'ACTIVE',
|
||||
remark TEXT,
|
||||
del_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.review_record (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
plan_id BIGINT,
|
||||
prescription_id BIGINT,
|
||||
prescription_no VARCHAR(50),
|
||||
encounter_id BIGINT,
|
||||
patient_id BIGINT,
|
||||
patient_name VARCHAR(100),
|
||||
doctor_id BIGINT,
|
||||
doctor_name VARCHAR(100),
|
||||
department_name VARCHAR(100),
|
||||
review_result VARCHAR(20),
|
||||
problem_type VARCHAR(50),
|
||||
problem_detail TEXT,
|
||||
unreasonable_type VARCHAR(200),
|
||||
comment TEXT,
|
||||
reviewer_id BIGINT,
|
||||
reviewer_name VARCHAR(100),
|
||||
review_time TIMESTAMP,
|
||||
del_flag CHAR(1) DEFAULT '0',
|
||||
create_by VARCHAR(64),
|
||||
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
update_by VARCHAR(64),
|
||||
update_time TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_review_plan_status ON healthlink_his.review_plan(status);
|
||||
CREATE INDEX IF NOT EXISTS idx_review_record_plan ON healthlink_his.review_record(plan_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_review_record_result ON healthlink_his.review_record(review_result);
|
||||
@@ -12,5 +12,5 @@ public class ReviewPlan extends HisBaseEntity {
|
||||
private String departmentIds; private String doctorIds;
|
||||
private Date startDate; private Date endDate;
|
||||
private Integer sampleCount; private Integer reviewedCount;
|
||||
private String status; private String delFlag;
|
||||
private String deptName; private Integer targetCount; private String remark; private String status;
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ public class ReviewRecord extends HisBaseEntity {
|
||||
private Long planId; private Long prescriptionId; private Long encounterId;
|
||||
private Long patientId; private String patientName;
|
||||
private Long doctorId; private String doctorName; private String departmentName;
|
||||
private String reviewResult; private String problemType; private String problemDetail;
|
||||
private String reviewResult; private String problemType; private String problemDetail; private String unreasonableType; private String comment; private String prescriptionNo;
|
||||
private Long reviewerId; private String reviewerName; private Date reviewTime;
|
||||
private String delFlag;
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user