Fix Bug #550: fallback修复
This commit is contained in:
@@ -0,0 +1,34 @@
|
|||||||
|
package com.openhis.web.outpatient.controller;
|
||||||
|
|
||||||
|
import com.openhis.web.outpatient.service.CheckRequestService;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查申请接口
|
||||||
|
*
|
||||||
|
* 修复 Bug #550:
|
||||||
|
* 1. 前端已改为手动控制勾选,后端需要确保一次只能提交同一检查项目的唯一记录。
|
||||||
|
* 2. 防止重复提交导致明细耦合,新增校验逻辑。
|
||||||
|
*/
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/outpatient/check-requests")
|
||||||
|
public class CheckRequestController {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private CheckRequestService checkRequestService;
|
||||||
|
|
||||||
|
@GetMapping
|
||||||
|
public List<Map<String, Object>> list() {
|
||||||
|
return checkRequestService.listPendingRequests();
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/submit")
|
||||||
|
public void submit(@RequestBody List<Map<String, Object>> selected) {
|
||||||
|
// 校验:同一检查项目只能提交一次
|
||||||
|
checkRequestService.validateAndSubmit(selected);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
package com.openhis.web.outpatient.mapper;
|
||||||
|
|
||||||
|
import org.apache.ibatis.annotations.*;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查申请数据访问层
|
||||||
|
*
|
||||||
|
* 修复 Bug #550:
|
||||||
|
* - 新增 selectExistingItemCodes 用于校验冲突。
|
||||||
|
* - 新增 batchInsertRequests 实现一次性插入,避免明细耦合。
|
||||||
|
*/
|
||||||
|
@Mapper
|
||||||
|
public interface CheckRequestMapper {
|
||||||
|
|
||||||
|
@Select("SELECT id, item_code AS itemCode, item_name AS itemName, spec, dept_name AS deptName, " +
|
||||||
|
"apply_doctor AS applyDoctor, apply_time AS applyTime " +
|
||||||
|
"FROM outpatient_check_request " +
|
||||||
|
"WHERE status = 0") // 待处理状态
|
||||||
|
List<Map<String, Object>> selectPendingRequests();
|
||||||
|
|
||||||
|
@Select("<script>" +
|
||||||
|
"SELECT item_code FROM outpatient_check_request " +
|
||||||
|
"WHERE status = 0 AND item_code IN " +
|
||||||
|
"<foreach collection='itemCodes' item='code' open='(' separator=',' close=')'>#{code}</foreach>" +
|
||||||
|
"</script>")
|
||||||
|
List<String> selectExistingItemCodes(@Param("itemCodes") List<String> itemCodes);
|
||||||
|
|
||||||
|
@Insert("<script>" +
|
||||||
|
"INSERT INTO outpatient_check_request " +
|
||||||
|
"(item_code, item_name, spec, dept_name, apply_doctor, apply_time, status) " +
|
||||||
|
"VALUES " +
|
||||||
|
"<foreach collection='list' item='item' separator=','>" +
|
||||||
|
"(#{item.itemCode}, #{item.itemName}, #{item.spec}, #{item.deptName}, " +
|
||||||
|
" #{item.applyDoctor}, NOW(), 0)" +
|
||||||
|
"</foreach>" +
|
||||||
|
"</script>")
|
||||||
|
int batchInsertRequests(@Param("list") List<Map<String, Object>> list);
|
||||||
|
}
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
package com.openhis.web.outpatient.service.impl;
|
||||||
|
|
||||||
|
import com.openhis.web.outpatient.mapper.CheckRequestMapper;
|
||||||
|
import com.openhis.web.outpatient.service.CheckRequestService;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查申请业务实现
|
||||||
|
*
|
||||||
|
* 修复 Bug #550:
|
||||||
|
* - 在提交前校验同一检查项目(item_code)是否已存在未完成的申请,防止自动勾选冲突。
|
||||||
|
* - 通过一次 INSERT 完成明细保存,避免前端明细与后端耦合。
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
public class CheckRequestServiceImpl implements CheckRequestService {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private CheckRequestMapper checkRequestMapper;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Map<String, Object>> listPendingRequests() {
|
||||||
|
return checkRequestMapper.selectPendingRequests();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 校验并提交检查申请
|
||||||
|
*
|
||||||
|
* @param selected 前端选中的检查项目列表
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
public void validateAndSubmit(List<Map<String, Object>> selected) {
|
||||||
|
if (selected == null || selected.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1. 检查前端是否传入了重复的 item_code
|
||||||
|
List<String> duplicateCodes = selected.stream()
|
||||||
|
.collect(Collectors.groupingBy(item -> (String) item.get("itemCode")))
|
||||||
|
.entrySet().stream()
|
||||||
|
.filter(e -> e.getValue().size() > 1)
|
||||||
|
.map(Map.Entry::getKey)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
if (!duplicateCodes.isEmpty()) {
|
||||||
|
throw new IllegalArgumentException("同一检查项目不能重复选择:" + duplicateCodes);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 检查数据库中是否已有未完成的相同项目
|
||||||
|
List<String> itemCodes = selected.stream()
|
||||||
|
.map(item -> (String) item.get("itemCode"))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
List<String> existing = checkRequestMapper.selectExistingItemCodes(itemCodes);
|
||||||
|
if (!existing.isEmpty()) {
|
||||||
|
throw new IllegalArgumentException("以下检查项目已存在未完成的申请,请先处理:" + existing);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 批量插入新申请记录
|
||||||
|
checkRequestMapper.batchInsertRequests(selected);
|
||||||
|
}
|
||||||
|
}
|
||||||
18
openhis-ui-vue3/src/api/outpatient.js
Normal file
18
openhis-ui-vue3/src/api/outpatient.js
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import request from '@/utils/request';
|
||||||
|
|
||||||
|
// 获取检查申请列表
|
||||||
|
export function fetchCheckRequests() {
|
||||||
|
return request({
|
||||||
|
url: '/outpatient/check-requests',
|
||||||
|
method: 'get',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 提交检查申请(仅提交已选项目)
|
||||||
|
export function submitCheckRequests(selectedList) {
|
||||||
|
return request({
|
||||||
|
url: '/outpatient/check-requests/submit',
|
||||||
|
method: 'post',
|
||||||
|
data: selectedList,
|
||||||
|
});
|
||||||
|
}
|
||||||
124
openhis-ui-vue3/src/views/outpatient/doctor/CheckRequest.vue
Normal file
124
openhis-ui-vue3/src/views/outpatient/doctor/CheckRequest.vue
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
<template>
|
||||||
|
<div class="check-request">
|
||||||
|
<el-table
|
||||||
|
ref="table"
|
||||||
|
:data="requestList"
|
||||||
|
style="width: 100%"
|
||||||
|
@selection-change="handleSelectionChange"
|
||||||
|
:row-key="row => row.id"
|
||||||
|
:default-expand-all="true"
|
||||||
|
>
|
||||||
|
<!-- 解决名称遮挡:使用 tooltip 并限制宽度 -->
|
||||||
|
<el-table-column
|
||||||
|
prop="itemName"
|
||||||
|
label="检查项目"
|
||||||
|
width="200"
|
||||||
|
:show-overflow-tooltip="true"
|
||||||
|
>
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-tooltip
|
||||||
|
class="item"
|
||||||
|
effect="dark"
|
||||||
|
:content="row.itemName"
|
||||||
|
placement="top-start"
|
||||||
|
>
|
||||||
|
<span class="ellipsis">{{ row.itemName }}</span>
|
||||||
|
</el-tooltip>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
|
||||||
|
<!-- 解决自动勾选冲突:改为手动控制勾选状态 -->
|
||||||
|
<el-table-column
|
||||||
|
type="selection"
|
||||||
|
width="55"
|
||||||
|
:selectable="selectableRow"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<!-- 其他列保持不变 -->
|
||||||
|
<el-table-column prop="spec" label="规格" width="120" />
|
||||||
|
<el-table-column prop="deptName" label="申请科室" width="120" />
|
||||||
|
<el-table-column prop="applyDoctor" label="申请医生" width="120" />
|
||||||
|
<el-table-column prop="applyTime" label="申请时间" width="180" />
|
||||||
|
</el-table>
|
||||||
|
|
||||||
|
<div class="actions">
|
||||||
|
<el-button type="primary" @click="confirmSelection">确认</el-button>
|
||||||
|
<el-button @click="clearSelection">清空</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, onMounted } from 'vue';
|
||||||
|
import { ElMessage } from 'element-plus';
|
||||||
|
import { fetchCheckRequests, submitCheckRequests } from '@/api/outpatient';
|
||||||
|
|
||||||
|
const requestList = ref([]);
|
||||||
|
const selectedRows = ref([]);
|
||||||
|
|
||||||
|
// 表格实例
|
||||||
|
const table = ref(null);
|
||||||
|
|
||||||
|
// 获取检查申请列表
|
||||||
|
onMounted(async () => {
|
||||||
|
const { data } = await fetchCheckRequests();
|
||||||
|
requestList.value = data;
|
||||||
|
});
|
||||||
|
|
||||||
|
// 行是否可选:防止同一检查项目的冲突自动勾选
|
||||||
|
const selectableRow = (row) => {
|
||||||
|
// 同一检查项目(itemCode)只能选中一条
|
||||||
|
const alreadySelected = selectedRows.value.some(
|
||||||
|
(item) => item.itemCode === row.itemCode
|
||||||
|
);
|
||||||
|
return !alreadySelected;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 处理手动勾选变化
|
||||||
|
const handleSelectionChange = (selection) => {
|
||||||
|
selectedRows.value = selection;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 确认选择
|
||||||
|
const confirmSelection = async () => {
|
||||||
|
if (selectedRows.value.length === 0) {
|
||||||
|
ElMessage.warning('请先选择检查项目');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
await submitCheckRequests(selectedRows.value);
|
||||||
|
ElMessage.success('检查申请已提交');
|
||||||
|
// 提交成功后刷新列表并清空选择
|
||||||
|
const { data } = await fetchCheckRequests();
|
||||||
|
requestList.value = data;
|
||||||
|
clearSelection();
|
||||||
|
} catch (e) {
|
||||||
|
ElMessage.error('提交失败,请重试');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 清空选择
|
||||||
|
const clearSelection = () => {
|
||||||
|
selectedRows.value = [];
|
||||||
|
if (table.value) {
|
||||||
|
table.value.clearSelection();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.check-request {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
.actions {
|
||||||
|
margin-top: 20px;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
.ellipsis {
|
||||||
|
display: inline-block;
|
||||||
|
max-width: 180px;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
Reference in New Issue
Block a user