소스 검색

小程序接口和功能优化

lighter 2 년 전
부모
커밋
1d8a4665fe
31개의 변경된 파일1034개의 추가작업 그리고 82개의 파일을 삭제
  1. 5 0
      pom.xml
  2. 5 26
      src/main/java/thyyxxk/wxservice_server/Test.java
  3. 435 0
      src/main/java/thyyxxk/wxservice_server/api/WxAppletApi.java
  4. 23 0
      src/main/java/thyyxxk/wxservice_server/constant/AppletOrderType.java
  5. 0 2
      src/main/java/thyyxxk/wxservice_server/controller/WxAppletController.java
  6. 97 0
      src/main/java/thyyxxk/wxservice_server/dao/AppletDao.java
  7. 2 0
      src/main/java/thyyxxk/wxservice_server/dao/WxApiDao.java
  8. 5 5
      src/main/java/thyyxxk/wxservice_server/entity/analyzeidcard/IdCardAnalyzeResult.java
  9. 3 2
      src/main/java/thyyxxk/wxservice_server/entity/appointment/PatientBriefInfo.java
  10. 1 0
      src/main/java/thyyxxk/wxservice_server/entity/wxapi/WxPayOrder.java
  11. 16 0
      src/main/java/thyyxxk/wxservice_server/entity/wxapi/wxapplet/request/AppletType.java
  12. 9 0
      src/main/java/thyyxxk/wxservice_server/entity/wxapi/wxapplet/request/AppletUserInquiry.java
  13. 20 0
      src/main/java/thyyxxk/wxservice_server/entity/wxapi/wxapplet/request/BaseInquiry.java
  14. 9 0
      src/main/java/thyyxxk/wxservice_server/entity/wxapi/wxapplet/request/CardInquiry.java
  15. 10 0
      src/main/java/thyyxxk/wxservice_server/entity/wxapi/wxapplet/request/TcInquiry.java
  16. 10 0
      src/main/java/thyyxxk/wxservice_server/entity/wxapi/wxapplet/request/TemplateThumb.java
  17. 26 0
      src/main/java/thyyxxk/wxservice_server/entity/wxapi/wxapplet/response/AppletMallCart.java
  18. 14 0
      src/main/java/thyyxxk/wxservice_server/entity/wxapi/wxapplet/response/AppletUserInfo.java
  19. 167 1
      src/main/java/thyyxxk/wxservice_server/entity/wxapi/wxapplet/response/WxAppletOrder.java
  20. 25 18
      src/main/java/thyyxxk/wxservice_server/scheduled/QuestionnaireAfterVisit.java
  21. 1 1
      src/main/java/thyyxxk/wxservice_server/service/ElectronicHealthCardService.java
  22. 25 18
      src/main/java/thyyxxk/wxservice_server/service/IdCardAnalyzeService.java
  23. 52 0
      src/main/java/thyyxxk/wxservice_server/service/WxRefundService.java
  24. 4 0
      src/main/java/thyyxxk/wxservice_server/utils/DecimalTool.java
  25. 4 0
      src/main/java/thyyxxk/wxservice_server/utils/SnowFlakeId.java
  26. 50 1
      src/main/java/thyyxxk/wxservice_server/utils/wxpay/WxPayConfigUtil.java
  27. 1 0
      src/main/resources/application-8083.yml
  28. 1 0
      src/main/resources/application-8085.yml
  29. 4 5
      src/main/resources/application.yml
  30. 3 2
      src/main/resources/logback-spring.xml
  31. 7 1
      src/main/resources/weChatOfficialAccounts.properties

+ 5 - 0
pom.xml

@@ -100,6 +100,11 @@
             <artifactId>aspectjrt</artifactId>
             <version>1.9.5</version>
         </dependency>
+        <dependency>
+            <groupId>com.github.wechatpay-apiv3</groupId>
+            <artifactId>wechatpay-java</artifactId>
+            <version>0.2.12</version>
+        </dependency>
         <dependency>
             <groupId>com.baidu.aip</groupId>
             <artifactId>java-sdk</artifactId>

+ 5 - 26
src/main/java/thyyxxk/wxservice_server/Test.java

@@ -1,5 +1,6 @@
 package thyyxxk.wxservice_server;
 
+import com.alibaba.fastjson.JSONArray;
 import org.apache.commons.codec.binary.Base64;
 import org.bouncycastle.jce.provider.BouncyCastleProvider;
 import thyyxxk.wxservice_server.utils.SnowFlakeId;
@@ -9,36 +10,14 @@ import javax.crypto.spec.SecretKeySpec;
 import java.security.Security;
 import java.text.DateFormat;
 import java.text.SimpleDateFormat;
