| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.smartor.common; |
| | | |
| | | import com.smartor.domain.ShiyiSmsRequest; |
| | | import com.smartor.domain.ShiyiSmsResponse; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.apache.commons.lang3.StringUtils; |
| | | import org.springframework.beans.factory.annotation.Value; |
| | | import org.springframework.http.HttpEntity; |
| | | import org.springframework.http.HttpHeaders; |
| | | import org.springframework.http.MediaType; |
| | | import org.springframework.http.ResponseEntity; |
| | | import org.springframework.stereotype.Component; |
| | | import org.springframework.web.client.RestTemplate; |
| | | |
| | | import javax.xml.parsers.DocumentBuilder; |
| | | import javax.xml.parsers.DocumentBuilderFactory; |
| | | import org.w3c.dom.Document; |
| | | import org.w3c.dom.Element; |
| | | import org.w3c.dom.Node; |
| | | import org.w3c.dom.NodeList; |
| | | import org.xml.sax.InputSource; |
| | | |
| | | import java.io.StringReader; |
| | | import java.nio.charset.StandardCharsets; |
| | | import java.text.SimpleDateFormat; |
| | | import java.util.Date; |
| | | |
| | | /** |
| | | * æå·å¸ä¸ HIS æå¡å¹³å°å¯¹æ¥å·¥å
፱ȋ |
| | | * <p> |
| | | * æãHIS æå¡å¹³å°æ¥å£è¯´æææ¡£ V1.1ã3.1 è约å®ï¼HIS 对å¤ä»¥ WCF Web Service å½¢å¼åå¸ï¼ |
| | | * ç»ä¸å
¥å£ï¼{@code int RunService(string TradeType, string TradeMsg, ref string TradeOut)}ï¼ |
| | | * å
¶ä¸ {@code TradeType} æ è¯ä¸å¡ç¼ç ï¼{@code TradeMsg} 为 UTF-8 ç¼ç ç XML æ¥æã |
| | | * æ¬å·¥å
·ç±»ç®åå®ç° 5.2 çä¿¡ä¸å¡ (TradeType=FASONGDX)ã |
| | | */ |
| | | @Slf4j |
| | | @Component |
| | | public class ShiyiSmsUtil { |
| | | |
| | | /** 5.2 çä¿¡ä¸å¡ TradeType */ |
| | | public static final String TRADE_TYPE_FASONGDX = "FASONGDX"; |
| | | |
| | | /** WCF å½å空é´ï¼é»è®¤éææ¡£ç¤ºä¾ */ |
| | | private String namespace = "http://tempuri.org/"; |
| | | |
| | | /** é»è®¤æä½å代ç */ |
| | | @Value("${his.service.defaultCaozuoydm:}") |
| | | private String defaultCaozuoydm; |
| | | |
| | | /** é»è®¤æä½åå§å */ |
| | | @Value("${his.service.defaultCaozuoyxm:}") |
| | | private String defaultCaozuoyxm; |
| | | |
| | | /** é»è®¤ç³»ç»æ è¯ */ |
| | | private String defaultXitongbs = "0"; |
| | | |
| | | /** é»è®¤åé¢ä»£ç */ |
| | | private String defaultFenyuandm = "1"; |
| | | |
| | | /** é»è®¤æºæä»£ç */ |
| | | private String defaultJigoudm = "1"; |
| | | |
| | | private final RestTemplate restTemplate = new RestTemplate(); |
| | | |
| | | /** |
| | | * åéçä¿¡ (FASONGDX) |
| | | */ |
| | | public ShiyiSmsResponse sendSms(ShiyiSmsRequest request) { |
| | | if (request == null) { |
| | | throw new IllegalArgumentException("ç信请æ±ä¸è½ä¸ºç©º"); |
| | | } |
| | | if (StringUtils.isBlank(request.getShoujihao())) { |
| | | throw new IllegalArgumentException("ææºå·ä¸è½ä¸ºç©º"); |
| | | } |
| | | if (StringUtils.isBlank(request.getDuanxinnr())) { |
| | | throw new IllegalArgumentException("çä¿¡å
容ä¸è½ä¸ºç©º"); |
| | | } |
| | | |
| | | applyDefaults(request); |
| | | |
| | | String tradeMsg = buildFasongdxXml(request); |
| | | log.info("å¸ä¸ ç信请æ±, TradeType={}, TradeMsg={}", TRADE_TYPE_FASONGDX, tradeMsg); |
| | | |
| | | String tradeOut = invokeRunService(TRADE_TYPE_FASONGDX, tradeMsg); |
| | | log.info("å¸ä¸ çä¿¡ååº, TradeOut={}", tradeOut); |
| | | |
| | | return parseFasongdxResponse(tradeOut); |
| | | } |
| | | |
| | | /** |
| | | * è°ç¨ å¸ä¸ WCF RunService æ¥å£ãSOAP 1.1 åºç¡é´ææææ¡£ä¸è¦æ±ã |
| | | * |
| | | * @return TradeOut æ¥æï¼æå¡ç«¯ ref åæ°ï¼ |
| | | */ |
| | | public String invokeRunService(String tradeType, String tradeMsg) { |
| | | String soapEnvelope = buildSoapEnvelope(tradeType, tradeMsg); |
| | | String hisServiceUrl = "http://192.200.54.57:7790/MediInfoHis.svc"; |
| | | |
| | | HttpHeaders headers = new HttpHeaders(); |
| | | headers.setContentType(new MediaType("text", "xml", StandardCharsets.UTF_8)); |
| | | headers.set("SOAPAction", joinNamespace(namespace, "IMediInfoHis/RunService")); |
| | | |
| | | HttpEntity<String> entity = new HttpEntity<>(soapEnvelope, headers); |
| | | try { |
| | | ResponseEntity<String> response = restTemplate.postForEntity(hisServiceUrl, entity, String.class); |
| | | String body = response.getBody(); |
| | | if (StringUtils.isBlank(body)) { |
| | | throw new RuntimeException("HIS è¿å空ååº"); |
| | | } |
| | | return extractTradeOut(body); |
| | | } catch (Exception e) { |
| | | log.error("è°ç¨ HIS RunService 失败, url={}, tradeType={}, err={}", hisServiceUrl, tradeType, e.getMessage(), e); |
| | | throw new RuntimeException("è°ç¨ HIS RunService 失败: " + e.getMessage(), e); |
| | | } |
| | | } |
| | | |
| | | private void applyDefaults(ShiyiSmsRequest request) { |
| | | if (StringUtils.isBlank(request.getCaozuoydm())) { |
| | | request.setCaozuoydm(defaultCaozuoydm); |
| | | } |
| | | if (StringUtils.isBlank(request.getCaozuoyxm())) { |
| | | request.setCaozuoyxm(defaultCaozuoyxm); |
| | | } |
| | | if (StringUtils.isBlank(request.getCaozuorq())) { |
| | | request.setCaozuorq(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())); |
| | | } |
| | | if (StringUtils.isBlank(request.getXitongbs())) { |
| | | request.setXitongbs(defaultXitongbs); |
| | | } |
| | | if (StringUtils.isBlank(request.getFenyuandm())) { |
| | | request.setFenyuandm(defaultFenyuandm); |
| | | } |
| | | if (StringUtils.isBlank(request.getJigoudm())) { |
| | | request.setJigoudm(defaultJigoudm); |
| | | } |
| | | if (StringUtils.isBlank(request.getDuanxinlx())) { |
| | | request.setDuanxinlx("0"); |
| | | } |
| | | } |
| | | |
| | | private String buildFasongdxXml(ShiyiSmsRequest r) { |
| | | StringBuilder sb = new StringBuilder(512); |
| | | sb.append("<?xml version=\"1.0\" encoding=\"utf-8\"?>"); |
| | | sb.append("<FASONGDX_IN>"); |
| | | sb.append("<BASEINFO>"); |
| | | appendTag(sb, "CAOZUOYDM", r.getCaozuoydm()); |
| | | appendTag(sb, "CAOZUOYXM", r.getCaozuoyxm()); |
| | | appendTag(sb, "CAOZUORQ", r.getCaozuorq()); |
| | | appendTag(sb, "XITONGBS", r.getXitongbs()); |
| | | appendTag(sb, "FENYUANDM", r.getFenyuandm()); |
| | | appendTag(sb, "JIGOUDM", r.getJigoudm()); |
| | | appendTag(sb, "JIGOUMC", r.getJigoumc()); |
| | | appendTag(sb, "JIGOUYZM", r.getJigouyzm()); |
| | | appendTag(sb, "JIESHOUJGDM", r.getJieshoujgdm()); |
| | | appendTag(sb, "ZHONGDUANJB", r.getZhongduanjb()); |
| | | appendTag(sb, "ZHONGDUANLS", r.getZhongduanls()); |
| | | appendTag(sb, "IPADDRESS", r.getIpaddress()); |
| | | appendTag(sb, "YEWULX", r.getYewulx()); |
| | | appendTag(sb, "JIERUCSDM", r.getJierucsdm()); |
| | | sb.append("</BASEINFO>"); |
| | | appendTag(sb, "DUANXINLX", r.getDuanxinlx()); |
| | | appendTag(sb, "SHOUJIHAO", r.getShoujihao()); |
| | | appendTag(sb, "DUANXINNR", r.getDuanxinnr()); |
| | | sb.append("</FASONGDX_IN>"); |
| | | return sb.toString(); |
| | | } |
| | | |
| | | private ShiyiSmsResponse parseFasongdxResponse(String xml) { |
| | | ShiyiSmsResponse resp = new ShiyiSmsResponse(); |
| | | resp.setRawXml(xml); |
| | | if (StringUtils.isBlank(xml)) { |
| | | resp.setErrno("-1"); |
| | | resp.setErrmsg("HIS è¿å空 TradeOut"); |
| | | return resp; |
| | | } |
| | | try { |
| | | Document doc = parseXml(xml); |
| | | resp.setErrno(readTag(doc, "ERRNO")); |
| | | resp.setErrmsg(readTag(doc, "ERRMSG")); |
| | | resp.setErrmsgex(readTag(doc, "ERRMSGEX")); |
| | | resp.setMessageId(readTag(doc, "MessageID")); |
| | | resp.setDuanxinid(readTag(doc, "DUANXINID")); |
| | | } catch (Exception e) { |
| | | log.error("è§£æ HIS çä¿¡ååºå¤±è´¥, xml={}, err={}", xml, e.getMessage(), e); |
| | | resp.setErrno("-1"); |
| | | resp.setErrmsg("è§£æååºå¤±è´¥: " + e.getMessage()); |
| | | } |
| | | return resp; |
| | | } |
| | | |
| | | private String buildSoapEnvelope(String tradeType, String tradeMsg) { |
| | | // WCF é»è®¤ BasicHttpBinding 䏿¥å£æ¹æ³ä»¥ Message Contract å½¢å¼åå¸ï¼åæ°èç¹åä¸ C# æ¹æ³ç¾åä¸è´ã |
| | | // TradeOut 为 ref åæ°ï¼å
¥åä¹éä¼ ç©ºå
ç´ å ä½ã |
| | | return "<?xml version=\"1.0\" encoding=\"utf-8\"?>" |
| | | + "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:t=\"" + namespace + "\">" |
| | | + "<s:Body>" |
| | | + "<t:RunService>" |
| | | + "<t:TradeType>" + escapeXml(tradeType) + "</t:TradeType>" |
| | | + "<t:TradeMsg>" + escapeXml(tradeMsg) + "</t:TradeMsg>" |
| | | + "<t:TradeOut></t:TradeOut>" |
| | | + "</t:RunService>" |
| | | + "</s:Body>" |
| | | + "</s:Envelope>"; |
| | | } |
| | | |
| | | private String extractTradeOut(String soapResponse) throws Exception { |
| | | Document doc = parseXml(soapResponse); |
| | | // TradeOut èç¹ (带å½å空é´) |
| | | String tradeOut = readTagIgnoreNs(doc, "TradeOut"); |
| | | if (StringUtils.isBlank(tradeOut)) { |
| | | // æäº WCF å®ç°ä¼æ ref åæ°è¾åºèç¹å½å为 RunServiceResult çå级 TradeOut |
| | | tradeOut = readTagIgnoreNs(doc, "tradeOut"); |
| | | } |
| | | return tradeOut; |
| | | } |
| | | |
| | | private Document parseXml(String xml) throws Exception { |
| | | DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); |
| | | factory.setNamespaceAware(false); |
| | | factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); |
| | | factory.setFeature("http://xml.org/sax/features/external-general-entities", false); |
| | | factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false); |
| | | DocumentBuilder builder = factory.newDocumentBuilder(); |
| | | return builder.parse(new InputSource(new StringReader(xml))); |
| | | } |
| | | |
| | | private String readTag(Document doc, String name) { |
| | | NodeList list = doc.getElementsByTagName(name); |
| | | if (list.getLength() == 0) { |
| | | return null; |
| | | } |
| | | return list.item(0).getTextContent(); |
| | | } |
| | | |
| | | private String readTagIgnoreNs(Document doc, String localName) { |
| | | NodeList all = doc.getElementsByTagName("*"); |
| | | for (int i = 0; i < all.getLength(); i++) { |
| | | Node node = all.item(i); |
| | | if (node instanceof Element) { |
| | | Element el = (Element) node; |
| | | String local = el.getLocalName() != null ? el.getLocalName() : el.getNodeName(); |
| | | if (local.equalsIgnoreCase(localName) || local.endsWith(":" + localName)) { |
| | | return el.getTextContent(); |
| | | } |
| | | if (el.getNodeName().endsWith(":" + localName) || el.getNodeName().equalsIgnoreCase(localName)) { |
| | | return el.getTextContent(); |
| | | } |
| | | } |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | private void appendTag(StringBuilder sb, String tag, String value) { |
| | | sb.append('<').append(tag).append('>'); |
| | | if (StringUtils.isNotBlank(value)) { |
| | | sb.append(escapeXml(value)); |
| | | } |
| | | sb.append("</").append(tag).append('>'); |
| | | } |
| | | |
| | | private String escapeXml(String input) { |
| | | if (input == null) { |
| | | return ""; |
| | | } |
| | | StringBuilder out = new StringBuilder(input.length()); |
| | | for (int i = 0; i < input.length(); i++) { |
| | | char c = input.charAt(i); |
| | | switch (c) { |
| | | case '&': |
| | | out.append("&"); |
| | | break; |
| | | case '<': |
| | | out.append("<"); |
| | | break; |
| | | case '>': |
| | | out.append(">"); |
| | | break; |
| | | case '"': |
| | | out.append("""); |
| | | break; |
| | | case '\'': |
| | | out.append("'"); |
| | | break; |
| | | default: |
| | | out.append(c); |
| | | } |
| | | } |
| | | return out.toString(); |
| | | } |
| | | |
| | | private String joinNamespace(String ns, String op) { |
| | | if (ns == null) { |
| | | return "\"" + op + "\""; |
| | | } |
| | | if (ns.endsWith("/")) { |
| | | return "\"" + ns + op + "\""; |
| | | } |
| | | return "\"" + ns + "/" + op + "\""; |
| | | } |
| | | } |