package thyyxxk.webserver.service.ca; import cn.hutool.core.codec.Base64; import cn.hutool.core.io.FileUtil; import cn.hutool.core.util.IdUtil; import cn.hutool.core.util.IdcardUtil; import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.URLUtil; import cn.hutool.crypto.asymmetric.KeyType; import cn.hutool.crypto.asymmetric.RSA; import cn.hutool.json.JSONArray; import cn.hutool.json.JSONObject; import cn.hutool.json.JSONUtil; import com.dtflys.forest.Forest; import com.dtflys.forest.http.ForestRequest; import com.dtflys.forest.logging.LogConfiguration; import com.dtflys.forest.utils.ForestDataType; import lombok.*; import lombok.extern.slf4j.Slf4j; import org.jetbrains.annotations.NotNull; import org.springframework.stereotype.Service; import thyyxxk.webserver.config.Assertion; import thyyxxk.webserver.config.envionment.Archive; import thyyxxk.webserver.config.envionment.CaData; import thyyxxk.webserver.config.exception.BizException; import thyyxxk.webserver.config.exception.ExceptionEnum; import thyyxxk.webserver.constants.YesOrNo; import thyyxxk.webserver.dao.his.ca.EmrCaSignDao; import thyyxxk.webserver.dao.his.zhuyuanyisheng.emr.EmrPatientDao; import thyyxxk.webserver.entity.ResultVo; import thyyxxk.webserver.entity.ca.*; import thyyxxk.webserver.entity.login.UserInfo; import thyyxxk.webserver.entity.zhuyuanyisheng.emr.EmrPatientData; import thyyxxk.webserver.service.RedisServer; import thyyxxk.webserver.service.redislike.RedisLikeService; import thyyxxk.webserver.service.zhuyuanyisheng.emr.EmrServer; import thyyxxk.webserver.utils.ResultVoUtil; import java.io.UnsupportedEncodingException; import java.net.URLDecoder; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; import java.util.Objects; @Slf4j @Service public class CaServer implements Assertion { private final CaData caData; private final String SUCCRSS = "success"; private final RedisLikeService redisLikeService; private final EmrServer emrServer; private final EmrPatientDao dao; private final RedisServer redisServer; private final EmrCaSignDao emrCaSignDao; private final EmrPatientDao emrPatientDao; private final Archive archive; private final static String privateKey = "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAKAc+jzqVKHsjNdyuDGL7u6zZR55j87l+0GGT4/yEV+K8ZtL1GoIgOD6vv6eEg2iOwl6Wh6hq8XR4cIyzVyvMZ35dIw3oGN89ObtuCha7gLOmWYUIGVnkUBkLFeIdwe4F8bq1q15o7azsTVekSyNJf/wnxWMp8ibtvO41T8DIzZPAgMBAAECgYAeeLVVD4Gw2VyKij4sy8Var1CQxrDMPu+c7ktJCVguFzrZA8r2rQyeBIqB2aJ07smOibcJ+lm/Ca0N8O4tc/gcm4g/JsjBALFUmqyCkLQpRyIbz8V9uAmrLcchT74GS727Uv4KVTmQNDZTPIDdSZWSJ8S+WKmj8q3wUF1HLF1eXQJBAOC7D6xIB2XNS6Voa7C7m8aeg53lfcDUVJ6m0YFdvMbsU2cnlxnCSX1OsghFuVQLKcGeN+aXiTEileb3pAl2RC0CQQC2ZDtDb+1OmLmRNugp29Pmm8MM8KpktfqiBFi92Q747XwEeMgLi3qetq1wDT4dgvyURs2McsbF7diVtFv7DMXrAkAoYzXj3mYF86k+ps+DyZOrVF2PCOlauE4k3RIVz8TXcy1iAolzRalzbastNWqjIgZ1F3wwYtdzDyYlhifi03BZAkEArkgidPMbwDGhiAf+WhkrZz1JaTECsM9PGcergGVLsENFcQR0qstxtPz7x4lv5EVI0urA+Man93OptIsuJTr0VwJAd86EbHyOn5Lah7ptPGsmsqkrdMZcJ3chRj6pDtvb0A2lhaJfDRxmAiWCnviPhTApH6/1WRzBqc1ZTymyRTF9kA=="; private final static String publicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCgHPo86lSh7IzXcrgxi+7us2UeeY/O5ftBhk+P8hFfivGbS9RqCIDg+r7+nhINojsJeloeoavF0eHCMs1crzGd+XSMN6BjfPTm7bgoWu4CzplmFCBlZ5FAZCxXiHcHuBfG6tateaO2s7E1XpEsjSX/8J8VjKfIm7bzuNU/AyM2TwIDAQAB"; RSA idCardRsa = new RSA(privateKey, publicKey); private String getRedisKey(String documentId) { return "ca-emr-" + documentId; } public CaServer(CaData caData, RedisLikeService redisLikeService, EmrServer emrServer, EmrPatientDao dao, RedisServer redisServer, EmrCaSignDao emrCaSignDao, EmrPatientDao emrPatientDao, Archive archive) { this.caData = caData; this.redisLikeService = redisLikeService; this.emrServer = emrServer; this.dao = dao; this.redisServer = redisServer; this.emrCaSignDao = emrCaSignDao; this.emrPatientDao = emrPatientDao; this.archive = archive; } public List sendBatchByCode(CaSignClass.Send send) { List returnValue = new ArrayList<>(); for (int i = 0; i < send.getCount(); i++) { returnValue.add(sendByCode(send)); } return returnValue; } public CaReturn.CaData sendByCode(CaSignClass.Send send) { UserInfo userInfoByCode = redisLikeService.getUserInfoByCode(send.getId()); send.setBizSn(IdUtil.simpleUUID()); String msg = CaUtils.GetSHA256FormString(send.getMsg()); CaReturn push = push(send, msg); signatureAuthentication(msg, push); String timeStamp = generateTimestampByInData(msg); push.getData().setTimeStamp(timeStamp); push.getData().setBizSn(send.getBizSn()); push.getData().setSignature("/doctorSignatureImage/" + send.getId() + ".png"); push.getData().setName(userInfoByCode.getName()); push.getData().setMsg(msg); push.getData().setCode(send.getId()); return push.getData(); } /** * 推送用户签名 * * @param send * @return 返回值 */ private CaReturn push(CaSignClass.Send send, String msg) { CaData.MobileApp app = caData.getMobileApp(); CaSendParams params = CaSendParams .builder() .appId(app.getAppId()) .id(send.getId()) .bizSn(send.getBizSn()) .msg(msg) .msgWrapper("0") .desc(URLUtil.encode(send.getDesc())) .url(URLUtil.encode("http://127.0.0.1")) .mode("redirect") .sign(CaUtils.getSign(app.getPrivatekey(), msg)) .build(); String execute = Forest .post(app.getUrl() + "/v1/push/sign") .bodyType(ForestDataType.FORM) .addBody(params).execute(String.class); String decode = URLUtil.decode(execute); log.info("返回数据:{}", decode); CaReturn caReturn = JSONUtil.toBean(decode, CaReturn.class); if (!SUCCRSS.equals(caReturn.getRet())) { throw new BizException(ExceptionEnum.LOGICAL_ERROR, "签名失败。" + caReturn.getMsg()); } if (Integer.valueOf(1).equals(caReturn.getData().getIsTrust())) { throw new BizException(ExceptionEnum.LOGICAL_ERROR, "签名失败,请用户打开免密签名。"); } if (Integer.valueOf(2).equals(caReturn.getData().getIsTrust())) { return caReturn; } throw new BizException(ExceptionEnum.LOGICAL_ERROR, "签名失败。" + caReturn.getMsg()); } /** * 认证签名 * * @param msg 消息 * @param caReturn */ private void signatureAuthentication(String msg, CaReturn caReturn) { CaData.SignAuthentication signData = caData.getSignAuthentication(); CaWSDL caWSDL = new CaWSDL(signData.getUrl()); String replace = getVerifySignDataParams(msg, caReturn, signData); JSONObject rst = caWSDL.invoke("verifySignData", replace); log.info("认证签名xml:{}\n认证签名返回值:{}\n", replace, rst.toString()); Integer code = rst.getByPath("Root.RetCode", Integer.class); if (!YesOrNo.YES.getCode().equals(code)) { String rstmsg = rst.getByPath("Root.RetMsg", String.class); throw new BizException(ExceptionEnum.LOGICAL_ERROR, rstmsg); } } private static @NotNull String getVerifySignDataParams(String msg, CaReturn caReturn, CaData.SignAuthentication signData) { String request = "\n" + "\n" + "\t${AppCode}\n" + "\t${AppPWD}\n" + "\t\n" + "\t\t${Cert}\n" + "\t\t${SignAlg}\n" + "\t\t${InData}\n" + "\t\t${SignData}\n" + "\t\n" + ""; return request.replace("${AppCode}", signData.getAppCode()) .replace("${AppPWD}", signData.getAppPwd()) .replace("${Cert}", caReturn.getData().getCert()) .replace("${SignAlg}", signData.getSignAig()) .replace("${InData}", msg) .replace("${SignData}", caReturn.getData().getSignValue()); } /** * 认证时间戳并返回 * * @param msg 数据 * @return */ private String generateTimestampByInData(String msg) { String data = "\n" + "\n" + "\t${AppCode}\n" + "\t${AppPWD}\n" + "\t\n" + "\t\t${InData}\n" + "${HashAlg}\n" + "${CertReq}\n" + "\t\n" + ""; CaData.TimestampAuthentication timeData = caData.getTimestampAuthentication(); String request = data.replace("${AppCode}", timeData.getAppCode()) .replace("${AppPWD}", timeData.getAppPwd()) .replace("${InData}", msg) .replace("${HashAlg}", "SHA256") .replace("${CertReq}", "true"); CaWSDL caWSDL = new CaWSDL(timeData.getUrl()); JSONObject ret = caWSDL.invoke("generateTimestampByInData", request); Integer code = ret.getByPath("Root.RetCode", Integer.class); log.info("认证时间戳xml:{}\n认证时间戳返回值:{}\n", request, ret); if (code != 1) { String retmsg = ret.getByPath("Root.RetMsg", String.class); throw new BizException(ExceptionEnum.LOGICAL_ERROR, retmsg); } return ret.getByPath("Root.Response.TimeStamp", String.class); } public ResultVo getSignInfoByBizSn(String bizSn) { CaData.MobileApp app = caData.getMobileApp(); ForestRequest post = Forest .post(app.getUrl() + "/v1/sign/info/" + bizSn) .setLogConfiguration(new LogConfiguration() {{ setLogEnabled(false); }}) .bodyType(ForestDataType.JSON); CaSendParams send = new CaSendParams(); send.setAppId(app.getAppId()); String indata = "appId=" + app.getAppId() + "&bizSn=" + bizSn; String sign = CaUtils.getSign(app.getPrivatekey(), indata); send.setSign(sign); String ret = post.addBody(send).execute(String.class); CaSingBizSnReturnData execute = JSONUtil.toBean(ret, CaSingBizSnReturnData.class); if (SUCCRSS.equals(execute.getRet())) { CaSingBizSnReturnData.SingData data = execute.getData(); try { data.setSignData(URLDecoder.decode(data.getSignData(), "UTF-8")); data.setCert(URLDecoder.decode(data.getCert(), "UTF-8")); data.setSeal(URLDecoder.decode(data.getSeal(), "UTF-8")); } catch (UnsupportedEncodingException e) { log.error(e.getMessage()); return ResultVoUtil.success(ExceptionEnum.LOGICAL_ERROR, "解码失败请联系管理员" + bizSn); } return ResultVoUtil.success(execute); } return ResultVoUtil.success(ExceptionEnum.LOGICAL_ERROR, execute.getMsg()); } private void verifyMedicalRecordStatus(String documentId, EmrSignType type) { EmrPatientData emrData = emrPatientDao.selectById(documentId); // if (emrData == null || emrData.getDelFlag() == 1) { // throw new BizException(ExceptionEnum.LOGICAL_ERROR, "病历已被删除。"); // } isTrueErr(emrData.getSignComplete(), "签名流程已完成请勿重复点击"); if (emrData.getSignType() != null && !Objects.equals(type.getCode(), emrData.getSignType())) { throw new BizException(ExceptionEnum.LOGICAL_ERROR, "请使用上次选择的方式完成CA流程中途不得修改。"); } else { emrPatientDao.updateById(EmrPatientData.builder() .emrDocumentId(documentId) .signType(type.getCode()) .build()); } } /** * 发送签名到移动平板 * * @param moreEventSign 数据 * @return 暂无 */ public ResultVo sendMoreEventSign(CaSignClass.MoreEventSign moreEventSign) { verifyMedicalRecordStatus(moreEventSign.getDocumentId(), EmrSignType.MOVE); moreEventSign.getData().forEach(item -> { isBlank(item.getIdCard(), "身份证不能为空。"); isBlank(item.getSignName(), "签名人名字不能为空。"); isBlank(item.getSignType(), "与患者的关系不能为空。"); item.setIdCard(idCardRsa.decryptStr(item.getIdCard(), KeyType.PrivateKey)); }); String redisKey = getRedisKey(moreEventSign.getDocumentId()); String redisData = redisServer.getData(redisKey, () -> null); if (redisData != null) { cancelSignature(redisData); } redisServer.delData(redisKey); String uuid = IdUtil.simpleUUID(); JSONObject data = getMoreEventSignData(moreEventSign, uuid); String execute = Forest.post(caData.getHBoardSign().getUrl() + "/mobile/hBoardSign") .addBody(data) .bodyType(ForestDataType.JSON) .execute(String.class); JSONObject rst = JSONUtil.parseObj(execute); String msg = rst.getByPath("ret_msg", String.class); String decode = URLUtil.decode(msg); log.info("返回值:{},\n错误信息:{}", execute, decode); if (rst.getInt("ret_code") == 0) { redisServer.setData(redisKey, uuid, 30); return ResultVoUtil.success(ExceptionEnum.SUCCESS_AND_EL_MESSAGE, "发送成功。"); } else { return ResultVoUtil.fail(ExceptionEnum.LOGICAL_ERROR, "请联系管理员" + decode); } } private JSONObject getMoreEventSignData(CaSignClass.MoreEventSign moreEventSign, String uuid) { JSONObject ret = new JSONObject(); CaData.HBoardSign app = caData.getHBoardSign(); ret.set("api_key", app.getApiKey()); ret.set("api_secret", app.getApiSecret()); JSONObject data = new JSONObject(); data.set("document_no", uuid); data.set("pdf", emrServer.getPdfBase(moreEventSign.getDocumentId())); data.set("return_url", URLUtil.encode(app.getReturnUrl() + "/" + moreEventSign.getDocumentId())); JSONArray signature = new JSONArray(); for (int i = 0; i < moreEventSign.getData().size(); i++) { CaSignClass.MoreEventSignData item = moreEventSign.getData().get(i); JSONObject entries = new JSONObject(); entries.set("type", "keyword"); entries.set("sign_type", URLUtil.encode(item.getSignTypeName())); entries.set("keyword", new JSONObject() {{ set("page", "0"); set("content", URLUtil.encode(item.getContent())); }}); JSONObject user_info = new JSONObject() {{ set("name", item.getSignName()); set("idno", item.getIdCard()); set("age", IdcardUtil.getAgeByIdCard(item.getIdCard())); }}; user_info.set("relation", item.getSignType()); if (StrUtil.isNotBlank(item.getSignOpinion())) { JSONObject informed_keyword = JSONUtil.createObj() .set("informed_page", 0) .set("informed _content", URLUtil.encode(item.getSignOpinion())); JSONObject informed_stamp = JSONUtil.createObj() .set("informed_tip", URLUtil.encode("签名意见")) .set("informed_num", i + 1) .set("informed_keyword", informed_keyword); entries.set("informed_stamp", informed_stamp); } entries.set("user_info", user_info); signature.put(entries); } // 签署信息 data.set("signature", signature); data.set("doctor_info", new JSONObject() {{ set("doctor_no", moreEventSign.getSendCode()); }}); data.set("pic_size", new JSONObject() {{ set("width", "60"); set("height", "10"); }}); ret.set("data", data); return ret; } /** * h5 签名返回一个http连接 * * @param value * @return * @deprecated 没啥用 */ public ResultVo h5EventSign(CaSignClass.H5EventSign value) { JSONObject ret = new JSONObject(); String uuid = IdUtil.simpleUUID(); CaData.HBoardSign app = caData.getHBoardSign(); ret.set("api_key", app.getApiKey()); ret.set("api_secret", app.getApiSecret()); ret.set("data", new JSONObject() {{ set("document_no", uuid); set("pdf", emrServer.getPdfBase(value.getDocumentId())); set("type", "keyword"); set("keyword", new JSONObject() {{ set("page", "0"); set("content", URLUtil.encode(value.getContent())); }}); }}); String execute = Forest .post(caData.getHBoardSign().getUrl() + "/seal/h5EventSign") .addBody(ret) .bodyType(ForestDataType.JSON) .execute(String.class); return getStringResultVo(execute, uuid, ""); } @Getter public enum EmrSignType { QR(1, "扫码"), MOVE(2, "移动平板"), HANDWRITING_BOARD(3, "有线手写板"); private final Integer code; private final String name; EmrSignType(int code, String name) { this.code = code; this.name = name; } } /** * CA 扫码签名 * * @param value * @return */ public ResultVo hBoardSignV2(CaSignClass.H5EventSign value) { isNoll(value.getSignType(), "请选择和患者的关系"); isBlank(value.getIdCard(), "身份证不能为空。"); isBlank(value.getName(), "姓名不能为空。"); JSONObject ret = new JSONObject(); String caDocumentId = IdUtil.simpleUUID(); verifyMedicalRecordStatus(value.getDocumentId(), EmrSignType.QR); CaSignClass.H5EventSignRedis redisData = clearHBoardSignV2Data(value); String idCard = idCardRsa.decryptStr(value.getIdCard(), KeyType.PrivateKey); ret.set("api_key", caData.getHBoardSign().getApiKey()); ret.set("api_secret", caData.getHBoardSign().getApiSecret()); ret.set("data", new JSONObject() {{ set("document_no", caDocumentId); set("pdf", redisData.getPdf()); set("sign_way", "face,hand_sign"); set("keyword", new JSONObject() {{ set("page", "0"); set("content", URLUtil.encode(value.getContent())); }}); set("user_info", new JSONObject() {{ set("name", URLUtil.encode(value.getName())); set("idno", idCard); }}); set("collection_source", "face_photos,hand_sign"); if (StrUtil.isNotBlank(value.getSignOpinion())) { set("informed_stamp", new JSONObject() {{ set("informed_type", "informed_keyword"); set("informed_keyword", new JSONObject() {{ set("informed_page", "0"); set("informed_content", URLUtil.encode(value.getSignOpinion())); }}); }}); } }}); String execute = Forest .post(caData.getHBoardSign().getUrl() + "/v2/seal/hBoardSign") .addBody(ret) .bodyType(ForestDataType.JSON) .execute(String.class); redisData.setUuid(value.getUuid()); redisServer.setData("ca-emr-" + value.getDocumentId(), redisData, 30); return getStringResultVo(execute, caDocumentId, value.getUuid()); } private CaSignClass.H5EventSignRedis clearHBoardSignV2Data(CaSignClass.H5EventSign value) { CaSignClass.H5EventSignRedis redisData = new CaSignClass.H5EventSignRedis(); String redisKey = getRedisKey(value.getDocumentId()); // 如果 uuid 是空的就是第一次 if (StrUtil.isBlank(value.getUuid())) { value.setUuid(IdUtil.simpleUUID()); redisServer.delData(redisKey); redisData.setPdf(emrServer.getPdfBase(value.getDocumentId())); } else { redisData = redisServer.getData(redisKey); if (redisData == null) { throw new BizException(ExceptionEnum.LOGICAL_ERROR, "签名超时请重新签名。"); } if (!redisData.getUuid().equals(value.getUuid())) { throw new BizException(ExceptionEnum.LOGICAL_ERROR, "有其他人正在签署,本次签名失效。"); } } return redisData; } @NotNull private ResultVo getStringResultVo(String execute, String caDocumentId, String uuid) { JSONObject rst = JSONUtil.parseObj(execute); String msg = URLUtil.decode(rst.getByPath("ret_msg", String.class)); if (0 == rst.getByPath("ret_code", Integer.class)) { CaSignClass.H5Return build = CaSignClass.H5Return.builder() .result(rst.getByPath("sign_url", String.class)) .caDocumentId(caDocumentId) .uuid(uuid) .build(); return ResultVoUtil.success(build); } return ResultVoUtil.fail(ExceptionEnum.NO_DATA_EXIST, "签名失败" + msg); } public ResultVo downloadSealV2(String id, String documentId) { JSONObject ret = new JSONObject(); ret.set("api_key", caData.getHBoardSign().getApiKey()); ret.set("api_secret", caData.getHBoardSign().getApiSecret()); ret.set("data", new JSONObject() {{ set("document_no", id); }}); String execute = Forest .post(caData.getHBoardSign().getUrl() + "/v2/seal/download") .addBody(ret) .bodyType(ForestDataType.JSON) .execute(String.class); JSONObject rst = JSONUtil.parseObj(execute); String msg = URLUtil.decode(rst.getByPath("ret_msg", String.class)); if (0 == rst.getByPath("ret_code", Integer.class)) { return ResultVoUtil.success(rst); } return ResultVoUtil.fail(ExceptionEnum.NO_DATA_EXIST, msg); } /** * 判断扫码是否完成以及回写扫码的数据 * * @return 提示 */ public ResultVo scanCodeVerification(String id, String documentId, String uuid) { ResultVo res = downloadSealV2(id, documentId); if (!res.getCode().equals(ExceptionEnum.SUCCESS.getCode())) { return ResultVoUtil.fail(ExceptionEnum.LOGICAL_ERROR, res.getMessage()); } JSONObject rst = res.getData(); String key = getRedisKey(documentId); CaSignClass.H5EventSignRedis redisData = redisServer.getData(key); if (redisData == null) { throw new BizException(ExceptionEnum.LOGICAL_ERROR, "签名错误,文书不存在。"); } if (!redisData.getUuid().equals(uuid)) { throw new BizException(ExceptionEnum.LOGICAL_ERROR, "有其他人正在签署,本次签名失效。"); } redisData.setPdf(rst.getByPath("pdf", String.class)); JSONObject saveData = new JSONObject(); saveData.set("hand_sign", rst.get("hand_sign")); saveData.set("fingerprint", rst.get("hand_sign")); saveData.set("informed_img", rst.get("hand_sign")); saveData.set("face_photo", rst.get("hand_sign")); saveData.set("biz_sn", id); if (null == redisData.getData()) { redisData.setData(new ArrayList<>()); } redisData.getData().add(saveData); redisServer.setData(key, redisData, 30); return ResultVoUtil.success(true); } public static void main(String[] args) { System.out.println(EmrServer.getPdfBase("927936636556216320")); } /** * 扫码流程完成 */ public void completeQrCode(String documentId) { EmrPatientData data = emrPatientDao.selectById(documentId); String redisKey = getRedisKey(documentId); CaSignClass.H5EventSignRedis redisServerData = redisServer.getData(redisKey); CaSignClass.Upload upload = CaSignClass.Upload.builder() .file(redisServerData.getPdf()) .fileName(documentId) .patNo(data.getPatNo()) .times(data.getTimes()) .caData(JSONUtil.toJsonStr(redisServerData)) .build(); String path = uploadBase64(upload); EmrPatientData build = EmrPatientData .builder() .emrDocumentId(documentId) .signComplete(true) .archivePath(path) .build(); emrPatientDao.updateById(build); redisServer.delData(redisKey); } public void cancelSignature(String id) { JSONObject ret = new JSONObject(); CaData.HBoardSign app = caData.getHBoardSign(); ret.set("api_key", app.getApiKey()); ret.set("api_secret", app.getApiSecret()); ret.set("data", new JSONObject() {{ set("document_no", id); }}); Forest.post(caData.getHBoardSign().getUrl() + "/v2/seal/cancelSigne") .addBody(ret) .bodyType(ForestDataType.JSON) .execute(JSONObject.class); } /** * 签名回调 * * @param document 文档id * @param data 签名数据 * @return */ public JSONObject hBoardSignReturnUrl(String document, JSONObject data) { // TODO 完成一定手写板签名的时候,需要保存一下PDF log.info("回调:{},数据:{}", document, data); EmrPatientData emrData = emrPatientDao.selectById(document); String redisKey = getRedisKey(document); String redisData = redisServer.getData(redisKey, () -> null); if (emrData == null || redisData == null) { return R(-1, "病历文档不存在"); } JSONObject signData = appDownload(redisData); CaSignClass.Upload build = CaSignClass.Upload .builder() .patNo(emrData.getPatNo()) .times(emrData.getTimes()) .file(signData.getStr("pdf")) .fileName(document) .caData(signData.getStr("signed")) .build(); String path = uploadBase64(build); EmrPatientData update = EmrPatientData.builder() .emrDocumentId(document) .signComplete(true) .archivePath(path) .build(); redisServer.delData(redisKey); emrPatientDao.updateById(update); return R(1, "success"); } private JSONObject R(Integer code, String msg) { return new JSONObject() {{ set("ret_code", code); set("res_msg", msg); }}; } public JSONObject appDownload(String bizSn) { JSONObject data = new JSONObject() {{ set("api_key", caData.getHBoardSign().getApiKey()); set("api_secret", caData.getHBoardSign().getApiSecret()); set("data", new JSONObject() {{ set("document_no", bizSn); }}); }}; JSONObject execute = Forest .post(caData.getHBoardSign().getUrl() + "/app/download") .bodyType(ForestDataType.JSON) .addBody(data.toJSONString(0)) .execute(JSONObject.class); execute.set("ret_msg", URLUtil.decode(execute.getStr("ret_msg"))); log.info("返回:{}", execute); if (execute.getInt("code") != 0) { throw new BizException(ExceptionEnum.LOGICAL_ERROR, execute.getStr("ret_msg")); } return execute; } public String uploadBase64(CaSignClass.Upload upload) { byte[] file = Base64.decode(upload.getFile()); String tmpPath = "/emr" + "/" + upload.getPatNo() + "/" + upload.getTimes() + "/" + upload.getFileName(); String pdfPath = tmpPath + ".pdf"; String caPath = "/emr" + "/" + upload.getPatNo() + "/" + upload.getTimes() + "/ca/" + upload.getFileName() + ".txt"; // 写入CA数据 FileUtil.writeString(upload.getCaData(), archive.getPath() + caPath, StandardCharsets.UTF_8); // 写入pdf FileUtil.writeBytes(file, archive.getPath() + pdfPath); return "/archive" + pdfPath; } }