-import java.util.Date;
-import java.util.UUID;
+import java.util.*;
 
 public class Test {
     public static void main(String[] args) {
-        Date now = new Date();
-        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
-        try {
-            Date sdate = new SimpleDateFormat("yyyy-MM-dd").parse("2023-03-23");
-            System.out.println(now.before(sdate));
-        } catch (Exception e) {
-        }
-
-//加密前:98134EAF0F594CD6A85CE3BB4367C72A&oao6f0y4oF7jd60QhzPzMD9C3bBU&37156
-//加密后:zTS83k5dG5jaDl0EmRsy35qt37Nliu+kFvebbJxrFddhH/f1/eP4UjDQmeoiBb9W0mbSBJg3nT4KDVYtqGzWNOPM5UyixrFI4Cv8YewVa6I=
+        String carts = "[12,19,49,86]";
 
-//        String uoh = "98134EAF0F594CD6A85CE3BB4367C72A&oao6f0y4oF7jd60QhzPzMD9C3bBU&37156";
-//        byte[] contentBytes = uoh.getBytes();
-//
-//        String secret = "085181bf219749878035f575e0a1986a";
-//        byte[] keyBytes = secret.getBytes();
-//
-//        //加密
-//        String en = encrypt(contentBytes, keyBytes);
-//        System.out.println(en);
-//        //解密,用于本地验证
-//        byte[] enb = en.getBytes();
-//        byte[] de = decrypt(enb, keyBytes);
-//        String str = new String(de);
-//        System.out.println(str);
+        List<Integer> list = JSONArray.parseObject(carts, List.class);
+        System.out.println(list);
     }
 
     /**

+ 435 - 0
src/main/java/thyyxxk/wxservice_server/api/WxAppletApi.java

@@ -0,0 +1,435 @@
+package thyyxxk.wxservice_server.api;
+
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import com.wechat.pay.java.core.Config;
+import com.wechat.pay.java.service.payments.jsapi.JsapiService;
+import com.wechat.pay.java.service.payments.jsapi.JsapiServiceExtension;
+import com.wechat.pay.java.service.payments.jsapi.model.*;
+import com.wechat.pay.java.service.payments.model.Transaction;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.client.RestTemplate;
+import org.springframework.web.multipart.MultipartFile;
+import thyyxxk.wxservice_server.config.exception.ExceptionEnum;
+import thyyxxk.wxservice_server.constant.AppletOrderType;
+import thyyxxk.wxservice_server.dao.AppletDao;
+import thyyxxk.wxservice_server.dao.PatientCardsDao;
+import thyyxxk.wxservice_server.entity.ResultVo;
+import thyyxxk.wxservice_server.entity.analyzeidcard.IdCardAnalyzeResult;
+import thyyxxk.wxservice_server.entity.appointment.PatientBriefInfo;
+import thyyxxk.wxservice_server.entity.electronichealthcard.ElectronicHealthCard;
+import thyyxxk.wxservice_server.entity.patientcards.ModifyBindParam;
+import thyyxxk.wxservice_server.entity.wxapi.wxapplet.request.*;
+import thyyxxk.wxservice_server.entity.wxapi.wxapplet.response.AppletMallCart;
+import thyyxxk.wxservice_server.entity.wxapi.wxapplet.response.WxAppletOrder;
+import thyyxxk.wxservice_server.service.ElectronicHealthCardService;
+import thyyxxk.wxservice_server.service.IdCardAnalyzeService;
+import thyyxxk.wxservice_server.service.WxRefundService;
+import thyyxxk.wxservice_server.utils.*;
+import thyyxxk.wxservice_server.utils.wxpay.WxPayConfigUtil;
+
+import java.io.File;
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+
+@Slf4j
+@RestController
+@RequestMapping("/wxApplet")
+public class WxAppletApi {
+    private final IdCardAnalyzeService idCardAnalyzeService;
+    private final WxRefundService wxRefundService;
+    private final ElectronicHealthCardService electronicHealthCardService;
+    private final AppletDao appletDao;
+    private final PatientCardsDao cardsDao;
+    private final static String GET_USER_INFO = "https://api.weixin.qq.com/sns/jscode2session?appid=APPID&secret=SECRET&js_code=JSCODE&grant_type=authorization_code";
+
+    @Value("${appletThmzUrl}")
+    String appletThmzUrl;
+    
+    @Autowired
+    public WxAppletApi(IdCardAnalyzeService idCardAnalyzeService, WxRefundService wxRefundService, ElectronicHealthCardService electronicHealthCardService, AppletDao dao, PatientCardsDao cardsDao) {
+        this.idCardAnalyzeService = idCardAnalyzeService;
+        this.wxRefundService = wxRefundService;
+        this.electronicHealthCardService = electronicHealthCardService;
+        this.appletDao = dao;
+        this.cardsDao = cardsDao;
+    }
+
+    @PostMapping("/getAppletUserInfo")
+    public ResultVo<JSONObject> getAppletUserInfo(@RequestBody AppletUserInquiry inquiry) {
+        AppletType appletType = inquiry.getAppletType();
+        if (null == appletType) {
+            return ResultVoUtil.fail(ExceptionEnum.NULL_POINTER, "小程序类别不能为空。");
+        }
+        if (appletType != AppletType.APPLET_MALL && appletType != AppletType.HOSPITAL_SERVICE) {
+            return ResultVoUtil.fail(ExceptionEnum.NULL_POINTER, "小程序类别无效。");
+        }
+        String jscode = inquiry.getCode();
+        if (null == jscode) {
+            return ResultVoUtil.fail(ExceptionEnum.NULL_POINTER, "登录凭证不能为空。");
+        }
+        String appId = appletType == AppletType.HOSPITAL_SERVICE ?
+                PropertiesUtil.getLocalProperty("appletAppId") :
+                PropertiesUtil.getLocalProperty("appletMallAppId");
+        String appSecret = appletType == AppletType.HOSPITAL_SERVICE ?
+                PropertiesUtil.getLocalProperty("appletAppSecret") :
+                PropertiesUtil.getLocalProperty("appletMallAppSecret");
+        String url = GET_USER_INFO.replace("APPID", appId)
+                .replace("SECRET", appSecret)
+                .replace("JSCODE", jscode);
+        String response = new RestTemplate().getForObject(url, String.class);
+        log.info("小程序登录:{}\n参数:{}\n结果:{}", url, JSONObject.toJSON(inquiry), response);
+        return ResultVoUtil.success(JSONObject.parseObject(response));
+    }
+
+    @PostMapping("/createOrder")
+    public ResultVo<WxAppletOrder> createOrder(@RequestBody WxAppletOrder order) {
+        AppletType appletType = order.getAppletType();
+        if (null == appletType) {
+            return ResultVoUtil.fail(ExceptionEnum.NULL_POINTER, "小程序类别不能为空。");
+        }
+        AppletOrderType orderType = order.getOrderType();
+        if (null == orderType) {
+            return ResultVoUtil.fail(ExceptionEnum.NULL_POINTER, "订单类别不能为空。");
+        }
+        String appId = appletType == AppletType.HOSPITAL_SERVICE ?
+                PropertiesUtil.getLocalProperty("appletAppId") :
+                PropertiesUtil.getLocalProperty("appletMallAppId");
+        String mchId = PropertiesUtil.getLocalProperty("mchId");
+        String tradeNo = SnowFlakeId.instance().nextWxAppletTradeNo();
+        Config config = WxPayConfigUtil.getInstance().getConfig();
+        JsapiServiceExtension service = new JsapiServiceExtension .Builder().config(config).build();
+        PrepayRequest request = new PrepayRequest();
+        Amount amount = new Amount();
+        amount.setTotal(order.getTotalAmount());
+        request.setAmount(amount);
+        request.setDescription(order.getDescription());
+        request.setAppid(appId);
+        request.setMchid(mchId);
+        request.setNotifyUrl(PropertiesUtil.getLocalProperty("notifyUrl"));
+        request.setOutTradeNo(tradeNo);
+        Payer payer = new Payer();
+        payer.setOpenid(order.getOpenId());
+        request.setPayer(payer);
+        PrepayWithRequestPaymentResponse response = service.prepayWithRequestPayment(request);
+        order.setPrepayId(response.getPackageVal());
+        order.setPayment(response);
+        order.setAppId(appId);
+        order.setMchId(mchId);
+        order.setTradeNo(tradeNo);
+        order.setSerialNo(SnowFlakeId.instance().nextId());
+        log.info("小程序支付下单:\n{}", JSONObject.toJSON(order));
+        if (ListUtil.notEmpty(order.getCartIdList())) {
+            StringBuilder sb = new StringBuilder(",");
+            for (int cartId : order.getCartIdList()) {
+                sb.append(cartId).append(",");
+            }
+            order.setCartIds(sb.toString());
+        }
+        appletDao.insertNewOrder(order);
+        return ResultVoUtil.success(order);
+    }
+
+    @PostMapping("/queryOrderState")
+    public ResultVo<Transaction> queryOrderState(@RequestBody WxAppletOrder order) {
+        Config config = WxPayConfigUtil.getInstance().getConfig();
+        JsapiService service = new JsapiService.Builder().config(config).build();
+        QueryOrderByOutTradeNoRequest request = new QueryOrderByOutTradeNoRequest();
+        request.setMchid(PropertiesUtil.getLocalProperty("mchId"));
+        request.setOutTradeNo(order.getTradeNo());
+        Transaction transaction = service.queryOrderByOutTradeNo(request);
+        log.info("小程序查询订单状态【{}】:{}", order.getTradeNo(), transaction);
+        Transaction.TradeStateEnum state = transaction.getTradeState();
+        if (state != Transaction.TradeStateEnum.NOTPAY) {
+            appletDao.updateOrderTradeState(order.getTradeNo(), state);
+            String cartIds = appletDao.selectCartIds(order.getTradeNo());
+            String[] splitArr = cartIds.split(",");
+            if (splitArr.length > 2) {
+                List<Integer> idList = new ArrayList<>();
+                for (int i = 1; i < splitArr.length - 1; i ++) {
+                    idList.add(Integer.parseInt(splitArr[i]));
+                }
+                appletDao.updateCartTradeState(idList, state);
+            }
+        }
+        return ResultVoUtil.success(transaction);
+    }
+
+    @PostMapping("/getCartInfo")
+    public ResultVo<List<AppletMallCart>> getCartInfo(@RequestBody @Validated BaseInquiry inquiry) {
+        log.info("查询购物车:{}", JSONObject.toJSON(inquiry));
+        if (null == inquiry.getTradeState()) {
+            inquiry.setTradeState(Transaction.TradeStateEnum.NOTPAY);
+        }
+        return ResultVoUtil.success(appletDao.selectUserCart(inquiry));
+    }
+
+    @PostMapping("/addItemToCart")
+    public ResultVo<Integer> addItemToCart(@RequestBody AppletMallCart cart) {
+        log.info("添加购物车:{}", JSONObject.toJSON(cart));
+        String serialNo = SnowFlakeId.instance().nextId();
+        cart.setSerialNo(serialNo);
+        appletDao.insertItemToCart(cart);
+        return ResultVoUtil.success(appletDao.selectIdBySerialNo(serialNo));
+    }
+
+    @PostMapping("/modifyCart")
+    public ResultVo<String> modifyCart(@RequestBody AppletMallCart cart) {
+        log.info("修改购物车:{}", JSONObject.toJSON(cart));
+        if (cart.getTemplateAmount() == 0) {
+            appletDao.deleteItemById(cart.getId());
+        } else {
+            appletDao.updateTemplateAmount(cart.getTemplateAmount(), cart.getId());
+        }
+        return ResultVoUtil.success("操作成功。");
+    }
+
+    @PostMapping("/deleteItemFromCart")
+    public ResultVo<String> deleteItemFromCart(@RequestBody List<Integer> cartIds) {
+        log.info("删除购物车:{}", cartIds);
+        appletDao.deleteItemByIds(cartIds);
+        return ResultVoUtil.success("删除购物车成功。");
+    }
+
+    @PostMapping("/revokeCart")
+    public ResultVo<String> revokeCart(@RequestBody AppletMallCart cart) throws Exception {
+        if (StringUtil.isBlank(cart.getOpenId())) {
+            return ResultVoUtil.fail(ExceptionEnum.NULL_POINTER, "openId不能为空。");
+        }
+        AppletMallCart existCart = appletDao.selectCartById(cart.getId());
+        if (null == existCart) {
+            return ResultVoUtil.fail(ExceptionEnum.NO_DATA_EXIST, "订单不存在!");
+        }
+
+        WxAppletOrder order = appletDao.selectPayedOrderByCartId("%," + cart.getId() + ",%");
+        if (null == order) {
+            appletDao.updateCartTradeStateById(cart.getId(), Transaction.TradeStateEnum.REVOKED);
+            return ResultVoUtil.success("订单已取消。");
+        }
+
+        if (StringUtil.isBlank(cart.getRefundReason())) {
+            existCart.setRefundReason("无");
+        }
+
+        BigDecimal totalFee = DecimalTool.multiply(existCart.getTemplatePrice(),
+                new BigDecimal(existCart.getTemplateAmount()));
+        order.setRefundAmount(DecimalTool.moneyYuanToFen(totalFee));
+
+        ResultVo<WxAppletOrder> refundResult = wxRefundService.wxAppletRefund(order);
+        if (refundResult.getCode() != ExceptionEnum.SUCCESS.getCode()) {
+            return ResultVoUtil.fail(ExceptionEnum.LOGICAL_ERROR, refundResult.getMessage());
+        }
+
+        WxAppletOrder refundedOrder = refundResult.getData();
+        if (refundedOrder.getTotalAmount() == Integer.parseInt(refundedOrder.getRefundAmount())) {
+            order.setRefundOpDatetime(new Date());
+            order.setRefundReason(existCart.getRefundReason());
+            appletDao.updateOrderRefundState(order);
+        }
+
+        existCart.setRefundId(order.getRefundId());
+        existCart.setRefundOpCode(cart.getOpenId());
+        existCart.setRefundDateTime(refundedOrder.getRefundOpDatetime());
+        existCart.setRefundReason(refundedOrder.getRefundReason());
+        existCart.setTradeState(Transaction.TradeStateEnum.REFUND);
+        appletDao.updateCartRefund(existCart);
+        return ResultVoUtil.success("订单已取消");
+    }
+
+    @PostMapping("/queryTcDepartment")
+    public ResultVo<JSONArray> queryTcDepartment() {
+        String url = appletThmzUrl + "/queryTcDiscuntDept";
+        ResultVo response = new RestTemplate().getForObject(url, ResultVo.class);
+        if (null == response) {
+            return ResultVoUtil.fail(ExceptionEnum.NETWORK_ERROR);
+        }
+        if (response.getCode() == 0) {
+            response.setCode(200);
+        }
+        return response;
+    }
+
+    @PostMapping("/queryAllDepartment")
+    public ResultVo<JSONArray> queryAllDepartment() {
+        String url = appletThmzUrl + "/queryAllDept";
+        ResultVo response = new RestTemplate().getForObject(url, ResultVo.class);
+        if (null == response) {
+            return ResultVoUtil.fail(ExceptionEnum.NETWORK_ERROR);
+        }
+        if (response.getCode() == 0) {
+            response.setCode(200);
+        }
+        return response;
+    }
+
+    @PostMapping("/queryTcByDepartment")
+    public ResultVo queryTcByDepartment(@RequestBody TcInquiry inquiry) {
+        String url = appletThmzUrl + "/queryTcByDeptCode";
+        ResultVo response = new RestTemplate().postForObject(url, inquiry, ResultVo.class);
+        if (null == response) {
+            return ResultVoUtil.fail(ExceptionEnum.NETWORK_ERROR);
+        }
+        if (response.getCode() == 0) {
+            response.setCode(200);
+        }
+        return response;
+    }
+
+    @PostMapping("/queryTcByTemplateId")
+    public ResultVo queryTcByTemplateId(@RequestBody TcInquiry inquiry) {
+        String url = appletThmzUrl + "/queryTcByTemplateId";
+        ResultVo response = new RestTemplate().postForObject(url, inquiry, ResultVo.class);
+        if (null == response) {
+            return ResultVoUtil.fail(ExceptionEnum.NETWORK_ERROR);
+        }
+        if (response.getCode() == 0) {
+            response.setCode(200);
+        }
+        return response;
+    }
+
+    @PostMapping("/getBoundCards")
+    public ResultVo<List<PatientBriefInfo>> getBoundCards(@RequestBody CardInquiry inquiry) {
+        AppletType appletType = inquiry.getAppletType();
+        if (null == appletType) {
+            return ResultVoUtil.fail(ExceptionEnum.NULL_POINTER, "小程序类别不能为空。");
+        }
+        String openId = makeSpecialOpenId(appletType, inquiry.getOpenId());
+        log.info("查询小程序绑卡:{}", openId);
+        return ResultVoUtil.success(appletDao.getBindPatientCard(openId));
+    }
+
+    @PostMapping("/bindPatientId")
+    public ResultVo<HashMap<String, Object>> bindPatientId(@RequestBody IdCardAnalyzeResult param) {
+        AppletType appletType = param.getAppletType();
+        if (null == appletType) {
+            return ResultVoUtil.fail(ExceptionEnum.NULL_POINTER, "小程序类别不能为空。");
+        }
+        if (StringUtil.isBlank(param.getOpenId())) {
+            return ResultVoUtil.fail(ExceptionEnum.NULL_POINTER, "openId不能为空。");
+        }
+        param.setOpenId(makeSpecialOpenId(appletType, param.getOpenId()));
+        param.setSocialNo(param.getCardNo());
+        log.info("绑定就诊卡:{}", JSONObject.toJSON(param));
+        String column = param.getCardType() == 0 ? "patient_id" : "social_no";
+        List<IdCardAnalyzeResult> list = cardsDao.selectMzPatientBriefInfo(column, param.getCardNo());
+        if (null == list || list.isEmpty()) {
+            return idCardAnalyzeService.readInput(param);
+        }
+        if (list.size() > 1) {
+            HashMap<String, Object> map = new HashMap<>();
+            map.put("code", 1);
+            map.put("cards", list);
+            return ResultVoUtil.success(map);
+        }
+        IdCardAnalyzeResult mzPatientMi = list.get(0);
+        if (param.getName().trim().equals(mzPatientMi.getName())) {
+            param.setPatientId(mzPatientMi.getPatientId());
+            param.setIcCardNo(mzPatientMi.getIcCardNo());
+            param.setSocialNo(mzPatientMi.getSocialNo());
+            int historyBindCount = cardsDao.selectHistoryBindCount(param.getPatientId(), param.getOpenId());
+            if (historyBindCount > 0) {
+                cardsDao.unfrozenPatientCard(param);
+                HashMap<String, Object> map = new HashMap<>();
+                map.put("code", 0);
+                map.put("cards", cardsDao.getBindPatientCard(param.getOpenId()));
+                return ResultVoUtil.success(map);
+            }
+            int validBindCount = cardsDao.selectValidBindCount(param.getOpenId());
+            param.setIsDefault(validBindCount == 0 ? 1 : 0);
+            cardsDao.bindPatientCard(param);
+            HashMap<String, Object> map = new HashMap<>();
+            map.put("code", 0);
+            map.put("cards", cardsDao.getBindPatientCard(param.getOpenId()));
+            return ResultVoUtil.success(map);
+        }
+        return ResultVoUtil.fail(ExceptionEnum.LOGICAL_ERROR, "就诊卡姓名信息不匹配!");
+    }
+
+    @PostMapping("/relieveBindCard")
+    public ResultVo<List<IdCardAnalyzeResult>> relieveBindCard(@RequestBody IdCardAnalyzeResult param) {
+        AppletType appletType = param.getAppletType();
+        if (null == appletType) {
+            return ResultVoUtil.fail(ExceptionEnum.NULL_POINTER, "小程序类别不能为空。");
+        }
+        if (StringUtil.isBlank(param.getOpenId())) {
+            return ResultVoUtil.fail(ExceptionEnum.NULL_POINTER, "openId不能为空。");
+        }
+        param.setOpenId(makeSpecialOpenId(appletType, param.getOpenId()));
+        log.info("解除就诊卡绑定:{}", JSONObject.toJSON(param));
+        cardsDao.frozenPatientCard(param.getPatientId(), param.getOpenId());
+        return ResultVoUtil.success(cardsDao.getBindPatientCard(param.getOpenId()));
+    }
+
+    @PostMapping("/modifyBindInfo")
+    public ResultVo<String> modifyBindInfo(@RequestBody ModifyBindParam param) {
+        log.info("修改个人信息:{}", JSONObject.toJSON(param));
+        cardsDao.modifyPatientBindInfo(param);
+        cardsDao.updateWechatBind(param);
+        return ResultVoUtil.success();
+    }
+
+    @PostMapping("/getOrders")
+    public ResultVo<List<AppletMallCart>> getOrders(BaseInquiry inquiry) {
+        return ResultVoUtil.success(appletDao.selectUserCart(inquiry));
+    }
+
+    @PostMapping("/updateTemplateThumb")
+    public ResultVo<String> updateTemplateThumb(TemplateThumb thumb) {
+        MultipartFile file = thumb.getFile();
+        if (null == file) {
+            return ResultVoUtil.fail(ExceptionEnum.NULL_POINTER, "请选择图片!");
+        }
+        String contentType = file.getContentType();
+        if (null == contentType) {
+            return ResultVoUtil.fail(ExceptionEnum.NULL_POINTER, "请选择图片!");
+        }
+        if (!contentType.equals("image/jpeg") && !contentType.equals("image/png")) {
+            return ResultVoUtil.fail(ExceptionEnum.INTERNAL_SERVER_ERROR, "请选择 jpg/png 格式的图片!");
+        }
+        int id = thumb.getTemplateId();
+        String prefix = contentType.split("/")[1];
+        String fileName = "thumb_" + id + "." + prefix;
+        String path = "/home/images/template-thumbs/" + fileName;
+        try {
+            File newFile = new File(path);
+            if (newFile.exists()) {
+                newFile.delete();
+                appletDao.deleteTemplateThumb(id);
+            }
+            file.transferTo(newFile);
+            String url = "http://staticweb.hnthyy.cn/images/template-thumbs/" + fileName;
+            appletDao.insertTemplateThumb(id, url);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return ResultVoUtil.success(path);
+    }
+
+    private String makeSpecialOpenId(AppletType appletType, String openId) {
+        return appletType + "@" + openId;
+    }
+
+    @PostMapping("/linkHealthCard")
+    public ResultVo<ElectronicHealthCard> linkHealthCard(@RequestBody BaseInquiry inquiry) {
+        AppletType appletType = inquiry.getAppletType();
+        if (null == appletType) {
+            return ResultVoUtil.fail(ExceptionEnum.NULL_POINTER, "小程序类别不能为空。");
+        }
+        if (StringUtil.isBlank(inquiry.getOpenId())) {
+            return ResultVoUtil.fail(ExceptionEnum.NULL_POINTER, "openId不能为空。");
+        }
+        inquiry.setOpenId(makeSpecialOpenId(appletType, inquiry.getOpenId()));
+        return electronicHealthCardService.linkHealthCard(inquiry.getHealthCode(), inquiry.getOpenId());
+    }
+}

+ 23 - 0
src/main/java/thyyxxk/wxservice_server/constant/AppletOrderType.java

@@ -0,0 +1,23 @@
+package thyyxxk.wxservice_server.constant;
+
+public enum AppletOrderType {
+    /**
+     * 挂号费
+     * */
+    REGISTRATION,
+
+    /**
+     * 门诊缴费
+     * */
+    OUTPATIENT,
+
+    /**
+     * 住院预交金
+     * */
+    INPATIENT_PRE_PAY,
+
+    /**
+     * 套餐
+     * */
+    TEMPLATE,
+}

