feat: 门诊手术中计费功能

- 数据库:在adm_charge_item表添加SourceBillNo字段
- 后端实体类:更新ChargeItem.java添加SourceBillNo字段
- 前端组件:创建手术计费界面(基于门诊划价界面)
- 后端API:扩展PrePrePaymentDto支持手术计费标识
- 后端Service:扩展getChargeItems方法支持手术计费过滤
- 门诊手术安排界面:添加【计费】按钮

注意事项:
- 需要手动执行SQL脚本:openhis-server-new/sql/add_source_bill_no_to_adm_charge_item.sql
- 术后一站式结算功能待后续开发
This commit is contained in:
2026-02-05 23:47:02 +08:00
parent f3d56bff45
commit 89bf85fd97
117 changed files with 30248 additions and 44 deletions

View File

@@ -0,0 +1,95 @@
package com.openhis.web.consultation.controller;
import com.core.common.core.controller.BaseController;
import com.core.common.core.domain.AjaxResult;
import com.core.common.annotation.Log;
import com.core.common.enums.BusinessType;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.openhis.consultation.domain.ConsultationConfirmation;
import com.openhis.consultation.service.IConsultationConfirmationService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import java.time.LocalDateTime;
import java.util.List;
/**
* 会诊确认 Controller
*
* @author his
*/
@RestController
@RequestMapping("/consultation/confirmation")
public class ConsultationConfirmationController extends BaseController {
@Autowired
private IConsultationConfirmationService consultationConfirmationService;
/**
* 查询会诊确认列表
*/
@PreAuthorize("@ss.hasPermi('consultation:confirmation:list')")
@GetMapping("/list")
public AjaxResult list(ConsultationConfirmation consultationConfirmation,
@RequestParam(defaultValue = "1") Integer pageNum,
@RequestParam(defaultValue = "10") Integer pageSize) {
LambdaQueryWrapper<ConsultationConfirmation> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(consultationConfirmation.getConsultationRequestId() != null,
ConsultationConfirmation::getConsultationRequestId,
consultationConfirmation.getConsultationRequestId())
.eq(consultationConfirmation.getConfirmationStatus() != null,
ConsultationConfirmation::getConfirmationStatus,
consultationConfirmation.getConfirmationStatus())
.orderByDesc(ConsultationConfirmation::getCreateTime);
Page<ConsultationConfirmation> page = new Page<>(pageNum, pageSize);
Page<ConsultationConfirmation> result = consultationConfirmationService.page(page, queryWrapper);
return AjaxResult.success(result);
}
/**
* 获取会诊确认详细信息
*/
@PreAuthorize("@ss.hasPermi('consultation:confirmation:query')")
@GetMapping(value = "/{id}")
public AjaxResult getInfo(@PathVariable("id") Long id) {
return AjaxResult.success(consultationConfirmationService.getById(id));
}
/**
* 新增会诊确认
*/
@PreAuthorize("@ss.hasPermi('consultation:confirmation:add')")
@Log(title = "会诊确认", businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@RequestBody ConsultationConfirmation consultationConfirmation) {
consultationConfirmation.setCreatorId(getUserId());
consultationConfirmation.setCreatorName(getUsername());
return toAjax(consultationConfirmationService.save(consultationConfirmation));
}
/**
* 确认会诊(同意或拒绝)
*/
@PreAuthorize("@ss.hasPermi('consultation:confirmation:confirm')")
@Log(title = "会诊确认", businessType = BusinessType.UPDATE)
@PutMapping("/confirm")
public AjaxResult confirm(@RequestBody ConsultationConfirmation consultationConfirmation) {
// 设置确认日期
consultationConfirmation.setConfirmationDate(LocalDateTime.now());
consultationConfirmation.setUpdaterId(getUserId());
consultationConfirmation.setUpdaterName(getUsername());
return toAjax(consultationConfirmationService.updateById(consultationConfirmation));
}
/**
* 删除会诊确认
*/
@PreAuthorize("@ss.hasPermi('consultation:confirmation:remove')")
@Log(title = "会诊确认", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
public AjaxResult remove(@PathVariable Long[] ids) {
return toAjax(consultationConfirmationService.removeByIds(java.util.Arrays.asList(ids)));
}
}

View File

@@ -0,0 +1,92 @@
package com.openhis.web.consultation.controller;
import com.core.common.core.controller.BaseController;
import com.core.common.core.domain.AjaxResult;
import com.core.common.annotation.Log;
import com.core.common.enums.BusinessType;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.openhis.consultation.domain.ConsultationRecord;
import com.openhis.consultation.service.IConsultationRecordService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* 会诊记录 Controller
*
* @author his
*/
@RestController
@RequestMapping("/consultation/record")
public class ConsultationRecordController extends BaseController {
@Autowired
private IConsultationRecordService consultationRecordService;
/**
* 查询会诊记录列表
*/
@PreAuthorize("@ss.hasPermi('consultation:record:list')")
@GetMapping("/list")
public AjaxResult list(ConsultationRecord consultationRecord,
@RequestParam(defaultValue = "1") Integer pageNum,
@RequestParam(defaultValue = "10") Integer pageSize) {
LambdaQueryWrapper<ConsultationRecord> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(consultationRecord.getConsultationRequestId() != null,
ConsultationRecord::getConsultationRequestId,
consultationRecord.getConsultationRequestId())
.eq(consultationRecord.getParticipantDoctorId() != null,
ConsultationRecord::getParticipantDoctorId,
consultationRecord.getParticipantDoctorId())
.orderByDesc(ConsultationRecord::getRecordDate);
Page<ConsultationRecord> page = new Page<>(pageNum, pageSize);
Page<ConsultationRecord> result = consultationRecordService.page(page, queryWrapper);
return AjaxResult.success(result);
}
/**
* 获取会诊记录详细信息
*/
@PreAuthorize("@ss.hasPermi('consultation:record:query')")
@GetMapping(value = "/{id}")
public AjaxResult getInfo(@PathVariable("id") Long id) {
return AjaxResult.success(consultationRecordService.getById(id));
}
/**
* 新增会诊记录
*/
@PreAuthorize("@ss.hasPermi('consultation:record:add')")
@Log(title = "会诊记录", businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@RequestBody ConsultationRecord consultationRecord) {
consultationRecord.setCreatorId(getUserId());
consultationRecord.setCreatorName(getUsername());
return toAjax(consultationRecordService.save(consultationRecord));
}
/**
* 修改会诊记录
*/
@PreAuthorize("@ss.hasPermi('consultation:record:edit')")
@Log(title = "会诊记录", businessType = BusinessType.UPDATE)
@PutMapping
public AjaxResult edit(@RequestBody ConsultationRecord consultationRecord) {
consultationRecord.setUpdaterId(getUserId());
consultationRecord.setUpdaterName(getUsername());
return toAjax(consultationRecordService.updateById(consultationRecord));
}
/**
* 删除会诊记录
*/
@PreAuthorize("@ss.hasPermi('consultation:record:remove')")
@Log(title = "会诊记录", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
public AjaxResult remove(@PathVariable Long[] ids) {
return toAjax(consultationRecordService.removeByIds(java.util.Arrays.asList(ids)));
}
}

View File

@@ -0,0 +1,170 @@
package com.openhis.web.consultation.controller;
import com.core.common.core.controller.BaseController;
import com.core.common.core.domain.AjaxResult;
import com.core.common.annotation.Log;
import com.core.common.enums.BusinessType;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.openhis.consultation.domain.ConsultationRequest;
import com.openhis.consultation.service.IConsultationRequestService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import java.time.LocalDateTime;
import java.util.List;
/**
* 会诊申请 Controller
*
* @author his
*/
@RestController
@RequestMapping("/consultation/request")
public class ConsultationRequestController extends BaseController {
@Autowired
private IConsultationRequestService consultationRequestService;
/**
* 查询会诊申请列表
*/
@PreAuthorize("@ss.hasPermi('consultation:request:list')")
@GetMapping("/list")
public AjaxResult list(ConsultationRequest consultationRequest,
@RequestParam(defaultValue = "1") Integer pageNum,
@RequestParam(defaultValue = "10") Integer pageSize) {
LambdaQueryWrapper<ConsultationRequest> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.like(consultationRequest.getPatientName() != null, ConsultationRequest::getPatientName, consultationRequest.getPatientName())
.eq(consultationRequest.getConsultationStatus() != null, ConsultationRequest::getConsultationStatus, consultationRequest.getConsultationStatus())
.eq(consultationRequest.getConsultationActivityId() != null, ConsultationRequest::getConsultationActivityId, consultationRequest.getConsultationActivityId())
.orderByDesc(ConsultationRequest::getConsultationRequestDate);
Page<ConsultationRequest> page = new Page<>(pageNum, pageSize);
Page<ConsultationRequest> result = consultationRequestService.page(page, queryWrapper);
return AjaxResult.success(result);
}
/**
* 获取会诊申请详细信息
*/
@PreAuthorize("@ss.hasPermi('consultation:request:query')")
@GetMapping(value = "/{id}")
public AjaxResult getInfo(@PathVariable("id") Long id) {
return AjaxResult.success(consultationRequestService.getById(id));
}
/**
* 新增会诊申请
*/
@PreAuthorize("@ss.hasPermi('consultation:request:add')")
@Log(title = "会诊申请", businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@RequestBody ConsultationRequest consultationRequest) {
// 自动生成申请单号
String consultationId = "CS" + LocalDateTime.now().toString().replaceAll("[^0-9]", "").substring(0, 14) + String.format("%04d", (int)(Math.random() * 10000));
consultationRequest.setConsultationId(consultationId);
consultationRequest.setCreateBy(getUsername());
consultationRequest.setConsultationRequestDate(LocalDateTime.now());
consultationRequest.setConsultationStatus(0); // 新开状态
return toAjax(consultationRequestService.save(consultationRequest));
}
/**
* 修改会诊申请
*/
@PreAuthorize("@ss.hasPermi('consultation:request:edit')")
@Log(title = "会诊申请", businessType = BusinessType.UPDATE)
@PutMapping
public AjaxResult edit(@RequestBody ConsultationRequest consultationRequest) {
consultationRequest.setUpdateBy(getUsername());
return toAjax(consultationRequestService.updateById(consultationRequest));
}
/**
* 删除会诊申请
*/
@PreAuthorize("@ss.hasPermi('consultation:request:remove')")
@Log(title = "会诊申请", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
public AjaxResult remove(@PathVariable Long[] ids) {
return toAjax(consultationRequestService.removeByIds(java.util.Arrays.asList(ids)));
}
/**
* 提交会诊申请
*/
@PreAuthorize("@ss.hasPermi('consultation:request:submit')")
@Log(title = "会诊申请", businessType = BusinessType.UPDATE)
@PutMapping("/submit/{id}")
public AjaxResult submit(@PathVariable Long id) {
int result = consultationRequestService.submitConsultation(id, getUserId(), getUsername());
return result > 0 ? AjaxResult.success("提交成功") : AjaxResult.error("提交失败");
}
/**
* 取消提交会诊申请
*/
@PreAuthorize("@ss.hasPermi('consultation:request:cancelSubmit')")
@Log(title = "会诊申请", businessType = BusinessType.UPDATE)
@PutMapping("/cancelSubmit/{id}")
public AjaxResult cancelSubmit(@PathVariable Long id) {
int result = consultationRequestService.cancelSubmitConsultation(id);
return result > 0 ? AjaxResult.success("取消提交成功") : AjaxResult.error("取消提交失败");
}
/**
* 结束会诊申请
*/
@PreAuthorize("@ss.hasPermi('consultation:request:end')")
@Log(title = "会诊申请", businessType = BusinessType.UPDATE)
@PutMapping("/end/{id}")
public AjaxResult end(@PathVariable Long id) {
int result = consultationRequestService.endConsultation(id, getUserId(), getUsername());
return result > 0 ? AjaxResult.success("结束成功") : AjaxResult.error("结束失败,请确保会诊已签名");
}
/**
* 作废会诊申请
*/
@PreAuthorize("@ss.hasPermi('consultation:request:cancel')")
@Log(title = "会诊申请", businessType = BusinessType.UPDATE)
@PutMapping("/cancel/{id}")
public AjaxResult cancel(@PathVariable Long id) {
int result = consultationRequestService.cancelConsultation(id, getUserId(), getUsername());
return result > 0 ? AjaxResult.success("作废成功") : AjaxResult.error("作废失败");
}
/**
* 确认会诊
*/
@PreAuthorize("@ss.hasPermi('consultation:request:confirm')")
@Log(title = "会诊申请", businessType = BusinessType.UPDATE)
@PutMapping("/confirm")
public AjaxResult confirm(@RequestBody ConsultationRequest request) {
int result = consultationRequestService.confirmConsultation(
request.getId(),
request.getConfirmingPhysicianName(),
String.valueOf(request.getConfirmingPhysicianId()),
request.getConsultationOpinion()
);
return result > 0 ? AjaxResult.success("确认成功") : AjaxResult.error("确认失败");
}
/**
* 签名会诊
*/
@PreAuthorize("@ss.hasPermi('consultation:request:sign')")
@Log(title = "会诊申请", businessType = BusinessType.UPDATE)
@PutMapping("/sign")
public AjaxResult sign(@RequestBody ConsultationRequest request) {
int result = consultationRequestService.signConsultation(
request.getId(),
request.getSignature(),
getUserId(),
getUsername()
);
return result > 0 ? AjaxResult.success("签名成功") : AjaxResult.error("签名失败,请确保会诊已确认");
}
}

View File

@@ -190,8 +190,15 @@ public class PaymentRecServiceImpl implements IPaymentRecService {
@Override
public R<?> prePayment(PrePaymentDto prePaymentDto) {
logger.info("预结算:参数:" + JSON.toJSONString(prePaymentDto));
// 查收费项
List<ChargeItem> chargeItemList = getChargeItems(prePaymentDto.getChargeItemIds());
// 查收费项(支持手术计费)
List<ChargeItem> chargeItemList;
if (prePaymentDto.getGenerateSourceEnum() != null && prePaymentDto.getGenerateSourceEnum() == 2) {
// 手术计费根据generateSourceEnum和sourceBillNo过滤
chargeItemList = getChargeItems(prePaymentDto.getChargeItemIds(), prePaymentDto.getGenerateSourceEnum(), prePaymentDto.getSourceBillNo());
} else {
// 普通门诊划价
chargeItemList = getChargeItems(prePaymentDto.getChargeItemIds());
}
if (chargeItemList.isEmpty()) {
return R.fail("未选择收费项");
}
@@ -2093,6 +2100,24 @@ public class PaymentRecServiceImpl implements IPaymentRecService {
return chargeItemService.list(new LambdaQueryWrapper<ChargeItem>().in(ChargeItem::getId, chargeItemIds));
}
/**
* 获取收费项集合(支持手术计费)
*
* @param chargeItemIds 收费项id集合
* @param generateSourceEnum 账单生成来源2手术计费
* @param sourceBillNo 来源业务单据号(手术单号)
* @return 收费项集合
*/
private List<ChargeItem> getChargeItems(List<Long> chargeItemIds, Integer generateSourceEnum, String sourceBillNo) {
LambdaQueryWrapper<ChargeItem> wrapper = new LambdaQueryWrapper<ChargeItem>().in(ChargeItem::getId, chargeItemIds);
// 如果是手术计费,需要额外过滤条件
if (generateSourceEnum != null && generateSourceEnum == 2 && sourceBillNo != null) {
wrapper.eq(ChargeItem::getGenerateSourceEnum, 2)
.eq(ChargeItem::getSourceBillNo, sourceBillNo);
}
return chargeItemService.list(wrapper);
}
/**
* 类型转换收费项chargeItem转成PaymentedItemModel
*

View File

@@ -0,0 +1,94 @@
package com.openhis.web.ybmanage.controller;
import com.core.common.core.domain.AjaxResult;
import com.core.common.core.domain.PageResult;
import com.core.common.core.page.TableDataInfo;
import com.core.common.core.controller.BaseController;
import com.openhis.yb.domain.DayEndMedicalInsuranceSettlement;
import com.openhis.yb.service.IDayEndMedicalInsuranceSettlementService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* 日结医保结算Controller
*
* @author
* @date 2026-02-02
*/
@Api(tags = "日结医保结算")
@RestController
@RequestMapping("/ybmanage/dayEndMedicalInsuranceSettlement")
public class DayEndMedicalInsuranceSettlementController extends BaseController {
@Autowired
private IDayEndMedicalInsuranceSettlementService dayEndMedicalInsuranceSettlementService;
/**
* 查询日结医保结算列表
*/
@ApiOperation("查询日结医保结算列表")
@PreAuthorize("@ss.hasPermi('ybmanage:dayEndMedicalInsuranceSettlement:list')")
@GetMapping("/list")
public TableDataInfo list(DayEndMedicalInsuranceSettlement dayEndMedicalInsuranceSettlement) {
startPage();
List<DayEndMedicalInsuranceSettlement> list = dayEndMedicalInsuranceSettlementService.selectDayEndMedicalInsuranceSettlementList(dayEndMedicalInsuranceSettlement);
return getDataTable(list);
}
/**
* 分页查询日结医保结算列表
*/
@ApiOperation("分页查询日结医保结算列表")
@PreAuthorize("@ss.hasPermi('ybmanage:dayEndMedicalInsuranceSettlement:list')")
@GetMapping("/page")
public PageResult<DayEndMedicalInsuranceSettlement> page(DayEndMedicalInsuranceSettlement dayEndMedicalInsuranceSettlement,
@RequestParam(defaultValue = "1") int pageNum,
@RequestParam(defaultValue = "10") int pageSize) {
return dayEndMedicalInsuranceSettlementService.selectDayEndMedicalInsuranceSettlementPage(dayEndMedicalInsuranceSettlement, pageNum, pageSize);
}
/**
* 获取日结医保结算详细信息
*/
@ApiOperation("获取日结医保结算详细信息")
@PreAuthorize("@ss.hasPermi('ybmanage:dayEndMedicalInsuranceSettlement:query')")
@GetMapping(value = "/{id}")
public AjaxResult getInfo(@PathVariable("id") Long id) {
return AjaxResult.success(dayEndMedicalInsuranceSettlementService.selectDayEndMedicalInsuranceSettlementById(id));
}
/**
* 新增日结医保结算
*/
@ApiOperation("新增日结医保结算")
@PreAuthorize("@ss.hasPermi('ybmanage:dayEndMedicalInsuranceSettlement:add')")
@PostMapping
public AjaxResult add(@RequestBody DayEndMedicalInsuranceSettlement dayEndMedicalInsuranceSettlement) {
return toAjax(dayEndMedicalInsuranceSettlementService.insertDayEndMedicalInsuranceSettlement(dayEndMedicalInsuranceSettlement));
}
/**
* 修改日结医保结算
*/
@ApiOperation("修改日结医保结算")
@PreAuthorize("@ss.hasPermi('ybmanage:dayEndMedicalInsuranceSettlement:edit')")
@PutMapping
public AjaxResult edit(@RequestBody DayEndMedicalInsuranceSettlement dayEndMedicalInsuranceSettlement) {
return toAjax(dayEndMedicalInsuranceSettlementService.updateDayEndMedicalInsuranceSettlement(dayEndMedicalInsuranceSettlement));
}
/**
* 删除日结医保结算
*/
@ApiOperation("删除日结医保结算")
@PreAuthorize("@ss.hasPermi('ybmanage:dayEndMedicalInsuranceSettlement:remove')")
@DeleteMapping("/{ids}")
public AjaxResult remove(@PathVariable Long[] ids) {
return toAjax(dayEndMedicalInsuranceSettlementService.deleteDayEndMedicalInsuranceSettlementByIds(ids));
}
}

View File

@@ -90,4 +90,15 @@ server:
port: 18080
servlet:
# 应用的访问路径
context-path: /openhis
context-path: /openhis
# 开发环境日志配置
logging:
level:
com.openhis: debug
com.baomidou.mybatisplus: debug
com.openhis.mapper: debug
com.openhis.domain: debug
org.springframework.jdbc.core: debug
com.alibaba.druid: debug
com.alibaba.druid.sql: debug

View File

@@ -32,16 +32,17 @@ server:
# 日志配置
logging:
level:
com.openhis: info
com.openhis: debug
org.springframework: warn
# MyBatis和MyBatis-Plus日志
com.baomidou.mybatisplus: info
com.openhis.web.regdoctorstation.mapper: info
com.baomidou.mybatisplus: debug
com.openhis.mapper: debug
com.openhis.domain: debug
# JDBC日志
org.springframework.jdbc.core: info
org.springframework.jdbc.core: debug
# Druid SQL日志
com.alibaba.druid: info
com.alibaba.druid.sql: info
com.alibaba.druid: debug
com.alibaba.druid.sql: debug
# 用户配置
user:
@@ -91,6 +92,9 @@ mybatis-plus:
mapperLocations: classpath*:mapper/**/*Mapper.xml
# 加载全局的配置文件
configLocation: classpath:mybatis/mybatis-config.xml
configuration:
# 开启 SQL 日志输出
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
global-config:
db-config:
logic-delete-field: validFlag # 全局逻辑删除的实体字段名

View File

@@ -12,7 +12,7 @@
<!-- 配置默认的执行器.SIMPLE就是普通执行器;REUSE执行器会重用预处理语句(prepared statements);BATCH执行器将重用语句并执行批量更新 -->
<setting name="defaultExecutorType" value="SIMPLE"/>
<!-- 指定 MyBatis 所用日志的具体实现 -->
<setting name="logImpl" value="SLF4J"/>
<setting name="logImpl" value="STDOUT"/>
<!-- <setting name="logImpl" value="org.apache.ibatis.logging.stdout.StdOutImpl"/>-->
<!-- 使用驼峰命名法转换字段 -->
<setting name="mapUnderscoreToCamelCase" value="true"/>

View File

@@ -0,0 +1,5 @@
modulelist=com.baomidou.mybatisplus.extension.p6spy.MybatisPlusLogFactory,com.p6spy.engine.outage.P6OutageFactory
logMessageFormat=com.baomidou.mybatisplus.extension.p6spy.P6SpyLogger
outagedetection=true
outagedetectioninterval=2000
appender=com.baomidou.mybatisplus.extension.p6spy.StdoutLogger