package thyyxxk.webserver.service.medicalinsurance; import cn.hutool.core.util.StrUtil; import com.alibaba.fastjson.JSONObject; import lombok.extern.slf4j.Slf4j; import org.apache.commons.codec.binary.Base64; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.stereotype.Service; import org.springframework.web.client.RestTemplate; import thyyxxk.webserver.config.envionment.MedinsurConfig; import thyyxxk.webserver.config.exception.BizException; import thyyxxk.webserver.constants.sidicts.SiFunction; import thyyxxk.webserver.dao.his.medicalinsurance.SiZyDao; import thyyxxk.webserver.utils.DateUtil; import thyyxxk.webserver.utils.StringUtil; import thyyxxk.webserver.utils.TokenUtil; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; import java.text.SimpleDateFormat; import java.util.Date; import java.util.HashMap; import java.util.Map; /** * @description: 医保交易执行 * @author: DingJie * @create: 2021-05-28 15:57:26 **/ @Slf4j @Service public class ExecService { private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss"); private static final String RESULT_CODE = "infcode"; private static final String OUTPUT = "output"; private static int serial = 1000; private static final int MIN_VAL = 1000; private static final int MAX_VAL = 9999; private volatile String signNo; private final SiZyDao dao; private final RestTemplate template; private final MedinsurConfig cfg; @Autowired public ExecService(SiZyDao dao, RestTemplate template, MedinsurConfig cfg) { this.dao = dao; this.template = template; this.cfg = cfg; } public synchronized String makeMsgId() { serial += 1; if (serial > MAX_VAL) { serial = MIN_VAL; } return cfg.getHospId() + sdf.format(new Date()) + serial; } private synchronized String getSignNo() { if (StringUtil.isBlank(signNo)) { signIn(); } return signNo; } public synchronized void signIn() { String dbSignNo = dao.getSignInNo(); if (StrUtil.isNotBlank(dbSignNo)) { signNo = dbSignNo; log.info("获取历史签到号成功:{}", dbSignNo); return; } JSONObject input = makeSignHeader(SiFunction.SIGN_IN); JSONObject signIn = new JSONObject(); signIn.put("opter_no", "99999"); signIn.put("mac", cfg.getMacAddress()); signIn.put("ip", cfg.getIpAddress()); input.getJSONObject("input").put("signIn", signIn); JSONObject result = executeTrade(input, SiFunction.SIGN_IN); if (null != result && result.getInteger(RESULT_CODE) == 0) { try { JSONObject output = result.getJSONObject(OUTPUT); signNo = output.getJSONObject("signinoutb").getString("sign_no"); dao.updateSignNo(signNo); log.info("签到成功,签到号:{}", signNo); } catch (Exception e) { throw new BizException(); } } } public void signOut() { if (StringUtil.notBlank(signNo)) { JSONObject input = makeSignHeader(SiFunction.SIGN_OUT); JSONObject signOut = new JSONObject(); signOut.put("sign_no", signNo); signOut.put("opter_no", "99999"); input.getJSONObject("input").put("signOut", signOut); JSONObject result = executeTrade(input, SiFunction.SIGN_OUT); if (null != result && result.getIntValue(RESULT_CODE) == 0) { log.info("医保签退成功。"); signNo = null; dao.updateSignNo(signNo); } } } public String getInstitutionArea(String insureArea) { String hospArea = cfg.getHospArea(); String prefix = hospArea.substring(0, 4); return insureArea.startsWith(prefix) ? hospArea : prefix.substring(0, 2) + "9900"; } private JSONObject makeSignHeader(SiFunction function) { JSONObject header = makePublicHeader(""); header.put("infno", function.getCode()); header.put("opter", "99999"); header.put("opter_name", "全院"); return header; } public JSONObject makeTradeHeader(SiFunction function) { String staffId = TokenUtil.getInstance().getTokenUserId(); String staffName = dao.selectStaffName(staffId); JSONObject header = makePublicHeader(cfg.getHospArea()); header.put("infno", function.getCode()); header.put("opter", staffId); header.put("opter_name", staffName); header.replace("sign_no", getSignNo()); return header; } public JSONObject makeTradeHeaderWithInsureArea(SiFunction function, String insureArea) { String staffId = TokenUtil.getInstance().getTokenUserId(); String staffName = dao.selectStaffName(staffId); JSONObject header = makePublicHeader(insureArea); header.put("infno", function.getCode()); header.put("opter", staffId); header.put("opter_name", staffName); header.replace("sign_no", getSignNo()); return header; } private JSONObject makePublicHeader(String insureArea) { if (null == insureArea) { insureArea = ""; } JSONObject header = new JSONObject(); JSONObject input = new JSONObject(); header.put("msgid", makeMsgId()); header.put("mdtrtarea_admvs", getInstitutionArea(insureArea)); header.put("insuplc_admdvs", insureArea); header.put("recer_sys_code", "医院"); header.put("dev_no", ""); header.put("dev_safe_info", ""); header.put("cainfo", ""); header.put("signtype", ""); header.put("infver", cfg.getApiVersion()); header.put("opter_type", "1"); header.put("inf_time", DateUtil.now()); header.put("fixmedins_code", cfg.getHospId()); header.put("fixmedins_name", cfg.getHospName()); header.put("sign_no", ""); if (StrUtil.isNotBlank(cfg.getSoftDeveloper())) { header.put("fixmedins_soft_fcty", cfg.getSoftDeveloper()); } header.put("input", input); return header; } public Map getSiHeaderMap(SiFunction function) { Map headers = new HashMap<>(); String timestamp = String.valueOf(System.currentTimeMillis()); String signature = getSignature(timestamp, function.getCode()); // 医保原始地址添加header // headers.put("_api_access_key", apiAccessKey); // headers.put("_api_secreKey", apiSecretKey); // headers.put("_api_name", function.getCode()); // headers.put("_api_version", API_VERSION); // headers.put("_api_timestamp", timestamp); // headers.put("_api_signature", signature); // webhis.thyy.cn代理地址添加header headers.put("apiAccessKey", cfg.getAccessKey()); headers.put("apiSecreKey", cfg.getSecretKey()); headers.put("apiName", StrUtil.isBlank(cfg.getApiName()) ? function.getCode() : cfg.getApiName()); headers.put("apiVersion", cfg.getApiVersion()); headers.put("apiTimestamp", timestamp); headers.put("apiSignature", signature); return headers; } public HttpHeaders getHttpHeaders(SiFunction function) { String timestamp = String.valueOf(System.currentTimeMillis()); String signature = getSignature(timestamp, function.getCode()); HttpHeaders headers = new HttpHeaders(); headers.add("apiAccessKey", cfg.getAccessKey()); headers.add("apiSecreKey", cfg.getSecretKey()); headers.add("apiName", StrUtil.isBlank(cfg.getApiName()) ? function.getCode() : cfg.getApiName()); headers.add("apiVersion", cfg.getApiVersion()); headers.add("apiTimestamp", timestamp); headers.add("apiSignature", signature); return headers; } public String getSignature(String timestamp, String function) { String source = "_api_access_key=" + cfg.getAccessKey() + "&_api_name=" + function + "&_api_timestamp=" + timestamp + "&_api_version=" + cfg.getApiVersion(); return hmacSha1(source); } private String hmacSha1(String src) { byte[] result = null; try { SecretKeySpec signInKey = new SecretKeySpec(cfg.getSecretKey().getBytes(), "HmacSHA1"); Mac mac = Mac.getInstance("HmacSHA1"); mac.init(signInKey); byte[] rawHmac = mac.doFinal(src.getBytes()); result = Base64.encodeBase64(rawHmac); } catch (Exception e) { log.error("HmacSHA1加密出错", e); } return null == result ? null : new String(result); } public JSONObject executeTrade(JSONObject input, SiFunction function) { try { log.info("医保接口调用:【接口号:" + function.getCode() + ",接口名:" + function.getName() + ",操作员:" + input.getString("opter_name")); String result = template.postForObject(cfg.getApiUrl(), new HttpEntity<>(input, getHttpHeaders(function)), String.class); log.info("医保接口调用:返回:" + result); return JSONObject.parseObject(result); } catch (Exception e) { log.error("医保网络出错", e); JSONObject object = new JSONObject(); object.put("infcode", -1); if (e.getMessage().contains("Connection timed out")) { object.put("err_msg", "医保中心网络故障,连接超时。"); } else { object.put("err_msg", e.getMessage()); } return object; } } /** * 工伤接口URL */ // 模拟接口地址(当前使用) private static final String WORK_INJURY_API_URL = "http://130.150.161.72:9206/thyy/api/public/injury/workinjury"; // 真实接口地址(注释掉,需要时手动切换) // private static final String WORK_INJURY_API_URL = "http://localhost:8321/api/entry/workinjury"; /** * 执行工伤接口调用(工伤接口使用不同的请求头格式) */ public JSONObject executeWorkInjuryTrade(JSONObject input) { HttpHeaders headers = new HttpHeaders(); headers.add("Content-Type", "application/json"); // 工伤接口可能不需要医保接口的认证头,直接使用简单的JSON头 return template.postForObject(WORK_INJURY_API_URL, new HttpEntity<>(input, headers), JSONObject.class); } }