+ 0 - 2
src/main/java/thyyxxk/wxservice_server/controller/WxAppletController.java

@@ -1,2 +0,0 @@
-package thyyxxk.wxservice_server.controller;public class WxAppletController {
-}

+ 97 - 0
src/main/java/thyyxxk/wxservice_server/dao/AppletDao.java

@@ -0,0 +1,97 @@
+package thyyxxk.wxservice_server.dao;
+
+import com.wechat.pay.java.service.payments.model.Transaction;
+import org.apache.ibatis.annotations.*;
+import thyyxxk.wxservice_server.entity.appointment.PatientBriefInfo;
+import thyyxxk.wxservice_server.entity.wxapi.wxapplet.request.BaseInquiry;
+import thyyxxk.wxservice_server.entity.wxapi.wxapplet.response.AppletMallCart;
+import thyyxxk.wxservice_server.entity.wxapi.wxapplet.response.WxAppletOrder;
+
+import java.util.List;
+
+@Mapper
+public interface AppletDao {
+    @Insert("insert into t_wechat_applet_order (trade_no,applet_type,order_type,description,cart_ids," +
+            "open_id,patient_id,patient_name,total_amount,app_id,mch_id,serial_no," +
+            "prepay_id,mzy_request_id,ap_time,his_ord_num,yj_req_no,inpatient_no,admiss_times) " +
+            "values (#{tradeNo},#{appletType},#{orderType},#{description},#{cartIds},#{openId}," +
+            "#{patientId},#{patientName},#{totalAmount},#{appId},#{mchId},#{serialNo},#{prepayId}," +
+            "#{mzyRequestId},#{apTime},#{hisOrdNum},#{yjReqNo},#{inpatientNo},#{admissTimes})")
+    void insertNewOrder(WxAppletOrder order);
+
+    @Select("select a.*, " +
+            "thumbPath=(select d.thumb_path from t_applet_template_thumb d " +
+            "where d.template_id=a.template_id) " +
+            "from t_applet_mall_cart a where open_id=#{openId} " +
+            "and patient_id=#{patientId} and trade_state=#{tradeState}")
+    List<AppletMallCart> selectUserCart(BaseInquiry inquiry);
+
+    @Select("select * from t_applet_mall_cart where open_id=#{openId} and patient_id=#{patientId} " +
+            "and template_id=#{templateId} and template_price=#{templatePrice} and trade_state='NOTPAY'")
+    AppletMallCart selectExistCartItem(AppletMallCart cart);
+
+    @Insert("insert into t_applet_mall_cart (serial_no,patient_id,open_id,template_id,template_name," +
+            "template_price,template_amount) " +
+            "values (#{serialNo},#{patientId},#{openId},#{templateId},#{templateName}," +
+            "#{templatePrice},#{templateAmount})")
+    void insertItemToCart(AppletMallCart cart);
+
+    @Select("select id from t_applet_mall_cart where serial_no=#{serialNo}")
+    Integer selectIdBySerialNo(String serialNo);
+
+    @Select("select * from t_applet_mall_cart where id=#{id}")
+    AppletMallCart selectCartById(Integer id);
+
+    @Select("select * from t_wechat_applet_order where trade_state='SUCCESS' and cart_ids like #{cartId}")
+    WxAppletOrder selectPayedOrderByCartId(String cartId);
+
+    @Update("update t_applet_mall_cart set template_amount=#{amount} where id=#{id}")
+    void updateTemplateAmount(int amount, int id);
+
+    @Delete("delete from t_applet_mall_cart where id=#{id}")
+    void deleteItemById(int id);
+
+    @Delete("<script>" +
+            "delete from t_applet_mall_cart where id in " +
+            "<foreach collection='ids' item='id' open='(' separator=',' close=')'> " +
+            "#{id}" +
+            "</foreach>" +
+            "</script>")
+    void deleteItemByIds(List<Integer> ids);
+
+    @Select("select name, patient_id, phone, social_no, is_default from " +
+            "t_wechat_patient_bind with(nolock) where open_id=#{openId} and del_flag=0")
+    List<PatientBriefInfo> getBindPatientCard(@Param("openId") String openId);
+
+    @Update("update t_wechat_applet_order set trade_state=#{state} where trade_no=#{tradeNo}")
+    void updateOrderTradeState(String tradeNo, Transaction.TradeStateEnum state);
+
+    @Select("select cart_ids from t_wechat_applet_order where trade_no=#{tradeNo}")
+    String selectCartIds(String tradeNo);
+
+    @Update("<script>" +
+            "update t_applet_mall_cart set trade_state=#{state} where id in " +
+            "<foreach collection='list' item='id' open='(' separator=',' close=')'> " +
+            "#{id}" +
+            "</foreach>" +
+            "</script>")
+    void updateCartTradeState(List<Integer> list, Transaction.TradeStateEnum state);
+
+    @Update("update t_applet_mall_cart set trade_state=#{state} where id=#{id}")
+    void updateCartTradeStateById(Integer id, Transaction.TradeStateEnum state);
+
+    @Delete("delete from t_applet_template_thumb where template_id=#{id}")
+    void deleteTemplateThumb(int id);
+
+    @Delete("insert into t_applet_template_thumb (template_id, thumb_path) values (#{id}, #{path})")
+    void insertTemplateThumb(int id, String path);
+
+    @Update("update t_wechat_applet_order set refund_id=#{refundId}, refund_op_code=#{refundOpCode}, " +
+            "his_status=0, refund_op_datetime=#{refundOpDatetime}, refund_reason=#{refundReason}, " +
+            "trade_state=#{tradeState} where trade_no=#{tradeNo}")
+    void updateOrderRefundState(WxAppletOrder order);
+
+    @Update("update t_applet_mall_cart set trade_state=#{tradeState},refund_date_time=#{refundDateTime}," +
+            "refund_reason=#{refundReason},refund_id=#{refundId},refund_op_code=#{refundOpCode} where id=#{id}")
+    void updateCartRefund(AppletMallCart cart);
+}

