WxApiService.java 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413
  1. package thyyxxk.wxservice_server.service;
  2. import com.alibaba.fastjson.JSONObject;
  3. import lombok.extern.slf4j.Slf4j;
  4. import org.apache.http.client.methods.CloseableHttpResponse;
  5. import org.apache.http.client.methods.HttpPost;
  6. import org.apache.http.entity.StringEntity;
  7. import org.apache.http.impl.client.CloseableHttpClient;
  8. import org.apache.http.util.EntityUtils;
  9. import org.dom4j.Document;
  10. import org.dom4j.DocumentException;
  11. import org.dom4j.DocumentHelper;
  12. import org.dom4j.Element;
  13. import org.springframework.beans.factory.annotation.Autowired;
  14. import org.springframework.http.converter.HttpMessageConverter;
  15. import org.springframework.http.converter.StringHttpMessageConverter;
  16. import org.springframework.stereotype.Service;
  17. import org.springframework.web.client.RestTemplate;
  18. import thyyxxk.wxservice_server.config.exception.ExceptionEnum;
  19. import thyyxxk.wxservice_server.constant.OrderType;
  20. import thyyxxk.wxservice_server.constant.TradeState;
  21. import thyyxxk.wxservice_server.dao.WxApiDao;
  22. import thyyxxk.wxservice_server.entity.ResultVo;
  23. import thyyxxk.wxservice_server.entity.appointment.DoctorInfo;
  24. import thyyxxk.wxservice_server.entity.appointment.WeChatPayParam;
  25. import thyyxxk.wxservice_server.entity.wxapi.JsApiSHA1;
  26. import thyyxxk.wxservice_server.entity.wxapi.GenMzPayQrcodeParam;
  27. import thyyxxk.wxservice_server.entity.wxapi.WxPayOrder;
  28. import thyyxxk.wxservice_server.entity.wxapi.WxPyQrcdPrm;
  29. import thyyxxk.wxservice_server.utils.*;
  30. import java.io.IOException;
  31. import java.nio.charset.StandardCharsets;
  32. import java.util.*;
  33. import java.util.concurrent.TimeUnit;
  34. /**
  35. * @author dj
  36. */
  37. @Slf4j
  38. @Service
  39. public class WxApiService {
  40. private final WxApiDao dao;
  41. private final SpringRetryService retryService;
  42. private final SavePayResultService savePayResultService;
  43. @Autowired
  44. public WxApiService(WxApiDao dao, SpringRetryService retryService, SavePayResultService savePayResultService) {
  45. this.dao = dao;
  46. this.retryService = retryService;
  47. this.savePayResultService = savePayResultService;
  48. }
  49. public ResultVo<JsApiSHA1> getJsapiSHA1Sign(JsApiSHA1 data) throws Exception {
  50. data.setAppId(PropertiesUtil.getLocalProperty("appId"));
  51. data.setTicket(PropertiesUtil.getLocalProperty("ticket"));
  52. data.setNoncestr(SnowFlakeId.instance().nextId());
  53. data.setTimestamp(String.valueOf(System.currentTimeMillis() / 1000));
  54. String toSign = "jsapi_ticket=" + data.getTicket() + "&noncestr=" + data.getNoncestr() +
  55. "&timestamp=" + data.getTimestamp() + "&url=" + data.getUrl();
  56. data.setSignature(WxPaySignUtil.sha1Encode(toSign));
  57. log.info("获取wxJsApi签名:{}", data);
  58. return ResultVoUtil.success(data);
  59. }
  60. public ResultVo<WxPayOrder> createPayOrder(WeChatPayParam param) throws Exception {
  61. WxPayOrder existOrder = null;
  62. if (param.getOrderType() == OrderType.CLINIC_REGISTER.getCode() ||
  63. param.getOrderType() == OrderType.REGISTRATION.getCode()) {
  64. existOrder = dao.selectSameGhOrder(param.getPatientId(), param.getMzyRequestId());
  65. } else if (param.getOrderType() == OrderType.OUTPATIENT.getCode()) {
  66. existOrder = dao.selectSameMzPayOrder(param.getHisOrdNum(), param.getTotalFee().doubleValue());
  67. }
  68. if (null != existOrder) {
  69. if (existOrder.getPayStatus() == TradeState.SUCCESS.getCode()) {
  70. return ResultVoUtil.fail(ExceptionEnum.LOGICAL_ERROR, "您已支付过一笔相同金额的订单,请勿重复支付。");
  71. }
  72. if (StringUtil.notBlank(existOrder.getPaySign()) && StringUtil.notBlank(existOrder.getPrepayId())) {
  73. if (existOrder.getPayStatus() == TradeState.NEWORDER.getCode() ||
  74. existOrder.getPayStatus() == TradeState.NOTPAY.getCode()) {
  75. if (DateUtil.orderValid(existOrder.getCreateDatetime())) {
  76. return ResultVoUtil.success(existOrder);
  77. }
  78. }
  79. if (null == param.getYjReqNo()) {
  80. param.setYjReqNo(existOrder.getYjReqNo());
  81. }
  82. }
  83. }
  84. String appId = PropertiesUtil.getLocalProperty("appId");
  85. String merchantId = PropertiesUtil.getLocalProperty("mchId");
  86. String tradeNo = SnowFlakeId.instance().nextId();
  87. String nonceStr = SnowFlakeId.instance().nextId();
  88. String totalFee = DecimalTool.moneyYuanToFen(param.getTotalFee());
  89. String notifyUrl = "http://staticweb.hnthyy.cn/wxserver/wxPayNotify/notify2";
  90. TreeMap<String, String> map = new TreeMap<>();
  91. map.put("appid", appId);
  92. map.put("mch_id", merchantId);
  93. map.put("device_info", "WEB");
  94. map.put("body", param.getBody());
  95. map.put("trade_type", "JSAPI");
  96. map.put("nonce_str", nonceStr);
  97. map.put("notify_url", notifyUrl);
  98. map.put("out_trade_no", tradeNo);
  99. map.put("total_fee", totalFee);
  100. map.put("spbill_create_ip", param.getClientIp());
  101. map.put("openid", param.getOpenId());
  102. String createOrderSign = WxPaySignUtil.createSign(map);
  103. String xml = "<xml>" +
  104. "<appid>" + appId + "</appid>" +
  105. "<body>" + param.getBody() + "</body>" +
  106. "<device_info>WEB</device_info>" +
  107. "<mch_id>" + merchantId + "</mch_id>" +
  108. "<nonce_str>" + nonceStr + "</nonce_str>" +
  109. "<notify_url>" + notifyUrl + "</notify_url>" +
  110. "<openid>" + param.getOpenId() + "</openid>" +
  111. "<out_trade_no>" + tradeNo + "</out_trade_no>" +
  112. "<total_fee>" + totalFee + "</total_fee>" +
  113. "<spbill_create_ip>" + param.getClientIp() + "</spbill_create_ip>" +
  114. "<trade_type>JSAPI</trade_type>" +
  115. "<sign>" + createOrderSign + "</sign>" +
  116. "</xml>";
  117. String url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
  118. RestTemplate restTemplate = new RestTemplate();
  119. List<HttpMessageConverter<?>> list = restTemplate.getMessageConverters();
  120. for (HttpMessageConverter<?> httpMessageConverter : list) {
  121. if (httpMessageConverter instanceof StringHttpMessageConverter) {
  122. ((StringHttpMessageConverter) httpMessageConverter).setDefaultCharset(StandardCharsets.UTF_8);
  123. break;
  124. }
  125. }
  126. String str = restTemplate.postForObject(url, xml, String.class);
  127. try {
  128. assert str != null;
  129. Document document = DocumentHelper.parseText(str);
  130. Element root = document.getRootElement();
  131. if ("SUCCESS".equals(root.element("return_code").getStringValue())) {
  132. TreeMap<String, String> back = new TreeMap<>();
  133. if (null == root.element("prepay_id")) {
  134. final String message = root.element("err_code_des").getStringValue();
  135. log.info("微信统一下单失败:{}", message);
  136. return ResultVoUtil.fail(ExceptionEnum.LOGICAL_ERROR, message);
  137. }
  138. String prepayId = root.element("prepay_id").getStringValue();
  139. String timeStamp = Long.toString(System.currentTimeMillis());
  140. back.put("appId", PropertiesUtil.getLocalProperty("appId"));
  141. back.put("timeStamp", timeStamp);
  142. back.put("nonceStr", nonceStr);
  143. back.put("package", "prepay_id=" + prepayId);
  144. back.put("signType", "MD5");
  145. String paySign = WxPaySignUtil.createSign(back);
  146. WxPayOrder order = new WxPayOrder();
  147. order.setBody(param.getBody());
  148. order.setOrderType(param.getOrderType());
  149. order.setOpenId(param.getOpenId());
  150. order.setTotalFee(param.getTotalFee());
  151. order.setPatientId(param.getPatientId());
  152. order.setPatientName(dao.selectPatientName(param.getPatientId()));
  153. order.setAppId(appId);
  154. order.setMchId(merchantId);
  155. order.setPrepayId(prepayId);
  156. order.setTimeStamp(timeStamp);
  157. order.setSerialNo(nonceStr);
  158. order.setTradeNo(tradeNo);
  159. order.setCreateOrderSign(createOrderSign);
  160. order.setPaySign(paySign);
  161. order.setSpbillCreateIp(param.getClientIp());
  162. order.setCreateDatetime(new Date());
  163. order.setPayStatus(TradeState.NEWORDER.getCode());
  164. order.setHisOrdNum(param.getHisOrdNum());
  165. order.setMzyRequestId(param.getMzyRequestId());
  166. order.setYjReqNo(param.getYjReqNo());
  167. order.setInpatientNo(param.getInpatientNo());
  168. order.setAdmissTimes(param.getAdmissTimes());
  169. dao.insertNewOrder(order);
  170. log.info("统一下单成功:{}", order);
  171. return ResultVoUtil.success(order);
  172. }
  173. final String message = root.element("return_msg").getStringValue();
  174. log.error("微信统一下单失败:{}", message);
  175. return ResultVoUtil.fail(ExceptionEnum.LOGICAL_ERROR, message);
  176. } catch (DocumentException e) {
  177. e.printStackTrace();
  178. return ResultVoUtil.fail(ExceptionEnum.INTERNAL_SERVER_ERROR, e.getMessage());
  179. }
  180. }
  181. public ResultVo<Object> queryOrderStateOnly(String tradeNo) throws IOException {
  182. return ResultVoUtil.success(retryService.queryOrderStateFromTencent(tradeNo, OrderType.OUTPATIENT));
  183. }
  184. public ResultVo<Object> queryOrderState(String tradeNo) {
  185. try {
  186. while (TradeVectorUtil.tradeNoBeingQuery(tradeNo) || TradeVectorUtil.tradeNoBeingRefund(tradeNo)) {
  187. log.info("订单号:{} 正在查询状态中,进入等待区。", tradeNo);
  188. TimeUnit.SECONDS.sleep(3);
  189. }
  190. TradeVectorUtil.addBeingQuery(tradeNo);
  191. WxPayOrder order = dao.selectOrderByTradeNo(tradeNo);
  192. OrderType orderType = OrderType.get(order.getOrderType());
  193. int hasSaved = 0;
  194. if (orderType == OrderType.REGISTRATION || orderType == OrderType.CLINIC_REGISTER) {
  195. hasSaved = dao.alreadyPaidRegisterFee(order.getTradeNo());
  196. } else if (orderType == OrderType.OUTPATIENT) {
  197. hasSaved = dao.alreadyPayed(order.getTradeNo());
  198. }
  199. if (hasSaved == 1) {
  200. dao.updateSuccessHisStatus(tradeNo);
  201. if (orderType == OrderType.REGISTRATION) {
  202. log.info("订单号:{} 的挂号信息已保存,无需再次查询订单状态。", order.getTradeNo());
  203. return ResultVoUtil.success("保存挂号信息成功。");
  204. }
  205. log.info("订单号:{} 的门诊缴费信息已保存,无需再次查询订单状态。", order.getTradeNo());
  206. return ResultVoUtil.success("保存门诊缴费信息成功。");
  207. }
  208. JSONObject obj = retryService.queryOrderStateFromTencent(tradeNo, orderType);
  209. TradeState tradeState = TradeState.get(obj.getString("trade_state"));
  210. if (tradeState.equals(TradeState.SUCCESS)) {
  211. String successTime = obj.getString("success_time")
  212. .split("\\+")[0].replace("T", " ");
  213. dao.updatePayStatusAndPayTime(tradeNo, tradeState.getCode(), successTime);
  214. switch (orderType) {
  215. case CLINIC_REGISTER:
  216. return savePayResultService.saveClinicRegister(order);
  217. case REGISTRATION:
  218. return savePayResultService.saveAppointment(order);
  219. case OUTPATIENT:
  220. return savePayResultService.saveMzChargeInfo(order, successTime);
  221. case INPATIENT_PRE_PAY:
  222. return savePayResultService.saveZyYjjInfo(order);
  223. case SELF_HELP_MACHINE:
  224. case INSPECTIONS:
  225. return ResultVoUtil.success("订单已支付。");
  226. default:
  227. return ResultVoUtil.fail(ExceptionEnum.LOGICAL_ERROR, "未识别到的订单类型,请联系服务中心。");
  228. }
  229. }
  230. dao.updatePayStatusAndQueryTimes(tradeNo, tradeState.getCode());
  231. return ResultVoUtil.fail(ExceptionEnum.INTERNAL_SERVER_ERROR, tradeState.getLabel());
  232. } catch (Exception e) {
  233. e.printStackTrace();
  234. return ResultVoUtil.fail(ExceptionEnum.LOGICAL_ERROR, "服务器错误,请联系管理员处理。【订单号:" + tradeNo + "】");
  235. } finally {
  236. TradeVectorUtil.removeBeingQuery(tradeNo);
  237. }
  238. }
  239. public ResultVo<String> generateMzGuideBillPayQrcode(GenMzPayQrcodeParam param) throws Exception {
  240. log.info("【{}】请求门诊指引单二维码入参:{}", param.getHisOrdNum(), param);
  241. String outTradeNo = SnowFlakeId.instance().nextId();
  242. JSONObject body = new JSONObject();
  243. body.put("appid", PropertiesUtil.getLocalProperty("appId"));
  244. body.put("mchid", PropertiesUtil.getLocalProperty("mchId"));
  245. body.put("description", "湖南泰和医院-门诊缴费");
  246. body.put("attach", "湖南泰和医院-门诊缴费");
  247. body.put("out_trade_no", outTradeNo);
  248. body.put("notify_url", PropertiesUtil.getLocalProperty("notifyUrl"));
  249. JSONObject cny = new JSONObject();
  250. cny.put("total", param.getTotalAmt());
  251. cny.put("currency", "CNY");
  252. body.put("amount", cny);
  253. String reqdata = JSONObject.toJSONString(body);
  254. HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/pay/transactions/native");
  255. StringEntity entity = new StringEntity(reqdata, StandardCharsets.UTF_8);
  256. entity.setContentEncoding("UTF-8");
  257. entity.setContentType("application/json");
  258. httpPost.setEntity(entity);
  259. httpPost.addHeader("Content-Type", "application/json;charset=UTF-8");
  260. httpPost.addHeader("Accept", "application/json");
  261. //完成签名并执行请求
  262. CloseableHttpClient httpClient = WxHttpUtil.getClosableHttpClient();
  263. CloseableHttpResponse response = httpClient.execute(httpPost);
  264. int statusCode = response.getStatusLine().getStatusCode();
  265. String ret = EntityUtils.toString(response.getEntity());
  266. httpClient.close();
  267. log.info("【{}】请求门诊指引单二维码结果:{}", param.getHisOrdNum(), ret);
  268. if (statusCode == ExceptionEnum.SUCCESS.getCode()) {
  269. long timesStamp = System.currentTimeMillis() / 1000;
  270. WxPayOrder order = new WxPayOrder();
  271. order.setAppId(PropertiesUtil.getLocalProperty("appId"));
  272. order.setBody("门诊缴费");
  273. order.setOpenId("");
  274. order.setTotalFee(DecimalTool.moneyFenToYuan(param.getTotalAmt()));
  275. order.setPatientId(param.getPatientId());
  276. order.setPatientName(dao.selectPatientName(param.getPatientId()));
  277. order.setMchId(PropertiesUtil.getLocalProperty("mchId"));
  278. order.setTimeStamp(String.valueOf(timesStamp));
  279. order.setTradeNo(outTradeNo);
  280. order.setCreateDatetime(new Date());
  281. order.setPayStatus(TradeState.NOTPAY.getCode());
  282. order.setSerialNo(SnowFlakeId.instance().nextId());
  283. order.setOrderType(OrderType.OUTPATIENT.getCode());
  284. order.setHisOrdNum(param.getHisOrdNum());
  285. dao.insertNewOrder(order);
  286. JSONObject retObj = JSONObject.parseObject(ret);
  287. return ResultVoUtil.success(retObj.getString("code_url"));
  288. }
  289. log.error("【{}】请求门诊指引单二维码失败:{}",param.getHisOrdNum(), ret);
  290. return ResultVoUtil.fail(ExceptionEnum.LOGICAL_ERROR, "请求微信支付二维码失败,请联系管理员。");
  291. }
  292. public ResultVo<Map<String, String>> getWxPayQrcode(WxPyQrcdPrm param) throws Exception {
  293. String patName = dao.selectPatientName(param.getPatientId());
  294. if (null == patName) {
  295. return ResultVoUtil.fail(ExceptionEnum.NULL_POINTER, "没有找到患者信息,请检查patientId是否正确!");
  296. }
  297. WxPayOrder order = new WxPayOrder();
  298. if (param.getOrderType() == OrderType.INPATIENT_PRE_PAY.getCode()) {
  299. if (StringUtil.isBlank(param.getInpatientNo())) {
  300. return ResultVoUtil.fail(ExceptionEnum.NULL_POINTER, "费用类型为住院预交金时,住院号不能为空!");
  301. }
  302. if (null == param.getAdmissTimes()) {
  303. return ResultVoUtil.fail(ExceptionEnum.NULL_POINTER, "费用类型为住院预交金时,住院次数不能为空!");
  304. }
  305. order.setInpatientNo(param.getInpatientNo());
  306. order.setAdmissTimes(param.getAdmissTimes());
  307. }
  308. String outTradeNo = SnowFlakeId.instance().nextId();
  309. JSONObject body = new JSONObject();
  310. body.put("appid", PropertiesUtil.getLocalProperty("appId"));
  311. body.put("mchid", PropertiesUtil.getLocalProperty("mchId"));
  312. body.put("description", param.getDescription());
  313. body.put("attach", param.getDescription());
  314. body.put("out_trade_no", outTradeNo);
  315. body.put("notify_url", PropertiesUtil.getLocalProperty("notifyUrl"));
  316. JSONObject cny = new JSONObject();
  317. cny.put("total", param.getTotalAmt());
  318. cny.put("currency", "CNY");
  319. body.put("amount", cny);
  320. String reqdata = JSONObject.toJSONString(body);
  321. HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/pay/transactions/native");
  322. StringEntity entity = new StringEntity(reqdata, StandardCharsets.UTF_8);
  323. entity.setContentEncoding("UTF-8");
  324. entity.setContentType("application/json");
  325. httpPost.setEntity(entity);
  326. httpPost.addHeader("Content-Type", "application/json;charset=UTF-8");
  327. httpPost.addHeader("Accept", "application/json");
  328. CloseableHttpClient httpClient = WxHttpUtil.getClosableHttpClient();
  329. CloseableHttpResponse response = httpClient.execute(httpPost);
  330. int statusCode = response.getStatusLine().getStatusCode();
  331. String ret = EntityUtils.toString(response.getEntity());
  332. httpClient.close();
  333. log.info("请求微信支付二维码:{},结果:{}", param, ret);
  334. if (statusCode == ExceptionEnum.SUCCESS.getCode()) {
  335. long timesStamp = System.currentTimeMillis() / 1000;
  336. String orderTypeName = OrderType.get(param.getOrderType()).getLabel();
  337. order.setAppId(PropertiesUtil.getLocalProperty("appId"));
  338. order.setBody(orderTypeName);
  339. order.setOpenId("");
  340. order.setTotalFee(DecimalTool.moneyFenToYuan(param.getTotalAmt()));
  341. order.setPatientId(param.getPatientId());
  342. order.setPatientName(patName);
  343. order.setMchId(PropertiesUtil.getLocalProperty("mchId"));
  344. order.setTimeStamp(String.valueOf(timesStamp));
  345. order.setTradeNo(outTradeNo);
  346. order.setCreateDatetime(new Date());
  347. order.setPayStatus(TradeState.NOTPAY.getCode());
  348. order.setSerialNo(SnowFlakeId.instance().nextId());
  349. order.setOrderType(param.getOrderType());
  350. dao.insertNewOrder(order);
  351. JSONObject retObj = JSONObject.parseObject(ret);
  352. Map<String, String> map = new HashMap<>();
  353. map.put("qrcodeUrl", retObj.getString("code_url"));
  354. map.put("tradeNo", order.getTradeNo());
  355. map.put("serialNo", order.getSerialNo());
  356. return ResultVoUtil.success(map);
  357. }
  358. return ResultVoUtil.fail(ExceptionEnum.LOGICAL_ERROR, "请求微信支付二维码失败,请联系管理员。");
  359. }
  360. public ResultVo<String> closeWxOrder(WxPayOrder order) throws Exception {
  361. Integer payStatus = dao.selectOrderStatus(order.getTradeNo());
  362. if (null == payStatus) {
  363. return ResultVoUtil.fail(ExceptionEnum.NULL_POINTER, "订单不存在,请检查订单号是否正确。");
  364. }
  365. if (payStatus == TradeState.CLOSED.getCode()) {
  366. return ResultVoUtil.success("订单已是关闭状态,请勿重复关闭订单。");
  367. }
  368. String url = "https://api.mch.weixin.qq.com/v3/pay/transactions/out-trade-no/" + order.getTradeNo() + "/close";
  369. JSONObject body = new JSONObject();
  370. body.put("mchid", PropertiesUtil.getLocalProperty("mchId"));
  371. String reqdata = JSONObject.toJSONString(body);
  372. HttpPost httpPost = new HttpPost(url);
  373. StringEntity entity = new StringEntity(reqdata, StandardCharsets.UTF_8);
  374. entity.setContentEncoding("UTF-8");
  375. entity.setContentType("application/json");
  376. httpPost.setEntity(entity);
  377. httpPost.addHeader("Content-Type", "application/json;charset=UTF-8");
  378. httpPost.addHeader("Accept", "application/json");
  379. CloseableHttpClient httpClient = WxHttpUtil.getClosableHttpClient();
  380. CloseableHttpResponse response = httpClient.execute(httpPost);
  381. int statusCode = response.getStatusLine().getStatusCode();
  382. log.info("关闭微信订单:{},结果:{}", order.getTradeNo(), statusCode);
  383. if (statusCode == 204) {
  384. httpClient.close();
  385. dao.updatePayStatusOnly(order.getTradeNo(), TradeState.CLOSED.getCode());
  386. return ResultVoUtil.success("关闭订单成功。");
  387. }
  388. if (null != response.getEntity()) {
  389. String ret = EntityUtils.toString(response.getEntity());
  390. return ResultVoUtil.fail(ExceptionEnum.INTERNAL_SERVER_ERROR, ret);
  391. }
  392. return ResultVoUtil.fail(ExceptionEnum.NULL_POINTER, "关闭订单失败。");
  393. }
  394. public ResultVo<DoctorInfo> getDoctorInfo(String doctorCode) {
  395. DoctorInfo doctorInfo = dao.selectDoctorInfo(doctorCode);
  396. if (null == doctorInfo) {
  397. return ResultVoUtil.fail(ExceptionEnum.NULL_POINTER, "未找到医生信息!");
  398. }
  399. return ResultVoUtil.success(doctorInfo);
  400. }
  401. }