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 executeMedicalInsuranceReconciliation(String trade_date) { HashMap 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 queryMap = new HashMap<>(); queryMap.put("trade_date", trade_date); // 查询所有有险种和清算类别的记录(不限制PayType) List> 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 deleteMap = new HashMap<>(); deleteMap.put("trade_date", trade_date); reconciliationService.deleteMedicalInsuranceReconciliationResult(deleteMap); // 2. 遍历每个险种和清算类别的组合,调用医保局接口 List> reconciliationResults = new ArrayList<>(); for (HashMap 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 callResult = callMedicalInsuranceAPI( trade_date, insutype, clrType, medfee_sumamt, fund_pay_sumamt, acct_pay, fixmedins_setl_cnt ); // 4. 记录对账结果 HashMap 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 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 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 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; } }