update:医保对账

This commit is contained in:
Yuan
2025-10-27 08:49:28 +08:00
parent 94e8850a40
commit 3e9a25dd38
32 changed files with 2134 additions and 38 deletions

View File

@@ -0,0 +1,150 @@
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.log.LogUtil;
import com.saye.hospitalgd.scheduler.jobMethod.MedicalInsuranceReconciliationMethod;
import com.saye.hospitalgd.service.MedicalInsuranceReconciliationService;
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.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
/**
* @author thuang
* @version 1.0
* @description: 医保对账Controller
* @date 2025/10/24
*/
@Api(value = "医保对账相关接口")
@Controller
@RequestMapping("/medicalInsuranceReconciliation")
public class MedicalInsuranceReconciliationController {
@Autowired
private MedicalInsuranceReconciliationService medicalInsuranceReconciliationService;
/**
* 跳转到医保对账页面
*/
@RequestMapping("/toMedicalInsuranceReconciliation")
public String toMedicalInsuranceReconciliation(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);
return "financialReconciliation/medicalInsuranceReconciliation";
}
/**
* 跳转到医保对账结果页面
*/
@RequestMapping("/toMedicalInsuranceReconciliationResult")
public String toMedicalInsuranceReconciliationResult(ModelMap modelMap) {
return "financialReconciliation/medicalInsuranceReconciliationResult";
}
/**
* 执行医保对账
*/
@ApiOperation(value = "执行医保对账", notes = "")
@PostMapping("/executeMedicalInsuranceReconciliation")
@ResponseBody
public HashMap<Object, Object> executeMedicalInsuranceReconciliation(
@ApiParam(value = "对账日期") @RequestParam String trade_date) {
HashMap<Object, Object> responseMap = new HashMap<>();
try {
LogUtil.info(this.getClass(), "开始执行医保对账,日期:" + trade_date);
MedicalInsuranceReconciliationMethod reconciliationMethod = new MedicalInsuranceReconciliationMethod();
HashMap<Object, Object> result = reconciliationMethod.executeMedicalInsuranceReconciliation(trade_date);
responseMap.put("code", "0".equals(result.get("errCode")) ? 0 : 1);
responseMap.put("msg", result.get("errMsg"));
responseMap.put("data", result.get("reconciliationResults"));
} catch (Exception e) {
e.printStackTrace();
LogUtil.error(this.getClass(), "医保对账失败:" + e.getMessage());
responseMap.put("code", 1);
responseMap.put("msg", "医保对账失败:" + e.getMessage());
}
return responseMap;
}
/**
* 查询医保对账结果
*/
@ApiOperation(value = "查询医保对账结果", notes = "")
@GetMapping("/findMedicalInsuranceReconciliationResult")
@ResponseBody
public HashMap<Object, Object> findMedicalInsuranceReconciliationResult(
@ApiParam(value = "开始日期") String startDate,
@ApiParam(value = "结束日期") String endDate,
@ApiParam(value = "险种类型") String insutype,
@ApiParam(value = "清算类别") String clrType,
@ApiParam(value = "对账结果") String stmtRslt,
@ApiParam(value = "页码") int page,
@ApiParam(value = "每页数量") int limit) {
HashMap<Object, Object> responseMap = new HashMap<>();
try {
HashMap<Object, Object> queryMap = new HashMap<>();
if (startDate != null && !"".equals(startDate)) {
queryMap.put("startDate", startDate);
}
if (endDate != null && !"".equals(endDate)) {
queryMap.put("endDate", endDate);
}
if (insutype != null && !"".equals(insutype)) {
queryMap.put("insutype", insutype);
}
if (clrType != null && !"".equals(clrType)) {
queryMap.put("clr_type", clrType);
}
if (stmtRslt != null && !"".equals(stmtRslt)) {
queryMap.put("stmt_rslt", stmtRslt);
}
PageHelper.startPage(page, limit);
PageInfo<HashMap<Object, Object>> pageInfo = new PageInfo<>(
medicalInsuranceReconciliationService.findMedicalInsuranceReconciliationResult(queryMap)
);
responseMap.put("code", 0);
responseMap.put("msg", "OK");
responseMap.put("count", pageInfo.getTotal());
responseMap.put("data", pageInfo.getList());
} catch (Exception e) {
e.printStackTrace();
LogUtil.error(this.getClass(), "查询医保对账结果失败:" + e.getMessage());
responseMap.put("code", 1);
responseMap.put("msg", "查询失败:" + e.getMessage());
}
return responseMap;
}
}

View File