+ 2 - 0
src/main/java/thyyxxk/wxservice_server/dao/WxApiDao.java

@@ -6,6 +6,7 @@ import thyyxxk.wxservice_server.entity.appointment.DoctorInfo;
 import thyyxxk.wxservice_server.entity.electronichealthcard.HisRegister;
 import thyyxxk.wxservice_server.entity.scheduled.TradeNo;
 import thyyxxk.wxservice_server.entity.wxapi.WxPayOrder;
+import thyyxxk.wxservice_server.entity.wxapi.wxapplet.response.WxAppletOrder;
 
 import java.util.List;
 
@@ -116,6 +117,7 @@ public interface WxApiDao {
 
     @Update("update t_wechat_pay_order set pay_status=4, refund_reason=#{msg}, his_status=0 where trade_no=#{tradeNo}")
     void alreadyRefund(@Param("tradeNo") String tradeNo, @Param("msg") String msg);
+
     @Update("update t_si_setlinfo set mz_saved=1 where pat_no=#{patId} and times=#{times} and revoked=0")
     void updateMzSavedFlag(@Param("patId") String patId, @Param("times") String times);
 }

+ 5 - 5
src/main/java/thyyxxk/wxservice_server/entity/analyzeidcard/IdCardAnalyzeResult.java

@@ -1,15 +1,15 @@
 package thyyxxk.wxservice_server.entity.analyzeidcard;
 
 import lombok.Data;
