hurixing преди 1 година
родител
ревизия
7b2012aa0f
променени са 22 файла, в които са добавени 770 реда и са изтрити 44 реда
  1. 17 0
      hcp-core/src/main/java/com/yingyangfly/core/config/WechatConfig.java
  2. 14 0
      hcp-core/src/main/java/com/yingyangfly/core/config/WxJsConfig.java
  3. 5 0
      hcp-core/src/main/java/com/yingyangfly/core/domain/PayOrder.java
  4. 79 0
      hcp-core/src/main/java/com/yingyangfly/core/dto/WxTencentDto.java
  5. 2 0
      hcp-core/src/main/java/com/yingyangfly/core/service/AppUserHeadService.java
  6. 2 0
      hcp-core/src/main/java/com/yingyangfly/core/service/LearnPackageService.java
  7. 10 0
      hcp-core/src/main/java/com/yingyangfly/core/service/PayService.java
  8. 16 1
      hcp-core/src/main/java/com/yingyangfly/core/service/impl/AppUserHeadServiceImpl.java
  9. 84 32
      hcp-core/src/main/java/com/yingyangfly/core/service/impl/AppUserService.java
  10. 10 3
      hcp-core/src/main/java/com/yingyangfly/core/service/impl/LearnPackageServiceImpl.java
  11. 86 2
      hcp-core/src/main/java/com/yingyangfly/core/service/impl/PayServiceImpl.java
  12. 21 0
      hcp-core/src/main/java/com/yingyangfly/core/service/impl/WeChatService.java
  13. 3 3
      hcp-core/src/main/java/com/yingyangfly/core/util/SmgUtil.java
  14. 206 1
      hcp-core/src/main/java/com/yingyangfly/core/util/WXPayUtil.java
  15. 118 0
      hcp-core/src/main/java/com/yingyangfly/core/util/WxPayJsApiSignUtil.java
  16. 13 0
      hcp-core/src/main/java/com/yingyangfly/core/vo/WxPayJsApiPreVO.java
  17. 8 0
      hcp-platform/src/main/java/com/yingyangfly/platform/controller/LearningPackageController.java
  18. 2 0
      hcp-platform/src/main/java/com/yingyangfly/platform/controller/PatientController.java
  19. 6 0
      hcp-platform/src/main/java/com/yingyangfly/platform/controller/PayOrderController.java
  20. 62 0
      hcp-platform/src/main/java/com/yingyangfly/platform/controller/WxTencentController.java
  21. 4 2
      hcp-platform/src/main/resources/application-dev.yml
  22. 2 0
      hcp-platform/src/main/resources/application-prod.yml

+ 17 - 0
hcp-core/src/main/java/com/yingyangfly/core/config/WechatConfig.java

@@ -0,0 +1,17 @@
+package com.yingyangfly.core.config;
+
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+@ConfigurationProperties(prefix = "wx")
+@Component
+public class WechatConfig {
+
+    private String appId;
+    private String appSecret;
+
+    public String getAppId() { return appId; }
+    public void setAppId(String appId) { this.appId = appId; }
+    public String getAppSecret() { return appSecret; }
+    public void setAppSecret(String appSecret) { this.appSecret = appSecret; }
+}

+ 14 - 0
hcp-core/src/main/java/com/yingyangfly/core/config/WxJsConfig.java

@@ -0,0 +1,14 @@
+package com.yingyangfly.core.config;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+
+@Data
+@AllArgsConstructor
+public class WxJsConfig {
+
+    private String appId;
+    private long timestamp;
+    private String nonceStr;
+    private String signature;
+}

+ 5 - 0
hcp-core/src/main/java/com/yingyangfly/core/domain/PayOrder.java

@@ -138,4 +138,9 @@ public class PayOrder extends BaseEntity {
 
     private String mobile;
 
+    /**
+     * 公众号防重复拉起支付码
+     */
+    private String state;
+
 }

+ 79 - 0
hcp-core/src/main/java/com/yingyangfly/core/dto/WxTencentDto.java

@@ -0,0 +1,79 @@
+package com.yingyangfly.core.dto;
+
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import com.yingyangfly.core.util.Sm4JacksonSerialize;
+import lombok.Data;
+
+@Data
+public class WxTencentDto {
+
+    private String state;
+
+    private String code;
+
+    // 套餐id
+    private Long packageId;
+
+    // 姓名
+    private String name;
+
+    /**
+     * 性别
+     */
+    private String gender;
+
+    // 年龄
+    private Integer age;
+
+    //手机号
+    @JsonSerialize(using = Sm4JacksonSerialize.class)
+    private String mobile;
+
+    // 身份证
+    @JsonSerialize(using = Sm4JacksonSerialize.class)
+    private String idCard;
+
+    // 出生日期
+    private String birthDate;
+
+    /**
+     * 教育程度
+     */
+    private String  education;
+
+    /**
+     * 现病史
+     */
+    private String medicalCurrentTypes;
+
+    /**
+     * 既往史
+     */
+    private String medicalHisTypes;
+
+    /**
+     * 家族史
+     */
+    private String geneticDiseases;
+
+    /**
+     * 诊疗结果
+     */
+    private String diagnoseResult;
+
+    /**
+     * 认知障碍
+     */
+    private String tags;
+
+    /**
+     * 押金
+     */
+    private String equipmentPledge;
+
+
+    /**
+     * 推荐医生
+     */
+    private String doctorName;
+}

+ 2 - 0
hcp-core/src/main/java/com/yingyangfly/core/service/AppUserHeadService.java

@@ -15,4 +15,6 @@ public interface AppUserHeadService {
     void saveAppUserHead(AppUserHead appUserHead);
 
     void delById(Long id);
+
+    String randomGetOneHeadByGender2(String genderType,String orgCode);
 }

+ 2 - 0
hcp-core/src/main/java/com/yingyangfly/core/service/LearnPackageService.java

@@ -21,4 +21,6 @@ public interface LearnPackageService {
     LearningPackage selectById(Long relationId);
 
     List<LearningPackage> getList(String orgCode,String status);
+
+    List<LearningPackage> tencentLearnPackage();
 }