@@ -141,13 +141,7 @@ public class MilitaryInsuranceController {
map.put("endTime", endTime.substring(0, 10)); // 提取日期部分
}
// 添加调试信息
System.out.println("军保统计查询参数: " + map.toString());
List<HashMap<Object, Object>> hisDetailCount = hisDetailService.findHisDetailCountData(map);
// 添加调试信息
System.out.println("军保统计查询结果: " + (hisDetailCount != null ? hisDetailCount.toString() : "null"));
if (hisDetailCount != null && hisDetailCount.size() > 0) {
responseMap.put("money", hisDetailCount.get(0).get("MONEY"));
@@ -236,8 +230,8 @@ public class MilitaryInsuranceController {
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", "SOURCE"};
String[] DISTANCE_HEADERNAME = {"交易状态", "业务类型", "支付方式", "交易时间", "交易日期", "操作员", "总金额", "平台交易号", "his订单号", "HIS交易ID", "患者id", "患者姓名", "来源", "清算类别", "险种类型"};
String[] sqlKey = {"TRADINGSTATUS", "BIZTYPE", "PAYTYPE", "TRADETIME", "TRADE_DATE", "HISOPERCODE", "AMOUNT", "PLATFORMTRANSID", "HISTRANSID", "HISTRANSID", "PATIENTID", "PATIENTNAME", "SOURCE", "CLR_TYPE", "INSUTYPE"};
List<Object> rulList = new ArrayList<Object>(list);
@@ -263,6 +257,9 @@ public class MilitaryInsuranceController {
exportXLS.modifyWidthOfHeader("5000", 9);
exportXLS.modifyWidthOfHeader("5000", 10);
exportXLS.modifyWidthOfHeader("10000", 11);
exportXLS.modifyWidthOfHeader("5000", 12); // 来源
exportXLS.modifyWidthOfHeader("5000", 13); // 清算类别
exportXLS.modifyWidthOfHeader("5000", 14); // 险种类型
// 文件名称
// 产生4位长度的随机码由字母和数字组成

View File

@@ -144,6 +144,10 @@ public class PaymentStatisticsController {
LinkedHashMap<String, String> zffsMap = numMap.get(str_zffs);
if (zffsMap != null) {
String countNum = zffsMap.get(c_jyrq);
// 安全处理countNum如果为null或空字符串则默认为"0"
if (countNum == null || countNum.isEmpty()) {
countNum = "0";
}
countNum = new BigDecimal(countNum).add(new BigDecimal(num1)).toString();
zffsMap.put(c_jyrq, countNum);
}
@@ -181,6 +185,10 @@ public class PaymentStatisticsController {
LinkedHashMap<String, String> zffsMap = moneyMap.get(str_zffs);
if (zffsMap != null) {
String countNum = zffsMap.get(c_jyrq);
// 安全处理countNum如果为null或空字符串则默认为"0"
if (countNum == null || countNum.isEmpty()) {
countNum = "0";
}
countNum = new BigDecimal(countNum).add(new BigDecimal(money)).toString();
zffsMap.put(c_jyrq, countNum);
}
@@ -316,32 +324,86 @@ public class PaymentStatisticsController {
String amount = StringDUtil.changeNullToEmpty(hashMap.get("AMOUNT")).trim();
String paytype = StringDUtil.changeNullToEmpty(hashMap.get("PAYTYPE")).trim();
// 数据核准确保PayType在预定义范围内
if (!hisMoneyData.containsKey(paytype)) {
log.warn("发现未预定义的PayType: [{}], 数据: {}, 已归类为其他支付方式", paytype, hashMap);
paytype = "4"; // 归类为其他支付方式
}
// 数据核准确保I_JYQD在预定义范围内
if (!thirdMoneyData.containsKey(i_zffs)) {
log.warn("发现未预定义的I_JYQD: [{}], 数据: {}, 已归类为其他支付方式", i_zffs, hashMap);
i_zffs = "5"; // 归类为其他支付方式
}
// 如果银行端金额不为空
if (StrUtil.isNotBlank(i_jyje)) {
log.info("hashMap is :" + hashMap);
log.info("aaa is : " + thirdMoneyData.get(i_zffs));
BigDecimal money = Convert.toBigDecimal(thirdMoneyData.get(i_zffs));
money = money.add(new BigDecimal(i_jyje));
thirdMoneyData.put(i_zffs, money.toString());
try {
log.info("处理银行端数据: i_zffs={}, i_jyje={}", i_zffs, i_jyje);
// 验证金额格式
BigDecimal transAmount = new BigDecimal(i_jyje);
if (transAmount.compareTo(BigDecimal.ZERO) < 0) {
log.warn("发现负数金额: i_jyje={}, 数据: {}", i_jyje, hashMap);
}
// 安全获取当前金额如果为null则默认为BigDecimal.ZERO
BigDecimal money = Convert.toBigDecimal(thirdMoneyData.get(i_zffs), BigDecimal.ZERO);
money = money.add(transAmount);
thirdMoneyData.put(i_zffs, money.toString());
int num = Integer.parseInt(StringDUtil.changeNullToEmpty(thirdNumData.get(i_zffs)));
num++;
thirdNumData.put(i_zffs, num + "");
// 安全获取当前数量如果为null或空字符串则默认为"0"
String currentNumStr = StringDUtil.changeNullToEmpty(thirdNumData.get(i_zffs));
if (currentNumStr.isEmpty()) {
currentNumStr = "0";
}
int num = Integer.parseInt(currentNumStr);
num++;
thirdNumData.put(i_zffs, num + "");
} catch (NumberFormatException e) {
log.error("银行端金额格式错误: i_jyje=[{}], 数据: {}, 错误: {}", i_jyje, hashMap, e.getMessage());
}
}
// 如果his端金额不为空
if (StrUtil.isNotBlank(amount)) {
log.info("hashMap is :" + hashMap);
BigDecimal money = new BigDecimal(StringDUtil.changeNullToEmpty(hisMoneyData.get(paytype)));
money = money.add(new BigDecimal(amount));
hisMoneyData.put(paytype, money.toString());
try {
log.info("处理HIS端数据: paytype={}, amount={}", paytype, amount);
// 验证金额格式
BigDecimal transAmount = new BigDecimal(amount);
if (transAmount.compareTo(BigDecimal.ZERO) < 0) {
log.warn("发现负数金额: amount={}, 数据: {}", amount, hashMap);
}
// 安全获取当前金额如果为null或空字符串则默认为"0"
String currentMoneyStr = StringDUtil.changeNullToEmpty(hisMoneyData.get(paytype));
if (currentMoneyStr.isEmpty()) {
currentMoneyStr = "0";
}
BigDecimal money = new BigDecimal(currentMoneyStr);
money = money.add(transAmount);
hisMoneyData.put(paytype, money.toString());
int num = Integer.parseInt(StringDUtil.changeNullToEmpty(hisNumData.get(paytype)));
num++;
hisNumData.put(paytype, num + "");
// 安全获取当前数量如果为null或空字符串则默认为"0"
String currentNumStr = StringDUtil.changeNullToEmpty(hisNumData.get(paytype));
if (currentNumStr.isEmpty()) {
currentNumStr = "0";
}
int num = Integer.parseInt(currentNumStr);
num++;
hisNumData.put(paytype, num + "");
} catch (NumberFormatException e) {
log.error("HIS端金额格式错误: amount=[{}], 数据: {}, 错误: {}", amount, hashMap, e.getMessage());
}
}
}

View File

@@ -48,6 +48,17 @@ public class BaseQuartzConfigController{
@Autowired
private DicinfoService dicinfoService;
/**
* @description 跳转到定时任务管理页面
* @author thuang
* @created 2025/10/24
* @return
*/
@RequestMapping("/toQuartzManage")
public String toQuartzManage() {
return "system/quartzManage";
}
/**
* @description 查询所有的定时任务
* @author thuang

View File

@@ -23,4 +23,8 @@ public class HisbillsHistory {
private String PatientName;
private String source;
private String trade_date;
private String clrType; // 清算类别
private String insutype; // 险种类型
private String ybzhAmount; // 医保账户金额
private String ybtcAmount; // 医保统筹金额
}

View File

@@ -0,0 +1,29 @@
package com.saye.hospitalgd.entity;
import lombok.Data;
/**
* @author thuang
* @version 1.0
* @description: 医保对账结果实体类
* @date 2025/10/24
*/
@Data
public class MedicalInsuranceReconciliationResult {
private String id; // 主键ID
private String tradeDate; // 对账日期
private String insutype; // 险种类型
private String clrType; // 清算类别
private String medfee_sumamt; // 医疗费用总额
private String fund_pay_sumamt; // 基金支付总额(医保统筹)
private String acct_pay; // 账户支付金额(医保账户)
private String fixmedins_setl_cnt; // 定点医药机构结算笔数
private String stmt_rslt; // 对账结果0-成功1-失败)
private String stmt_rslt_dscr; // 对账结果描述
private String apiResult; // 接口调用结果
private String createTime; // 创建时间
private String modifyTime; // 修改时间
}

View File

@@ -41,4 +41,6 @@ public interface HisDetailMapper {
void deleteHisBillOriginalHYByParam(HashMap<Object, Object> deleteMap) throws Exception;
void insertAllHisBillOriginalHY(List<HashMap<Object, Object>> addList) throws Exception;
List<HashMap<Object, Object>> findMedicalInsuranceGroupData(HashMap<Object, Object> map) throws Exception;
}

View File

@@ -0,0 +1,38 @@
package com.saye.hospitalgd.mapper;
import org.apache.ibatis.annotations.Mapper;
import java.util.HashMap;
import java.util.List;
/**
* @author thuang
* @version 1.0
* @description: 医保对账Mapper
* @date 2025/10/24
*/
@Mapper
public interface MedicalInsuranceReconciliationMapper {
/**
* 插入医保对账结果
*/
void insertMedicalInsuranceReconciliationResult(HashMap<Object, Object> map) throws Exception;
/**
* 批量插入医保对账结果
*/
void insertMedicalInsuranceReconciliationResultBatch(List<HashMap<Object, Object>> list) throws Exception;
/**
* 查询医保对账结果
*/
List<HashMap<Object, Object>> findMedicalInsuranceReconciliationResult(HashMap<Object, Object> map) throws Exception;
/**
* 删除医保对账结果
*/
void deleteMedicalInsuranceReconciliationResult(HashMap<Object, Object> map) throws Exception;
}

View File

@@ -72,4 +72,5 @@ public interface FinanceUserMapper {

View File

@@ -6,6 +6,7 @@ public class BaseQuartzConfigEntity {
private String quartzGroup;
private String status;
private String quartzClass;
private String quartzType;
private String remark;
private String createUserId;
private String expression;
@@ -41,6 +42,12 @@ public class BaseQuartzConfigEntity {
public void setQuartzClass(String quartzClass) {
this.quartzClass = quartzClass;
}
public String getQuartzType() {
return quartzType;
}
public void setQuartzType(String quartzType) {
this.quartzType = quartzType;
}
public String getRemark() {
return remark;
}

View File

@@ -40,4 +40,5 @@ public class FinanceUser {

View File

@@ -0,0 +1,58 @@
package com.saye.hospitalgd.scheduler.job;
import com.saye.hospitalgd.commons.log.LogUtil;
import com.saye.hospitalgd.scheduler.jobMethod.MedicalInsuranceReconciliationMethod;
import org.quartz.Job;
import org.quartz.JobDataMap;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import java.util.HashMap;
/**
* @author yuan
* @version 1.0
* @description: 医保对账定时任务
* @date 2025/10/24
*/
public class MedicalInsuranceReconciliationJob implements Job {
@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
JobDataMap jobDataMap = jobExecutionContext.getMergedJobDataMap();
String id = jobDataMap.getString("id");
String name = jobDataMap.getString("name");
String trade_date = jobDataMap.getString("trade_date");
// 如果没有指定对账日期,使用前一天的日期(医保对账通常对前一天的数据)
if (trade_date == null || trade_date.trim().isEmpty()) {
java.util.Calendar calendar = java.util.Calendar.getInstance();
calendar.add(java.util.Calendar.DATE, -1); // 前一天
java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat("yyyy-MM-dd");
trade_date = sdf.format(calendar.getTime());
LogUtil.info(this.getClass(), "未指定对账日期,自动使用前一天日期:" + trade_date);
}
LogUtil.info(this.getClass(), "医保对账定时任务【" + name + "】开始执行,日期:" + trade_date);
try {
MedicalInsuranceReconciliationMethod method = new MedicalInsuranceReconciliationMethod();
HashMap<Object, Object> result = method.executeMedicalInsuranceReconciliation(trade_date);
String errCode = (String) result.get("errCode");
if ("0".equals(errCode)) {
LogUtil.info(this.getClass(), "医保对账定时任务【" + name + "】执行成功");
} else {
LogUtil.error(this.getClass(), "医保对账定时任务【" + name + "】执行失败:" + result.get("errMsg"));
}
} catch (Exception e) {
e.printStackTrace();
LogUtil.error(this.getClass(), "医保对账定时任务【" + name + "】执行异常:" + e.getMessage());
throw new JobExecutionException(e);
}
}
}

View File

@@ -158,6 +158,12 @@ public class HISGetDataMethodByJH {
String amount = StringDUtil.changeNullToEmpty(hisBillHashMap.get("amount"));
String tradeTime = StringDUtil.changeNullToEmpty(hisBillHashMap.get("tradeTime"));
// 提取新增字段:清算类别、险种类型、医保账户金额、医保统筹金额
String clrType = StringDUtil.changeNullToEmpty(hisBillHashMap.get("clrType"));
String insutype = StringDUtil.changeNullToEmpty(hisBillHashMap.get("insutype"));
String ybzhAmount = StringDUtil.changeNullToEmpty(hisBillHashMap.get("ybzhAmount"));
String ybtcAmount = StringDUtil.changeNullToEmpty(hisBillHashMap.get("ybtcAmount"));
// 只有当缺少关键业务信息时才跳过患者ID、金额、交易时间都为空
if ("".equals(patientId) && "".equals(amount) && "".equals(tradeTime)){
log.warn("跳过缺少关键业务信息的记录: powerTranID={}, receiptNO={}, hisTransId={}",
@@ -222,6 +228,10 @@ public class HISGetDataMethodByJH {
addMap.put("patientName",patientName);
addMap.put("trade_date",thistrade_date);
addMap.put("his_wsdl_id",his_wsdl_id);
addMap.put("clrType",clrType); // 清算类别
addMap.put("insutype",insutype); // 险种类型
addMap.put("ybzhAmount",ybzhAmount); // 医保账户金额
addMap.put("ybtcAmount",ybtcAmount); // 医保统筹金额
//如果是现金记录先放进现金集合内
if (cash_code.equals(payType)){
@@ -259,6 +269,10 @@ public class HISGetDataMethodByJH {
addMap.put("patientName","");
addMap.put("trade_date",trade_date);
addMap.put("his_wsdl_id",his_wsdl_id);
addMap.put("clrType",""); // 现金记录没有清算类别
addMap.put("insutype",""); // 现金记录没有险种类型
addMap.put("ybzhAmount","0"); // 现金记录没有医保账户金额
addMap.put("ybtcAmount","0"); // 现金记录没有医保统筹金额
cashMap.put(hisOperCode+"_"+payMethod,addMap);
}else {
@@ -300,6 +314,10 @@ public class HISGetDataMethodByJH {
addMap.put("patientName","");
addMap.put("trade_date",trade_date);
addMap.put("his_wsdl_id",his_wsdl_id);
addMap.put("clrType",""); // 支票记录没有清算类别
addMap.put("insutype",""); // 支票记录没有险种类型
addMap.put("ybzhAmount","0"); // 支票记录没有医保账户金额
addMap.put("ybtcAmount","0"); // 支票记录没有医保统筹金额
zhipiaoMap.put(hisOperCode+"_"+payMethod,addMap);
}else {

View File

@@ -0,0 +1,355 @@
package com.saye.hospitalgd.scheduler.jobMethod;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.saye.hospitalgd.commons.date.DateDUtil;
import com.saye.hospitalgd.commons.getBean.GetBeanUtil;
import com.saye.hospitalgd.commons.log.LogUtil;
import com.saye.hospitalgd.commons.string.StringDUtil;
import com.saye.hospitalgd.commons.uuid.UUIDGenerator;
import com.saye.hospitalgd.service.HisDetailService;
import com.saye.hospitalgd.service.MedicalInsuranceReconciliationService;
import com.saye.hospitalgd.service.impl.HisDetailServiceImpl;
import com.saye.hospitalgd.service.impl.MedicalInsuranceReconciliationServiceImpl;
import com.saye.hospitalgd.service.system.DicinfoService;
import com.saye.hospitalgd.service.system.impl.DicinfoServiceImpl;
import com.saye.hospitalgd.util.SSLUtil;
import com.saye.hospitalgd.util.pojo.HttpResult;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.util.*;
/**
* @author thuang
* @version 1.0
* @description: 医保对账方法
* @date 2025/10/24
*/
public class MedicalInsuranceReconciliationMethod {
// 普通医保局对账接口地址
private static final String MEDICAL_INSURANCE_URL = "https://10.134.97.159/fsi/api/ybSettlementStmtService/stmtTotal";
// 工商医保对账接口地址险种99410专用
private static final String GONGSHANG_INSURANCE_URL = "http://10.150.4.74:8111/gssetl/api/hisbiz";
// 工商医保险种代码
private static final String GONGSHANG_INSUTYPE = "99410";
// 固定参数
private static final String SETL_OPTINS = "640104";
private static final String INFNO = "3201";
private static final String INFVER = "1.0";
private static final String FIXMEDINS_CODE = "H64010400022";
private static final String FIXMEDINS_NAME = "中国人民武装警察部队宁夏回族自治区总队医院";
private static final String INSUPLC_ADMDVS = "410000";
private static final String MDTRTAREA_ADMVS = "411302";
private static final String OPTER = "ADMIN";
private static final String OPTER_NAME = "系统管理员";
private static final String OPTER_TYPE = "1";
private static final String RECER_SYS_CODE = "HIS";
private static final String SIGNTYPE = "SM3";
/**
* 执行医保对账
* @param trade_date 对账日期
* @return 对账结果
*/
public HashMap<Object, Object> executeMedicalInsuranceReconciliation(String trade_date) {
HashMap<Object, Object> responseMap = new HashMap<>();
String errCode = "0";
String errMsg = "";
try {
LogUtil.info(this.getClass(), "开始执行医保对账,日期:" + trade_date);
LogUtil.info(this.getClass(), "开始查询医保数据不限制PayType");
// 1. 从数据库查询医保数据按险种和清算类别分组统计不限制PayType
HisDetailService hisDetailService = GetBeanUtil.getBean(HisDetailServiceImpl.class);
HashMap<Object, Object> queryMap = new HashMap<>();
queryMap.put("trade_date", trade_date);
// 查询所有有险种和清算类别的记录不限制PayType
List<HashMap<Object, Object>> medicalRecords = hisDetailService.findMedicalInsuranceGroupData(queryMap);
if (medicalRecords == null || medicalRecords.size() == 0) {
LogUtil.info(this.getClass(), "医保对账:当日没有医保数据,日期:" + trade_date);
errMsg = "当日没有医保数据";
responseMap.put("errCode", errCode);
responseMap.put("errMsg", errMsg);
responseMap.put("reconciliationResults", new ArrayList<>());
return responseMap;
}
LogUtil.info(this.getClass(), "查询到医保分组数据:" + medicalRecords.size() + "");
// 删除当天旧的对账结果
MedicalInsuranceReconciliationService reconciliationService =
GetBeanUtil.getBean(MedicalInsuranceReconciliationServiceImpl.class);
HashMap<Object, Object> deleteMap = new HashMap<>();
deleteMap.put("trade_date", trade_date);
reconciliationService.deleteMedicalInsuranceReconciliationResult(deleteMap);
// 2. 遍历每个险种和清算类别的组合,调用医保局接口
List<HashMap<Object, Object>> reconciliationResults = new ArrayList<>();
for (HashMap<Object, Object> record : medicalRecords) {
String insutype = StringDUtil.changeNullToEmpty(record.get("INSUTYPE"));
String clrType = StringDUtil.changeNullToEmpty(record.get("CLR_TYPE"));
String medfee_sumamt = StringDUtil.changeNullToEmpty(record.get("MEDFEE_SUMAMT"));
String fund_pay_sumamt = StringDUtil.changeNullToEmpty(record.get("FUND_PAY_SUMAMT"));
String acct_pay = StringDUtil.changeNullToEmpty(record.get("ACCT_PAY"));
String fixmedins_setl_cnt = StringDUtil.changeNullToEmpty(record.get("FIXMEDINS_SETL_CNT"));
// 跳过空的险种或清算类别
if ("".equals(insutype) || "".equals(clrType)) {
LogUtil.info(this.getClass(), "跳过险种或清算类别为空的记录");
continue;
}
LogUtil.info(this.getClass(), String.format("处理险种:%s清算类别%s交易笔数%s总金额%s",
insutype, clrType, fixmedins_setl_cnt, medfee_sumamt));
try {
// 3. 调用医保局对账接口
HashMap<Object, Object> callResult = callMedicalInsuranceAPI(
trade_date, insutype, clrType, medfee_sumamt,
fund_pay_sumamt, acct_pay, fixmedins_setl_cnt
);
// 4. 记录对账结果
HashMap<Object, Object> resultMap = new HashMap<>();
resultMap.put("id", UUIDGenerator.getUUID());
resultMap.put("trade_date", trade_date);
resultMap.put("insutype", insutype);
resultMap.put("clr_type", clrType);
resultMap.put("medfee_sumamt", medfee_sumamt);
resultMap.put("fund_pay_sumamt", fund_pay_sumamt);
resultMap.put("acct_pay", acct_pay);
resultMap.put("fixmedins_setl_cnt", fixmedins_setl_cnt);
resultMap.put("setl_optins", callResult.get("setl_optins"));
resultMap.put("stmt_rslt", callResult.get("stmt_rslt"));
resultMap.put("stmt_rslt_dscr", callResult.get("stmt_rslt_dscr"));
resultMap.put("infcode", callResult.get("infcode"));
resultMap.put("err_msg", callResult.get("err_msg"));
resultMap.put("warn_msg", callResult.get("warn_msg"));
resultMap.put("refmsg_time", callResult.get("refmsg_time"));
resultMap.put("respond_time", callResult.get("respond_time"));
resultMap.put("inf_refmsgid", callResult.get("inf_refmsgid"));
resultMap.put("api_result", callResult.get("api_result"));
resultMap.put("create_time", DateDUtil.getCurrentDate(DateDUtil.yyyy_MM_dd_HH_mm_ss));
resultMap.put("modify_time", DateDUtil.getCurrentDate(DateDUtil.yyyy_MM_dd_HH_mm_ss));
reconciliationResults.add(resultMap);
} catch (Exception e) {
LogUtil.error(this.getClass(), String.format("医保对账失败,险种:%s清算类别%s错误%s",
insutype, clrType, e.getMessage()));
// 记录失败结果
HashMap<Object, Object> resultMap = new HashMap<>();
resultMap.put("id", UUIDGenerator.getUUID());
resultMap.put("trade_date", trade_date);
resultMap.put("insutype", insutype);
resultMap.put("clr_type", clrType);
resultMap.put("medfee_sumamt", medfee_sumamt);
resultMap.put("fund_pay_sumamt", fund_pay_sumamt);
resultMap.put("acct_pay", acct_pay);
resultMap.put("fixmedins_setl_cnt", fixmedins_setl_cnt);
resultMap.put("stmt_rslt", "9");
resultMap.put("stmt_rslt_dscr", "调用医保局接口失败:" + e.getMessage());
resultMap.put("api_result", "ERROR");
resultMap.put("create_time", DateDUtil.getCurrentDate(DateDUtil.yyyy_MM_dd_HH_mm_ss));
resultMap.put("modify_time", DateDUtil.getCurrentDate(DateDUtil.yyyy_MM_dd_HH_mm_ss));
reconciliationResults.add(resultMap);
}
}
// 5. 批量保存对账结果到数据库
if (reconciliationResults.size() > 0) {
reconciliationService.insertMedicalInsuranceReconciliationResultBatch(reconciliationResults);
}
responseMap.put("reconciliationResults", reconciliationResults);
LogUtil.info(this.getClass(), "医保对账完成,共处理 " + reconciliationResults.size() + " 组数据");
} catch (Exception e) {
e.printStackTrace();
errCode = "999";
errMsg = "医保对账失败:" + e.getMessage();
LogUtil.error(this.getClass(), errMsg);
}
responseMap.put("errCode", errCode);
responseMap.put("errMsg", errMsg);
return responseMap;
}
/**
* 调用医保局对账接口
*/
private HashMap<Object, Object> callMedicalInsuranceAPI(
String trade_date, String insutype, String clr_type,
String medfee_sumamt, String fund_pay_sumamt, String acct_pay,
String fixmedins_setl_cnt) throws Exception {
HashMap<Object, Object> result = new HashMap<>();
// 计算日期范围(当日到当日)
String stmt_begndate = trade_date;
String stmt_enddate = trade_date;
// 生成当前时间
String inf_time = DateDUtil.getCurrentDate(DateDUtil.yyyy_MM_dd_HH_mm_ss);
// 生成消息ID机构编码+时间戳+序号)
String msgid = FIXMEDINS_CODE + DateDUtil.DateToStr("yyyyMMddHHmmss", new Date()) + "0001";
// 构建请求JSON
JSONObject requestJson = new JSONObject();
requestJson.put("cainfo", "");
requestJson.put("dev_no", "");
requestJson.put("dev_safe_info", "");
requestJson.put("fixmedins_code", FIXMEDINS_CODE);
requestJson.put("fixmedins_name", FIXMEDINS_NAME);
requestJson.put("inf_time", inf_time);
requestJson.put("infno", INFNO);
requestJson.put("infver", INFVER);
requestJson.put("insuplc_admdvs", INSUPLC_ADMDVS);
requestJson.put("mdtrtarea_admvs", MDTRTAREA_ADMVS);
requestJson.put("msgid", msgid);
requestJson.put("opter", OPTER);
requestJson.put("opter_name", OPTER_NAME);
requestJson.put("opter_type", OPTER_TYPE);
requestJson.put("recer_sys_code", RECER_SYS_CODE);
requestJson.put("sign_no", "");
requestJson.put("signtype", SIGNTYPE);
// 构建input.data
JSONObject inputData = new JSONObject();
inputData.put("acct_pay", acct_pay);
inputData.put("clr_type", clr_type);
inputData.put("fixmedins_setl_cnt", fixmedins_setl_cnt);
inputData.put("fund_pay_sumamt", fund_pay_sumamt);
inputData.put("insutype", insutype);
inputData.put("medfee_sumamt", medfee_sumamt);
inputData.put("setl_optins", SETL_OPTINS);
inputData.put("stmt_begndate", stmt_begndate);
inputData.put("stmt_enddate", stmt_enddate);
JSONObject input = new JSONObject();
input.put("data", inputData);
requestJson.put("input", input);
LogUtil.info(this.getClass(), "医保对账请求(日期范围:" + stmt_begndate + "" + stmt_enddate + "" + requestJson.toJSONString());
// 根据险种选择接口地址
String apiUrl;
boolean isGongshang = GONGSHANG_INSUTYPE.equals(insutype);
if (isGongshang) {
apiUrl = GONGSHANG_INSURANCE_URL;
LogUtil.info(this.getClass(), "险种99410使用工商医保接口当日到当日" + apiUrl);
} else {
apiUrl = MEDICAL_INSURANCE_URL;
LogUtil.info(this.getClass(), "使用普通医保接口(当日到当日):" + apiUrl);
}
// 发送HTTP请求使用信任所有SSL证书的HttpClient
CloseableHttpClient httpClient = null;
try {
httpClient = SSLUtil.createSSLClientDefault();
} catch (Exception e) {
LogUtil.error(this.getClass(), "创建SSL HttpClient失败" + e.getMessage());
// 如果创建SSL HttpClient失败使用默认的HttpClient
httpClient = HttpClients.createDefault();
}
HttpPost httpPost = new HttpPost(apiUrl);
httpPost.setHeader("Content-Type", "application/json;charset=UTF-8");
httpPost.setEntity(new StringEntity(requestJson.toJSONString(), StandardCharsets.UTF_8));
CloseableHttpResponse response = null;
try {
response = httpClient.execute(httpPost);
String responseStr = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8);
LogUtil.info(this.getClass(), "医保对账响应:" + responseStr);
// 解析响应
JSONObject responseJson = JSON.parseObject(responseStr);
// 存储基础响应信息
result.put("infcode", responseJson.getInteger("infcode"));
result.put("err_msg", responseJson.getString("err_msg"));
result.put("warn_msg", responseJson.getString("warn_msg"));
result.put("refmsg_time", responseJson.getString("refmsg_time"));
result.put("respond_time", responseJson.getString("respond_time"));
result.put("inf_refmsgid", responseJson.getString("inf_refmsgid"));
Integer infcode = responseJson.getInteger("infcode");
if (infcode != null && infcode == 0) {
// 请求成功
JSONObject output = responseJson.getJSONObject("output");
if (output != null && output.containsKey("stmtinfo")) {
JSONObject stmtinfo = output.getJSONObject("stmtinfo");
result.put("setl_optins", stmtinfo.getString("setl_optins"));
String stmt_rslt = stmtinfo.getString("stmt_rslt");
String stmt_rslt_dscr = stmtinfo.getString("stmt_rslt_dscr");
// 工商医保的对账结果处理99410险种
if (isGongshang) {
// 工商医保返回格式:
// stmt_rslt: "101" 表示有差异(对应普通医保的"1"失败)
// stmt_rslt: "0" 表示一致(对应普通医保的"0"成功)
if ("101".equals(stmt_rslt)) {
result.put("stmt_rslt", "1"); // 转换为标准的失败标识
LogUtil.info(this.getClass(), "工商医保对账结果:有差异 - " + stmt_rslt_dscr);
} else if ("0".equals(stmt_rslt)) {
result.put("stmt_rslt", "0"); // 成功
LogUtil.info(this.getClass(), "工商医保对账结果:一致");
} else {
result.put("stmt_rslt", stmt_rslt); // 保持原值
LogUtil.info(this.getClass(), "工商医保对账结果:" + stmt_rslt);
}
} else {
// 普通医保直接使用返回值
result.put("stmt_rslt", stmt_rslt);
}
result.put("stmt_rslt_dscr", stmt_rslt_dscr);
result.put("api_result", "SUCCESS");
} else {
result.put("stmt_rslt", "9");
result.put("stmt_rslt_dscr", "返回数据格式错误");
result.put("api_result", "ERROR");
}
} else {
// 请求失败
String err_msg = responseJson.getString("err_msg");
result.put("stmt_rslt", "9");
result.put("stmt_rslt_dscr", "医保局返回错误:" + err_msg);
result.put("api_result", "ERROR");
}
} finally {
if (response != null) {
response.close();
}
httpClient.close();
}
return result;
}
}

View File

@@ -79,6 +79,17 @@ public class ReconciliationMethod {
military_code = StringDUtil.changeNullToEmpty(militaryPayTypeList.get(0).get("DICVALUE"));
}
searchMap.put("military_code", military_code);
// 查询医保支付方式的dicvalue需要从常规对账中过滤掉
String medical_insurance_code = ""; // 默认为空,表示不过滤
HashMap<String, String> medicalSearchMap = new HashMap<>();
medicalSearchMap.put("parentCode", "PAY_TYPE");
medicalSearchMap.put("dicname", "医保");
List<HashMap<Object, Object>> medicalPayTypeList = dicinfoService.selectDicinfoListByCondition(medicalSearchMap);
if (medicalPayTypeList != null && medicalPayTypeList.size() > 0) {
medical_insurance_code = StringDUtil.changeNullToEmpty(medicalPayTypeList.get(0).get("DICVALUE"));
}
searchMap.put("medical_insurance_code", medical_insurance_code);
//查询his和三方记录
List<HashMap<Object, Object>> hisbillsList = hisbillsHistoryService.findHisBillsByDate(searchMap);

View File

@@ -30,4 +30,6 @@ public interface HisDetailService {
List<HashMap<Object, Object>> findHisCashDetail(HashMap<Object, Object> map) throws Exception;
List<HashMap<Object, Object>> findHisDetailCountData(HashMap<Object, Object> map) throws Exception;
List<HashMap<Object, Object>> findMedicalInsuranceGroupData(HashMap<Object, Object> map) throws Exception;
}

View File

@@ -0,0 +1,35 @@
package com.saye.hospitalgd.service;
import java.util.HashMap;
import java.util.List;
/**
* @author thuang
* @version 1.0
* @description: 医保对账Service
* @date 2025/10/24
*/
public interface MedicalInsuranceReconciliationService {
/**
* 插入医保对账结果
*/
void insertMedicalInsuranceReconciliationResult(HashMap<Object, Object> map) throws Exception;
/**
* 批量插入医保对账结果
*/
void insertMedicalInsuranceReconciliationResultBatch(List<HashMap<Object, Object>> list) throws Exception;
/**
* 查询医保对账结果
*/
List<HashMap<Object, Object>> findMedicalInsuranceReconciliationResult(HashMap<Object, Object> map) throws Exception;
/**
* 删除医保对账结果
*/
void deleteMedicalInsuranceReconciliationResult(HashMap<Object, Object> map) throws Exception;
}

View File

@@ -179,5 +179,9 @@ public class HisDetailServiceImpl implements HisDetailService {
return hisDetailMapper.findHisDetailCountData(map);
}
@Override
public List<HashMap<Object, Object>> findMedicalInsuranceGroupData(HashMap<Object, Object> map) throws Exception {
return hisDetailMapper.findMedicalInsuranceGroupData(map);
}
}

View File

@@ -0,0 +1,46 @@
package com.saye.hospitalgd.service.impl;
import com.saye.hospitalgd.mapper.MedicalInsuranceReconciliationMapper;
import com.saye.hospitalgd.service.MedicalInsuranceReconciliationService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.HashMap;
import java.util.List;
/**
* @author thuang
* @version 1.0
* @description: 医保对账ServiceImpl
* @date 2025/10/24
*/
@Service
@Transactional
public class MedicalInsuranceReconciliationServiceImpl implements MedicalInsuranceReconciliationService {
@Autowired
private MedicalInsuranceReconciliationMapper medicalInsuranceReconciliationMapper;
@Override
public void insertMedicalInsuranceReconciliationResult(HashMap<Object, Object> map) throws Exception {
medicalInsuranceReconciliationMapper.insertMedicalInsuranceReconciliationResult(map);
}
@Override
public void insertMedicalInsuranceReconciliationResultBatch(List<HashMap<Object, Object>> list) throws Exception {
medicalInsuranceReconciliationMapper.insertMedicalInsuranceReconciliationResultBatch(list);
}
@Override
public List<HashMap<Object, Object>> findMedicalInsuranceReconciliationResult(HashMap<Object, Object> map) throws Exception {
return medicalInsuranceReconciliationMapper.findMedicalInsuranceReconciliationResult(map);
}
@Override
public void deleteMedicalInsuranceReconciliationResult(HashMap<Object, Object> map) throws Exception {
medicalInsuranceReconciliationMapper.deleteMedicalInsuranceReconciliationResult(map);
}
}

View File

@@ -76,4 +76,5 @@ public interface FinanceUserService {

View File

@@ -106,4 +106,5 @@ public class FinanceUserServiceImpl implements FinanceUserService {

View File

@@ -764,6 +764,20 @@ public class HisUtil {
if (powerTranIDElm != null) {
powerTranID = powerTranIDElm.getText();
}
//清算类别(医保记录才有)
Element clrTypeElm = item.element("clr_type");
String clrType = "";
if (clrTypeElm != null) {
clrType = clrTypeElm.getText();
}
//险种类型(医保记录才有)
Element insutypeElm = item.element("insutype");
String insutype = "";
if (insutypeElm != null) {
insutype = insutypeElm.getText();
}
addMap.put("visitzOrg", visitzOrg);
addMap.put("bizType", bizType);
@@ -782,7 +796,9 @@ public class HisUtil {
addMap.put("hisTime", hisTime);
addMap.put("hisOperCode", hisOperNum); // 修改字段名以匹配HISGetDataMethodByJH中的使用
addMap.put("powerTranID", powerTranID);
addMap.put("hisTransId", receiptNO); // 新增HisTransId字段使用ReceiptNO的值
addMap.put("hisTransId", receiptNO);
addMap.put("clrType", clrType); // 清算类别
addMap.put("insutype", insutype); // 险种类型 // 新增HisTransId字段使用ReceiptNO的值
addList.add(addMap);
}

View File

@@ -208,4 +208,5 @@ public class HttpClientUtil {

View File

@@ -0,0 +1,114 @@
package com.saye.hospitalgd.util;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.TrustStrategy;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContextBuilder;
import javax.net.ssl.*;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
/**
* SSL证书工具类
* 用于处理HTTPS请求时的SSL证书验证问题
*
* @author thuang
* @date 2025/10/24
*/
public class SSLUtil {
/**
* 创建一个信任所有证书的HttpClient
* 注意此方法会跳过SSL证书验证仅用于开发/测试环境
* 生产环境建议导入正确的SSL证书到JVM信任库
*
* @return CloseableHttpClient
* @throws KeyStoreException
* @throws NoSuchAlgorithmException
* @throws KeyManagementException
*/
public static CloseableHttpClient createSSLClientDefault()
throws KeyStoreException, NoSuchAlgorithmException, KeyManagementException {
// 创建信任所有证书的策略
TrustStrategy acceptingTrustStrategy = new TrustStrategy() {
@Override
public boolean isTrusted(X509Certificate[] chain, String authType)
throws CertificateException {
return true;
}
};
// 构建SSL上下文
SSLContext sslContext = SSLContextBuilder
.create()
.loadTrustMaterial(null, acceptingTrustStrategy)
.build();
// 创建SSL连接工厂不验证主机名
SSLConnectionSocketFactory sslSocketFactory = new SSLConnectionSocketFactory(
sslContext,
new String[]{"TLSv1", "TLSv1.1", "TLSv1.2", "SSLv3"},
null,
SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER
);
// 创建HttpClient
return HttpClients.custom()
.setSSLSocketFactory(sslSocketFactory)
.build();
}
/**
* 全局禁用SSL证书验证针对HttpsURLConnection
* 注意此方法会影响整个JVM请谨慎使用
*/
public static void disableSSLVerification() {
try {
// 创建信任所有证书的TrustManager
TrustManager[] trustAllCerts = new TrustManager[]{
new X509TrustManager() {
@Override
public X509Certificate[] getAcceptedIssuers() {
return null;
}
@Override
public void checkClientTrusted(X509Certificate[] certs, String authType) {
}
@Override
public void checkServerTrusted(X509Certificate[] certs, String authType) {
}
}
};
// 安装信任所有证书的TrustManager
SSLContext sc = SSLContext.getInstance("SSL");
sc.init(null, trustAllCerts, new java.security.SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
// 创建不进行主机名验证的HostnameVerifier
HostnameVerifier allHostsValid = new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
};
// 安装不进行主机名验证的HostnameVerifier
HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);
} catch (NoSuchAlgorithmException | KeyManagementException e) {
e.printStackTrace();
}
}
}