bugfix:对账对平

This commit is contained in:
Yuan
2025-11-27 20:48:57 +08:00
parent 10ca4fae06
commit 3f931c2e29
14 changed files with 3072 additions and 30 deletions

View File

@@ -0,0 +1,418 @@
package com.saye.hospitalgd.controller;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.saye.hospitalgd.commons.date.DateDUtil;
import com.saye.hospitalgd.commons.excel.ExportXLSX;
import com.saye.hospitalgd.commons.excel.HashMapConversionImpl;
import com.saye.hospitalgd.commons.excel.IConversionByExport;
import com.saye.hospitalgd.commons.log.ExceptionDUtil;
import com.saye.hospitalgd.commons.log.LogUtil;
import com.saye.hospitalgd.commons.string.StringDUtil;
import com.saye.hospitalgd.model.Dicinfo;
import com.saye.hospitalgd.model.StatusDefine;
import com.saye.hospitalgd.service.InpatientBillService;
import com.saye.hospitalgd.service.system.DicinfoService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.*;
import java.io.File;
import java.util.*;
/**
* @author thuang
* @version 1.0
* @description: 住院账单查询
* @date 2025/11/21
*/
@Api(value = "住院账单查询相关接口")
@Controller
@RequestMapping("/inpatientBill")
public class InpatientBillController {
@Autowired
private InpatientBillService inpatientBillService;
@Autowired
private DicinfoService dicinfoService;
/**
* 跳转到住院账单查询页面
*/
@RequestMapping("/toInpatientBill")
public String toInpatientBill(ModelMap modelMap) {
Calendar calendar = Calendar.getInstance();
calendar.setTime(new Date());
calendar.add(Calendar.DATE, -1);
Date startDate = calendar.getTime();
String startTime = DateDUtil.DateToStr(DateDUtil.yyyy_MM_dd, startDate);
String endTime = DateDUtil.getCurrentDate(DateDUtil.yyyy_MM_dd);
modelMap.addAttribute("startTime", startTime);
modelMap.addAttribute("endTime", endTime);
// 支付方式
List<Dicinfo> pay_type = dicinfoService.findDicinfoTreeNodeList("PAY_TYPE");
modelMap.addAttribute("payTypeList", pay_type);
// 业务类型
List<Dicinfo> biz_type = dicinfoService.findDicinfoTreeNodeList("BIZ_TYPE");
modelMap.addAttribute("bizTypeList", biz_type);
// 三方支付方式
List<Dicinfo> third_pay = dicinfoService.findDicinfoTreeNodeList("THIRD_PAY");
modelMap.addAttribute("thirdPayList", third_pay);
return "financialReconciliation/inpatientBill";
}
/**
* 查询HIS住院账单明细
*/
@ApiOperation(value = "查询HIS住院账单明细", notes = "")
@GetMapping("/findHisInpatientDetail")
@ResponseBody
public HashMap<Object, Object> findHisInpatientDetail(
@ApiParam(value = "开始时间") String startTime,
@ApiParam(value = "结束时间") String endTime,
@ApiParam(value = "支付方式") String payType,
@ApiParam(value = "业务类型") String bizType,
@ApiParam(value = "模糊查询字段") String likeFiled,
@ApiParam(value = "页码") int page,
@ApiParam(value = "每页限制个数") int limit) {
HashMap<Object, Object> responseMap = new HashMap<>();
try {
HashMap<Object, Object> map = new HashMap<>();
map.put("startTime", startTime);
map.put("endTime", endTime);
map.put("payType", payType);
map.put("bizType", bizType);
map.put("likeFiled", likeFiled);
String militaryPaymentCode = getPayTypeCodeByNames("军保支付", "医院垫支");
String checkPaymentCode = getPayTypeCodeByNames("支票支付", "支票");
if (checkPaymentCode == null || "".equals(checkPaymentCode)) {
checkPaymentCode = "";
}
map.put("military_payment_code", militaryPaymentCode);
map.put("check_payment_code", checkPaymentCode);
PageHelper.startPage(page, limit);
PageInfo<HashMap<Object, Object>> pageInfo = new PageInfo<>(inpatientBillService.findHisInpatientDetail(map));
responseMap.put("code", 0);
responseMap.put("msg", "OK");
responseMap.put("count", pageInfo.getTotal());
responseMap.put("data", pageInfo.getList());
} catch (Exception e) {
e.printStackTrace();
String msg = e.getMessage();
responseMap.put("code", 1);
responseMap.put("msg", "查询失败,原因:" + msg);
}
return responseMap;
}
/**
* 查询银行住院账单明细
*/
@ApiOperation(value = "查询银行住院账单明细", notes = "")
@GetMapping("/findBankInpatientDetail")
@ResponseBody
public HashMap<Object, Object> findBankInpatientDetail(
@ApiParam(value = "开始时间") String startTime,
@ApiParam(value = "结束时间") String endTime,
@ApiParam(value = "支付方式") String c_zffs,
@ApiParam(value = "模糊查询字段") String likeFiled,
@ApiParam(value = "页码") int page,
@ApiParam(value = "每页限制个数") int limit) {
HashMap<Object, Object> responseMap = new HashMap<>();
try {
HashMap<Object, Object> map = new HashMap<>();
map.put("startTime", startTime);
map.put("endTime", endTime);
map.put("c_zffs", c_zffs);
map.put("likeFiled", likeFiled);
PageHelper.startPage(page, limit);
PageInfo<HashMap<Object, Object>> pageInfo = new PageInfo<>(inpatientBillService.findBankInpatientDetail(map));
responseMap.put("code", 0);
responseMap.put("msg", "OK");
responseMap.put("count", pageInfo.getTotal());
responseMap.put("data", pageInfo.getList());
} catch (Exception e) {
e.printStackTrace();
String msg = e.getMessage();
responseMap.put("code", 1);
responseMap.put("msg", "查询失败,原因:" + msg);
}
return responseMap;
}
/**
* 查询每日汇总数据
*/
@ApiOperation(value = "查询每日汇总数据", notes = "")
@GetMapping("/findDailySummary")
@ResponseBody
public HashMap<Object, Object> findDailySummary(
@ApiParam(value = "开始时间") String startTime,
@ApiParam(value = "结束时间") String endTime) {
HashMap<Object, Object> responseMap = new HashMap<>();
try {
HashMap<Object, Object> map = new HashMap<>();
map.put("startTime", startTime);
map.put("endTime", endTime);
String militaryPaymentCode = getPayTypeCodeByNames("军保支付", "医院垫支");
String checkPaymentCode = getPayTypeCodeByNames("支票支付", "支票");
if (checkPaymentCode == null || "".equals(checkPaymentCode)) {
checkPaymentCode = "";
}
map.put("military_payment_code", militaryPaymentCode);
map.put("check_payment_code", checkPaymentCode);
List<HashMap<Object, Object>> summaryList = inpatientBillService.findDailySummary(map);
responseMap.put("code", 0);
responseMap.put("msg", "OK");
responseMap.put("data", summaryList);
} catch (Exception e) {
e.printStackTrace();
String msg = e.getMessage();
responseMap.put("code", 1);
responseMap.put("msg", "查询失败,原因:" + msg);
}
return responseMap;
}
/**
* 导出HIS住院账单明细
*/
@RequestMapping("/exportHisInpatientDetail")
@ResponseBody
@ApiOperation(value = "导出HIS住院账单明细", notes = "")
public HashMap<Object, Object> exportHisInpatientDetail(@RequestBody HashMap<Object, Object> map) {
HashMap<Object, Object> responseMap = new HashMap<>();
String errCode = "0";
String errMsg = "";
String dlName = "";
String fileName = "";
String dowloadName = StringDUtil.changeNullToEmpty(map.get("dowloadName"));
try {
String militaryPaymentCode = getPayTypeCodeByNames("军保支付", "医院垫支");
String checkPaymentCode = getPayTypeCodeByNames("支票支付", "支票");
if (checkPaymentCode == null || "".equals(checkPaymentCode)) {
checkPaymentCode = "";
}
map.put("military_payment_code", militaryPaymentCode);
map.put("check_payment_code", checkPaymentCode);
List<HashMap<Object, Object>> list = inpatientBillService.findHisInpatientDetail(map);
// 支付方式
List<Dicinfo> pay_type = dicinfoService.findDicinfoTreeNodeList("PAY_TYPE");
HashMap<String, String> payTypeMap = new HashMap<>();
for (Dicinfo dicinfo : pay_type) {
payTypeMap.put(dicinfo.getDicvalue(), dicinfo.getDicname());
}
// 业务类型
List<Dicinfo> biz_type = dicinfoService.findDicinfoTreeNodeList("BIZ_TYPE");
HashMap<String, String> bizTypeMap = new HashMap<>();
for (Dicinfo dicinfo : biz_type) {
bizTypeMap.put(dicinfo.getDicvalue(), dicinfo.getDicname());
}
for (HashMap<Object, Object> hashMap : list) {
String tradingStatus = StringDUtil.changeNullToEmpty(hashMap.get("TRADINGSTATUS"));
if ("1".equals(tradingStatus)) {
hashMap.put("TRADINGSTATUS", "收款记录");
} else if ("2".equals(tradingStatus)) {
hashMap.put("TRADINGSTATUS", "退款记录");
} else {
hashMap.put("TRADINGSTATUS", "");
}
String biztype = StringDUtil.changeNullToEmpty(hashMap.get("BIZTYPE"));
hashMap.put("BIZTYPE", bizTypeMap.get(biztype));
// 支付方式字典转换
String paytype = StringDUtil.changeNullToEmpty(hashMap.get("PAYTYPE"));
hashMap.put("PAYTYPE", payTypeMap.get(paytype));
hashMap.put("PAYMETHOD", "住院");
}
if (list.size() > 0) {
// 定义标题头和文件名
String[] DISTANCE_HEADERNAME = {"交易状态", "业务类型", "支付方式", "交易时间", "交易日期", "操作员", "总金额", "平台交易号", "his订单号", "HIS交易ID", "患者id", "患者姓名"};
String[] sqlKey = {"TRADINGSTATUS", "BIZTYPE", "PAYTYPE", "TRADETIME", "TRADE_DATE", "HISOPERCODE", "AMOUNT", "PLATFORMTRANSID", "HISTRANSID", "HISTRANSID", "PATIENTID", "PATIENTNAME"};
List<Object> rulList = new ArrayList<>(list);
// 创建工作表
ExportXLSX exportXLS = new ExportXLSX(DISTANCE_HEADERNAME, sqlKey, ExportXLSX.A3, false);
exportXLS.setTitleName(dowloadName);
IConversionByExport conversion = new HashMapConversionImpl();
exportXLS.setConversion(conversion);
exportXLS.setData(rulList);
exportXLS.modifyWidthOfHeader("5000", 0);
exportXLS.modifyWidthOfHeader("5000", 1);
exportXLS.modifyWidthOfHeader("5000", 2);
exportXLS.modifyWidthOfHeader("5000", 3);
exportXLS.modifyWidthOfHeader("5000", 4);
exportXLS.modifyWidthOfHeader("5000", 5);
exportXLS.modifyWidthOfHeader("8000", 6);
exportXLS.modifyWidthOfHeader("5000", 7);
exportXLS.modifyWidthOfHeader("5000", 8);
exportXLS.modifyWidthOfHeader("5000", 9);
exportXLS.modifyWidthOfHeader("5000", 10);
exportXLS.modifyWidthOfHeader("10000", 11);
// 文件名称
String randomStr = StringDUtil.generateRandomCodeForLength(4);
dlName = DateDUtil.DateToStr(DateDUtil.yyyyMMddHHmmss, new Date()) + randomStr;
fileName = dlName + ".xlsx";
String uploadPath = StatusDefine.filePath + "/InpatientBill/";
File uploadPathFile = new File(uploadPath);
if (!uploadPathFile.exists()) uploadPathFile.mkdirs();
String savePath = uploadPath + fileName;
exportXLS.execGenerateExcel(savePath);
}
} catch (Exception e) {
errCode = "999";
errMsg = "未知异常:" + ExceptionDUtil.getDetailExceptionMsg(e);
LogUtil.error(this.getClass(), "@@@系统出错!【" + errMsg + "");
}
responseMap.put("errCode", errCode);
responseMap.put("errMsg", errMsg);
responseMap.put("dlName", "InpatientBill/" + fileName);
return responseMap;
}
private String getPayTypeCodeByNames(String... names) {
for (String name : names) {
HashMap<String, String> param = new HashMap<>();
param.put("parentCode", "PAY_TYPE");
param.put("dicname", name);
List<HashMap<Object, Object>> list = dicinfoService.selectDicinfoListByCondition(param);
if (list != null && list.size() > 0) {
Object val = list.get(0).get("dicvalue");
String code = StringDUtil.changeNullToEmpty(val);
if (!"".equals(code)) {
return code;
}
}
}
return "";
}
/**
* 导出银行住院账单明细
*/
@RequestMapping("/exportBankInpatientDetail")
@ResponseBody
@ApiOperation(value = "导出银行住院账单明细", notes = "")
public HashMap<Object, Object> exportBankInpatientDetail(@RequestBody HashMap<Object, Object> map) {
HashMap<Object, Object> responseMap = new HashMap<>();
String errCode = "0";
String errMsg = "";
String dlName = "";
String fileName = "";
String dowloadName = StringDUtil.changeNullToEmpty(map.get("dowloadName"));
try {
List<HashMap<Object, Object>> list = inpatientBillService.findBankInpatientDetail(map);
// 支付方式
List<Dicinfo> pay_type = dicinfoService.findDicinfoTreeNodeList("THIRD_PAY");
HashMap<String, String> peyTypeMap = new HashMap<>();
for (Dicinfo dicinfo : pay_type) {
peyTypeMap.put(dicinfo.getDicvalue(), dicinfo.getDicname());
}
for (HashMap<Object, Object> hashMap : list) {
String c_zffs = StringDUtil.changeNullToEmpty(hashMap.get("C_ZFFS"));
hashMap.put("C_ZFFS", peyTypeMap.get(c_zffs));
}
if (list.size() > 0) {
// 定义标题头和文件名
String[] DISTANCE_HEADERNAME = {"交易日期", "交易时间", "清算日期", "流水号", "商户订单号", "银商订单号", "交易类型", "卡号", "发卡行", "交易金额", "清算金额", "手续费", "实际支付金额", "终端号", "支付方式", "卡类型"};
String[] sqlKey = {"C_JYRQ", "C_JYSJ", "C_QSRQ", "C_LSH", "C_SHDDH", "C_YSDDH", "C_JYLX", "C_CARD", "C_FKH", "C_JYJE", "C_QSJE", "C_SXF", "C_SJZFJE", "C_ZDH", "C_ZFFS", "C_KLX"};
List<Object> rulList = new ArrayList<>(list);
// 创建工作表
ExportXLSX exportXLS = new ExportXLSX(DISTANCE_HEADERNAME, sqlKey, ExportXLSX.A3, false);
exportXLS.setTitleName(dowloadName);
IConversionByExport conversion = new HashMapConversionImpl();
exportXLS.setConversion(conversion);
exportXLS.setData(rulList);
exportXLS.modifyWidthOfHeader("5000", 0);
exportXLS.modifyWidthOfHeader("5000", 1);
exportXLS.modifyWidthOfHeader("5000", 2);
exportXLS.modifyWidthOfHeader("10000", 3);
exportXLS.modifyWidthOfHeader("6000", 4);
exportXLS.modifyWidthOfHeader("10000", 5);
exportXLS.modifyWidthOfHeader("5000", 6);
exportXLS.modifyWidthOfHeader("5000", 7);
exportXLS.modifyWidthOfHeader("5000", 8);
exportXLS.modifyWidthOfHeader("5000", 9);
exportXLS.modifyWidthOfHeader("5000", 10);
exportXLS.modifyWidthOfHeader("5000", 11);
exportXLS.modifyWidthOfHeader("5000", 12);
exportXLS.modifyWidthOfHeader("5000", 13);
exportXLS.modifyWidthOfHeader("5000", 14);
exportXLS.modifyWidthOfHeader("10000", 15);
// 文件名称
String randomStr = StringDUtil.generateRandomCodeForLength(4);
dlName = DateDUtil.DateToStr(DateDUtil.yyyyMMddHHmmss, new Date()) + randomStr;
fileName = dlName + ".xlsx";
String uploadPath = StatusDefine.filePath + "/InpatientBill/";
File uploadPathFile = new File(uploadPath);
if (!uploadPathFile.exists()) uploadPathFile.mkdirs();
String savePath = uploadPath + fileName;
exportXLS.execGenerateExcel(savePath);
}
} catch (Exception e) {
errCode = "999";
errMsg = "未知异常:" + ExceptionDUtil.getDetailExceptionMsg(e);
LogUtil.error(this.getClass(), "@@@系统出错!【" + errMsg + "");
}
responseMap.put("errCode", errCode);
responseMap.put("errMsg", errMsg);
responseMap.put("dlName", "InpatientBill/" + fileName);
return responseMap;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,30 @@
package com.saye.hospitalgd.mapper;
import java.util.HashMap;
import java.util.List;
/**
* @author thuang
* @version 1.0
* @description: 住院账单查询Mapper接口
* @date 2025/11/21
*/
public interface InpatientBillMapper {
/**
* 查询HIS住院账单明细
*/
List<HashMap<Object, Object>> findHisInpatientDetail(HashMap<Object, Object> map) throws Exception;
/**
* 查询银行住院账单明细
*/
List<HashMap<Object, Object>> findBankInpatientDetail(HashMap<Object, Object> map) throws Exception;
/**
* 查询每日汇总数据
*/
List<HashMap<Object, Object>> findDailySummary(HashMap<Object, Object> map) throws Exception;
}

View File

@@ -696,7 +696,7 @@ public class BankGetDataMethodByJHLZF {
// 根据终端号判断是否为住院订单
// 终端号为10091548或10091549的为住院订单不参与对账
if ("10091548".equals(zdh) || "10091549".equals(zdh) || "10091546".equals(zdh)) {
if ("10091548".equals(zdh) || "10091549".equals(zdh) || "10091546".equals(zdh) || "10091547".equals(zdh) || "10091544".equals(zdh) || "10091545".equals(zdh)) {
bankbillHistory.setIsInpatient("1"); // 标记为住院订单
log.info("标记为住院订单: 终端号=" + zdh + ", 订单号=" + bankbillHistory.getCShddh());
} else {

View File

@@ -34,6 +34,8 @@ public class HISGetDataMethodByJH {
private static final Logger log = LoggerFactory.getLogger(HISGetDataMethodByJH.class);
public HashMap<Object,Object> getDate(String id, String name, String trade_date, String his_wsdl_id){
HashMap<Object,Object> responseMap=new HashMap<>();
String errCode="0";
@@ -48,6 +50,8 @@ public class HISGetDataMethodByJH {
HisInterFaceConfigService hisInterFaceConfigService = GetBeanUtil.getBean(HisInterFaceConfigServiceImpl.class);
try {
//查询需要用到的参数
@@ -146,7 +150,7 @@ public class HISGetDataMethodByJH {
//获取PowerTranID和ReceiptNO作为唯一标识
String powerTranID = StringDUtil.changeNullToEmpty(hisBillHashMap.get("powerTranID"));
String receiptNO = StringDUtil.changeNullToEmpty(hisBillHashMap.get("receiptNO"));
String hisTransId = StringDUtil.changeNullToEmpty(hisBillHashMap.get("hisTransId"));
// String hisTransId = StringDUtil.changeNullToEmpty(hisBillHashMap.get("hisTransId")); // 已在HisUtil中赋值此处不再重复获取
// 修改跳过逻辑:只有当关键业务信息都缺失时才跳过
// 检查是否有足够的业务信息来处理这条记录
@@ -163,13 +167,13 @@ public class HISGetDataMethodByJH {
// 只有当缺少关键业务信息时才跳过患者ID、金额、交易时间都为空
if ("".equals(patientId) && "".equals(amount) && "".equals(tradeTime)){
log.warn("跳过缺少关键业务信息的记录: powerTranID={}, receiptNO={}, hisTransId={}",
powerTranID, receiptNO, hisTransId);
log.warn("跳过缺少关键业务信息的记录: powerTranID={}, receiptNO={}",
powerTranID, receiptNO);
continue;
}
// 对于没有唯一标识的记录,记录警告但继续处理
if ("".equals(powerTranID) && "".equals(receiptNO) && "".equals(hisTransId)){
if ("".equals(powerTranID) && "".equals(receiptNO)){
log.warn("处理无唯一标识的记录: patientID={}, amount={}, tradeTime={}",
patientId, amount, tradeTime);
}
@@ -189,6 +193,10 @@ public class HISGetDataMethodByJH {
//支付方式 严格按照字典表转换
String originalPayType = StringDUtil.changeNullToEmpty(hisBillHashMap.get("payType"));
// 保持接口原始的医保账户金额和统筹金额,不做按支付类型的替换或清零
// 转换PayType为标准编码
String payType = convertPayTypeByDictionary(originalPayType, payTypeMap);
//操作员
@@ -197,10 +205,10 @@ public class HISGetDataMethodByJH {
//说明
String remarks = "";
//银商订单号优先使用PowerTranID如果为空则使用ReceiptNO或HisTransId
//银商订单号优先使用PowerTranID如果为空则使用ReceiptNO
String platformTransId = powerTranID;
if ("".equals(platformTransId)) {
platformTransId = "".equals(receiptNO) ? hisTransId : receiptNO;
platformTransId = receiptNO;
}
//患者姓名
@@ -209,7 +217,6 @@ public class HISGetDataMethodByJH {
//交易日期
String thistrade_date = StringDUtil.changeNullToEmpty(hisBillHashMap.get("trade_date"));
//接口厂商
HashMap<Object,Object> addMap=new HashMap<>();
addMap.put("payMethod",payMethod);
@@ -230,6 +237,7 @@ public class HISGetDataMethodByJH {
addMap.put("ybzhAmount",ybzhAmount); // 医保账户金额
addMap.put("ybtcAmount",ybtcAmount); // 医保统筹金额
addMap.put("zfAmount",zfAmount); // 自费金额(混合支付中的自费部分)
addMap.put("hisTransId",receiptNO); // HisTransId字段使用ReceiptNO的值
// 不再合并现金和支票记录,直接保留原始数据
// 因为有些医保账户余额不足时会用现金支付,但仍然属于医保相关记录

View File

@@ -70,15 +70,18 @@ public class MedicalInsuranceReconciliationMethod {
try {
LogUtil.info(this.getClass(), "开始执行医保对账,日期:" + trade_date);
LogUtil.info(this.getClass(), "开始查询医保数据(不限制PayType");
LogUtil.info(this.getClass(), "开始查询医保数据(仅统计PayType为9和10的记录包含账户支付和统筹支付按HisTransId去重合并统计");
// 1. 从数据库查询医保数据,按险种和清算类别分组统计不限制PayType
// 1. 从数据库查询医保数据,按险种和清算类别分组统计
// 只统计PayType为'9'(账户支付)和'10'(统筹支付)的记录
// 同一个HisTransId可能有多条记录账户支付和统筹支付需要合并统计金额
// 通过HisTransId去重确保每个交易只统计一次
HisDetailService hisDetailService = GetBeanUtil.getBean(HisDetailServiceImpl.class);
HashMap<Object, Object> queryMap = new HashMap<>();
queryMap.put("trade_date", trade_date);
// 查询所有有险种和清算类别的记录不限制PayType
// 查询医保数据仅统计PayType为9和10的记录按HisTransId去重合并统计
List<HashMap<Object, Object>> medicalRecords = hisDetailService.findMedicalInsuranceGroupData(queryMap);
if (medicalRecords == null || medicalRecords.size() == 0) {

View File

@@ -297,6 +297,7 @@ public class ReconciliationMethod {
manyToOneSearchMap.put("h_jylx", i_jylx);
manyToOneSearchMap.put("trade_date", thistrade_date);
manyToOneSearchMap.put("orderby_je", "true");
manyToOneSearchMap.put("payType", "2");
List<HashMap<Object, Object>> hisList = hisbillsHistoryService.findHisDetailByParam(manyToOneSearchMap);
@@ -634,6 +635,7 @@ public class ReconciliationMethod {
searchNotUniqueObjMap.put("orderby_je", "true");
searchNotUniqueObjMap.put("payType", "2");
// his端重复记录
List<HashMap<Object, Object>> list = hisbillsHistoryService.findHisDetailByParam(searchNotUniqueObjMap);

View File

@@ -0,0 +1,30 @@
package com.saye.hospitalgd.service;
import java.util.HashMap;
import java.util.List;
/**
* @author thuang
* @version 1.0
* @description: 住院账单查询服务接口
* @date 2025/11/21
*/
public interface InpatientBillService {
/**
* 查询HIS住院账单明细
*/
List<HashMap<Object, Object>> findHisInpatientDetail(HashMap<Object, Object> map) throws Exception;
/**
* 查询银行住院账单明细
*/
List<HashMap<Object, Object>> findBankInpatientDetail(HashMap<Object, Object> map) throws Exception;
/**
* 查询每日汇总数据
*/
List<HashMap<Object, Object>> findDailySummary(HashMap<Object, Object> map) throws Exception;
}

View File

@@ -0,0 +1,39 @@
package com.saye.hospitalgd.service.impl;
import com.saye.hospitalgd.mapper.InpatientBillMapper;
import com.saye.hospitalgd.service.InpatientBillService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.List;
/**
* @author thuang
* @version 1.0
* @description: 住院账单查询服务实现
* @date 2025/11/21
*/
@Service
public class InpatientBillServiceImpl implements InpatientBillService {
@Autowired
private InpatientBillMapper inpatientBillMapper;
@Override
public List<HashMap<Object, Object>> findHisInpatientDetail(HashMap<Object, Object> map) throws Exception {
return inpatientBillMapper.findHisInpatientDetail(map);
}
@Override
public List<HashMap<Object, Object>> findBankInpatientDetail(HashMap<Object, Object> map) throws Exception {
return inpatientBillMapper.findBankInpatientDetail(map);
}
@Override
public List<HashMap<Object, Object>> findDailySummary(HashMap<Object, Object> map) throws Exception {
return inpatientBillMapper.findDailySummary(map);
}
}

View File

@@ -247,22 +247,37 @@
</where>
</select>
<!-- 医保对账:按险种和清算类别分组统计(不限制PayType -->
<!-- 医保对账:按险种和清算类别分组统计(按ReceiptNO去重即HisTransId
1. 医疗总金额MEDFEE_SUMAMT统计同一收据号HisTransId下的自费总额取该收据号的ZFAmount并按组求和
2. 统筹金额FUND_PAY_SUMAMT和账户金额ACCT_PAY只统计PayType为9和10的记录按收据号合并
3. 按HisTransId去重合并统计金额 -->
<select id="findMedicalInsuranceGroupData" parameterType="HashMap" resultType="HashMap">
select
insutype as INSUTYPE,
clr_type as CLR_TYPE,
count(1) as FIXMEDINS_SETL_CNT,
cast(IFNULL(sum(Amount),0) as decimal(19,2)) as MEDFEE_SUMAMT,
cast(IFNULL(sum(ybtcAmount),0) as decimal(19,2)) as FUND_PAY_SUMAMT,
cast(IFNULL(sum(ybzhAmount),0) as decimal(19,2)) as ACCT_PAY
from hisbill_history
where trade_date = #{trade_date}
and insutype is not null
and insutype != ''
and clr_type is not null
and clr_type != ''
group by insutype, clr_type
order by insutype, clr_type
select
t.insutype as INSUTYPE,
t.clr_type as CLR_TYPE,
count(distinct t.HisTransId) as FIXMEDINS_SETL_CNT,
cast(IFNULL(sum(t.ybtc),0) + IFNULL(sum(t.ybzh),0) + IFNULL(sum(t.zf),0) as decimal(19,2)) as MEDFEE_SUMAMT,
cast(IFNULL(sum(t.ybtc),0) as decimal(19,2)) as FUND_PAY_SUMAMT,
cast(IFNULL(sum(t.ybzh),0) as decimal(19,2)) as ACCT_PAY
from (
select
insutype,
clr_type,
HisTransId,
max(cast(IFNULL(ybtcAmount,0) as decimal(19,2))) as ybtc,
max(cast(IFNULL(ybzhAmount,0) as decimal(19,2))) as ybzh,
max(cast(IFNULL(zfAmount,0) as decimal(19,2))) as zf
from hisbill_history
where trade_date = #{trade_date}
and insutype is not null
and insutype != ''
and clr_type is not null
and clr_type != ''
and HisTransId is not null
and HisTransId != ''
group by insutype, clr_type, HisTransId
) as t
group by t.insutype, t.clr_type
order by t.insutype, t.clr_type
</select>
</mapper>
</mapper>

View File

@@ -55,7 +55,9 @@
from hisbill_history
where trade_date=#{trade_date}
and PayMethod != '2' <!-- 排除PayMethod=2的记录不参与对账 -->
and PayMethod != '2'
and cast(IFNULL(ybzhAmount,0) as decimal(19,2)) = 0
and cast(IFNULL(ybtcAmount,0) as decimal(19,2)) = 0
<if test="military_code != null and military_code != ''">
and PayType != #{military_code}
</if>
@@ -68,6 +70,9 @@
<if test="military_payment_code != null and military_payment_code != ''">
and PayType != #{military_payment_code}
</if>
<if test="payType!=null and payType!=''">
and PayType = #{payType}
</if>
<if test="tranID!=null and tranID!=''">
and HisTransId=#{tranID}
</if>
@@ -140,4 +145,4 @@
)
</foreach>
</insert>
</mapper>
</mapper>

View File

@@ -0,0 +1,156 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.saye.hospitalgd.mapper.InpatientBillMapper">
<!-- 查询HIS住院账单明细 -->
<select id="findHisInpatientDetail" parameterType="HashMap" resultType="HashMap">
select HisOperCode,
PayMethod,
TradingStatus,
BizType,
PayType,
TradeTime,
Amount,
PlatformTransId,
HisTransId,
PatientId as PATIENTID,
PatientName,
trade_date
from hisbill_history
<where>
PayMethod = '2' <!-- 住院账单 -->
and PayType != '5' <!-- 排除现金支付 -->
and PayType != '3' <!-- 排除军保支付(医院垫支) -->
and PayType != '7' <!-- 排除预交金 -->
and PayType != '9' <!-- 排除统筹支付 -->
and PayType != '10' <!-- 排除账户支付 -->
and PayType != '8'
and PayType != '11'
<if test="military_payment_code != null and military_payment_code != ''">
and PayType != #{military_payment_code}
</if>
<if test="check_payment_code != null and check_payment_code != ''">
and PayType != #{check_payment_code}
</if>
<if test="startTime != null and startTime != ''">
and trade_date &gt;= #{startTime}
</if>
<if test="endTime != null and endTime != ''">
and trade_date &lt;= #{endTime}
</if>
<if test="payType != null and payType != ''">
and PayType = #{payType}
</if>
<if test="bizType != null and bizType != ''">
and BizType = #{bizType}
</if>
<if test="likeFiled != null and likeFiled != ''">
and (PlatformTransId like concat('%', #{likeFiled}, '%')
or HisTransId like concat('%', #{likeFiled}, '%')
or PatientId like concat('%', #{likeFiled}, '%')
or PatientName like concat('%', #{likeFiled}, '%'))
</if>
</where>
order by trade_date desc, TradeTime desc
</select>
<!-- 查询银行住院账单明细 -->
<select id="findBankInpatientDetail" parameterType="HashMap" resultType="HashMap">
select C_JYRQ,
C_JYSJ,
C_QSRQ,
C_LSH,
C_SHDDH,
C_YSDDH,
C_JYLX,
C_CARD,
C_FKH,
C_JYJE,
C_QSJE,
C_SXF,
C_SJZFJE,
C_ZDH,
C_ZFFS,
C_KLX
from bankbill_history
<where>
is_inpatient = '1' <!-- 住院订单 -->
<if test="startTime != null and startTime != ''">
and C_JYRQ &gt;= #{startTime}
</if>
<if test="endTime != null and endTime != ''">
and C_JYRQ &lt;= #{endTime}
</if>
<if test="c_zffs != null and c_zffs != ''">
and C_ZFFS = #{c_zffs}
</if>
<if test="likeFiled != null and likeFiled != ''">
and (C_YSDDH like concat('%', #{likeFiled}, '%')
or C_SHDDH like concat('%', #{likeFiled}, '%')
or C_LSH like concat('%', #{likeFiled}, '%'))
</if>
</where>
order by C_JYRQ desc, C_JYSJ desc
</select>
<!-- 查询每日汇总数据(按日期汇总 HIS 与 银行住院账单,各一条记录) -->
<select id="findDailySummary" parameterType="HashMap" resultType="HashMap">
select
t.TRADE_DATE,
sum(t.HIS_COUNT) as HIS_COUNT,
sum(t.HIS_AMOUNT) as HIS_AMOUNT,
sum(t.BANK_COUNT) as BANK_COUNT,
sum(t.BANK_AMOUNT) as BANK_AMOUNT
from (
-- HIS 住院汇总
select
trade_date as TRADE_DATE,
count(distinct PlatformTransId) as HIS_COUNT,
ifnull(sum(cast(Amount as decimal(18,2))), 0) as HIS_AMOUNT,
0 as BANK_COUNT,
0 as BANK_AMOUNT
from hisbill_history
where PayMethod = '2'
and PayType != '5' <!-- 排除现金支付 -->
and PayType != '3' <!-- 排除军保支付(医院垫支) -->
and PayType != '7' <!-- 排除预交金 -->
and PayType != '9' <!-- 排除统筹支付 -->
and PayType != '10' <!-- 排除账户支付 -->
<if test="military_payment_code != null and military_payment_code != ''">
and PayType != #{military_payment_code}
</if>
<if test="check_payment_code != null and check_payment_code != ''">
and PayType != #{check_payment_code}
</if>
<if test="startTime != null and startTime != ''">
and trade_date &gt;= #{startTime}
</if>
<if test="endTime != null and endTime != ''">
and trade_date &lt;= #{endTime}
</if>
group by trade_date
union all
-- 银行住院汇总
select
C_JYRQ as TRADE_DATE,
0 as HIS_COUNT,
0 as HIS_AMOUNT,
count(1) as BANK_COUNT,
ifnull(sum(cast(C_JYJE as decimal(18,2))), 0) as BANK_AMOUNT
from bankbill_history
where is_inpatient = '1'
<if test="startTime != null and startTime != ''">
and C_JYRQ &gt;= #{startTime}
</if>
<if test="endTime != null and endTime != ''">
and C_JYRQ &lt;= #{endTime}
</if>
group by C_JYRQ
) t
group by t.TRADE_DATE
order by t.TRADE_DATE desc
</select>
</mapper>

View File

@@ -123,6 +123,8 @@
, '1'
from (select * from hisbill_history where trade_date = #{trade_date} and payType!=#{cash_code}
and PayMethod != '2' -- 排除不参与对账的记录
and PayType != '9' -- 排除账户支付,不参与常规对账
and PayType != '10' -- 排除统筹支付,不参与常规对账
<if test="military_code != null and military_code != ''">
and payType!=#{military_code}
</if>
@@ -167,6 +169,8 @@
, '1'
from (select * from hisbill_history where trade_date = #{trade_date} and payType!=#{cash_code}
and PayMethod != '2' -- 排除不参与对账的记录
and PayType != '9' -- 排除账户支付,不参与常规对账
and PayType != '10' -- 排除统筹支付,不参与常规对账
<if test="military_code != null and military_code != ''">
and payType!=#{military_code}
</if>
@@ -212,6 +216,8 @@
, '1'
from (select * from hisbill_history where trade_date = #{trade_date} and payType!=#{cash_code}
and PayMethod != '2' -- 排除不参与对账的记录
and PayType != '9' -- 排除账户支付,不参与常规对账
and PayType != '10' -- 排除统筹支付,不参与常规对账
<if test="military_code != null and military_code != ''">
and payType!=#{military_code}
</if>

View File

@@ -0,0 +1,419 @@
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>住院账单查询</title>
<link rel="stylesheet" th:href="@{/layui/css/layui.css}">
<link rel="stylesheet" th:href="@{/css/common.css}">
<!-- 引入组件库 -->
<script th:src="@{/layui/jquery-3.4.1.min.js}"></script>
<script th:src="@{/layui/layui.js}"></script>
<script th:src="@{/layui/lay/xmSelect/xm-select.js}"></script>
<script th:src="@{/js/common.js}"></script>
<style>
body {
background: #f2f2f2
}
#boxDiv {
background: #ffffff
}
#titleDiv {
overflow: visible;
}
.content {
padding: 0 8px;
}
.tableTitle {
height: 20px;
margin-top: 10px;
margin-bottom: 10px;
}
.tableTitle > * {
height: 20px;
line-height: 20px;
display: inline-block;
margin-right: 5px;
}
.tableName {
border-left: 3px solid #0000FF;
padding-left: 5px;
font-size: 16px;
font-weight: bold;
}
.selectDate {
font-size: 14px;
color: rgba(0, 0, 0, 0.65)
}
</style>
</head>
<body class="layui-layout-body">
<div id="boxDiv" style="left:8px;right: 8px;bottom:8px;top:8px;position:absolute;">
<div class="toolbar" id="titleDiv">
<div style="display: inline-block;">
<form class="layui-form">
<div class="layui-inline">
<label class="layui-form-label">日期选择</label>
<div class="layui-input-inline">
<input type="text" class="layui-input formWidthTwo" id="searchDate" placeholder=" ~ "
th:value="${startTime+' ~ '+endTime}">
</div>
</div>
</form>
</div>
<div class="layui-inline">
<button class="layui-btn layui-btn-sm layui-btn-normal" data-type="search" onclick="search()"><i
class="layui-icon layui-icon-search"></i>查询
</button>
<button class="layui-btn layui-btn-sm layui-btn-normal" onclick="exportHisExcel()"><i class="layui-icon">&#xe67d;</i>导出HIS账单
</button>
<button class="layui-btn layui-btn-sm layui-btn-normal" onclick="exportBankExcel()"><i class="layui-icon">&#xe67d;</i>导出银行账单
</button>
</div>
</div>
<div class="content">
<div class="tableTitle">
<span class="tableName">每日汇总</span>
<span class="selectDate">&nbsp;</span>
</div>
<table id="summaryTable" lay-filter="summaryTable"></table>
<div class="tableTitle" style="margin-top: 30px;">
<span class="tableName">HIS住院账单明细</span>
<span class="selectDate">&nbsp;</span>
</div>
<table id="hisTable" lay-filter="hisTable"></table>
<div class="tableTitle" style="margin-top: 30px;">
<span class="tableName">银行住院账单明细</span>
<span class="selectDate">&nbsp;</span>
</div>
<table id="bankTable" lay-filter="bankTable"></table>
</div>
</div>
<script th:inline="javascript">
let payTypeList = [[${payTypeList}]];
let bizTypeList = [[${bizTypeList}]];
let thirdPayList = [[${thirdPayList}]];
// 兜底防止未传入字典导致脚本报错
payTypeList = payTypeList || [];
bizTypeList = bizTypeList || [];
thirdPayList = thirdPayList || [];
</script>
<script th:inline="none">
let layer, laydate, table, form;
layui.use(['element', 'table', 'laydate', 'layer', 'form'], function () {
layer = layui.layer;
table = layui.table;
laydate = layui.laydate;
form = layui.form;
// 每日汇总表格
table.render({
elem: '#summaryTable',
height: 300,
title: '每日汇总',
page: false,
id: 'summaryTable',
even: true,
cols: [[
{field: 'TRADE_DATE', align: 'center', title: '交易日期', width: 150, sort: false},
{field: 'HIS_COUNT', align: 'center', title: 'HIS账单笔数', width: 150, sort: false},
{field: 'HIS_AMOUNT', align: 'center', title: 'HIS账单金额', width: 150, sort: false, templet: function(d) {
return d.HIS_AMOUNT ? parseFloat(d.HIS_AMOUNT).toFixed(2) : '0.00';
}},
{field: 'BANK_COUNT', align: 'center', title: '银行账单笔数', width: 150, sort: false},
{field: 'BANK_AMOUNT', align: 'center', title: '银行账单金额', width: 150, sort: false, templet: function(d) {
return d.BANK_AMOUNT ? parseFloat(d.BANK_AMOUNT).toFixed(2) : '0.00';
}},
{field: 'DIFF_AMOUNT', align: 'center', title: '差额', width: 150, sort: false, templet: function(d) {
let hisAmount = d.HIS_AMOUNT ? parseFloat(d.HIS_AMOUNT) : 0;
let bankAmount = d.BANK_AMOUNT ? parseFloat(d.BANK_AMOUNT) : 0;
let diff = hisAmount - bankAmount;
return diff.toFixed(2);
}}
]],
data: []
});
// HIS住院账单表格
table.render({
elem: '#hisTable',
height: 'full-' + ($(".toolbar").height() + 400),
title: 'HIS住院账单明细',
page: true,
limit: 20,
limits: [20, 30, 50],
defaultToolbar: [],
id: 'hisTable',
even: true,
cols: [[
{field: 'HISOPERCODE', align: 'center', title: '操作员', width: 120, sort: false},
{
field: 'TRADINGSTATUS',
align: 'center',
title: '交易状态',
width: 120,
sort: false,
templet: function (d) {
if (d.TRADINGSTATUS === '1') {
return '收款记录';
} else if (d.TRADINGSTATUS === '2') {
return '退款记录';
}
return '';
}
},
{
field: 'BIZTYPE',
align: 'center',
title: '业务类型',
width: 120,
sort: false,
templet: function (d) {
let result = "";
for (let i = 0; i < bizTypeList.length; i++) {
let obj = bizTypeList[i];
if (d.BIZTYPE === obj.dicvalue) {
result = obj.dicname;
break;
}
}
return result;
}
},
{
field: 'PAYTYPE',
align: 'center',
title: '支付方式',
width: 120,
sort: false,
templet: function (d) {
let result = "";
for (let i = 0; i < payTypeList.length; i++) {
let obj = payTypeList[i];
if (d.PAYTYPE === obj.dicvalue) {
result = obj.dicname;
break;
}
}
return result || d.PAYTYPE;
}
},
{field: 'TRADETIME', align: 'center', title: '交易时间', width: 150, sort: false},
{field: 'TRADE_DATE', align: 'center', title: '交易日期', width: 120, sort: false},
{field: 'AMOUNT', align: 'center', title: '金额', width: 120, sort: false},
{field: 'PLATFORMTRANSID', align: 'center', title: '平台订单号', width: 180, sort: false},
{field: 'HISTRANSID', align: 'center', title: 'HIS订单号', width: 180, sort: false},
{field: 'PATIENTID', align: 'center', title: '患者ID', width: 120, sort: false},
{field: 'PATIENTNAME', align: 'center', title: '患者姓名', width: 120, sort: false}
]],
data: []
});
// 银行住院账单表格
table.render({
elem: '#bankTable',
height: 'full-' + ($(".toolbar").height() + 400),
title: '银行住院账单明细',
page: true,
limit: 20,
limits: [20, 30, 50],
defaultToolbar: [],
id: 'bankTable',
even: true,
cols: [[
{field: 'C_JYRQ', align: 'center', title: '交易日期', width: 120, sort: false},
{field: 'C_JYSJ', align: 'center', title: '交易时间', width: 120, sort: false},
{field: 'C_QSRQ', align: 'center', title: '清算日期', width: 120, sort: false},
{field: 'C_LSH', align: 'center', title: '流水号', width: 150, sort: false},
{field: 'C_SHDDH', align: 'center', title: '商户订单号', width: 180, sort: false},
{field: 'C_YSDDH', align: 'center', title: '银商订单号', width: 180, sort: false},
{
field: 'C_JYLX',
align: 'center',
title: '交易类型',
width: 120,
sort: false,
templet: function (d) {
if (d.C_JYLX === '1') {
return '收款记录';
} else if (d.C_JYLX === '2') {
return '退款记录';
}
return '';
}
},
{field: 'C_CARD', align: 'center', title: '卡号', width: 150, sort: false},
{field: 'C_FKH', align: 'center', title: '发卡行', width: 120, sort: false},
{field: 'C_JYJE', align: 'center', title: '交易金额', width: 120, sort: false},
{field: 'C_QSJE', align: 'center', title: '清算金额', width: 120, sort: false},
{field: 'C_SXF', align: 'center', title: '手续费', width: 120, sort: false},
{field: 'C_SJZFJE', align: 'center', title: '实际支付金额', width: 120, sort: false},
{field: 'C_ZDH', align: 'center', title: '终端号', width: 120, sort: false},
{
field: 'C_ZFFS',
align: 'center',
title: '支付方式',
width: 120,
sort: false,
templet: function (d) {
let result = "";
for (let i = 0; i < thirdPayList.length; i++) {
let obj = thirdPayList[i];
if (d.C_ZFFS === obj.dicvalue) {
result = obj.dicname;
break;
}
}
return result || d.C_ZFFS;
}
},
{field: 'C_KLX', align: 'center', title: '卡类型', width: 120, sort: false}
]],
data: []
});
//时间控件
laydate.render({
elem: '#searchDate'
, type: 'date'
, range: '~'
});
search();
});
//查询
function search(num) {
if (typeof table === 'undefined') { // 确保table模块已加载
return;
}
num = num === null ? 1 : num;
let date = $("#searchDate").val();
let param = {};
if (date !== '') {
let time = date.split("~");
let startTime = time[0].trim();
let endTime = time[1].trim();
param.startTime = startTime;
param.endTime = endTime;
$(".selectDate").text(startTime + " ~ " + endTime);
}
// 查询每日汇总
$.ajax({
type: "get",
url: "/inpatientBill/findDailySummary",
data: param,
dataType: "json",
success: function (data) {
if (data.code === 0) {
table.reload('summaryTable', {
data: data.data
});
}
},
error: function (xhr, status, error) {
console.error('查询每日汇总失败:', error);
}
});
// 查询HIS住院账单
table.reload('hisTable', {
method: 'get',
url: '/inpatientBill/findHisInpatientDetail',
where: param,
page: {
curr: num
}
});
// 查询银行住院账单
table.reload('bankTable', {
method: 'get',
url: '/inpatientBill/findBankInpatientDetail',
where: param,
page: {
curr: num
}
});
}
//导出HIS账单
function exportHisExcel() {
if (typeof layer === 'undefined') { // 确保layer模块已加载
return;
}
let url = "/inpatientBill/exportHisInpatientDetail";
let param = {};
let date = $("#searchDate").val();
if (date !== '') {
let time = date.split("~");
let startTime = time[0].trim();
let endTime = time[1].trim();
param.startTime = startTime;
param.endTime = endTime;
}
let dowloadName = "HIS住院账单明细";
param.dowloadName = dowloadName;
let load = layer.load();
AjaxPostJson(url, param, function (data) {
layer.close(load);
if (data.errCode === "0") {
let fileName = data.dlName;
location.href = '/download?fileName=' + fileName + '&dowloadName=' + dowloadName;
} else {
layer.alert(data.errMsg);
}
});
}
//导出银行账单
function exportBankExcel() {
if (typeof layer === 'undefined') { // 确保layer模块已加载
return;
}
let url = "/inpatientBill/exportBankInpatientDetail";
let param = {};
let date = $("#searchDate").val();
if (date !== '') {
let time = date.split("~");
let startTime = time[0].trim();
let endTime = time[1].trim();
param.startTime = startTime;
param.endTime = endTime;
}
let dowloadName = "银行住院账单明细";
param.dowloadName = dowloadName;
let load = layer.load();
AjaxPostJson(url, param, function (data) {
layer.close(load);
if (data.errCode === "0") {
let fileName = data.dlName;
location.href = '/download?fileName=' + fileName + '&dowloadName=' + dowloadName;
} else {
layer.alert(data.errMsg);
}
});
}
</script>
</body>
</html>