+ 10 - 0
hcp-core/src/main/java/com/yingyangfly/core/service/PayService.java

@@ -3,6 +3,7 @@ import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.yingyangfly.common.dto.ResultResponse;
 import com.yingyangfly.core.domain.PayOrder;
 import com.yingyangfly.core.vo.RefundParamVo;
+import com.yingyangfly.core.vo.WxPayJsApiPreVO;
 
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
@@ -92,4 +93,13 @@ public interface PayService {
     ResultResponse refundPay(String  orderNo,String refundMoneyYUAN) throws Exception;
 
     ResultResponse getRefundParam(String orderNo);
+
+
+    /**
+     * 创建订单,JSAPI
+     * @return
+     */
+    WxPayJsApiPreVO createJSAPIOrder(PayOrder payOrder,String code) throws Exception;
+
+    List<PayOrder> selectPayOrderByState(String state);
 }

+ 16 - 1
hcp-core/src/main/java/com/yingyangfly/core/service/impl/AppUserHeadServiceImpl.java

@@ -16,6 +16,7 @@ import org.apache.commons.lang3.StringUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
+import javax.annotation.Resource;
 import java.util.Date;
 import java.util.List;
 import java.util.Random;
@@ -29,7 +30,7 @@ import java.util.Random;
 @Service
 public class AppUserHeadServiceImpl implements AppUserHeadService {
 
-    @Autowired
+    @Resource
     private AppUserHeadMapper appUserHeadMapper;
     @Autowired
     private TokenUtil tokenUtil;
@@ -81,4 +82,18 @@ public class AppUserHeadServiceImpl implements AppUserHeadService {
     public void delById(Long id) {
         appUserHeadMapper.deleteById(id);
     }
+
+    @Override
+    public String randomGetOneHeadByGender2(String genderType, String orgCode) {
+        QueryWrapper<AppUserHead> queryWrapper = new QueryWrapper<>();
+        queryWrapper.eq("gender_type",genderType);
+        queryWrapper.eq("org_code",orgCode);
+        List<AppUserHead> appUserHeads = appUserHeadMapper.selectList(queryWrapper);
+        if(CollectionUtil.isEmpty(appUserHeads)){
+            return null;
+        }
+        Random rand = new Random();
+        AppUserHead appUserHead = appUserHeads.get(rand.nextInt(appUserHeads.size()));
+        return appUserHead.getHeadImgUrl();
+    }
 }

+ 84 - 32
hcp-core/src/main/java/com/yingyangfly/core/service/impl/AppUserService.java

@@ -29,6 +29,7 @@ import com.yingyangfly.core.util.AmountUtils;
 import com.yingyangfly.core.util.EntityConverter;
 import com.yingyangfly.core.util.Sm4Util;
 import com.yingyangfly.core.util.SmgUtil;
+import com.yingyangfly.core.vo.WxPayJsApiPreVO;
 import com.yingyangfly.redis.client.RedisClient;
 import lombok.extern.slf4j.Slf4j;
 import org.assertj.core.util.Sets;
@@ -59,58 +60,59 @@ import java.util.stream.Collectors;
 @Slf4j
 public class AppUserService extends ServiceImpl<AppUserMapper, AppUser> implements IService<AppUser> {
 
-    @Autowired
+    @Resource
     private AppUserMapper appUserMapper;
-    @Autowired
-    private PayOrderMapper payOrderMapper;
-    @Autowired
+    @Resource
     private SysOrgService sysOrgService;
-    @Autowired
+    @Resource
     private SysDictDataService sysDictDataService;
-    @Autowired
+    @Resource
     private BCryptPasswordEncoder bCryptPasswordEncoder;
-    @Autowired
+    @Resource
     private TokenUtil tokenUtil;
-    @Autowired
+    @Resource
     private GamePlayRecordService gamePlayRecordService;
-    @Autowired
+    @Resource
     private LoginRecordMapper loginRecordMapper;
-    @Autowired
+    @Resource
     private MsgWarnMapper msgWarnMapper;
-    @Autowired
+    @Resource
     private LearningPackagMapper learnPackageMapper;
-    @Autowired
+    @Resource
     private GameUserService gameUserService;
-    @Autowired
+    @Resource
     private GameTaskDetailService gameTaskDetailService;
-    @Autowired
+    @Resource
     private RedisClient redisClient;
-    @Autowired
+    @Resource
     private ImApi imApi;
-    @Autowired
+    @Resource
     private SysUserMapper sysUserMapper;
-    @Autowired
+    @Resource
     private EquipmentService equipmentService;
-    @Autowired
+    @Resource
     private EquipmentRentRecordService equipmentRentRecordService;
-    @Autowired
+    @Resource
     private CacheUserService cacheUserService;
-    @Autowired
+    @Resource
     private MsgWarnService msgWarnService;
-    @Autowired
+    @Resource
     private AppUserHeadService appUserHeadService;
-    @Autowired
+    @Resource
     private AppUserPackageHisService appUserPackageHisService;
-    @Autowired
+    @Resource
     private SysParamMapper sysParamMapper;
-    @Autowired
+    @Resource
     private RecommendFacade recommendFacade;
-    @Autowired
+    @Resource
     private DepartmentService departmentService;
 
     @Resource
     private ReviewTaskService reviewTaskService;
 
+    @Resource
+    private PayService payService;
+
     @Value("${query-daily-trai}")
     private String queryDailyTrai;
     /**
@@ -284,9 +286,6 @@ public class AppUserService extends ServiceImpl<AppUserMapper, AppUser> implemen
         return map;
     }
 
-    @Autowired
-    PayService payService;
-
 
     @Transactional(rollbackFor = Exception.class)
     public ResultResponse saveAppUser(AppUser appUser) {
@@ -628,11 +627,11 @@ public class AppUserService extends ServiceImpl<AppUserMapper, AppUser> implemen
         }
         Random rand = new Random();
         int randomNumber = rand.nextInt(1000000);
-        String random = String.format("%06d", randomNumber);
+//        String random = String.format("%06d", randomNumber);
         //6位随机数
-        Boolean isSuccess = SmgUtil.sendCheckCode(mobile, MsgTemplateEnums.GET_CHECK_CODE.getTempalteCode(), random);
-//        String random = "123456";
-//        Boolean isSuccess = true;
+//        Boolean isSuccess = SmgUtil.sendCheckCode(mobile, MsgTemplateEnums.GET_CHECK_CODE.getTempalteCode(), random);
+        String random = "123456";
+        Boolean isSuccess = true;
         if (isSuccess) {
             // 防机器
             redisClient.set("hcp:sms:mobile:"+mobile,random,60);
@@ -894,4 +893,57 @@ public class AppUserService extends ServiceImpl<AppUserMapper, AppUser> implemen
             redisClient.del(String.format("%s%s", "token:app:", Sm4Util.decrypt(appUser.getMobile())));
         }
     }
+
+
+    @Transactional(rollbackFor = Exception.class)
+    public WxPayJsApiPreVO wXTencentSave(WxTencentDto wxTencentDto) throws Exception {
+        // 第一步新增患者
+        AppUser appUser = EntityConverter.convertToTarget(wxTencentDto, AppUser.class);
+        if(StringUtils.isEmpty(appUser.getAvatar()) && !StringUtils.isEmpty(appUser.getGender())){
+            String avatar = appUserHeadService.randomGetOneHeadByGender2(appUser.getGender(),"system");
+            appUser.setAvatar(avatar);
+        }
+        appUser.setStatus("0");
+        appUser.setTestFlag("1");
+        appUser.setCreateTime(new Date());
+        appUser.setUpdateTime(new Date());
+        //新增用户脑力值是0分
+        appUser.setMentalEnergy(0);
+        appUser.setCreateBy("微信公众号");
+        appUser.setOrgCode("onlineUser");
+        appUser.setOrgName("线上用户");
+        appUser.setCreateTime(new Date());
+        appUserMapper.insert(appUser);
+        //首次登陆欢迎语
+        log.info("<<<<<<<<<<<<发送首次登陆欢迎语IM>>用户信息:{}>>>>>>>>>>",JSON.toJSONString(appUser));
+        String msgDesn = String.format("欢迎%s使用未来蓝豚康复平台!为您提供专业的认知康复支持和训练。帮助您提升认知能力,重建自信。小豚期待与您一同启程!",appUser.getName());
+        msgWarnService.save(buildMsgWarn(appUser.getId(),"B","欢迎加入未来蓝豚",msgDesn,appUser.getOrgCode(),appUser.getOrgName()));
+        // 游戏推荐
+        log.info("<<<<<<<<<<<<<<<<患者推送蓝豚和 首页 游戏>>>>>>>>>>>>>>>>");
+        recommendFacade.recommendGameUserJobByAppUser(appUser.getId());
+
+        LearningPackage learningPackage = learnPackageMapper.selectById(wxTencentDto.getPackageId());
+
+        BigDecimal price = BigDecimal.valueOf(learningPackage.getPrice());
+        BigDecimal pledge = new BigDecimal(wxTencentDto.getEquipmentPledge().trim());
+        BigDecimal totalAmount = price.add(pledge);
+
+        // JSPAI创建订单
+        PayOrder payOrder = new PayOrder();
+        payOrder.setDescription("您的训练套餐是:" + learningPackage.getPackageName() + ",总金额是:" + AmountUtils.F2Y4STR(Long.valueOf(totalAmount.toString())) + "元,其中押金:" + AmountUtils.F2Y4STR(Long.valueOf(wxTencentDto.getEquipmentPledge())) + "元");
+        payOrder.setEquipmentPledge(wxTencentDto.getEquipmentPledge());
+        payOrder.setOrderType("A,B");
+        payOrder.setOrgCode("onlineUser");
+        payOrder.setOrgName("线上用户");
+        payOrder.setState(wxTencentDto.getState());
+        payOrder.setRelationId(wxTencentDto.getPackageId());
+        payOrder.setPadNo("00000001");
+        payOrder.setPayMethod("1");
+        payOrder.setPayMoney(Long.valueOf(totalAmount.toString()));
+        payOrder.setUserId(appUser.getId());
+        payOrder.setUserName(wxTencentDto.getName());
+        payOrder.setCreateBy("微信公众号");
+        payOrder.setCreateTime(new Date());
+        return payService.createJSAPIOrder(payOrder,wxTencentDto.getCode());
+    }
 }

+ 10 - 3
hcp-core/src/main/java/com/yingyangfly/core/service/impl/LearnPackageServiceImpl.java

@@ -22,9 +22,7 @@ import org.springframework.stereotype.Service;
 import org.springframework.util.CollectionUtils;
 
 import javax.annotation.Resource;
-import java.util.Date;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
 import java.util.stream.Collectors;
 
 /**
@@ -153,4 +151,13 @@ public class LearnPackageServiceImpl implements LearnPackageService {
         }
         return learningPackages;
     }
+
+    @Override
+    public List<LearningPackage> tencentLearnPackage() {
+        LambdaQueryWrapper<LearningPackage> lambdaQueryWrapper = new LambdaQueryWrapper<>();
+        lambdaQueryWrapper.eq(LearningPackage::getStatus,"0");
+        lambdaQueryWrapper.in(LearningPackage::getTimeType, Arrays.asList("B","C","D"));
+        lambdaQueryWrapper.eq(LearningPackage::getOrgCode,"system");
+        return learningPackagMapper.selectList(lambdaQueryWrapper);
+    }
 }

+ 86 - 2
hcp-core/src/main/java/com/yingyangfly/core/service/impl/PayServiceImpl.java

@@ -25,6 +25,7 @@ import com.yingyangfly.core.security.util.TokenUtil;
 import com.yingyangfly.core.service.*;
 import com.yingyangfly.core.util.*;
 import com.yingyangfly.core.vo.RefundParamVo;
+import com.yingyangfly.core.vo.WxPayJsApiPreVO;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.lang3.StringUtils;
 import org.springframework.beans.BeanUtils;
@@ -45,7 +46,7 @@ import java.util.stream.Collectors;
  */
 @Slf4j
 @Service
-public class PayServiceImpl implements PayService {
+public class PayServiceImpl<createWXOrder> implements PayService {
 
     @Resource
     private PayOrderMapper payOrderMapper;
@@ -640,7 +641,9 @@ public class PayServiceImpl implements PayService {
                 log.info("<<<<<<<<<<<记录租赁相关历史>>>>>>>>>>>>");
                 //做租赁相关的操作
                 // update by hpt on 20231102 只有押金才记录
-                appUserService.doRentRecord(appUser);
+                if (!appUser.getOrgCode().equals("onlineUser")){
+                    appUserService.doRentRecord(appUser);
+                }
             }
             log.info("<<<<<<<<<<<<<<<<更新用户信息,用户信息~~:{}>>>>>>>>>>>>",JSON.toJSONString(appUser));
             appUserService.updateById(appUser);
@@ -767,6 +770,87 @@ public class PayServiceImpl implements PayService {
         return qrCode;
     }
 
+
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public WxPayJsApiPreVO createJSAPIOrder(PayOrder payOrder,String code) throws Exception {
+        Map<String, Object> mapAttach = new HashMap<>();
+        if(!StringUtils.isEmpty(payOrder.getPadNo())){
+            mapAttach.put("padNo",payOrder.getPadNo());
+        }
+        if(payOrder.getRelationId()!=null){
+            mapAttach.put("packageId",payOrder.getRelationId());
+        }
+
+        payOrder.setAttach(JSONUtil.toJsonStr(mapAttach));
+        // 商家系统生成订单号  唯一且不可重复,即使关闭订单后相同订单号也无法再次创建订单
+        String orderNo = "LT_" + IdUtils.getNextId();
+        payOrder.setPayOrderId(orderNo);
+        payOrder.setSendPayTime(DateUtils.getTime());
+        //支付状态1.支付中 2.支付成功 3.支付失败,4退款中,5已退款,6已关闭
+        payOrder.setPayStatus("1");
+        payOrder.setCreateTime(new Date());
+        payOrder.setUpdateTime(new Date());
+        //获取机构编码,如果机构编码为空,直接返回
+        if(StringUtils.isBlank(payOrder.getOrgCode())){
+            return null;
+        }
+        return createWXJSAPIOrder(payOrder,code);
+    }
+
+    @Override
+    public List<PayOrder> selectPayOrderByState(String state) {
+        LambdaQueryWrapper<PayOrder> lambdaQueryWrapper = new LambdaQueryWrapper<>();
+        lambdaQueryWrapper.eq(PayOrder::getState,state);
+        return payOrderMapper.selectList(lambdaQueryWrapper);
+    }
+
+    /**
+     * 创建微信 JSAPI支付订单
+     * @param payOrder
+     * @param code
+     * @return
+     * @throws Exception
+     */
+    private WxPayJsApiPreVO createWXJSAPIOrder(PayOrder payOrder,String code) throws Exception {
+        log.info("创建微信JSAPI订单start!");
+        //押金
+        AppUser appUser = appUserService.getById(payOrder.getUserId());
+        payOrder.setCreateBy(appUser.getName());
+        payOrder.setUpdateBy(appUser.getName());
+        //支付方式 1.微信 2.支付宝
+        payOrder.setPayMethod("1");
+        payOrder.setMobile(appUser.getMobile());
+        // 微信JSAPI 下单
+        String prepayId = wxPayUtil.getWXJSAPIPay(payOrder,code);
+        // 获取 JSAPI 调取支付参数
+        WxPayJsApiPreVO wxPayJsApiPreVO = wxPayUtil.convertWxPayJsApiPreInfo(prepayId);
+        if (!StringUtils.isEmpty(prepayId)) {
+            if("A,B".equals(payOrder.getOrderType())){
+                BigDecimal equipmentPledge = new BigDecimal(payOrder.getEquipmentPledge());
+                Long money = equipmentPledge.longValue();
+                Long packageMoney = payOrder.getPayMoney() - money;
+                payOrder.setOrderType("A");
+                payOrder.setPayMoney(money);
+                payOrderMapper.insert(payOrder);
+                //套餐
+                PayOrder payOrder2 = new PayOrder();
+                BeanUtils.copyProperties(payOrder,payOrder2,"id","payOrderId");
+                String orderNo = "LT_SUB_" + IdUtils.getNextId();
+                payOrder2.setPayOrderId(orderNo);
+                payOrder2.setPId(payOrder.getPayOrderId());
+                payOrder2.setPayMoney(packageMoney);
+                payOrder2.setOrderType("B");
+                payOrderMapper.insert(payOrder2);
+            }else{
+                payOrderMapper.insert(payOrder);
+            }
+        }
+        return wxPayJsApiPreVO;
+    }
+
+
     @Override
     public void callback(PayOrder payOrder,String transactionId,String openid){
         payOrder.setPayStatus("2");

+ 21 - 0
hcp-core/src/main/java/com/yingyangfly/core/service/impl/WeChatService.java

@@ -0,0 +1,21 @@
+package com.yingyangfly.core.service.impl;
+
+import com.yingyangfly.core.config.WxJsConfig;
+import lombok.Value;
+import org.springframework.stereotype.Service;
+
+@Service
+public class WeChatService {
+
+//    public WxJsConfig getJsConfig(String url) {
+//        // 1. 获取jsapi_ticket
+//        String jsapiTicket = getJsapiTicket();
+//
+//        // 2. 生成签名参数
+//        String nonceStr = generateNonceStr();
+//        long timestamp = System.currentTimeMillis() / 1000;
+//        String signature = generateSignature(jsapiTicket, nonceStr, timestamp, url);
+//
+//        return new WxJsConfig(appId, timestamp, nonceStr, signature);
+//    }
+}

+ 3 - 3
hcp-core/src/main/java/com/yingyangfly/core/util/SmgUtil.java

@@ -32,9 +32,9 @@ public class SmgUtil {
         return new Client(config);
     }
 
-    public static void main(String[] args) {
-        sendCheckCode("16673327580","SMS_470580061","125874");
-    }
+//    public static void main(String[] args) {
+//        sendCheckCode("16673327580","SMS_470580061","125874");
+//    }
 
     public static Boolean sendCheckCode(String mobile, String templateCode, String random) {
         try {

+ 206 - 1
hcp-core/src/main/java/com/yingyangfly/core/util/WXPayUtil.java

@@ -1,9 +1,13 @@
 package com.yingyangfly.core.util;
 
 import cn.hutool.core.date.DateUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.json.JSONUtil;
 import com.alibaba.fastjson2.JSON;
 import com.alibaba.fastjson2.JSONObject;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.google.gson.Gson;
+import com.google.gson.JsonObject;
 import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder;
 import com.wechat.pay.contrib.apache.httpclient.auth.*;
 import com.wechat.pay.contrib.apache.httpclient.cert.CertificatesManager;
@@ -12,9 +16,11 @@ import com.wechat.pay.contrib.apache.httpclient.notification.NotificationHandler
 import com.wechat.pay.contrib.apache.httpclient.notification.NotificationRequest;
 import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;
 import com.yingyangfly.common.utils.ids.IdUtils;
+import com.yingyangfly.core.config.WechatConfig;
 import com.yingyangfly.core.domain.OrgMerchant;
 import com.yingyangfly.core.domain.PayOrder;
 import com.yingyangfly.core.service.OrgMerchantService;
+import com.yingyangfly.core.vo.WxPayJsApiPreVO;
 import lombok.SneakyThrows;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.lang3.StringUtils;
@@ -27,15 +33,21 @@ import org.apache.http.util.EntityUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 
+import javax.annotation.Resource;
 import javax.servlet.ServletInputStream;
 import javax.servlet.http.HttpServletRequest;
 import java.io.BufferedReader;
 import java.io.ByteArrayInputStream;
 import java.io.IOException;
 import java.io.InputStreamReader;
+import java.net.HttpURLConnection;
+import java.net.URL;
 import java.nio.charset.StandardCharsets;
 import java.security.PrivateKey;
+import java.util.ArrayList;
 import java.util.Calendar;
+import java.util.List;
+import java.util.UUID;
 
 /**
  * @author wanghaiyang
@@ -50,6 +62,9 @@ public class WXPayUtil {
     private CloseableHttpClient httpClient;
 
 
+    @Resource
+    private WechatConfig wechatConfig;
+
     /**
      * 获取微信回调结果
      *
@@ -143,7 +158,7 @@ public class WXPayUtil {
      *
      * @return 私钥对象
      */
-    public PrivateKey getPrivateKey(String privateKey ) throws IOException {
+    public PrivateKey getPrivateKey(String privateKey) throws IOException {
         PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(
                 new ByteArrayInputStream(privateKey.getBytes()));
         return merchantPrivateKey;
@@ -230,6 +245,196 @@ public class WXPayUtil {
         }
     }
 
+
+    /**
+     * 微信JSAPI 下单接口
+     * @param payOrder
+     * @param Code
+     * @return
+     * @throws Exception
+     */
+    public String getWXJSAPIPay(PayOrder payOrder,String Code)throws Exception{
+        log.info("订单信息:{}",payOrder);
+        //根据orgCode获取商户信息
+        OrgMerchant orgMer = new OrgMerchant();
+        orgMer.setType("1");
+        orgMer.setOrgCode("system");
+        OrgMerchant orgMerchant = orgMerchantService.selectOne(orgMer);
+        String privateKey = orgMerchant.getPrivateKey().replace("-----BEGIN PRIVATE KEY-----", "")
+                .replace("-----END PRIVATE KEY-----", "")
+                .replaceAll("\\s+", "");
+        String appId= orgMerchant.getAppId();
+        String mchId = orgMerchant.getMchId();
+        String mchSerialNo= orgMerchant.getMchSerialNo();
+        String apiV3Key= orgMerchant.getApiKey();
+        String wxNotifyUrl= orgMerchant.getNotifyUrl();
+        String wxReturnNotifyUrl= orgMerchant.getReturnNotifyUrl();
+        String gateway= orgMerchant.getGateway();
+        // 修改1:接口地址改为JSAPI
+        HttpPost httpPost = new HttpPost(orgMerchant.getGateway() + "/v3/pay/transactions/jsapi");
+        // 下单接口金额单位为分
+        log.info("下单金额:{}",payOrder.getPayMoney());
+        Long payMoney = payOrder.getPayMoney();
+        log.info("下单金额:{}",payMoney);
+        //获取10分钟之后的时间,作为订单的结束时间
+        Calendar nowTime = Calendar.getInstance();
+        nowTime.add(Calendar.MINUTE, 10);
+        String format = DateUtil.format(nowTime.getTime(), "yyyy-MM-dd'T'HH:mm:ssXXX");
+
+        // 请求body参数
+        // 支付成功回调通知接口 notify_url,必须为https
+        // 修改2:请求body中增加 payer.openid 参数(JSAPI必传)
+        String reqdata = "{" +
+                "\"time_expire\": \"" + format + "\"," +
+                "\"amount\": {" +
+                "\"total\": " + payMoney + "," +
+                "\"currency\": \"CNY\"" +
+                "}," +
+                "\"mchid\": \"" + mchId + "\"," +
+                "\"out_trade_no\": \"" + payOrder.getPayOrderId() + "\"," +
+                "\"appid\": \"" + appId + "\"," +
+                "\"description\": \"" + payOrder.getDescription() + "\"," +
+                "\"attach\": \"" + "payOrder.getAttach()" + "\"," +
+                // --- JSAPI 新增参数 ---
+                "\"payer\": {" +
+                "\"openid\": \"" + getOpenId(Code) + "\"" +  // 必须传递用户的openid
+                "}," +
+                // ---------------------
+                "\"notify_url\": \"" + wxNotifyUrl + "/m" + mchId + "\"" +
+                "}";
+
+        StringEntity entity = new StringEntity(reqdata, "utf-8");
+        entity.setContentType("application/json");
+        httpPost.setEntity(entity);
+        httpPost.setHeader("Accept", "application/json");
+
+        // 加载商户私钥(privateKey:私钥字符串)
+        PrivateKey merchantPrivateKey = PemUtil
+                .loadPrivateKey(new ByteArrayInputStream(privateKey.getBytes("utf-8")));
+
+        // 加载平台证书(mchId:商户号,mchSerialNo:商户证书序列号,apiV3Key:V3密钥)
+        AutoUpdateCertificatesVerifier verifier = new AutoUpdateCertificatesVerifier(
+                new WechatPay2Credentials(mchId, new PrivateKeySigner(mchSerialNo, merchantPrivateKey)), apiV3Key.getBytes("utf-8"));
+
+        // 初始化httpClient
+        httpClient = WechatPayHttpClientBuilder.create()
+                .withMerchant(mchId, mchSerialNo, merchantPrivateKey)
+                .withValidator(new WechatPay2Validator(verifier)).build();
+        //完成签名并执行请求
+        CloseableHttpResponse response = httpClient.execute(httpPost);
+        try {
+            int statusCode = response.getStatusLine().getStatusCode();
+            //处理成功
+            if (statusCode == 200) {
+                String prepayId = EntityUtils.toString(response.getEntity());
+                JSONObject jsonObject = JSON.parseObject(prepayId);
+                log.info("success,return body = " + EntityUtils.toString(response.getEntity()));
+                return jsonObject.getString("prepay_id");
+            } else {
+                log.info("微信支付JSAPI下单失败:statusCode{},响应体:{}", statusCode, EntityUtils.toString(response.getEntity()));
+                throw new IOException("换取微信二维码异常");
+            }
+        } finally {
+            response.close();
+        }
+    }
+
+    /**
+     * 微信JSAPI 获取调起支付参数
+     * @param prepayId
+     * @return
+     * @throws Exception
+     */
+    public WxPayJsApiPreVO convertWxPayJsApiPreInfo(String prepayId) throws Exception {
+        OrgMerchant orgMer = new OrgMerchant();
+        orgMer.setType("1");
+        orgMer.setOrgCode("system");
+        OrgMerchant orgMerchant = orgMerchantService.selectOne(orgMer);
+
+        String appId = orgMerchant.getAppId();
+        String privateKey = orgMerchant.getPrivateKey();
+        if (StringUtils.isEmpty(prepayId)) {
+            throw new IllegalArgumentException("prepayId不能为空");
+        }
+
+        // 2. 构建基础参数(时间戳、随机字符串、package)
+        Long timestamp = System.currentTimeMillis(); // 秒级时间戳
+        String nonceStr = UUID.randomUUID().toString().replace("-", "");     // 32位随机字符串
+        String packageStr = "prepay_id=" + prepayId;                        // 固定格式
+
+        // 3. 构建签名参数列表(按字段名ASCII字典序排序)
+//        List<String> signParams = new ArrayList<>();
+//        signParams.add(appId);          // 公众号/小程序appId
+//        signParams.add(timeStamp);      // 时间戳
+//        signParams.add(nonceStr);       // 随机字符串
+//        signParams.add(packageStr);     // prepay_id=xxx
+
+        // 4. 生成签名
+//        String signMessage;
+//        String paySign;
+//        try {
+//            signMessage = WxPayJsApiSignUtil.buildSignMessage(signParams); // 拼接签名字符串
+//            paySign = WxPayJsApiSignUtil.signV3(signMessage.getBytes(StandardCharsets.UTF_8),privateKey); // SHA256-RSA签名
+//            log.debug("[微信JSAPI支付] 签名原始串: {}", signMessage);
+//        } catch (Exception e) {
+//            log.error("[微信JSAPI支付] 签名生成失败: {}", e.getMessage());
+//            throw new RuntimeException("支付签名生成失败", e);
+//        }
+        String paySign = WxPayJsApiSignUtil.getSign(appId , timestamp, nonceStr, packageStr,privateKey);//签名
+        log.info("paySignpaySignpaySign="+paySign);
+
+        // 5. 构造返回对象
+        WxPayJsApiPreVO result = new WxPayJsApiPreVO();
+        result.setWxAppId(appId);               // 公众号/小程序ID
+        result.setWxTimeStamp(timestamp);       // 时间戳
+        result.setWxNonceStr(nonceStr);         // 随机字符串
+        result.setWxPackage(packageStr);        // 固定格式:prepay_id=xxx
+        result.setWxSignType("RSA");            // 签名类型(固定RSA)
+        result.setWxPaySign(paySign);           // 签名
+
+        // 6. 日志记录结果
+        log.info("[微信JSAPI支付] 预支付参数构建完成: {}", JSONUtil.toJsonStr(result));
+        return result;
+    }
+
+    /**
+     * 获取openid
+     * @param code
+     * @return
+     * @throws Exception
+     */
+    public String getOpenId(String code) throws Exception {
+        // 构建请求URL
+        String requestUrl = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=" +wechatConfig.getAppId()
+                + "&secret=" + wechatConfig.getAppSecret()
+                + "&code=" + code
+                + "&grant_type=authorization_code";
+
+        // 发送HTTP GET请求
+        URL url = new URL(requestUrl);
+        HttpURLConnection connection = (HttpURLConnection) url.openConnection();
+        connection.setRequestMethod("GET");
+        int responseCode = connection.getResponseCode();
+
+        if (responseCode == HttpURLConnection.HTTP_OK) { // 请求成功
+            BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream()));
+            StringBuilder response = new StringBuilder();
+            String line;
+            while ((line = in.readLine()) != null) {
+                response.append(line);
+            }
+            in.close();
+
+            // 解析JSON响应,获取openid
+            String jsonResponse = response.toString();
+            JsonObject jsonObject = new Gson().fromJson(jsonResponse, JsonObject.class);
+            String openid = jsonObject.get("openid").getAsString();
+            return openid;
+        } else {
+            throw new Exception("HTTP GET请求失败:" + responseCode);
+        }
+    }
+
     @SneakyThrows
     public JSONObject queryOrder(PayOrder pay){
         OrgMerchant orgMerchant = orgMerchantService.getOrgMerchant(pay.getOrgCode(),"1");

+ 118 - 0
hcp-core/src/main/java/com/yingyangfly/core/util/WxPayJsApiSignUtil.java

@@ -0,0 +1,118 @@
+package com.yingyangfly.core.util;
+
+import java.security.KeyFactory;
+import java.security.PrivateKey;
+import java.security.Signature;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.util.Base64;
+import java.util.List;
+
+public class WxPayJsApiSignUtil {
+
+    /**
+     * 构建签名字符串(格式:参数1\n参数2\n...参数N\n)
+     */
+    public static String buildSignMessage(List<String> params) {
+        params.sort(String::compareTo); // ASCII字典序排序
+        return String.join("\n", params) + "\n"; // 最后加换行符
+    }
+
+    /**
+     * SHA256-RSA签名(微信支付V3规范)
+     * @param message 待签名的原始数据(按微信规则拼接的字符串)
+     * @param privateKeyStr 商户私钥(PEM格式,需去除头尾标记)
+     * @return 签名结果(Base64编码)
+     * @throws Exception 签名失败时抛出异常
+     */
+    public static String signV3(byte[] message, String privateKeyStr) throws Exception {
+        // 1. 校验参数
+        if (message == null || message.length == 0) {
+            throw new IllegalArgumentException("签名数据不能为空");
+        }
+        if (privateKeyStr == null || privateKeyStr.trim().isEmpty()) {
+            throw new IllegalArgumentException("商户私钥不能为空");
+        }
+
+        // 2. 清理私钥格式(去除PEM头尾和换行符)
+        privateKeyStr = privateKeyStr
+                .replace("-----BEGIN PRIVATE KEY-----", "")
+                .replace("-----END PRIVATE KEY-----", "")
+                .replaceAll("\\s+", "");
+
+        try {
+            // 3. 加载私钥
+            byte[] privateKeyBytes = Base64.getDecoder().decode(privateKeyStr);
+            PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKeyBytes);
+            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
+            PrivateKey privateKey = keyFactory.generatePrivate(keySpec);
+
+            // 4. 初始化Signature对象(SHA256withRSA)
+            Signature signature = Signature.getInstance("SHA256withRSA");
+            signature.initSign(privateKey);
+            signature.update(message);
+
+            // 5. 生成签名并Base64编码
+            byte[] signBytes = signature.sign();
+            return Base64.getEncoder().encodeToString(signBytes);
+        } catch (Exception e) {
+            throw new Exception("SHA256-RSA签名失败: " + e.getMessage(), e);
+        }
+    }
+
+
+
+    /**
+     * 作用:使用字段appId、timeStamp、nonceStr、package计算得出的签名值
+     * 场景:根据微信统一下单接口返回的 prepay_id 生成调启支付所需的签名值
+     * @param appId
+     * @param timestamp
+     * @param nonceStr
+     * @param pack package
+     * @return
+     * @throws Exception
+     */
+    public static String getSign(String appId, long timestamp, String nonceStr, String pack,String privateKeyStr) throws Exception{
+        String message = buildMessage(appId, timestamp, nonceStr, pack);
+        String paySign = sign(message.getBytes("utf-8"),privateKeyStr);
+        return paySign;
+    }
+
+
+    private static String buildMessage(String appId, long timestamp, String nonceStr, String pack) {
+        return appId + "\n"
+                + timestamp + "\n"
+                + nonceStr + "\n"
+                + pack + "\n";
+    }
+
+    private static String sign(byte[] message,String privateKeyStr) throws Exception{
+        Signature sign = Signature.getInstance("SHA256withRSA");
+        //这里需要一个PrivateKey类型的参数,就是商户的私钥。
+        //获取商户私钥
+        //PrivateKey privateKey = wxPayConfig.getPrivateKey(wxPayConfig.getPrivateKeyPath());
+        PrivateKey privateKey = getPrivateKey(privateKeyStr);
+        sign.initSign(privateKey);
+        sign.update(message);
+        return Base64.getEncoder().encodeToString(sign.sign());
+    }
+
+    /**
+     * 获取私钥。
+     *
+     * @param privateKeyStr 私钥  (required)
+     * @return 私钥对象
+     */
+    public static PrivateKey getPrivateKey(String privateKeyStr) throws Exception {
+        try {
+            String privateKey = privateKeyStr.replace("-----BEGIN PRIVATE KEY-----", "")
+                    .replace("-----END PRIVATE KEY-----", "")
+                    .replaceAll("\\s+", "");
+
+            KeyFactory kf = KeyFactory.getInstance("RSA");
+            return kf.generatePrivate(
+                    new PKCS8EncodedKeySpec(Base64.getMimeDecoder().decode(privateKey)));
+        }  catch (Exception e) {
+            throw new RuntimeException("无效的密钥格式");
+        }
+    }
+}

+ 13 - 0
hcp-core/src/main/java/com/yingyangfly/core/vo/WxPayJsApiPreVO.java

@@ -0,0 +1,13 @@
+package com.yingyangfly.core.vo;
+
+import lombok.Data;
+
+@Data // Lombok注解
+public class WxPayJsApiPreVO {
+    private String wxAppId;        // 公众号/小程序ID
+    private Long wxTimeStamp;    // 时间戳
+    private String wxNonceStr;     // 随机字符串
+    private String wxPackage;      // prepay_id=xxx
+    private String wxSignType;     // 签名类型(RSA)
+    private String wxPaySign;      // 签名
+}

+ 8 - 0
hcp-platform/src/main/java/com/yingyangfly/platform/controller/LearningPackageController.java

@@ -79,4 +79,12 @@ public class LearningPackageController {
         return ResultResponse.success();
     }
 
+    @PostMapping("/tencent/learnPackage")
+    @TraceLog
+    public ResultResponse tencentLearnPackage(){
+        List<LearningPackage> learningPackageList = learnPackageService.tencentLearnPackage();
+        List<LearningPackageVo> learningPackageVos = EntityConverter.convertList(learningPackageList, LearningPackageVo.class);
+        return ResultResponse.success(learningPackageVos);
+    }
+
 }

+ 2 - 0
hcp-platform/src/main/java/com/yingyangfly/platform/controller/PatientController.java

@@ -3,6 +3,7 @@ package com.yingyangfly.platform.controller;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
 import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
 import com.yingyangfly.common.dto.ResultResponse;
 import com.yingyangfly.common.log.annotation.TraceLog;
 import com.yingyangfly.core.annotation.Log;
@@ -17,6 +18,7 @@ import com.yingyangfly.core.service.EquipmentService;
 import com.yingyangfly.core.service.PayService;
 import com.yingyangfly.core.service.impl.SysOrgService;
 import com.yingyangfly.core.util.EntityConverter;
+import com.yingyangfly.core.util.Sm4Util;
 import com.yingyangfly.core.vo.AppUserVo;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;

+ 6 - 0
hcp-platform/src/main/java/com/yingyangfly/platform/controller/PayOrderController.java

@@ -4,6 +4,7 @@ import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.yingyangfly.common.dto.ResultResponse;
 import com.yingyangfly.common.log.annotation.TraceLog;
 import com.yingyangfly.core.annotation.Log;
+import com.yingyangfly.core.config.WechatConfig;
 import com.yingyangfly.core.domain.PayOrder;
 import com.yingyangfly.core.service.PayService;
 import com.yingyangfly.core.service.impl.CheckOrderService;
@@ -22,6 +23,7 @@ import org.springframework.web.bind.annotation.RestController;
 import javax.annotation.Resource;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
+import java.util.HashMap;
 import java.util.Map;
 
 /**
@@ -37,6 +39,9 @@ public class PayOrderController {
     @Autowired
     private PayService payService;
 
+    @Resource
+    private WechatConfig wechatConfig;
+
 
 //    @Log(title = "获取设备退款信息")
     @PostMapping("/getRefundParam")
@@ -145,4 +150,5 @@ public class PayOrderController {
 
     @Resource
     CheckOrderService checkOrderService;
+
 }

+ 62 - 0
hcp-platform/src/main/java/com/yingyangfly/platform/controller/WxTencentController.java

@@ -0,0 +1,62 @@
+package com.yingyangfly.platform.controller;
+
+import com.alibaba.excel.util.StringUtils;
+import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
+import com.yingyangfly.common.dto.ResultResponse;
+import com.yingyangfly.common.log.annotation.TraceLog;
+import com.yingyangfly.core.annotation.Log;
+import com.yingyangfly.core.config.WxJsConfig;
+import com.yingyangfly.core.domain.AppUser;
+import com.yingyangfly.core.domain.LearningPackage;
+import com.yingyangfly.core.domain.PayOrder;
+import com.yingyangfly.core.dto.WxTencentDto;
+import com.yingyangfly.core.mapper.LearningPackagMapper;
+import com.yingyangfly.core.mapper.PayOrderMapper;
+import com.yingyangfly.core.service.PayService;
+import com.yingyangfly.core.service.impl.AppUserService;
+import com.yingyangfly.core.util.Sm4Util;
+import com.yingyangfly.redis.client.RedisClient;
+import org.springframework.web.bind.annotation.*;
+
+import javax.annotation.Resource;
+import java.math.BigDecimal;
+import java.util.List;
+
+@RestController
+@RequestMapping("wx/tencent")
+public class WxTencentController {
+
+    @Resource
+    private AppUserService appUserService;
+
+    @Resource
+    private PayService payService;
+    /**
+     * 微信公众号  患者支付新增
+     * @return
+     */
+    @Log(title = "微信公众号")
+    @PostMapping("/save")
+    @TraceLog
+    public ResultResponse wXTencentSave(@RequestBody WxTencentDto wxTencentDto) throws Exception {
+        List<PayOrder> payOrders = payService.selectPayOrderByState(wxTencentDto.getState());
+        if (payOrders.size()>0){
+            return ResultResponse.fail(201,"重复的微信支付调用");
+        }
+        AppUser user = appUserService.selectByMobile(Sm4Util.encrypt(wxTencentDto.getMobile()), null);
+        if (ObjectUtils.isNotNull(user)){
+            return ResultResponse.fail(201,"手机号已经存在,请联系工作人员");
+        }
+        return ResultResponse.success(appUserService.wXTencentSave(wxTencentDto));
+    }
+
+
+//    @GetMapping("/config")
+//    public ResultResponse getConfig(@RequestParam String url) {
+//        if (StringUtils.isEmpty(url)) {
+//            return ResultResponse.fail("URL参数不能为空");
+//        }
+//        WxJsConfig config = weChatService.getJsConfig(url);
+//       return ResultResponse.success(config);
+//    }
+}

+ 4 - 2
hcp-platform/src/main/resources/application-dev.yml

@@ -73,8 +73,8 @@ oss:
   endpoint:  oss-cn-beijing.aliyuncs.com #填写自己oss endpoint
   outDomain: https://yaorongoss.yaorongmedical.com #填写自己oss 外网域名
 wx:
-  app-id: 填写自己微信支付key
-  app-secret: 填写自己微信支付密码
+  app-id: wxe6dbe98e8c6d1a4b
+  app-secret: 9012fccacb9a71d5d39ff2881b35388c
   pay-url-callback: http://自己微信支付回调:8601/pub/api/wx/pay/appointment/order/callback
   refund-url-callback: http://自己微信支付退款回调:8601/pub/api/wx/refund/appointment/order/callback
 live:
@@ -119,6 +119,8 @@ security:
       - /im/callback/**
       - /pay/callback/**/m**
       - /system/dict/data/selectByDictType
+      - /learn/package/tencent/learnPackage
+      - /wx/tencent/save
 
 query-daily-trai: http://60.205.122.15/h5-training-daily/index.html
 

+ 2 - 0
hcp-platform/src/main/resources/application-prod.yml

@@ -115,6 +115,8 @@ security:
       - /im/callback/**
       - /pay/callback/**/m**
       - /system/dict/data/selectByDictType
+      - /learn/package/tencent/learnPackage
+      - /wx/tencent/save
 
 query-daily-trai: https://yaorong.yaorongmedical.com/h5-training-daily/index.html