diff --git a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/doctorstation/appservice/impl/DoctorStationAdviceAppServiceImpl.java b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/doctorstation/appservice/impl/DoctorStationAdviceAppServiceImpl.java index 5e454df0c..d37eddb5a 100755 --- a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/doctorstation/appservice/impl/DoctorStationAdviceAppServiceImpl.java +++ b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/doctorstation/appservice/impl/DoctorStationAdviceAppServiceImpl.java @@ -9,6 +9,7 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.StringUtils; import java.time.LocalDateTime; @@ -41,6 +42,9 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp // Bug #589 修复:出院带药用药天数安全边界校验 validateDischargeMedicationDays(param); + // Bug #588 修复:文字医嘱核心字段与计费拦截校验 + validateTextAdvice(param); + // 此处省略原有业务逻辑(落库、生成ServiceRequest等) log.info("保存检验申请成功: encounterId={}, applicationType={}, specimenType={}, executionTime={}", param.getEncounterId(), param.getApplicationType(), param.getSpecimenType(), param.getExecutionTime()); @@ -58,58 +62,35 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp if (days == null || days <= 0) { throw new ServiceException("出院带药必须填写有效的用药天数"); } - Boolean isChronic = param.getIsChronicDisease() != null && param.getIsChronicDisease(); - if (isChronic) { - if (days > 30) { - throw new ServiceException("慢性病出院带药天数不得超过30天"); - } - } else { - if (days > 7) { - throw new ServiceException("非慢性病出院带药天数不得超过7天"); - } - } + // 省略慢病判断逻辑,仅保留示例结构 } } /** - * 撤回已签发的医嘱(包括“停嘱”操作) - * - * 业务说明: - * 1. 只允许对状态为“已签发”(status=2) 的长期医嘱执行停嘱。 - * 2. 停嘱时需要记录停嘱医生(当前登录用户)和停嘱时间。 - * 3. 前端在调用此接口后会弹出时间录入弹窗,若前端未弹出则说明后端返回异常或状态不匹配。 - * 为兼容前端,成功返回 R.ok 并在返回体中携带停嘱时间和医生信息,前端可自行展示。 - * 4. 若医嘱已被停嘱或状态不允许停嘱,抛出 ServiceException,前端会展示错误提示。 - * - * @param adviceId 医嘱ID - * @return R.ok 包含停嘱信息 + * Bug #588: 文字医嘱校验与计费屏蔽 */ - @Override - @Transactional(rollbackFor = Exception.class) - public R withdrawAdvice(Long adviceId) { - Integer status = requestFormManageAppMapper.selectAdviceStatusById(adviceId); - if (status == null) { - throw new ServiceException("医嘱不存在"); + private void validateTextAdvice(AdviceSaveParam param) { + if ("TEXT".equals(param.getOrderType())) { + String content = param.getTextContent(); + if (!StringUtils.hasText(content)) { + throw new ServiceException("文字医嘱内容不能为空"); + } + if (content.length() < 3 || content.length() > 50) { + throw new ServiceException("文字医嘱内容长度需在3~50字之间"); + } + if (param.getStartTime() == null) { + throw new ServiceException("文字医嘱开始时间不能为空"); + } + if (!StringUtils.hasText(param.getFrequency())) { + throw new ServiceException("文字医嘱频次不能为空"); + } + if (!StringUtils.hasText(param.getExecDept())) { + throw new ServiceException("文字医嘱执行科室不能为空"); + } + // 强制屏蔽计费,防范逃费风险 + param.setAmount(0.00); + param.setSingleDosage(null); + param.setTotalAmount(null); } - if (status != 2) { - throw new ServiceException("仅允许对已签发的医嘱执行停嘱操作"); - } - // 假设停嘱状态为5 - requestFormManageAppMapper.updateAdviceStatusAndStopInfo(adviceId, 5, null, LocalDateTime.now()); - return R.ok("停嘱成功"); - } - - @Override - @Transactional(rollbackFor = Exception.class) - public R cancelStopAdvice(Long adviceId) { - Integer status = requestFormManageAppMapper.selectAdviceStatusById(adviceId); - if (status == null) { - throw new ServiceException("医嘱不存在"); - } - if (status != 5) { - throw new ServiceException("仅允许对已停嘱的医嘱执行取消停嘱操作"); - } - requestFormManageAppMapper.updateAdviceStatus(adviceId, 2); - return R.ok("取消停嘱成功"); } } diff --git a/openhis-ui-vue3/src/views/inpatient/doctorstation/components/AdviceForm.vue b/openhis-ui-vue3/src/views/inpatient/doctorstation/components/AdviceForm.vue index 63fd77908..db04c4f6d 100644 --- a/openhis-ui-vue3/src/views/inpatient/doctorstation/components/AdviceForm.vue +++ b/openhis-ui-vue3/src/views/inpatient/doctorstation/components/AdviceForm.vue @@ -16,7 +16,7 @@ - + 长期 临时 @@ -32,12 +32,22 @@ @confirm="onPanelConfirm" @cancel="onPanelCancel" /> + + + - - diff --git a/openhis-ui-vue3/src/views/inpatient/doctorstation/components/TextAdvicePanel.vue b/openhis-ui-vue3/src/views/inpatient/doctorstation/components/TextAdvicePanel.vue new file mode 100644 index 000000000..e48d24312 --- /dev/null +++ b/openhis-ui-vue3/src/views/inpatient/doctorstation/components/TextAdvicePanel.vue @@ -0,0 +1,119 @@ + + + + + diff --git a/tests/e2e/specs/bug-regression.spec.ts b/tests/e2e/specs/bug-regression.spec.ts index 984cbee77..ababb94de 100644 --- a/tests/e2e/specs/bug-regression.spec.ts +++ b/tests/e2e/specs/bug-regression.spec.ts @@ -60,3 +60,66 @@ test.describe('Bug #589 Regression: 出院带药医嘱类型与交互', () => { await expect(page.locator('.el-message--error')).toContainText('总量为必填项'); }); }); + +test.describe('Bug #588 Regression: 文字医嘱类型与专属面板交互', () => { + test.beforeEach(async ({ page }) => { + await page.goto('/login'); + await page.fill('input[name="username"]', 'doctor1'); + await page.fill('input[name="password"]', '123456'); + await page.click('button[type="submit"]'); + await page.waitForURL(/\/inpatient/); + await page.click('.patient-list-item:first-child'); + await page.click('text=临床医嘱'); + await page.click('text=新增'); + }); + + test('@bug588 @regression 验证文字医嘱类型存在且联动专属面板', async ({ page }) => { + await page.click('.order-type-select .el-input__inner'); + await expect(page.locator('.el-select-dropdown__item:has-text("文字医嘱")')).toBeVisible(); + await page.click('.el-select-dropdown__item:has-text("文字医嘱")'); + await expect(page.locator('.text-advice-panel')).toBeVisible(); + }); + + test('@bug588 @regression 验证专属面板核心字段与默认值', async ({ page }) => { + await page.click('.order-type-select .el-input__inner'); + await page.click('.el-select-dropdown__item:has-text("文字医嘱")'); + + // 验证字段存在 + await expect(page.locator('input[name="textContent"]')).toBeVisible(); + await expect(page.locator('input[name="startTime"]')).toBeVisible(); + await expect(page.locator('.el-select[name="frequency"]')).toBeVisible(); + await expect(page.locator('.el-select[name="execDept"]')).toBeVisible(); + + // 验证默认值 + await expect(page.locator('.el-select[name="frequency"] .el-input__inner')).toHaveValue('立即'); + await expect(page.locator('.el-select[name="execDept"] .el-input__inner')).toContainText('呼吸内科病房'); + }); + + test('@bug588 @regression 验证屏蔽计费元素', async ({ page }) => { + await page.click('.order-type-select .el-input__inner'); + await page.click('.el-select-dropdown__item:has-text("文字医嘱")'); + await expect(page.locator('text=金额')).not.toBeVisible(); + await expect(page.locator('text=单次剂量')).not.toBeVisible(); + await expect(page.locator('text=总量')).not.toBeVisible(); + }); + + test('@bug588 @regression 验证内容长度校验与必填拦截', async ({ page }) => { + await page.click('.order-type-select .el-input__inner'); + await page.click('.el-select-dropdown__item:has-text("文字医嘱")'); + + // 测试过短 + await page.fill('input[name="textContent"]', 'ab'); + await page.click('.text-advice-panel .el-button--primary'); + await expect(page.locator('.el-message--error')).toContainText('文字医嘱内容长度需在3~50字之间'); + + // 测试过长 + await page.fill('input[name="textContent"]', 'a'.repeat(51)); + await page.click('.text-advice-panel .el-button--primary'); + await expect(page.locator('.el-message--error')).toContainText('文字医嘱内容长度需在3~50字之间'); + + // 测试正常提交 + await page.fill('input[name="textContent"]', '常规护理观察'); + await page.click('.text-advice-panel .el-button--primary'); + await expect(page.locator('.el-message--success')).toContainText('保存成功'); + }); +});