+import thyyxxk.wxservice_server.entity.wxapi.wxapplet.request.AppletType;
 
 /**
  * @author dj
  */
 @Data
 public class IdCardAnalyzeResult {
-    /**
-     * 微信用户关注公众号产生的唯一识别码,由腾讯生成 
-     * */
+    private AppletType appletType;
+
     private String openId;
     
     /**
@@ -83,9 +83,9 @@ public class IdCardAnalyzeResult {
     private String guardIdNo;
 
     /**
-     * 绑卡时选择的卡片类别(0:门诊ID,1:IC卡号,2:身份证号)
+     * 绑卡时选择的卡片类别,默认为身份证(0:门诊ID,1:IC卡号,2:身份证号)
      * */
-    private Integer cardType;
+    private Integer cardType = 2;
 
     /**
      * 绑卡时输入的卡号

+ 3 - 2
src/main/java/thyyxxk/wxservice_server/entity/appointment/PatientBriefInfo.java

@@ -6,6 +6,7 @@ import lombok.Data;
 public class PatientBriefInfo {
     private String patientId;
     private String name;
-    private String idCard;
-    private String gender;
+    private String phone;
+    private String socialNo;
+    private Integer isDefault;
 }

+ 1 - 0
src/main/java/thyyxxk/wxservice_server/entity/wxapi/WxPayOrder.java

@@ -15,6 +15,7 @@ public class WxPayOrder {
     private String body;
     /**
      * 订单类型,1:挂号费 2:门诊缴费 3:住院预交金 4:自助机缴费 5:体检缴费
+     *         6:小程序订单
      * */
     private Integer orderType;
     private String openId;

+ 16 - 0
src/main/java/thyyxxk/wxservice_server/entity/wxapi/wxapplet/request/AppletType.java

@@ -0,0 +1,16 @@
+package thyyxxk.wxservice_server.entity.wxapi.wxapplet.request;
+
+/**
+ * 小程序类别
+ * */
+public enum AppletType {
+    /**
+     * 医院服务
+     * */
+    HOSPITAL_SERVICE,
+
+    /**
+     * 商城
+     * */
+    APPLET_MALL
+}

+ 9 - 0
src/main/java/thyyxxk/wxservice_server/entity/wxapi/wxapplet/request/AppletUserInquiry.java

@@ -0,0 +1,9 @@
+package thyyxxk.wxservice_server.entity.wxapi.wxapplet.request;
+
+import lombok.Data;
+
+@Data
+public class AppletUserInquiry {
+    private AppletType appletType;
+    private String code;
+}

+ 20 - 0
src/main/java/thyyxxk/wxservice_server/entity/wxapi/wxapplet/request/BaseInquiry.java

@@ -0,0 +1,20 @@
+package thyyxxk.wxservice_server.entity.wxapi.wxapplet.request;
+
+import com.wechat.pay.java.service.payments.model.Transaction;
+import lombok.Data;
+
+import javax.validation.constraints.NotBlank;
+
+@Data
+public class BaseInquiry {
+    @NotBlank(message = "openId不能为空。")
+    private String openId;
+    @NotBlank(message = "patientId不能为空。")
+    private String patientId;
+
+    private AppletType appletType;
+
+    private String healthCode;
+
+    private Transaction.TradeStateEnum tradeState;
+}

+ 9 - 0
src/main/java/thyyxxk/wxservice_server/entity/wxapi/wxapplet/request/CardInquiry.java

@@ -0,0 +1,9 @@
+package thyyxxk.wxservice_server.entity.wxapi.wxapplet.request;
+
+import lombok.Data;
+
+@Data
+public class CardInquiry {
+    private AppletType appletType;
+    private String openId;
+}

+ 10 - 0
src/main/java/thyyxxk/wxservice_server/entity/wxapi/wxapplet/request/TcInquiry.java

@@ -0,0 +1,10 @@
+package thyyxxk.wxservice_server.entity.wxapi.wxapplet.request;
+
+import lombok.Data;
+
+@Data
+public class TcInquiry {
+    private Integer deptType;
+    private String deptNo;
+    private Integer templateId;
+}

+ 10 - 0
src/main/java/thyyxxk/wxservice_server/entity/wxapi/wxapplet/request/TemplateThumb.java

@@ -0,0 +1,10 @@
+package thyyxxk.wxservice_server.entity.wxapi.wxapplet.request;
+
+import lombok.Data;
+import org.springframework.web.multipart.MultipartFile;
+
+@Data
+public class TemplateThumb {
+    private Integer templateId;
+    private MultipartFile file;
+}

+ 26 - 0
src/main/java/thyyxxk/wxservice_server/entity/wxapi/wxapplet/response/AppletMallCart.java

@@ -0,0 +1,26 @@
+package thyyxxk.wxservice_server.entity.wxapi.wxapplet.response;
+
+import com.wechat.pay.java.service.payments.model.Transaction;
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.util.Date;
+
+@Data
+public class AppletMallCart {
+    private Integer id;
+    private String serialNo;
+    private String patientId;
+    private String openId;
+    private Integer templateId;
+    private String thumbPath;
+    private String templateName;
+    private BigDecimal templatePrice;
+    private Integer templateAmount;
+    private Date createTime;
+    private String refundId;
+    private String refundOpCode;
+    private Date refundDateTime;
+    private String refundReason;
+    private Transaction.TradeStateEnum tradeState;
+}

+ 14 - 0
src/main/java/thyyxxk/wxservice_server/entity/wxapi/wxapplet/response/AppletUserInfo.java

@@ -0,0 +1,14 @@
+package thyyxxk.wxservice_server.entity.wxapi.wxapplet.response;
+
+import com.alibaba.fastjson.annotation.JSONField;
+import lombok.Data;
+
+@Data
+public class AppletUserInfo {
+    @JSONField(name = "session_key")
+    private String sessionKey;
+    private String unionid;
+    private String openid;
+    private String errmsg;
+    private Integer errcode;
+}

+ 167 - 1
src/main/java/thyyxxk/wxservice_server/entity/wxapi/wxapplet/response/WxAppletOrder.java

@@ -1,2 +1,168 @@
-package thyyxxk.wxservice_server.entity.wxapi.wxapplet.response;public class WxAppletOrder {
+package thyyxxk.wxservice_server.entity.wxapi.wxapplet.response;
+
+import com.wechat.pay.java.service.payments.jsapi.model.PrepayWithRequestPaymentResponse;
+import com.wechat.pay.java.service.payments.model.Transaction;
+import lombok.Data;
+import thyyxxk.wxservice_server.constant.AppletOrderType;
+import thyyxxk.wxservice_server.entity.wxapi.wxapplet.request.AppletType;
+
+import java.math.BigDecimal;
+import java.util.Date;
+import java.util.List;
+
+@Data
+public class WxAppletOrder {
+    /**
+     * 订单号
+     */
+    private String tradeNo;
+
+    /**
+     * 小程序类别
+     */
+    private AppletType appletType;
+
+    /**
+     * 订单类型
+     */
+    private AppletOrderType orderType;
+
+    /**
+     * 商品描述
+     */
+    private String description;
+
+    /**
+     * 购物车id
+     * */
+    private String cartIds;
+
+    /**
+     * 用户openId
+     */
+    private String openId;
+
+    /**
+     * 患者门诊id
+     */
+    private String patientId;
+
+    /**
+     * 患者姓名
+     */
+    private String patientName;
+
+    /**
+     * 订单总金额(分)
+     */
+    private Integer totalAmount;
+
+    /**
+     * 退款金额
+     * */
+    private String refundAmount;
+
+    /**
+     * 小程序appId
+     */
+    private String appId;
+
+    /**
+     * 商户号
+     */
+    private String mchId;
+
+    /**
+     * 订单流水号
+     */
+    private String serialNo;
+
+    /**
+     * 预支付id
+     */
+    private String prepayId;
+
+    /**
+     * 订单创建时间
+     */
+    private Date createDatetime;
+
+    /**
+     * 订单支付状态
+     */
+    private Transaction.TradeStateEnum tradeState;
+
+    /**
+     * 支付时间
+     */
+    private Date payDatetime;
+
+    /**
+     * 门诊挂号排班号
+     */
+    private Integer mzyRequestId;
+
+    /**
+     * 挂号时间段
+     */
+    private String apTime;
+
+    /**
+     * HIS订单号
+     */
+    private String hisOrdNum;
+
+    /**
+     * 门诊医技号
+     */
+    private Integer yjReqNo;
+
+    /**
+     * 微信支付退款号
+     */
+    private String refundId;
+
+    /**
+     * 退款操作人
+     */
+    private String refundOpCode;
+
+    /**
+     * 退款时间
+     */
+    private Date refundOpDatetime;
+
+    /**
+     * 退款原因
+     */
+    private String refundReason;
+
+    /**
+     * 住院号
+     */
+    private String inpatientNo;
+
+    /**
+     * 住院次数
+     */
+    private Integer admissTimes;
+
+    /**
+     * HIS入库状态
+     */
+    private Integer hisStatus;
+
+    /**
+     * 订单状态查询次数
+     */
+    private Integer queryStateTimes;
+
+    /**
+     * 上一次查询订单状态时间
+     */
+    private Date lastQueryState;
+
+    private List<Integer> cartIdList;
+
+    private PrepayWithRequestPaymentResponse payment;
 }

+ 25 - 18
src/main/java/thyyxxk/wxservice_server/scheduled/QuestionnaireAfterVisit.java

@@ -3,6 +3,7 @@ package thyyxxk.wxservice_server.scheduled;
 import com.alibaba.fastjson.JSONObject;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
 import org.springframework.scheduling.annotation.Scheduled;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.RequestMapping;
@@ -25,6 +26,8 @@ public class QuestionnaireAfterVisit {
     private final AssessmentDao dao;
     private final RedisLikeService redis;
     private final PushWxMessageService messageService;
+    @Value("${production}")
+    private Boolean production;
 
     @Autowired
     public QuestionnaireAfterVisit(AssessmentDao dao, RedisLikeService redis, PushWxMessageService messageService) {
@@ -35,30 +38,34 @@ public class QuestionnaireAfterVisit {
 
     @Scheduled(cron = "0 0 11 * * ?")
     public void start() {
-        int frequency = dao.selectQuestionnaireFrequency("clinic_satisfied_questionnaire");
-        List<PushQuestionnaireVisit> list = dao.selectPushQuestionnairePatients();
-        for (PushQuestionnaireVisit item : list) {
-            if (null == item.getQuestionnaireOffset() || item.getQuestionnaireOffset() >= frequency) {
-                item.setDeptName(redis.getDepartmentName(item.getDeptCode()));
-                item.setDoctorName(redis.getEmployeeName(item.getDoctorCode()));
-                pushMessage(item);
+        if (production) {
+            int frequency = dao.selectQuestionnaireFrequency("clinic_satisfied_questionnaire");
+            List<PushQuestionnaireVisit> list = dao.selectPushQuestionnairePatients();
+            for (PushQuestionnaireVisit item : list) {
+                if (null == item.getQuestionnaireOffset() || item.getQuestionnaireOffset() >= frequency) {
+                    item.setDeptName(redis.getDepartmentName(item.getDeptCode()));
+                    item.setDoctorName(redis.getEmployeeName(item.getDoctorCode()));
+                    pushMessage(item);
+                }
             }
         }
     }
 
     @Scheduled(cron = "0 0 12 * * ?")
     public void test2() {
-        int frequency = dao.selectQuestionnaireFrequency("inpatient_satisfied_questionnaire");
-        List<PushQuestionnaireVisit> list = dao.selectInpatientQuestionnairePatients();
-        for (PushQuestionnaireVisit item : list) {
-            if (null == item.getAdmissDate() || null == item.getDisDate()) {
-                continue;
-            }
-            if (null == item.getQuestionnaireOffset() || item.getQuestionnaireOffset() >= frequency) {
-                item.setDeptName(redis.getDepartmentName(item.getDeptCode()));
-                item.setDoctorName(redis.getEmployeeName(item.getDoctorCode()));
-                item.setVisitDate(makeInpatientDate(item.getAdmissDate(), item.getDisDate()));
-                pushMessage(item);
+        if (production) {
+            int frequency = dao.selectQuestionnaireFrequency("inpatient_satisfied_questionnaire");
+            List<PushQuestionnaireVisit> list = dao.selectInpatientQuestionnairePatients();
+            for (PushQuestionnaireVisit item : list) {
+                if (null == item.getAdmissDate() || null == item.getDisDate()) {
+                    continue;
+                }
+                if (null == item.getQuestionnaireOffset() || item.getQuestionnaireOffset() >= frequency) {
+                    item.setDeptName(redis.getDepartmentName(item.getDeptCode()));
+                    item.setDoctorName(redis.getEmployeeName(item.getDoctorCode()));
+                    item.setVisitDate(makeInpatientDate(item.getAdmissDate(), item.getDisDate()));
+                    pushMessage(item);
+                }
             }
         }
     }

+ 1 - 1
src/main/java/thyyxxk/wxservice_server/service/ElectronicHealthCardService.java

@@ -93,7 +93,7 @@ public class ElectronicHealthCardService {
         try {
             idCardAnalyzeService.readInput(idCard);
         } catch (Exception e) {
-            e.printStackTrace();
+            log.error("新建就诊卡错误:", e);
         }
     }
 

+ 25 - 18
src/main/java/thyyxxk/wxservice_server/service/IdCardAnalyzeService.java

@@ -99,7 +99,7 @@ public class IdCardAnalyzeService {
         }
     }
 
-    public ResultVo<HashMap<String, Object>> readInput(IdCardAnalyzeResult param) throws Exception {
+    public ResultVo<HashMap<String, Object>> readInput(IdCardAnalyzeResult param) {
         if (StringUtil.isBlank(param.getSocialNo())) {
             param.setBirthday(DateUtil.matchDate(param.getBirthday(), "yyyyMMdd"));
             if (null == param.getBirthday()) {
@@ -139,16 +139,21 @@ public class IdCardAnalyzeService {
         return bindExistCard(param);
     }
 
-    private CreatCardParam makeCreateCardParam(IdCardAnalyzeResult idCard) throws Exception {
+    private CreatCardParam makeCreateCardParam(IdCardAnalyzeResult idCard) {
         CreatCardParam card = new CreatCardParam();
         card.setPatName(idCard.getName());
         card.setPatIdType(1);
         card.setPatIdNo(idCard.getSocialNo());
         card.setPatMobile(idCard.getPhone());
-        card.setPatAge(DateUtil.calculateAge(idCard.getSocialNo(), idCard.getBirthday()));
         card.setPatSex(StringUtil.isBlank(idCard.getSocialNo()) ? idCard.getSex() :
                 DateUtil.calculateSex(idCard.getSocialNo()));
-        card.setPatBirth(DateUtil.formatBirthday(idCard.getSocialNo(), idCard.getBirthday()));
+        try {
+            card.setPatAge(DateUtil.calculateAge(idCard.getSocialNo(), idCard.getBirthday()));
+            card.setPatBirth(DateUtil.formatBirthday(idCard.getSocialNo(), idCard.getBirthday()));
+        } catch (Exception e) {
+            card.setPatAge(0);
+            card.setPatBirth("1970-01-01");
+        }
         card.setPatType(1);
         String provinceName = redis.getRegionName(idCard.getProvince());
         String cityName = redis.getRegionName(idCard.getCity());
@@ -168,20 +173,22 @@ public class IdCardAnalyzeService {
         map.put("code", 0);
         map.put("cards", cardsDao.getBindPatientCard(temp.getOpenId()));
         log.info("绑定就诊卡成功:{}", temp);
-        String msgContent = "{\"touser\":\"\",\"data\":" +
-                "{\"keyword3\":{\"color\":\"#173177\",\"value\":\"" + temp.getIcCardNo() + "\"}," +
-                "\"keyword4\":{\"color\":\"#173177\",\"value\":\"" +
-                DateUtil.formatDatetime(new Date(), "yyyy-MM-dd HH:mm:ss") + "\"}," +
-                "\"keyword1\":{\"color\":\"#173177\",\"value\":\"" + temp.getName() + "\"}," +
-                "\"keyword2\":{\"color\":\"#173177\",\"value\":\"门诊就诊卡\"}," +
-                "\"remark\":{\"color\":\"#FF0000\",\"value\":\"感谢您的使用,祝您健康!\"}," +
-                "\"first\":{\"color\":\"#FF0000\",\"value\":\"您好,您已成功绑定就诊卡!\"}}," +
-                "\"template_id\":\"3bXASQD7J9t8qkJ1x-zIrutOCIadP9neI-dXQOBIQQk\"," +
-                "\"url\":\"\"}";
-        PushMessageParam pojo = new PushMessageParam();
-        pojo.setCardNo(temp.getIcCardNo());
-        pojo.setMsgContext(JSONObject.parseObject(msgContent));
-        pushWxMessageService.pushMessage2(pojo);
+        if (null == temp.getAppletType()) {
+            String msgContent = "{\"touser\":\"\",\"data\":" +
+                    "{\"keyword3\":{\"color\":\"#173177\",\"value\":\"" + temp.getIcCardNo() + "\"}," +
+                    "\"keyword4\":{\"color\":\"#173177\",\"value\":\"" +
+                    DateUtil.formatDatetime(new Date(), "yyyy-MM-dd HH:mm:ss") + "\"}," +
+                    "\"keyword1\":{\"color\":\"#173177\",\"value\":\"" + temp.getName() + "\"}," +
+                    "\"keyword2\":{\"color\":\"#173177\",\"value\":\"门诊就诊卡\"}," +
+                    "\"remark\":{\"color\":\"#FF0000\",\"value\":\"感谢您的使用,祝您健康!\"}," +
+                    "\"first\":{\"color\":\"#FF0000\",\"value\":\"您好,您已成功绑定就诊卡!\"}}," +
+                    "\"template_id\":\"3bXASQD7J9t8qkJ1x-zIrutOCIadP9neI-dXQOBIQQk\"," +
+                    "\"url\":\"\"}";
+            PushMessageParam pojo = new PushMessageParam();
+            pojo.setCardNo(temp.getIcCardNo());
+            pojo.setMsgContext(JSONObject.parseObject(msgContent));
+            pushWxMessageService.pushMessage2(pojo);
+        }
         return ResultVoUtil.success(map);
     }
 }

+ 52 - 0
src/main/java/thyyxxk/wxservice_server/service/WxRefundService.java

@@ -1,5 +1,6 @@
 package thyyxxk.wxservice_server.service;
 
+import com.wechat.pay.java.service.payments.model.Transaction;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.http.HttpEntity;
 import org.apache.http.HttpResponse;
@@ -25,6 +26,8 @@ import thyyxxk.wxservice_server.dao.WxApiDao;
 import thyyxxk.wxservice_server.entity.ResultVo;
 import thyyxxk.wxservice_server.entity.wxapi.RfndPrm;
 import thyyxxk.wxservice_server.entity.wxapi.WxPayOrder;
+import thyyxxk.wxservice_server.entity.wxapi.wxapplet.request.AppletType;
+import thyyxxk.wxservice_server.entity.wxapi.wxapplet.response.WxAppletOrder;
 import thyyxxk.wxservice_server.utils.*;
 
 import javax.net.ssl.KeyManagerFactory;
@@ -138,6 +141,55 @@ public class WxRefundService {
         return ResultVoUtil.fail(ExceptionEnum.LOGICAL_ERROR, message);
     }
 
+    public ResultVo<WxAppletOrder> wxAppletRefund(WxAppletOrder order) throws Exception {
+        String nonceStr = SnowFlakeId.instance().nextId();
+        String outRefundNo = SnowFlakeId.instance().nextId();
+        TreeMap<String, String> map = new TreeMap<>();
+        map.put("appid", WxCertUtil.APP_ID);
+        map.put("mch_id", WxCertUtil.MERCHANT_ID);
+        map.put("nonce_str", nonceStr);
+        map.put("out_refund_no", outRefundNo);
+        map.put("out_trade_no", order.getTradeNo());
+        map.put("refund_fee", order.getRefundAmount());
+        map.put("total_fee", String.valueOf(order.getTotalAmount()));
+        String refundSign = WxPaySignUtil.createWxPaySign(map);
+        String appId = order.getAppletType() == AppletType.HOSPITAL_SERVICE ?
+                PropertiesUtil.getLocalProperty("appletAppId") :
+                PropertiesUtil.getLocalProperty("appletMallAppId");
+        String xml = "<xml>" +
+                "<appid>" + appId + "</appid>" +
+                "<mch_id>" + WxCertUtil.MERCHANT_ID + "</mch_id>" +
+                "<nonce_str>" + nonceStr + "</nonce_str>" +
+                "<out_refund_no>" + outRefundNo + "</out_refund_no>" +
+                "<out_trade_no>" + order.getTradeNo() + "</out_trade_no>" +
+                "<refund_fee>" + order.getRefundAmount() + "</refund_fee>" +
+                "<total_fee>" + order.getTotalAmount() + "</total_fee>" +
+                "<sign>" + refundSign + "</sign>" +
+                "</xml>";
+        String str = requestWithSsl(xml);
+        Document document = DocumentHelper.parseText(str);
+        Element root = document.getRootElement();
+        if ("SUCCESS".equals(root.element("return_code").getStringValue())) {
+            Element refundIdEle = root.element("refund_id");
+            if (null == refundIdEle) {
+                String msg = root.element("err_code_des").getStringValue();
+                log.info("微信退款失败:{}", msg);
+                if ("订单已全额退款".equals(msg)) {
+                    order.setTradeState(Transaction.TradeStateEnum.REFUND);
+                    return ResultVoUtil.success(order);
+                }
+                return ResultVoUtil.fail(ExceptionEnum.LOGICAL_ERROR, msg);
+            }
+            order.setRefundId(refundIdEle.getStringValue());
+            order.setTradeState(Transaction.TradeStateEnum.REFUND);
+            log.info("微信退款成功:{}", order.getTradeNo());
+            return ResultVoUtil.success(order);
+        }
+        final String message = root.element("return_msg").getStringValue();
+        log.info("微信退款失败:{}", message);
+        return ResultVoUtil.fail(ExceptionEnum.LOGICAL_ERROR, message);
+    }
+
     private String requestWithSsl(String xml) throws Exception {
         BasicHttpClientConnectionManager connManager;
         WxCertUtil wxCertUtil = new WxCertUtil();

+ 4 - 0
src/main/java/thyyxxk/wxservice_server/utils/DecimalTool.java

@@ -13,6 +13,10 @@ public class DecimalTool {
         return aDecimal.add(bDecimal).toPlainString();
     }
 
+    public static BigDecimal multiply(BigDecimal a, BigDecimal b) {
+        return a.multiply(b);
+    }
+
     public static String moneyYuanToFen(BigDecimal fee) {
         BigDecimal hundred = new BigDecimal("100");
         return fee.multiply(hundred).setScale(0, RoundingMode.CEILING).toPlainString();

+ 4 - 0
src/main/java/thyyxxk/wxservice_server/utils/SnowFlakeId.java

@@ -99,4 +99,8 @@ public class SnowFlakeId {
         }
         return timestamp;
     }
+
+    public synchronized String nextWxAppletTradeNo() {
+        return "WX_APPLET_" + nextId();
+    }
 }

+ 50 - 1
src/main/java/thyyxxk/wxservice_server/utils/wxpay/WxPayConfigUtil.java

@@ -1,2 +1,51 @@
-package thyyxxk.wxservice_server.utils.wxpay;public class WxPayConfigUtil {
+package thyyxxk.wxservice_server.utils.wxpay;
+
+import com.wechat.pay.java.core.Config;
+import com.wechat.pay.java.core.RSAAutoCertificateConfig;
+import thyyxxk.wxservice_server.constant.Constants;
+import thyyxxk.wxservice_server.utils.PropertiesUtil;
+
+public class WxPayConfigUtil {
+    private final static String PRIVATE_KEY_PATH;
+    private Config config = null;
+
+    static {
+        if (Constants.WINDOWS_10.equals(PropertiesUtil.getLocalProperty(Constants.OS_NAME))) {
+            PRIVATE_KEY_PATH = "D:\\a.snapshot\\zwxcert\\apiclient_key.pem";
+        } else {
+            PRIVATE_KEY_PATH = "/home/wxcerts/apiclient_key.pem";
+        }
+    }
+
+    private WxPayConfigUtil() {}
+
+    private static volatile WxPayConfigUtil INSTANCE = null;
+
+    public static WxPayConfigUtil getInstance() {
+        if (null == INSTANCE) {
+            synchronized (WxPayConfigUtil.class) {
+                if (null == INSTANCE) {
+                    INSTANCE = new WxPayConfigUtil();
+                }
+            }
+        }
+        return INSTANCE;
+    }
+
+    public Config getConfig() {
+        if (null == config) {
+            config = initConfig();
+        }
+        return config;
+    }
+
+
+    private Config initConfig() {
+        return new RSAAutoCertificateConfig.Builder()
+                .merchantId(PropertiesUtil.getLocalProperty("mchId"))
+                .privateKeyFromPath(PRIVATE_KEY_PATH)
+                .merchantSerialNumber(PropertiesUtil.getLocalProperty("mchSerialNo"))
+                .apiV3Key(PropertiesUtil.getLocalProperty("mchApiV3Key"))
+                .build();
+    }
 }

+ 1 - 0
src/main/resources/application-8083.yml

@@ -32,6 +32,7 @@ mybatis:
     map-underscore-to-camel-case: true
 
 hrgApiUrl: http://172.16.32.160:81/thmz/api/v1
+appletThmzUrl: http://172.16.32.160:81/thmz
 inspectionUrl: http://172.16.32.178:622/pushservice.asmx?wsdl
 physicalCheck: http://172.16.32.183:8888/bdp/dataservice/api/
 production: true

+ 1 - 0
src/main/resources/application-8085.yml

@@ -32,6 +32,7 @@ mybatis:
     map-underscore-to-camel-case: true
 
 hrgApiUrl: http://172.16.32.160:81/thmz/api/v1
+appletThmzUrl: http://172.16.32.160:81/thmz
 inspectionUrl: http://172.16.32.178:622/pushservice.asmx?wsdl
 physicalCheck: http://172.16.32.183:8888/bdp/dataservice/api/
 production: true

+ 4 - 5
src/main/resources/application.yml

@@ -9,8 +9,8 @@ spring:
     cache: false
   datasource:
     driver-class-name: com.microsoft.sqlserver.jdbc.SQLServerDriver
-#    url: jdbc:sqlserver://172.16.32.179:1433;databaseName=thxyhisdb
-    url: jdbc:sqlserver://172.16.32.168:1433;databaseName=thxyhisdb
+    url: jdbc:sqlserver://172.16.32.179:1433;databaseName=thxyhisdb
+#    url: jdbc:sqlserver://172.16.32.168:1433;databaseName=thxyhisdb
     hikari:
       username: sa
       password:
@@ -32,10 +32,9 @@ mybatis:
   configuration:
     map-underscore-to-camel-case: true
 
-#hrgApiUrl: http://172.16.30.33:8089/thmz/api/v1
-#hrgApiUrl: http://172.16.30.33:8889/thmz/api/v1
 hrgApiUrl: http://172.16.32.160:81/thmz/api/v1
-#hrgApiUrl: http://172.16.30.22:8089/thmz/api/v1
+
+appletThmzUrl: http://172.16.30.119:8089/thmz
 
 inspectionUrl: http://172.16.32.178:622/pushservice.asmx?wsdl
 physicalCheck: http://172.16.32.183:8888/bdp/dataservice/api/

+ 3 - 2
src/main/resources/logback-spring.xml

@@ -21,7 +21,7 @@
     <property name="LOG_DIR" value="${LOG_HOME}/%d{yyyy-MM-dd}" />
 
     <!--对日志进行格式化-->
-    <property name="LOG_MSG" value="- [%d{yyyy-MM-dd HH:mm:ss.SSS}] [%level] [%thread] [%logger{16}] --> %msg%n "/>
+    <property name="LOG_MSG" value="[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%level] [%thread] [%logger{16}] --> %msg%n "/>
 
     <!--文件大小,默认10MB-->
     <property name="MAX_FILE_SIZE" value="3MB" />
@@ -38,7 +38,8 @@
                     converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter" />
     <!-- 彩色日志格式 -->
     <property name="CONSOLE_LOG_PATTERN"
-              value="-[%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){blue}] [%clr(%level)] [%clr(%thread)] [%clr(%logger{16}){cyan}] %clr(-->){red} %clr(%msg%n){yellow}"/>
+              value="%clr(============================================================================================================================================){green}\n
+[%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){blue}][%clr(%level)][%clr(%thread)][%clr(%logger{16}){cyan}] %clr(->){red} %clr(%msg%n){yellow}"/>
 
     <!--输出到控制台-->
     <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">

+ 7 - 1
src/main/resources/weChatOfficialAccounts.properties

@@ -12,4 +12,10 @@ notifyUrl=http://staticweb.hnthyy.cn/wxserver/wxPayNotify/notify
 
 qywxCorpId=wwf0b23c8b36012b34
 qywxSecret=wpHuNePfiDyotmpXjy5hUYGF0w8Ks5OPHSQp22z8oBk
-qywxToken=
+qywxToken=
+
+appletAppId=wxb36274d68249fa33
+appletAppSecret=e1063b25d70639115b7a22549c81f817
+
+appletMallAppId=wx5c6d5108df7917ff
+appletMallAppSecret=d2b4749aa2aef905e2134c64df5b22b2