SiZyFeeService.java 59 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219
  1. package thyyxxk.sizyfeeoprnsystm.service;
  2. import com.alibaba.fastjson.JSONArray;
  3. import com.alibaba.fastjson.JSONObject;
  4. import org.springframework.beans.factory.annotation.Value;
  5. import org.springframework.web.client.RestTemplate;
  6. import thyyxxk.sizyfeeoprnsystm.config.SocketConfig;
  7. import thyyxxk.sizyfeeoprnsystm.dao.SiLogDao;
  8. import thyyxxk.sizyfeeoprnsystm.dao.SiZyDao;
  9. import thyyxxk.sizyfeeoprnsystm.dicts.MdtrtCertType;
  10. import thyyxxk.sizyfeeoprnsystm.dicts.PsnSetlWay;
  11. import thyyxxk.sizyfeeoprnsystm.dicts.SiFunction;
  12. import thyyxxk.sizyfeeoprnsystm.dicts.YesOrNo;
  13. import lombok.extern.slf4j.Slf4j;
  14. import org.springframework.beans.factory.annotation.Autowired;
  15. import org.springframework.stereotype.Service;
  16. import thyyxxk.sizyfeeoprnsystm.pojo.*;
  17. import thyyxxk.sizyfeeoprnsystm.pojo.socketmsg.Message;
  18. import thyyxxk.sizyfeeoprnsystm.pojo.socketmsg.SocketMsg;
  19. import thyyxxk.sizyfeeoprnsystm.pojo.socketmsg.SocketTask;
  20. import thyyxxk.sizyfeeoprnsystm.utils.*;
  21. import java.text.SimpleDateFormat;
  22. import java.util.*;
  23. import java.util.concurrent.ConcurrentHashMap;
  24. import java.util.Date;
  25. /**
  26. * @author dj
  27. */
  28. @Slf4j
  29. @Service
  30. public class SiZyFeeService {
  31. private static final String RESULT_CODE = "infcode";
  32. private static final String ERROR_MESSAGE = "err_msg";
  33. private static final String OUTPUT = "output";
  34. private final SiZyDao zyDao;
  35. private final SiLogDao logDao;
  36. private final ExecService exec;
  37. private final RedisLikeService redis;
  38. private final RestTemplate template;
  39. private final SocketConfig cfg;
  40. private static final ConcurrentHashMap<String, CodeName> uploadingMap;
  41. private static final ConcurrentHashMap<String, CodeName> staffMap;
  42. static {
  43. uploadingMap = new ConcurrentHashMap<>();
  44. staffMap = new ConcurrentHashMap<>();
  45. }
  46. @Autowired
  47. public SiZyFeeService(SiZyDao zyDao, SiLogDao logDao, ExecService exec, RedisLikeService redis, RestTemplate template, SocketConfig cfg) {
  48. this.zyDao = zyDao;
  49. this.logDao = logDao;
  50. this.exec = exec;
  51. this.redis = redis;
  52. this.template = template;
  53. this.cfg = cfg;
  54. }
  55. private void revokeUploadFees(ZyPatientInfo p) {
  56. if (null == p.getDetailSns() || p.getDetailSns().isEmpty()) {
  57. return;
  58. }
  59. SiPatInfo siPatInfo = zyDao.selectSiPatInfoForZy(p.getInpatientNo(), p.getAdmissTimes(), p.getLedgerSn());
  60. if (null == siPatInfo || StringUtil.isBlank(siPatInfo.getMdtrtId())) {
  61. return;
  62. }
  63. JSONObject input = exec.makeTradeHeaderWithInsureArea(SiFunction.REVOKE_HOSPITALIZATION_FEE_DETAILS,
  64. siPatInfo.getInsuplcAdmdvs(), p.getStaffId());
  65. JSONArray data = new JSONArray();
  66. p.getDetailSns().forEach(detailSn -> {
  67. JSONObject item = new JSONObject();
  68. item.put("feedetl_sn", detailSn);
  69. item.put("mdtrt_id", siPatInfo.getMdtrtId());
  70. item.put("psn_no", siPatInfo.getPsnNo());
  71. data.add(item);
  72. });
  73. input.getJSONObject("input").put("data", data);
  74. JSONObject result = exec.executeTrade(input, SiFunction.REVOKE_HOSPITALIZATION_FEE_DETAILS);
  75. log.info("【操作员:{}】撤销已上传的费用:\n参数:{},\n结果:{}", p.getStaffId(), input, result);
  76. if (null == result || null == result.getInteger(RESULT_CODE)) {
  77. ResultVoUtil.fail(ExceptionEnum.NETWORK_ERROR);
  78. return;
  79. }
  80. if (result.getIntValue(RESULT_CODE) == 0) {
  81. if (null == p.getDetailSns() || p.getDetailSns().isEmpty()) {
  82. zyDao.revokeAllUploadFee(p.getInpatientNo(), p.getAdmissTimes(), p.getLedgerSn());
  83. zyDao.deleteSiChrmTemp(p.getInpatientNo(), p.getAdmissTimes(), p.getLedgerSn());
  84. } else {
  85. zyDao.revokePartUploadFee(p.getInpatientNo(), p.getAdmissTimes(), p.getLedgerSn(), p.getDetailSns());
  86. zyDao.deletePartSiChrmTemp(p.getInpatientNo(), p.getAdmissTimes(), p.getLedgerSn(), p.getDetailSns());
  87. }
  88. ResultVoUtil.success("撤销费用上传成功。");
  89. }
  90. }
  91. public ResultVo<String> uploadMultiplePatientFees(List<Overview> overviews) {
  92. String sid = overviews.get(0).getStaffId();
  93. SocketTask socketTask = new SocketTask();
  94. socketTask.setUserCode(sid);
  95. socketTask.setTextInside(true);
  96. socketTask.setId("feeupload"+sid);
  97. for (int i = 0; i < overviews.size(); i++) {
  98. Overview overview = overviews.get(i);
  99. if (!staffMap.containsKey(overview.getStaffId())) {
  100. staffMap.put(overview.getStaffId(), zyDao.selectStaff(overview.getStaffId()));
  101. }
  102. sendUpdatePatientIndexMsg(socketTask,i + 1, overviews.size());
  103. if (uploadingMap.containsKey(overview.getInpatientNo())) {
  104. String message = "该患者正在其他机器上传,操作人是" +
  105. uploadingMap.get(overview.getInpatientNo()).getStaff();
  106. sendDuringUpload(overview, message);
  107. continue;
  108. }
  109. uploadingMap.put(overview.getInpatientNo(), staffMap.get(overview.getStaffId()));
  110. ResultVo<String> resVo;
  111. try {
  112. resVo = uploadFeeDetail(overview, socketTask);
  113. } catch (Exception e) {
  114. resVo = ResultVoUtil.fail(ExceptionEnum.LOGICAL_ERROR, e.getMessage());
  115. } finally {
  116. uploadingMap.remove(overview.getInpatientNo());
  117. }
  118. String msg = resVo.getCode() ==
  119. ExceptionEnum.SUCCESS.getCode() ? resVo.getData() : resVo.getMessage();
  120. sendDuringUpload(overview, msg);
  121. if (overviews.size() == 1) {
  122. return resVo;
  123. }
  124. }
  125. return ResultVoUtil.success("上传完成,请点击【上传信息】查看详情。");
  126. }
  127. public ResultVo<String> uploadFeeDetail(Overview o, SocketTask socketTask) {
  128. if (null == o.getLedgerSn()) {
  129. Integer ledger = zyDao.selectMaxLedgerSn(o.getInpatientNo(), o.getAdmissTimes());
  130. if (null == ledger) {
  131. ledger = 1;
  132. }
  133. o.setLedgerSn(ledger);
  134. }
  135. if (null == o.getMidSetl()) {
  136. o.setMidSetl(false);
  137. }
  138. SiPatInfo siPatInfo = zyDao.selectSiPatInfoForZy(o.getInpatientNo(), o.getAdmissTimes(), o.getLedgerSn());
  139. if (null == siPatInfo || StringUtil.isBlank(siPatInfo.getMdtrtId())) {
  140. return ResultVoUtil.fail(ExceptionEnum.LOGICAL_ERROR, "此患者没有有效的医保在院信息!");
  141. }
  142. beforeUpload(o);
  143. zyDao.hisRecount(o.getInpatientNo(), o.getAdmissTimes(), o.getLedgerSn());
  144. zyDao.fillReferPhysician(o.getInpatientNo(), o.getAdmissTimes());
  145. ZyPatientInfo zyPtnt = zyDao.selectPatientInfo(o.getInpatientNo());
  146. zyPtnt.setSid(o.getStaffId());
  147. zyPtnt.setStaffId(o.getStaffId());
  148. zyPtnt.setLedgerSn(o.getLedgerSn());
  149. zyPtnt.setMdtrtId(siPatInfo.getMdtrtId());
  150. zyPtnt.setPsnNo(siPatInfo.getPsnNo());
  151. zyPtnt.setMedType(siPatInfo.getMedType());
  152. zyPtnt.setAdmdvs(siPatInfo.getInsuplcAdmdvs());
  153. if (o.getMidSetl()) {
  154. List<Integer> notAllowedSns = zyDao.selectNotAllowedSnForMidSetl(o.getInpatientNo(), o.getAdmissTimes(),
  155. o.getLedgerSn(), o.getEndtime());
  156. if (!notAllowedSns.isEmpty()) {
  157. List<Integer> tempSns = new ArrayList<>();
  158. for (int sn : notAllowedSns) {
  159. tempSns.add(sn);
  160. if (tempSns.size() == 50) {
  161. zyPtnt.setDetailSns(tempSns);
  162. revokeUploadFees(zyPtnt);
  163. tempSns.clear();
  164. }
  165. }
  166. if (!tempSns.isEmpty()) {
  167. zyPtnt.setDetailSns(tempSns);
  168. revokeUploadFees(zyPtnt);
  169. }
  170. }
  171. }
  172. Map<String, Queue<FeeDtle>> allFees = getAllFeesNotUploaded(o);
  173. Queue<FeeDtle> allPositiveFees = allFees.get("positive");
  174. Queue<FeeDtle> allNegativeFees = allFees.get("negative");
  175. if (allPositiveFees.isEmpty() && allNegativeFees.isEmpty()) {
  176. return hospitalizationPreSettlement(zyPtnt, o);
  177. }
  178. int index = 0;
  179. int feeSize = allPositiveFees.size() + allNegativeFees.size();
  180. int[] pstvres = prepareUploadFees(allPositiveFees, index, feeSize, zyPtnt, socketTask);
  181. index = pstvres[0];
  182. int[] ngtvres = prepareUploadFees(allNegativeFees, index, feeSize, zyPtnt, socketTask);
  183. index = ngtvres[0];
  184. if (pstvres[1] == 1 || ngtvres[1] == 1) {
  185. allFees = getAllFeesNotUploaded(o);
  186. allPositiveFees = allFees.get("positive");
  187. allNegativeFees = allFees.get("negative");
  188. log.info("医保中心数据有遗漏,继续上传遗漏部分。");
  189. index = prepareUploadFees(allPositiveFees, index, feeSize, zyPtnt, socketTask)[0];
  190. prepareUploadFees(allNegativeFees, index, feeSize, zyPtnt, socketTask);
  191. }
  192. return hospitalizationPreSettlement(zyPtnt, o);
  193. }
  194. private Map<String, Queue<FeeDtle>> getAllFeesNotUploaded(Overview o) {
  195. Queue<FeeDtle> allPositiveFees, allNegativeFees;
  196. if (o.getMidSetl()) {
  197. allPositiveFees = zyDao.selectNotUploadedPositiveFeesForMidSetl(o.getInpatientNo(), o.getAdmissTimes(),
  198. o.getLedgerSn(), o.getBegntime(), o.getEndtime());
  199. allNegativeFees = zyDao.selectNotUploadedNegativeFeesForMidSetl(o.getInpatientNo(), o.getAdmissTimes(),
  200. o.getLedgerSn(), o.getBegntime(), o.getEndtime());
  201. } else {
  202. String today = getTodayEndTime();
  203. allPositiveFees = zyDao.selectNotUploadedPositiveFees(o.getInpatientNo(), o.getAdmissTimes(),
  204. o.getLedgerSn(), today);
  205. allPositiveFees.removeIf(item -> StringUtil.isBlank(item.getMedListCodg()));
  206. allNegativeFees = zyDao.selectNotUploadedNegativeFees(o.getInpatientNo(), o.getAdmissTimes(),
  207. o.getLedgerSn(), today);
  208. }
  209. Map<String, Queue<FeeDtle>> result = new HashMap<>();
  210. result.put("positive", allPositiveFees);
  211. result.put("negative", allNegativeFees);
  212. return result;
  213. }
  214. private String getTodayEndTime() {
  215. SimpleDateFormat smdate = new SimpleDateFormat("yyyy-MM-dd");
  216. String date = smdate.format(new Date());
  217. return date + " 23:59:59.999";
  218. }
  219. private int[] prepareUploadFees(Queue<FeeDtle> feeQueue, int index, int feeSize, ZyPatientInfo p, SocketTask socketTask) {
  220. int[] result = new int[] {0,0};
  221. if (feeQueue.isEmpty()) {
  222. return result;
  223. }
  224. JSONObject input = exec.makeTradeHeaderWithInsureArea(SiFunction.UPLOAD_HOSPITALIZATION_FEE_DETAILS,
  225. p.getAdmdvs(), p.getStaffId());
  226. List<FeeDtle> tempList = new ArrayList<>();
  227. while (!feeQueue.isEmpty()) {
  228. FeeDtle feeDtle = feeQueue.poll();
  229. assert feeDtle != null;
  230. if (StringUtil.isBlank(feeDtle.getBilgDrCodg())) {
  231. feeDtle.setBilgDrCodg(redis.getEmployeeSiCode(feeDtle.getOpIdCode()));
  232. }
  233. if (StringUtil.isBlank(feeDtle.getBilgDrCodg())) {
  234. if (StringUtil.notBlank(feeDtle.getDoctorCode())) {
  235. String siCode = redis.getEmployeeSiCode(feeDtle.getDoctorCode());
  236. if (StringUtil.isBlank(siCode)) {
  237. CodeName doctor = redis.getEmployeeCodeRs(feeDtle.getDoctorCode());
  238. sendDoctorNoSiCodeMessage(p, doctor.getStaff(), feeDtle.getFeedetlSn());
  239. continue;
  240. }
  241. } else {
  242. if (StringUtil.notBlank(feeDtle.getReferPhysician())) {
  243. String siCode = redis.getEmployeeSiCode(feeDtle.getReferPhysician());
  244. if (StringUtil.isBlank(siCode)) {
  245. CodeName doctor = redis.getEmployeeCodeRs(feeDtle.getReferPhysician());
  246. sendDoctorNoSiCodeMessage(p, doctor.getStaff(), feeDtle.getFeedetlSn());
  247. continue;
  248. }
  249. } else {
  250. if (StringUtil.notBlank(feeDtle.getOpIdCode())) {
  251. String siCode = redis.getEmployeeSiCode(feeDtle.getOpIdCode());
  252. if (StringUtil.isBlank(siCode)) {
  253. CodeName doctor = redis.getEmployeeCodeRs(feeDtle.getOpIdCode());
  254. sendDoctorNoSiCodeMessage(p, doctor.getStaff(), feeDtle.getFeedetlSn());
  255. continue;
  256. }
  257. }
  258. }
  259. }
  260. }
  261. feeDtle.setOrdersDrCode(feeDtle.getBilgDrCodg());
  262. feeDtle.setOrdersDrName(feeDtle.getBilgDrName());
  263. tempList.add(feeDtle);
  264. if (tempList.size() == 50) {
  265. int[] upldres = executeUploadFees(input, tempList, p);
  266. index += upldres[0];
  267. if (upldres[1] > 0) {
  268. result[1] = 1;
  269. }
  270. tempList.clear();
  271. socketTask.setPercentage(makePercentage(index, feeSize));
  272. sendTaskProgress(socketTask);
  273. }
  274. }
  275. if (!tempList.isEmpty()) {
  276. int[] upldres = executeUploadFees(input, tempList, p);
  277. index += upldres[0];
  278. if (upldres[1] > 0) {
  279. result[1] = 1;
  280. }
  281. socketTask.setPercentage(makePercentage(index, feeSize));
  282. sendTaskProgress(socketTask);
  283. }
  284. return result;
  285. }
  286. private void sendTaskProgress(SocketTask socketTask) {
  287. JSONObject js = new JSONObject();
  288. js.put("mode", "SINGLE");
  289. js.put("sid", socketTask.getUserCode());
  290. js.put("msg", SocketMsg.socketVo(Message.backgroundTask,
  291. JSONObject.toJSONString(socketTask)));
  292. template.postForObject(cfg.getApiUrl(), js, String.class);
  293. }
  294. private void sendDoctorNoSiCodeMessage(ZyPatientInfo p, String doctor, Integer feedetlSn) {
  295. String message = String.format("【流水号:%d】%s医师没有医保赋码,请联系医保科。", feedetlSn, doctor);
  296. sendUploadResponse(p, message);
  297. }
  298. private void sendUpdatePatientIndexMsg(SocketTask socketTask, int index, int total) {
  299. socketTask.setName("费用上传(共" + total + "人,正在上传第" + index + "人)");
  300. socketTask.setPercentage(0);
  301. JSONObject js = new JSONObject();
  302. js.put("mode", "SINGLE");
  303. js.put("sid", socketTask.getUserCode());
  304. js.put("msg", SocketMsg.socketVo(Message.backgroundTask, JSONObject.toJSONString(socketTask)));
  305. template.postForObject(cfg.getApiUrl(), js, String.class);
  306. }
  307. private void sendUploadResponse(ZyPatientInfo p, String message) {
  308. sendUploadResMsg(message, p.getInpatientNo(), p.getName(), p.getAdmissTimes(), p.getLedgerSn(), p.getStaffId());
  309. }
  310. private void sendDuringUpload(Overview overview, String message) {
  311. sendUploadResMsg(message, overview.getInpatientNo(), overview.getName(), overview.getAdmissTimes(), overview.getLedgerSn(), overview.getStaffId());
  312. }
  313. public void sendUploadResMsg(String message, String inpatientNo, String name, Integer admissTimes, Integer ledgerSn, String sid) {
  314. JSONObject msg = new JSONObject();
  315. msg.put("name", "uploadFeeResponse");
  316. msg.put("patNo", inpatientNo);
  317. msg.put("patName", name);
  318. msg.put("times", admissTimes);
  319. msg.put("ledgerSn", ledgerSn);
  320. msg.put("message", message);
  321. msg.put("title", String.format("住院号:【%s】,住院次数:【%d】,账页号:【%d】。",
  322. inpatientNo, admissTimes, ledgerSn));
  323. JSONObject js = new JSONObject();
  324. js.put("mode", "SINGLE");
  325. js.put("sid", sid);
  326. js.put("msg", SocketMsg.socketVo(Message.MEDINS_FEE_UPLOAD_PROGRESS, msg));
  327. template.postForObject(cfg.getApiUrl(), js, String.class);
  328. }
  329. private int[] executeUploadFees(JSONObject input, List<FeeDtle> fees, ZyPatientInfo p) {
  330. String ref = JSONArray.toJSONString(fees);
  331. input.getJSONObject("input").put("feedetail", JSONArray.parse(ref));
  332. JSONObject result = exec.executeTrade(input, SiFunction.UPLOAD_HOSPITALIZATION_FEE_DETAILS);
  333. int infcode = result.getIntValue(RESULT_CODE);
  334. String logMsg = infcode + "," + result.getString("inf_refmsgid");
  335. log.info("【操作员:{}】,医保费用上传:\n患者:{},\n结果:{},\n参数:{}", p.getStaffId(),
  336. p.getPsnInfo(), logMsg, input);
  337. if (infcode == 0) {
  338. JSONArray array = result.getJSONObject(OUTPUT).getJSONArray("result");
  339. List<SiChargeTemp> tempList = new ArrayList<>();
  340. for (int i = 0; i < array.size(); i++) {
  341. JSONObject upldretrn = array.getJSONObject(i);
  342. SiChargeTemp chrgtemp = JSONObject.parseObject(upldretrn.toJSONString(), SiChargeTemp.class);
  343. chrgtemp.setPatNo(p.getInpatientNo());
  344. chrgtemp.setTimes(p.getAdmissTimes());
  345. chrgtemp.setLedgerSn(p.getLedgerSn());
  346. tempList.add(chrgtemp);
  347. }
  348. zyDao.updateTransFlag(p.getInpatientNo(), p.getAdmissTimes(), tempList);
  349. zyDao.insertSiChargeTempFeeBatch(tempList);
  350. return new int[]{tempList.size(), fees.size() - tempList.size()};
  351. } else {
  352. String message = result.getString(ERROR_MESSAGE);
  353. sendUploadResponse(p, message);
  354. return new int[]{fees.size(), 0};
  355. }
  356. }
  357. public ResultVo<String> hospitalizationPreSettlement(ZyPatientInfo p, Overview o) {
  358. uploadingMap.remove(o.getInpatientNo());
  359. ZyPreSetlmt zyPreSetlmt;
  360. if (o.getMidSetl()) {
  361. zyPreSetlmt = zyDao.selectPreSetlmtForMidSetl(p.getInpatientNo(), p.getAdmissTimes(),
  362. p.getLedgerSn(), o.getBegntime(), o.getEndtime());
  363. } else {
  364. zyPreSetlmt = zyDao.selectPreSetlmt(p.getInpatientNo(), p.getAdmissTimes(),
  365. p.getLedgerSn(), getTodayEndTime());
  366. }
  367. if (null == zyPreSetlmt || StringUtil.isBlank(zyPreSetlmt.getMdtrtId())) {
  368. return ResultVoUtil.fail(ExceptionEnum.LOGICAL_ERROR, "此患者没有有效的医保在院信息!");
  369. }
  370. zyPreSetlmt.setMdtrtCertType(MdtrtCertType.RESIDENT_IDENTITY_CARD.getCode());
  371. zyPreSetlmt.setPsnSetlway(p.getDbg() ? PsnSetlWay.SETTLE_BY_QUOTA.getCode() : PsnSetlWay.SETTLE_BY_ITEMS.getCode());
  372. zyPreSetlmt.setAcctUsedFlag(YesOrNo.NO.getCodeStr());
  373. zyPreSetlmt.setMidSetlFlag(YesOrNo.NO.getCodeStr());
  374. JSONObject input = exec.makeTradeHeaderWithInsureArea(SiFunction.HOSPITALIZATION_PRE_SETTLEMENT,
  375. zyPreSetlmt.getInsuplcAdmdvs(), p.getStaffId());
  376. String ref = JSONObject.toJSONString(zyPreSetlmt);
  377. input.getJSONObject("input").put("data", JSONObject.parseObject(ref));
  378. JSONObject result = exec.executeTrade(input, SiFunction.HOSPITALIZATION_PRE_SETTLEMENT);
  379. log.info("预结算:\n参数:{},\n结果:{}", input, result);
  380. if (null == result) {
  381. return ResultVoUtil.fail(ExceptionEnum.NETWORK_ERROR);
  382. }
  383. Integer infcode = result.getInteger(RESULT_CODE);
  384. logDao.insert(new SiLog(input, result, p.getInpatientNo(), p.getAdmissTimes(), p.getLedgerSn(), infcode, zyPreSetlmt.getPsnNo()));
  385. if (null == infcode) {
  386. return ResultVoUtil.fail(ExceptionEnum.LOGICAL_ERROR, "医保中心报错:" + result.getString("message"));
  387. }
  388. if (infcode == 0) {
  389. JSONObject setlinfo = result.getJSONObject(OUTPUT).getJSONObject("setlinfo");
  390. String fundPay = setlinfo.getString("fund_pay_sumamt");
  391. zyDao.updateFundPay(fundPay, p.getInpatientNo(), p.getAdmissTimes(), p.getLedgerSn());
  392. zyDao.recountDeposit(p.getInpatientNo(), p.getAdmissTimes(), p.getLedgerSn());
  393. zyDao.updateBalance(p.getInpatientNo(), p.getAdmissTimes(), p.getLedgerSn());
  394. String balance = zyDao.selectLedgerBalance(p.getInpatientNo(), p.getAdmissTimes(), p.getLedgerSn());
  395. if (StringUtil.isBlank(balance)) {
  396. balance = "0";
  397. }
  398. zyDao.updateZyActPatientBalance(p.getInpatientNo(), balance);
  399. String message = "患者【" + p.getName() + "】院内总费用与医保中心总费用一致,医保报销金额为:¥ " + fundPay + "。";
  400. return ResultVoUtil.success(message);
  401. }
  402. return ResultVoUtil.fail(ExceptionEnum.LOGICAL_ERROR, result.getString(ERROR_MESSAGE));
  403. }
  404. private int makePercentage(int index, int size) {
  405. float per = (float)index / (float)size;
  406. return (int) (per * 100);
  407. }
  408. private void beforeUpload(Overview o) {
  409. Date disdate = zyDao.selectDscgDate(o.getInpatientNo(), o.getAdmissTimes());
  410. if (null != disdate) {
  411. int num = zyDao.updateChargedate(o.getInpatientNo(), o.getAdmissTimes(), disdate);
  412. log.info("修改费用时间大于出院医嘱时间的费用条目数:{}", num);
  413. }
  414. List<FeeCounteract> negativeCharges = zyDao.selectNegativeFeesWithOriDetlSn(
  415. o.getInpatientNo(), o.getAdmissTimes(), o.getLedgerSn());
  416. if (null == negativeCharges || negativeCharges.isEmpty()) {
  417. log.info("正负相抵完成,抵消费用总条目:0");
  418. return;
  419. }
  420. List<FeeCounteract> positiveCharges = new ArrayList<>();
  421. List<Integer> tempSn = new ArrayList<>();
  422. for (FeeCounteract feeCounteract : negativeCharges) {
  423. tempSn.add(feeCounteract.getOriDetailSn());
  424. if (tempSn.size() == 30) {
  425. positiveCharges.addAll(zyDao.selectPositiveFeesByDetlSn(
  426. o.getInpatientNo(), o.getAdmissTimes(), o.getLedgerSn(), tempSn));
  427. tempSn.clear();
  428. }
  429. }
  430. if (!tempSn.isEmpty()) {
  431. positiveCharges.addAll(zyDao.selectPositiveFeesByDetlSn(
  432. o.getInpatientNo(), o.getAdmissTimes(), o.getLedgerSn(), tempSn));
  433. }
  434. ConcurrentHashMap<Integer, FeeCounteract> positiveChargeMap = new ConcurrentHashMap<>();
  435. positiveCharges.forEach(itm -> {
  436. if (!positiveChargeMap.containsKey(itm.getDetailSn())) {
  437. positiveChargeMap.put(itm.getDetailSn(), itm);
  438. }
  439. });
  440. int count = 0;
  441. for (FeeCounteract negativeCharge : negativeCharges) {
  442. if (positiveChargeMap.containsKey(negativeCharge.getOriDetailSn())) {
  443. FeeCounteract positiveCharge = positiveChargeMap.get(negativeCharge.getOriDetailSn());
  444. if (positiveCharge.getUsed()) {
  445. continue;
  446. }
  447. if (negativeCharge.getChargeAmount() + positiveCharge.getChargeAmount() == 0d
  448. && negativeCharge.getChargeFee() + positiveCharge.getChargeFee() == 0d) {
  449. zyDao.updateYbTransFlagInPair(o.getInpatientNo(),
  450. o.getAdmissTimes(), negativeCharge.getDetailSn(), negativeCharge.getOriDetailSn());
  451. count += 2;
  452. positiveCharge.setUsed(true);
  453. }
  454. }
  455. }
  456. log.info("正负相抵完成,抵消费用总条目:{}", count);
  457. }
  458. /**
  459. * 工伤费用明细上传主方法
  460. * 功能:批量上传工伤患者的费用明细到工伤中心
  461. */
  462. public ResultVo<String> uploadWorkInjuryFees(List<Overview> overviews) {
  463. String sid = overviews.get(0).getStaffId();
  464. SocketTask socketTask = new SocketTask();
  465. socketTask.setUserCode(sid);
  466. socketTask.setTextInside(true);
  467. socketTask.setId("workInjuryFeeUpload"+sid);
  468. for (int i = 0; i < overviews.size(); i++) {
  469. Overview overview = overviews.get(i);
  470. if (!staffMap.containsKey(overview.getStaffId())) {
  471. staffMap.put(overview.getStaffId(), zyDao.selectStaff(overview.getStaffId()));
  472. }
  473. sendUpdatePatientIndexMsg(socketTask,i + 1, overviews.size());
  474. if (uploadingMap.containsKey(overview.getInpatientNo())) {
  475. String message = "该患者正在其他机器上传,操作人是" +
  476. uploadingMap.get(overview.getInpatientNo()).getStaff();
  477. sendDuringUpload(overview, message);
  478. continue;
  479. }
  480. uploadingMap.put(overview.getInpatientNo(), staffMap.get(overview.getStaffId()));
  481. ResultVo<String> resVo;
  482. try {
  483. resVo = uploadWorkInjuryFeeDetail(overview, socketTask);
  484. } catch (Exception e) {
  485. resVo = ResultVoUtil.fail(ExceptionEnum.LOGICAL_ERROR, e.getMessage());
  486. } finally {
  487. uploadingMap.remove(overview.getInpatientNo());
  488. }
  489. String msg = resVo.getCode() ==
  490. ExceptionEnum.SUCCESS.getCode() ? resVo.getData() : resVo.getMessage();
  491. sendDuringUpload(overview, msg);
  492. if (overviews.size() == 1) {
  493. return resVo;
  494. }
  495. }
  496. return ResultVoUtil.success("工伤费用上传完成,请点击【上传信息】查看详情。");
  497. }
  498. /**
  499. * 处理单个工伤患者的费用上传
  500. */
  501. public ResultVo<String> uploadWorkInjuryFeeDetail(Overview o, SocketTask socketTask) {
  502. if (null == o.getLedgerSn()) {
  503. Integer ledger = zyDao.selectMaxLedgerSn(o.getInpatientNo(), o.getAdmissTimes());
  504. if (null == ledger) {
  505. ledger = 1;
  506. }
  507. o.setLedgerSn(ledger);
  508. }
  509. if (null == o.getMidSetl()) {
  510. o.setMidSetl(false);
  511. }
  512. // 查询工伤患者信息(使用相同的表)
  513. SiPatInfo siPatInfo = zyDao.selectSiPatInfoForZy(o.getInpatientNo(), o.getAdmissTimes(), o.getLedgerSn());
  514. if (null == siPatInfo || StringUtil.isBlank(siPatInfo.getMdtrtId())) {
  515. return ResultVoUtil.fail(ExceptionEnum.LOGICAL_ERROR, "此患者没有有效的工伤在院信息!");
  516. }
  517. // 工伤费用预处理
  518. beforeWorkInjuryUpload(o);
  519. // 重新计算费用
  520. zyDao.hisRecount(o.getInpatientNo(), o.getAdmissTimes(), o.getLedgerSn());
  521. // 填充转诊医师信息
  522. zyDao.fillReferPhysician(o.getInpatientNo(), o.getAdmissTimes());
  523. // 构建工伤患者信息对象
  524. ZyPatientInfo zyPtnt = zyDao.selectPatientInfo(o.getInpatientNo());
  525. zyPtnt.setSid(o.getStaffId());
  526. zyPtnt.setStaffId(o.getStaffId());
  527. zyPtnt.setLedgerSn(o.getLedgerSn());
  528. zyPtnt.setMdtrtId(siPatInfo.getMdtrtId());
  529. zyPtnt.setPsnNo(siPatInfo.getPsnNo());
  530. zyPtnt.setMedType(siPatInfo.getMedType());
  531. zyPtnt.setAdmdvs(siPatInfo.getInsuplcAdmdvs());
  532. // 如果是中间结算,需要撤销超出时间范围的费用
  533. if (o.getMidSetl()) {
  534. List<Integer> notAllowedSns = zyDao.selectNotAllowedSnForMidSetl(o.getInpatientNo(), o.getAdmissTimes(),
  535. o.getLedgerSn(), o.getEndtime());
  536. if (!notAllowedSns.isEmpty()) {
  537. List<Integer> tempSns = new ArrayList<>();
  538. for (int sn : notAllowedSns) {
  539. tempSns.add(sn);
  540. if (tempSns.size() == 100) {
  541. zyPtnt.setDetailSns(tempSns);
  542. revokeWorkInjuryUploadFees(zyPtnt);
  543. tempSns.clear();
  544. }
  545. }
  546. if (!tempSns.isEmpty()) {
  547. zyPtnt.setDetailSns(tempSns);
  548. revokeWorkInjuryUploadFees(zyPtnt);
  549. }
  550. }
  551. }
  552. // 获取未上传的工伤费用(使用相同的表)
  553. Map<String, Queue<FeeDtle>> allFees = getAllFeesNotUploaded(o);
  554. Queue<FeeDtle> allPositiveFees = allFees.get("positive");
  555. Queue<FeeDtle> allNegativeFees = allFees.get("negative");
  556. if (allPositiveFees.isEmpty() && allNegativeFees.isEmpty()) {
  557. return workInjuryPreSettlement(zyPtnt, o);
  558. }
  559. int index = 0;
  560. int feeSize = allPositiveFees.size() + allNegativeFees.size();
  561. int[] pstvres = prepareWorkInjuryUploadFees(allPositiveFees, index, feeSize, zyPtnt, socketTask);
  562. index = pstvres[0];
  563. int[] ngtvres = prepareWorkInjuryUploadFees(allNegativeFees, index, feeSize, zyPtnt, socketTask);
  564. index = ngtvres[0];
  565. if (pstvres[1] == 1 || ngtvres[1] == 1) {
  566. allFees = getAllFeesNotUploaded(o);
  567. allPositiveFees = allFees.get("positive");
  568. allNegativeFees = allFees.get("negative");
  569. log.info("工伤中心数据有遗漏,继续上传遗漏部分。");
  570. index = prepareWorkInjuryUploadFees(allPositiveFees, index, feeSize, zyPtnt, socketTask)[0];
  571. prepareWorkInjuryUploadFees(allNegativeFees, index, feeSize, zyPtnt, socketTask);
  572. }
  573. return workInjuryPreSettlement(zyPtnt, o);
  574. }
  575. /**
  576. * 撤销工伤已上传的费用
  577. */
  578. private void revokeWorkInjuryUploadFees(ZyPatientInfo p) {
  579. if (null == p.getDetailSns() || p.getDetailSns().isEmpty()) {
  580. return;
  581. }
  582. SiPatInfo siPatInfo = zyDao.selectSiPatInfoForZy(p.getInpatientNo(), p.getAdmissTimes(), p.getLedgerSn());
  583. if (null == siPatInfo || StringUtil.isBlank(siPatInfo.getMdtrtId())) {
  584. return;
  585. }
  586. // 工伤撤销只能单个输入,需要遍历撤销每个明细
  587. List<Integer> successDetailSns = new ArrayList<>(); // 记录成功撤销的明细
  588. List<Integer> failedDetailSns = new ArrayList<>(); // 记录失败撤销的明细
  589. for (Integer detailSn : p.getDetailSns()) {
  590. // 构建工伤撤销请求(处方明细撤销)
  591. JSONObject input = new JSONObject();
  592. input.put("action", "transaction");
  593. input.put("transactionName", "2205");
  594. JSONObject businessParams = new JSONObject();
  595. businessParams.put("ipt_otp_no", siPatInfo.getMdtrtId()); // 门诊/住院流水号
  596. // 根据工伤文档要求:不允许传入处方号为空且处方流水号非空
  597. // 需要获取对应的处方号
  598. String rxno = zyDao.selectRxnoByDetailSn(p.getInpatientNo(), p.getAdmissTimes(), detailSn);
  599. businessParams.put("rxno", StringUtil.isBlank(rxno) ? "" : rxno); // 处方号
  600. businessParams.put("feedetl_sn", detailSn.toString()); // 处方流水号
  601. input.put("businessParams", businessParams);
  602. // 调用工伤撤销接口
  603. JSONObject result = exec.executeWorkInjuryTrade(input);
  604. log.info("【操作员:{}】撤销工伤已上传的费用明细{}:\n参数:{},\n结果:{}",
  605. p.getStaffId(), detailSn, input, result);
  606. // 工伤接口返回结果处理(支持模拟接口和真实接口切换)
  607. Integer infcode = extractWorkInjuryResultCode(result);
  608. if (null == result || null == infcode) {
  609. log.error("工伤撤销接口调用失败,明细:{}", detailSn);
  610. failedDetailSns.add(detailSn);
  611. continue; // 继续撤销下一个,不中断整个流程
  612. }
  613. if (infcode == 0) {
  614. log.info("工伤撤销成功,明细:{}", detailSn);
  615. successDetailSns.add(detailSn);
  616. } else {
  617. String errorMsg = extractWorkInjuryErrorMessage(result);
  618. log.error("工伤撤销失败,明细:{},错误:{}", detailSn, errorMsg);
  619. failedDetailSns.add(detailSn);
  620. }
  621. }
  622. // 根据撤销结果分别更新数据库状态
  623. if (successDetailSns.isEmpty()) {
  624. log.warn("工伤撤销全部失败,患者:{},明细数:{}", p.getInpatientNo(), p.getDetailSns().size());
  625. return; // 全部失败,不更新数据库状态
  626. }
  627. if (successDetailSns.size() == p.getDetailSns().size()) {
  628. // 全部撤销成功
  629. log.info("工伤撤销全部成功,患者:{},明细数:{}", p.getInpatientNo(), successDetailSns.size());
  630. if (null == p.getDetailSns() || p.getDetailSns().isEmpty()) {
  631. zyDao.revokeAllUploadFee(p.getInpatientNo(), p.getAdmissTimes(), p.getLedgerSn());
  632. zyDao.deleteSiChrmTemp(p.getInpatientNo(), p.getAdmissTimes(), p.getLedgerSn());
  633. } else {
  634. zyDao.revokePartUploadFee(p.getInpatientNo(), p.getAdmissTimes(), p.getLedgerSn(), p.getDetailSns());
  635. zyDao.deletePartSiChrmTemp(p.getInpatientNo(), p.getAdmissTimes(), p.getLedgerSn(), p.getDetailSns());
  636. }
  637. } else {
  638. // 部分撤销成功,只更新成功撤销的明细
  639. log.info("工伤撤销部分成功,患者:{},成功:{},失败:{}",
  640. p.getInpatientNo(), successDetailSns.size(), failedDetailSns.size());
  641. zyDao.revokePartUploadFee(p.getInpatientNo(), p.getAdmissTimes(), p.getLedgerSn(), successDetailSns);
  642. zyDao.deletePartSiChrmTemp(p.getInpatientNo(), p.getAdmissTimes(), p.getLedgerSn(), successDetailSns);
  643. }
  644. log.info("工伤撤销流程完成,患者:{},成功撤销:{},失败撤销:{}",
  645. p.getInpatientNo(), successDetailSns.size(), failedDetailSns.size());
  646. }
  647. /**
  648. * 准备并上传工伤费用
  649. */
  650. private int[] prepareWorkInjuryUploadFees(Queue<FeeDtle> feeQueue, int index, int feeSize, ZyPatientInfo p, SocketTask socketTask) {
  651. int[] result = new int[] {0,0};
  652. if (feeQueue.isEmpty()) {
  653. return result;
  654. }
  655. // 构建工伤费用上传请求头(处方明细上报)
  656. JSONObject input = new JSONObject();
  657. input.put("action", "transaction");
  658. input.put("transactionName", "2204");
  659. input.put("businessParams", new JSONArray());
  660. List<FeeDtle> tempList = new ArrayList<>();
  661. while (!feeQueue.isEmpty()) {
  662. FeeDtle feeDtle = feeQueue.poll();
  663. assert feeDtle != null;
  664. // 设置医生编码(工伤可能使用不同的编码规则)
  665. if (StringUtil.isBlank(feeDtle.getBilgDrCodg())) {
  666. feeDtle.setBilgDrCodg(redis.getEmployeeSiCode(feeDtle.getOpIdCode()));
  667. }
  668. if (StringUtil.isBlank(feeDtle.getBilgDrCodg())) {
  669. if (StringUtil.notBlank(feeDtle.getDoctorCode())) {
  670. String siCode = redis.getEmployeeSiCode(feeDtle.getDoctorCode());
  671. if (StringUtil.isBlank(siCode)) {
  672. CodeName doctor = redis.getEmployeeCodeRs(feeDtle.getDoctorCode());
  673. sendDoctorNoSiCodeMessage(p, doctor.getStaff(), feeDtle.getFeedetlSn());
  674. continue;
  675. }
  676. } else {
  677. if (StringUtil.notBlank(feeDtle.getReferPhysician())) {
  678. String siCode = redis.getEmployeeSiCode(feeDtle.getReferPhysician());
  679. if (StringUtil.isBlank(siCode)) {
  680. CodeName doctor = redis.getEmployeeCodeRs(feeDtle.getReferPhysician());
  681. sendDoctorNoSiCodeMessage(p, doctor.getStaff(), feeDtle.getFeedetlSn());
  682. continue;
  683. }
  684. } else {
  685. if (StringUtil.notBlank(feeDtle.getOpIdCode())) {
  686. String siCode = redis.getEmployeeSiCode(feeDtle.getOpIdCode());
  687. if (StringUtil.isBlank(siCode)) {
  688. CodeName doctor = redis.getEmployeeCodeRs(feeDtle.getOpIdCode());
  689. sendDoctorNoSiCodeMessage(p, doctor.getStaff(), feeDtle.getFeedetlSn());
  690. continue;
  691. }
  692. }
  693. }
  694. }
  695. }
  696. feeDtle.setOrdersDrCode(feeDtle.getBilgDrCodg());
  697. feeDtle.setOrdersDrName(feeDtle.getBilgDrName());
  698. tempList.add(feeDtle);
  699. if (tempList.size() == 100) {
  700. int[] upldres = executeWorkInjuryUploadFees(input, tempList, p);
  701. index += upldres[0];
  702. if (upldres[1] > 0) {
  703. result[1] = 1;
  704. }
  705. tempList.clear();
  706. socketTask.setPercentage(makePercentage(index, feeSize));
  707. sendTaskProgress(socketTask);
  708. }
  709. }
  710. if (!tempList.isEmpty()) {
  711. int[] upldres = executeWorkInjuryUploadFees(input, tempList, p);
  712. index += upldres[0];
  713. if (upldres[1] > 0) {
  714. result[1] = 1;
  715. }
  716. socketTask.setPercentage(makePercentage(index, feeSize));
  717. sendTaskProgress(socketTask);
  718. }
  719. return result;
  720. }
  721. /**
  722. * 执行工伤费用上传
  723. */
  724. private int[] executeWorkInjuryUploadFees(JSONObject input, List<FeeDtle> fees, ZyPatientInfo p) {
  725. // 构建符合工伤接口文档的businessParams数组
  726. JSONArray businessParams = new JSONArray();
  727. for (FeeDtle feeDtle : fees) {
  728. JSONObject feeItem = new JSONObject();
  729. // 根据工伤接口文档构建字段映射
  730. feeItem.put("ipt_otp_no", p.getMdtrtId()); // 门诊/住院流水号
  731. feeItem.put("list_type", feeDtle.getMedListCodgType() != null ? feeDtle.getMedListCodgType().toString() : "2"); // 三大目录类别:1-药品,2-诊疗项目,3-材料
  732. feeItem.put("rxno", feeDtle.getDrordNo()); // 处方号
  733. feeItem.put("feedetl_sn", feeDtle.getFeedetlSn()); // 处方流水号
  734. feeItem.put("fee_ocur_time", formatDateTime(feeDtle.getFeeOcurTime())); // 处方日期
  735. feeItem.put("med_list_codg", feeDtle.getMedListCodg()); // 收费项目中心编码
  736. feeItem.put("pric", feeDtle.getPric()); // 单价
  737. feeItem.put("cnt", feeDtle.getCnt()); // 数量
  738. feeItem.put("umamt", feeDtle.getDetItemFeeSumamt()); // 总金额
  739. feeItem.put("bilg_dr_codg", feeDtle.getBilgDrCodg()); // 医生编码
  740. feeItem.put("bilg_dept_codg", feeDtle.getBilgDeptCodg()); // 科室编码
  741. // 根据工伤文档要求:空默认为否,所以不传这两个字段,让系统使用默认值
  742. // feeItem.put("min_unit", ""); // 是否最小计量单位
  743. // feeItem.put("allSelfFlag", ""); // 全额自费标志
  744. businessParams.add(feeItem);
  745. }
  746. input.put("businessParams", businessParams);
  747. // 调用工伤费用上传接口
  748. JSONObject result = exec.executeWorkInjuryTrade(input);
  749. // 工伤接口返回结果处理(支持模拟接口和真实接口切换)
  750. Integer infcode = extractWorkInjuryResultCode(result);
  751. String logMsg = (infcode != null ? infcode.toString() : "null") + "," +
  752. (result != null ? result.getString("inf_refmsgid") : "null");
  753. log.info("【操作员:{}】,工伤费用上传:\n患者:{},\n结果:{}", p.getStaffId(),
  754. p.getPsnInfo(), logMsg);
  755. if (infcode == 0) {
  756. // 工伤接口返回结果提取(支持模拟接口和真实接口切换)
  757. JSONArray array = extractWorkInjuryUploadResult(result);
  758. List<SiChargeTemp> tempList = new ArrayList<>();
  759. for (int i = 0; i < array.size(); i++) {
  760. JSONObject upldretrn = array.getJSONObject(i);
  761. // 1. 获取费用明细流水号
  762. Integer feedetlSn = upldretrn.getInteger("feedetl_sn");
  763. if (feedetlSn == null) continue;
  764. // 2. 数据库补全工伤缺失字段
  765. Map<String, Object> missingFields = zyDao.selectWorkInjuryMissingFields(
  766. p.getInpatientNo(), p.getAdmissTimes().intValue(), feedetlSn);
  767. if (missingFields != null && !missingFields.isEmpty()) {
  768. if (upldretrn.getString("rxno") == null && missingFields.get("rxno") != null)
  769. upldretrn.put("rxno", missingFields.get("rxno"));
  770. if (upldretrn.getString("fee_ocur_time") == null && missingFields.get("feeOcurTime") != null)
  771. upldretrn.put("fee_ocur_time", missingFields.get("feeOcurTime"));
  772. if (upldretrn.getString("med_list_codg") == null && missingFields.get("medListCodg") != null)
  773. upldretrn.put("med_list_codg", missingFields.get("medListCodg"));
  774. }
  775. // 3. 字段映射(工伤→医保字段名)
  776. SiChargeTemp chrgtemp = new SiChargeTemp();
  777. chrgtemp.setPatNo(p.getInpatientNo());
  778. chrgtemp.setTimes(p.getAdmissTimes());
  779. chrgtemp.setLedgerSn(p.getLedgerSn());
  780. chrgtemp.setFeedetlSn(feedetlSn);
  781. chrgtemp.setDetItemFeeSumamt(upldretrn.getString("umamt"));
  782. chrgtemp.setFulamtOwnpayAmt(upldretrn.getString("ownpay_amt"));
  783. chrgtemp.setOverlmtAmt(upldretrn.getString("alwpay_amt"));
  784. chrgtemp.setPricUplmtAmt(upldretrn.getString("pric_uplmt_amt"));
  785. chrgtemp.setChrgitmLv(upldretrn.getString("chrgitm_lv"));
  786. chrgtemp.setMemo(upldretrn.getString("memo"));
  787. // 其他字段可设为null或默认
  788. tempList.add(chrgtemp);
  789. }
  790. zyDao.updateTransFlag(p.getInpatientNo(), p.getAdmissTimes(), tempList);
  791. zyDao.insertSiChargeTempFeeBatch(tempList);
  792. return new int[]{tempList.size(), fees.size() - tempList.size()};
  793. } else {
  794. String message = extractWorkInjuryErrorMessage(result);
  795. sendUploadResponse(p, message);
  796. return new int[]{fees.size(), 0};
  797. }
  798. }
  799. /**
  800. * 工伤预结算
  801. */
  802. public ResultVo<String> workInjuryPreSettlement(ZyPatientInfo p, Overview o) {
  803. uploadingMap.remove(o.getInpatientNo());
  804. ZyPreSetlmt zyPreSetlmt;
  805. if (o.getMidSetl()) {
  806. zyPreSetlmt = zyDao.selectPreSetlmtForMidSetl(p.getInpatientNo(), p.getAdmissTimes(),
  807. p.getLedgerSn(), o.getBegntime(), o.getEndtime());
  808. } else {
  809. zyPreSetlmt = zyDao.selectPreSetlmt(p.getInpatientNo(), p.getAdmissTimes(),
  810. p.getLedgerSn(), getTodayEndTime());
  811. }
  812. if (null == zyPreSetlmt || StringUtil.isBlank(zyPreSetlmt.getMdtrtId())) {
  813. return ResultVoUtil.fail(ExceptionEnum.LOGICAL_ERROR, "此患者没有有效的工伤在院信息!");
  814. }
  815. // 设置工伤预结算参数
  816. zyPreSetlmt.setMdtrtCertType(MdtrtCertType.RESIDENT_IDENTITY_CARD.getCode());
  817. zyPreSetlmt.setPsnSetlway(p.getDbg() ? PsnSetlWay.SETTLE_BY_QUOTA.getCode() : PsnSetlWay.SETTLE_BY_ITEMS.getCode());
  818. zyPreSetlmt.setAcctUsedFlag(YesOrNo.NO.getCodeStr());
  819. zyPreSetlmt.setMidSetlFlag(YesOrNo.NO.getCodeStr());
  820. // 构建工伤预结算请求(费用预结算)
  821. JSONObject input = new JSONObject();
  822. input.put("action", "transaction");
  823. input.put("transactionName", "2206");
  824. // 根据工伤接口文档构建预结算参数
  825. JSONObject businessParams = new JSONObject();
  826. businessParams.put("ipt_otp_no", zyPreSetlmt.getMdtrtId()); // 门诊/住院流水号
  827. businessParams.put("mdtrt_id", ""); // 单据号(预结算传空)
  828. businessParams.put("med_type", p.getMedType()); // 医疗类别(从患者信息获取)
  829. businessParams.put("setl_time", formatDateTime(new Date())); // 结算日期(当前时间)
  830. businessParams.put("dscg_time", formatDateTime(new Date())); // 出院日期(当前时间)
  831. businessParams.put("dscg_trt_rslt", "01"); // 出院原因(默认治愈)
  832. // 从数据库获取诊断信息
  833. String diagCode = zyDao.selectDiagCode(p.getInpatientNo(), p.getAdmissTimes());
  834. String diagDscr = zyDao.selectDiagDscr(p.getInpatientNo(), p.getAdmissTimes());
  835. businessParams.put("diag_code", StringUtil.isBlank(diagCode) ? "" : diagCode); // 出院诊断疾病主编码
  836. businessParams.put("psn_no", zyPreSetlmt.getPsnNo()); // 个人唯一识别码
  837. // 从数据库获取科室编码和医生编码
  838. String admDeptCodg = zyDao.selectAdmDeptCodg(p.getInpatientNo(), p.getAdmissTimes(), p.getLedgerSn());
  839. String atddrNo = zyDao.selectAtddrNo(p.getInpatientNo(), p.getAdmissTimes(), p.getLedgerSn());
  840. businessParams.put("adm_dept_codg", StringUtil.isBlank(admDeptCodg) ? "" : admDeptCodg); // 科室编码
  841. businessParams.put("atddr_no", StringUtil.isBlank(atddrNo) ? "" : atddrNo); // 医生编码
  842. businessParams.put("diag_dscr", StringUtil.isBlank(diagDscr) ? "" : diagDscr); // 出院诊断
  843. businessParams.put("occupationalType", "01"); // 职业病类型(默认普通住院)
  844. input.put("businessParams", businessParams);
  845. // 调用工伤预结算接口
  846. JSONObject result = exec.executeWorkInjuryTrade(input);
  847. log.info("工伤预结算:\n参数:{},\n结果:{}", input, result);
  848. if (null == result) {
  849. return ResultVoUtil.fail(ExceptionEnum.NETWORK_ERROR);
  850. }
  851. // 工伤接口返回结果处理(支持模拟接口和真实接口切换)
  852. Integer infcode = extractWorkInjuryResultCode(result);
  853. // 工伤接口日志记录(支持模拟接口和真实接口切换)
  854. JSONObject logInput = new JSONObject();
  855. // 模拟接口处理(当前使用)
  856. if (result != null && result.containsKey("data") && result.getJSONObject("data").containsKey("data")) {
  857. JSONObject innerData = result.getJSONObject("data").getJSONObject("data");
  858. if (innerData.containsKey("transformed_parameters")) {
  859. JSONObject transformedParams = innerData.getJSONObject("transformed_parameters");
  860. logInput.put("infno", transformedParams.getString("infno"));
  861. logInput.put("insuplc_admdvs", "");
  862. logInput.put("msgid", transformedParams.getString("msgid"));
  863. logInput.put("opter", transformedParams.getString("opter"));
  864. } else {
  865. // 如果没有transformed_parameters,使用默认值
  866. logInput.put("infno", "2206");
  867. logInput.put("insuplc_admdvs", "");
  868. logInput.put("msgid", "WORK_INJURY_" + System.currentTimeMillis());
  869. logInput.put("opter", p.getStaffId());
  870. }
  871. }
  872. // 真实接口处理(注释掉,需要时手动切换)
  873. // else if (result != null && result.containsKey("data")) {
  874. // JSONObject data = result.getJSONObject("data");
  875. // if (data.containsKey("transformed_parameters")) {
  876. // JSONObject transformedParams = data.getJSONObject("transformed_parameters");
  877. // logInput.put("infno", transformedParams.getString("infno"));
  878. // logInput.put("insuplc_admdvs", "");
  879. // logInput.put("msgid", transformedParams.getString("msgid"));
  880. // logInput.put("opter", transformedParams.getString("opter"));
  881. // } else {
  882. // // 如果没有transformed_parameters,使用默认值
  883. // logInput.put("infno", "2206");
  884. // logInput.put("insuplc_admdvs", "");
  885. // logInput.put("msgid", "WORK_INJURY_" + System.currentTimeMillis());
  886. // logInput.put("opter", p.getStaffId());
  887. // }
  888. // }
  889. else {
  890. // 如果result为空,使用默认值
  891. logInput.put("infno", "2206");
  892. logInput.put("insuplc_admdvs", "");
  893. logInput.put("msgid", "WORK_INJURY_" + System.currentTimeMillis());
  894. logInput.put("opter", p.getStaffId());
  895. }
  896. logDao.insert(new SiLog(logInput, result, p.getInpatientNo(), p.getAdmissTimes(), p.getLedgerSn(), infcode, zyPreSetlmt.getPsnNo()));
  897. if (null == infcode) {
  898. return ResultVoUtil.fail(ExceptionEnum.LOGICAL_ERROR, "工伤中心报错:" +
  899. (result != null ? result.getString("message") : "未知错误"));
  900. }
  901. if (infcode == 0) {
  902. // 工伤接口返回结果提取(支持模拟接口和真实接口切换)
  903. JSONObject setlinfo = extractWorkInjuryPreSettlementResult(result);
  904. String fundPay = setlinfo != null ? setlinfo.getString("hifp_pay") : "0"; // 工伤使用 hifp_pay 字段
  905. // 更新工伤报销金额(使用相同的表)
  906. zyDao.updateFundPay(fundPay, p.getInpatientNo(), p.getAdmissTimes(), p.getLedgerSn());
  907. zyDao.recountDeposit(p.getInpatientNo(), p.getAdmissTimes(), p.getLedgerSn());
  908. zyDao.updateBalance(p.getInpatientNo(), p.getAdmissTimes(), p.getLedgerSn());
  909. String balance = zyDao.selectLedgerBalance(p.getInpatientNo(), p.getAdmissTimes(), p.getLedgerSn());
  910. if (StringUtil.isBlank(balance)) {
  911. balance = "0";
  912. }
  913. zyDao.updateZyActPatientBalance(p.getInpatientNo(), balance);
  914. String message = "患者【" + p.getName() + "】工伤费用预结算成功,工伤报销金额为:¥ " + fundPay + "。";
  915. return ResultVoUtil.success(message);
  916. }
  917. return ResultVoUtil.fail(ExceptionEnum.LOGICAL_ERROR, extractWorkInjuryErrorMessage(result));
  918. }
  919. /**
  920. * 工伤接口返回结果代码提取(支持模拟接口和真实接口切换)
  921. */
  922. private Integer extractWorkInjuryResultCode(JSONObject result) {
  923. if (result == null) {
  924. return null;
  925. }
  926. // 模拟接口返回结构:result.getJSONObject("data").getJSONObject("data").getString("infcode")
  927. // 真实接口返回结构:result.getJSONObject("data").getString("infcode")
  928. // 模拟接口处理(当前使用)
  929. if (result.containsKey("data") && result.getJSONObject("data").containsKey("data")) {
  930. JSONObject innerData = result.getJSONObject("data").getJSONObject("data");
  931. return innerData != null ? innerData.getInteger(RESULT_CODE) : null;
  932. }
  933. // 真实接口处理(注释掉,需要时手动切换)
  934. // else if (result.containsKey("data")) {
  935. // JSONObject data = result.getJSONObject("data");
  936. // return data != null ? data.getInteger(RESULT_CODE) : null;
  937. // }
  938. return null;
  939. }
  940. /**
  941. * 工伤费用上传返回结果提取(支持模拟接口和真实接口切换)
  942. */
  943. private JSONArray extractWorkInjuryUploadResult(JSONObject result) {
  944. if (result == null) {
  945. return new JSONArray();
  946. }
  947. // 模拟接口返回结构:result.getJSONObject("data").getJSONObject("data").getJSONObject("output")
  948. // 真实接口返回结构:result.getJSONObject("data").getJSONObject("output")
  949. // 模拟接口处理(当前使用)
  950. if (result.containsKey("data") && result.getJSONObject("data").containsKey("data")) {
  951. JSONObject innerData = result.getJSONObject("data").getJSONObject("data");
  952. if (innerData != null && innerData.containsKey(OUTPUT)) {
  953. Object output = innerData.get(OUTPUT);
  954. if (output instanceof JSONArray) {
  955. return (JSONArray) output;
  956. }
  957. }
  958. }
  959. // 真实接口处理(注释掉,需要时手动切换)
  960. // else if (result.containsKey("data")) {
  961. // JSONObject data = result.getJSONObject("data");
  962. // if (data != null && data.containsKey(OUTPUT)) {
  963. // Object output = data.get(OUTPUT);
  964. // if (output instanceof JSONArray) {
  965. // return (JSONArray) output;
  966. // }
  967. // }
  968. // }
  969. return new JSONArray();
  970. }
  971. /**
  972. * 工伤预结算返回结果提取(支持模拟接口和真实接口切换)
  973. */
  974. private JSONObject extractWorkInjuryPreSettlementResult(JSONObject result) {
  975. if (result == null) {
  976. return new JSONObject();
  977. }
  978. // 模拟接口返回结构:result.getJSONObject("data").getJSONObject("data").getJSONObject("output")
  979. // 真实接口返回结构:result.getJSONObject("data").getJSONObject("output")
  980. // 模拟接口处理(当前使用)
  981. if (result.containsKey("data") && result.getJSONObject("data").containsKey("data")) {
  982. JSONObject innerData = result.getJSONObject("data").getJSONObject("data");
  983. if (innerData != null && innerData.containsKey(OUTPUT)) {
  984. Object output = innerData.get(OUTPUT);
  985. if (output instanceof JSONObject) {
  986. return (JSONObject) output;
  987. }
  988. }
  989. }
  990. // 真实接口处理(注释掉,需要时手动切换)
  991. // else if (result.containsKey("data")) {
  992. // JSONObject data = result.getJSONObject("data");
  993. // if (data != null && data.containsKey(OUTPUT)) {
  994. // Object output = data.get(OUTPUT);
  995. // if (output instanceof JSONObject) {
  996. // return (JSONObject) output;
  997. // }
  998. // }
  999. // }
  1000. return new JSONObject();
  1001. }
  1002. /**
  1003. * 工伤接口错误信息提取(支持模拟接口和真实接口切换)
  1004. */
  1005. private String extractWorkInjuryErrorMessage(JSONObject result) {
  1006. if (result == null) {
  1007. return "未知错误";
  1008. }
  1009. // 模拟接口返回结构:result.getJSONObject("data").getJSONObject("data").getString("err_msg")
  1010. // 真实接口返回结构:result.getJSONObject("data").getString("err_msg")
  1011. // 模拟接口处理(当前使用)
  1012. if (result.containsKey("data") && result.getJSONObject("data").containsKey("data")) {
  1013. JSONObject innerData = result.getJSONObject("data").getJSONObject("data");
  1014. if (innerData != null) {
  1015. String errorMsg = innerData.getString(ERROR_MESSAGE);
  1016. return StringUtil.isBlank(errorMsg) ? "未知错误" : errorMsg;
  1017. }
  1018. }
  1019. // 真实接口处理(注释掉,需要时手动切换)
  1020. // else if (result.containsKey("data")) {
  1021. // JSONObject data = result.getJSONObject("data");
  1022. // if (data != null) {
  1023. // String errorMsg = data.getString(ERROR_MESSAGE);
  1024. // return StringUtil.isBlank(errorMsg) ? "未知错误" : errorMsg;
  1025. // }
  1026. // }
  1027. return "未知错误";
  1028. }
  1029. /**
  1030. * 工伤费用预处理
  1031. */
  1032. private void beforeWorkInjuryUpload(Overview o) {
  1033. // 工伤费用预处理逻辑(与医保类似,但可能有特殊规则)
  1034. Date disdate = zyDao.selectDscgDate(o.getInpatientNo(), o.getAdmissTimes());
  1035. if (null != disdate) {
  1036. int num = zyDao.updateChargedate(o.getInpatientNo(), o.getAdmissTimes(), disdate);
  1037. log.info("修改工伤费用时间大于出院医嘱时间的费用条目数:{}", num);
  1038. }
  1039. // 工伤费用正负相抵逻辑(与医保类似)
  1040. List<FeeCounteract> negativeCharges = zyDao.selectNegativeFeesWithOriDetlSn(
  1041. o.getInpatientNo(), o.getAdmissTimes(), o.getLedgerSn());
  1042. if (null == negativeCharges || negativeCharges.isEmpty()) {
  1043. log.info("工伤正负相抵完成,抵消费用总条目:0");
  1044. return;
  1045. }
  1046. List<FeeCounteract> positiveCharges = new ArrayList<>();
  1047. List<Integer> tempSn = new ArrayList<>();
  1048. for (FeeCounteract feeCounteract : negativeCharges) {
  1049. tempSn.add(feeCounteract.getOriDetailSn());
  1050. if (tempSn.size() == 30) {
  1051. positiveCharges.addAll(zyDao.selectPositiveFeesByDetlSn(
  1052. o.getInpatientNo(), o.getAdmissTimes(), o.getLedgerSn(), tempSn));
  1053. tempSn.clear();
  1054. }
  1055. }
  1056. if (!tempSn.isEmpty()) {
  1057. positiveCharges.addAll(zyDao.selectPositiveFeesByDetlSn(
  1058. o.getInpatientNo(), o.getAdmissTimes(), o.getLedgerSn(), tempSn));
  1059. }
  1060. ConcurrentHashMap<Integer, FeeCounteract> positiveChargeMap = new ConcurrentHashMap<>();
  1061. positiveCharges.forEach(itm -> {
  1062. if (!positiveChargeMap.containsKey(itm.getDetailSn())) {
  1063. positiveChargeMap.put(itm.getDetailSn(), itm);
  1064. }
  1065. });
  1066. int count = 0;
  1067. for (FeeCounteract negativeCharge : negativeCharges) {
  1068. if (positiveChargeMap.containsKey(negativeCharge.getOriDetailSn())) {
  1069. FeeCounteract positiveCharge = positiveChargeMap.get(negativeCharge.getOriDetailSn());
  1070. if (positiveCharge.getUsed()) {
  1071. continue;
  1072. }
  1073. if (negativeCharge.getChargeAmount() + positiveCharge.getChargeAmount() == 0d
  1074. && negativeCharge.getChargeFee() + positiveCharge.getChargeFee() == 0d) {
  1075. zyDao.updateYbTransFlagInPair(o.getInpatientNo(),
  1076. o.getAdmissTimes(), negativeCharge.getDetailSn(), negativeCharge.getOriDetailSn());
  1077. count += 2;
  1078. positiveCharge.setUsed(true);
  1079. }
  1080. }
  1081. }
  1082. log.info("工伤正负相抵完成,抵消费用总条目:{}", count);
  1083. }
  1084. /**
  1085. * 格式化日期时间为工伤接口要求的格式
  1086. * @param date 日期时间
  1087. * @return 格式化的日期时间字符串 YYYYMMDDHH24MISS
  1088. */
  1089. private String formatDateTime(Date date) {
  1090. if (date == null) {
  1091. return "";
  1092. }
  1093. SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
  1094. return sdf.format(date);
  1095. }
  1096. }