Przeglądaj źródła

1、直播订单支付兼容直播订单

yfh 1 miesiąc temu
rodzic
commit
be43b7d8ee
19 zmienionych plików z 955 dodań i 99 usunięć
  1. 60 33
      fs-live-app/src/main/java/com/fs/live/websocket/auth/WebSocketConfigurator.java
  2. 398 36
      fs-live-app/src/main/java/com/fs/live/websocket/service/WebSocketServer.java
  3. 7 0
      fs-live-app/src/main/resources/application.yml
  4. 1 1
      fs-service/pom.xml
  5. 17 0
      fs-service/src/main/java/com/fs/app/service/AppPayService.java
  6. 64 0
      fs-service/src/main/java/com/fs/app/service/impl/AppPayServiceImpl.java
  7. 8 0
      fs-service/src/main/java/com/fs/his/config/AppConfig.java
  8. 1 0
      fs-service/src/main/java/com/fs/his/enums/BusinessTypeEnum.java
  9. 1 1
      fs-service/src/main/java/com/fs/his/enums/PaymentMethodEnum.java
  10. 5 0
      fs-service/src/main/java/com/fs/his/param/FsIntegralOrderDoPayParam.java
  11. 23 0
      fs-service/src/main/java/com/fs/his/vo/GameVo.java
  12. 3 0
      fs-service/src/main/java/com/fs/hisStore/domain/FsStoreOrderScrm.java
  13. 96 3
      fs-service/src/main/java/com/fs/hisStore/service/impl/FsStoreOrderScrmServiceImpl.java
  14. 115 15
      fs-service/src/main/java/com/fs/hisStore/service/impl/FsStorePaymentScrmServiceImpl.java
  15. 64 1
      fs-service/src/main/java/com/fs/live/service/impl/LiveOrderServiceImpl.java
  16. 6 7
      fs-service/src/main/resources/application-druid-zkzh.yml
  17. 84 0
      fs-user-app/src/main/java/com/fs/app/controller/AppPayController.java
  18. 1 1
      fs-user-app/src/main/java/com/fs/app/controller/app/AppController.java
  19. 1 1
      fs-user-app/src/main/java/com/fs/app/controller/live/LiveOrderController.java

+ 60 - 33
fs-live-app/src/main/java/com/fs/live/websocket/auth/WebSocketConfigurator.java

@@ -28,72 +28,99 @@ public class WebSocketConfigurator extends ServerEndpointConfig.Configurator {
     @Override
     public void modifyHandshake(ServerEndpointConfig sec, HandshakeRequest request, HandshakeResponse response) {
         Map<String, List<String>> parameterMap = request.getParameterMap();
+
+        // 1. 先检查必要参数是否存在
         if(!parameterMap.containsKey(AttrConstant.LIVE_ID)){
-            throw new BaseException("缺少必要的参数");
+            throw new BaseException("缺少必要的参数: liveId");
         }
         if(!parameterMap.containsKey(AttrConstant.USER_ID)){
-            throw new BaseException("缺少必要的参数");
+            throw new BaseException("缺少必要的参数: userId");
         }
 
         String tokenKey = jwtUtils.getHeader();
         if (!parameterMap.containsKey(tokenKey) && !parameterMap.containsKey(AttrConstant.SIGNATURE)) {
-            throw new BaseException("缺少必要的参数");
+            throw new BaseException("缺少必要的参数: token或signature");
         }
 
-        Long liveId = Long.valueOf(parameterMap.get(AttrConstant.LIVE_ID).get(0));
-        Long userId = Long.valueOf(parameterMap.get(AttrConstant.USER_ID).get(0));
+        // 2. 安全地解析LIVE_ID和USER_ID
+        Long liveId = safeParseLong(parameterMap.get(AttrConstant.LIVE_ID).get(0), AttrConstant.LIVE_ID);
+        Long userId = safeParseLong(parameterMap.get(AttrConstant.USER_ID).get(0), AttrConstant.USER_ID);
 
         Map<String, Object> userProperties = sec.getUserProperties();
         userProperties.put(AttrConstant.LIVE_ID, liveId);
         userProperties.put(AttrConstant.USER_ID, userId);
+
+        // 3. 安全地解析可选参数
         if (parameterMap.containsKey(AttrConstant.COMPANY_ID)) {
-            userProperties.put(AttrConstant.COMPANY_ID, Long.valueOf(parameterMap.get(AttrConstant.COMPANY_ID).get(0)));
+            userProperties.put(AttrConstant.COMPANY_ID,
+                    safeParseLong(parameterMap.get(AttrConstant.COMPANY_ID).get(0), AttrConstant.COMPANY_ID));
         }
         if (parameterMap.containsKey(AttrConstant.COMPANY_USER_ID)) {
-            userProperties.put(AttrConstant.COMPANY_USER_ID, Long.valueOf(parameterMap.get(AttrConstant.COMPANY_USER_ID).get(0)));
+            userProperties.put(AttrConstant.COMPANY_USER_ID,
+                    safeParseLong(parameterMap.get(AttrConstant.COMPANY_USER_ID).get(0), AttrConstant.COMPANY_USER_ID));
         }
         if (parameterMap.containsKey(AttrConstant.LOCATION)) {
             userProperties.put(AttrConstant.LOCATION, parameterMap.get(AttrConstant.LOCATION).get(0));
         }
         if (parameterMap.containsKey(AttrConstant.QW_USER_ID)) {
-            userProperties.put(AttrConstant.QW_USER_ID, Long.valueOf(parameterMap.get(AttrConstant.QW_USER_ID).get(0)));
+            userProperties.put(AttrConstant.QW_USER_ID,
+                    safeParseLong(parameterMap.get(AttrConstant.QW_USER_ID).get(0), AttrConstant.QW_USER_ID));
         }
         if (parameterMap.containsKey(AttrConstant.EXTERNAL_CONTACT_ID)) {
-            userProperties.put(AttrConstant.EXTERNAL_CONTACT_ID, Long.valueOf(parameterMap.get(AttrConstant.EXTERNAL_CONTACT_ID).get(0)));
+            userProperties.put(AttrConstant.EXTERNAL_CONTACT_ID,
+                    safeParseLong(parameterMap.get(AttrConstant.EXTERNAL_CONTACT_ID).get(0), AttrConstant.EXTERNAL_CONTACT_ID));
         }
 
-        // 验证token
+        // 4. 验证token
         if (parameterMap.containsKey(tokenKey)) {
             String token = parameterMap.get(tokenKey).get(0);
             Claims claims = jwtUtils.getClaimByToken(token);
             if(claims == null || jwtUtils.isTokenExpired(claims.getExpiration())){
-                throw new BaseException(jwtUtils.getHeader());
+                throw new BaseException("token无效或已过期");
             }
-
             userProperties.put(AttrConstant.USER_TYPE, 0L);
         }
-        String userTypeStr = parameterMap.get(AttrConstant.USER_TYPE).get(0);
-        userProperties.put(AttrConstant.USER_TYPE, Long.parseLong(userTypeStr));
 
-        // 验证签名
-//        if (parameterMap.containsKey(AttrConstant.SIGNATURE)) {
-//            String liveIdStr = parameterMap.get(AttrConstant.LIVE_ID).get(0);
-//            String userIdStr = parameterMap.get(AttrConstant.USER_ID).get(0);
-//            String userTypeStr = parameterMap.get(AttrConstant.USER_TYPE).get(0);
-//            String timestampStr = parameterMap.get(AttrConstant.TIMESTAMP).get(0);
-//            String signatureStr = parameterMap.get(AttrConstant.SIGNATURE).get(0);
-//
-//            try {
-//                if (!VerifyUtils.verifySignature(liveIdStr, userIdStr, userTypeStr, timestampStr, signatureStr)) {
-//                    throw new BaseException("缺少必要的参数");
-//                }
-//
-//                userProperties.put(AttrConstant.USER_TYPE, Long.parseLong(userTypeStr));
-//            } catch (Exception e) {
-//                log.warn("webSocket连接验签失败 msg: {}", e.getMessage(), e);
-//                throw new BaseException("缺少必要的参数");
-//            }
-//        }
+        // 5. 修复关键问题:安全地处理USER_TYPE参数
+        if (parameterMap.containsKey(AttrConstant.USER_TYPE)) {
+            String userTypeStr = parameterMap.get(AttrConstant.USER_TYPE).get(0);
+            try {
+                // 检查是否为undefined或null
+                if (userTypeStr == null || userTypeStr.trim().isEmpty() ||
+                        "undefined".equals(userTypeStr) || "null".equals(userTypeStr)) {
+                    log.warn("USER_TYPE参数值为空或无效: {}", userTypeStr);
+                    // 可以根据业务需求设置默认值或抛出异常
+                    userProperties.put(AttrConstant.USER_TYPE, 0L); // 设置默认值
+                } else {
+                    userProperties.put(AttrConstant.USER_TYPE, Long.parseLong(userTypeStr));
+                }
+            } catch (NumberFormatException e) {
+                log.error("USER_TYPE参数格式错误: {}", userTypeStr, e);
+                // 根据业务需求处理:设置默认值或抛出异常
+                userProperties.put(AttrConstant.USER_TYPE, 0L); // 设置默认值
+            }
+        } else {
+            log.warn("缺少USER_TYPE参数,使用默认值");
+            userProperties.put(AttrConstant.USER_TYPE, 0L); // 设置默认值
+        }
+
+        // 验证签名(注释掉的代码保持不变)
+        // ...
+    }
+
+    /**
+     * 安全地解析Long类型参数
+     */
+    private Long safeParseLong(String value, String paramName) {
+        if (value == null || value.trim().isEmpty() ||
+                "undefined".equals(value) || "null".equals(value)) {
+            throw new BaseException("参数 " + paramName + " 的值无效: " + value);
+        }
+        try {
+            return Long.valueOf(value);
+        } catch (NumberFormatException e) {
+            throw new BaseException("参数 " + paramName + " 格式错误: " + value);
+        }
     }
 
 }

+ 398 - 36
fs-live-app/src/main/java/com/fs/live/websocket/service/WebSocketServer.java

@@ -36,12 +36,15 @@ import javax.websocket.*;
 import javax.websocket.server.ServerEndpoint;
 import java.io.EOFException;
 import java.io.IOException;
+import java.nio.charset.StandardCharsets;
 import java.time.LocalDate;
 import java.time.LocalDateTime;
 import java.time.ZoneId;
 import java.time.format.DateTimeFormatter;
 import java.util.*;
 import java.util.concurrent.*;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicLong;
 import java.util.concurrent.locks.Lock;
 import java.util.concurrent.locks.ReentrantLock;
 
@@ -69,6 +72,22 @@ public class WebSocketServer {
     // admin房间消息发送线程池(单线程,保证串行化)
     private final static ConcurrentHashMap<Long, ExecutorService> adminExecutors = new ConcurrentHashMap<>();
 
+    // 消息队列系统
+    // 每个直播间的消息队列,使用优先级队列支持管理员消息插队
+    private final static ConcurrentHashMap<Long, PriorityBlockingQueue<QueueMessage>> messageQueues = new ConcurrentHashMap<>();
+    // 每个直播间的消费者线程
+    private final static ConcurrentHashMap<Long, Thread> consumerThreads = new ConcurrentHashMap<>();
+    // 每个直播间的消费者线程控制标志
+    private final static ConcurrentHashMap<Long, AtomicBoolean> consumerRunningFlags = new ConcurrentHashMap<>();
+    // 每个直播间队列的总大小(字节数)
+    private final static ConcurrentHashMap<Long, AtomicLong> queueSizes = new ConcurrentHashMap<>();
+    // 消息队列最大容量:10000
+    private final static int MAX_QUEUE_SIZE = 10000;
+    // 消息队列最大大小:200MB
+    private final static long MAX_QUEUE_SIZE_BYTES = 200L * 1024L * 1024L; // 200MB
+    // 上下线消息采样率:10%
+    private final static double ENTRY_EXIT_SAMPLE_RATE = 0.1;
+
     private final RedisCache redisCache = SpringUtils.getBean(RedisCache.class);
     private final ILiveMsgService liveMsgService = SpringUtils.getBean(ILiveMsgService.class);
     private final ILiveService liveService = SpringUtils.getBean(ILiveService.class);
@@ -189,7 +208,8 @@ public class WebSocketServer {
                 redisCache.incr(UNIQUE_VIEWERS_KEY + liveId, 1);
             }
             liveWatchUserVO.setMsgStatus(liveWatchUserVO.getMsgStatus());
-            if (1 == random.nextInt(10)) {
+            // 上线消息采样10%进入队列
+            if (random.nextDouble() < ENTRY_EXIT_SAMPLE_RATE) {
                 SendMsgVo sendMsgVo = new SendMsgVo();
                 sendMsgVo.setLiveId(liveId);
                 sendMsgVo.setUserId(userId);
@@ -199,8 +219,8 @@ public class WebSocketServer {
                 sendMsgVo.setData(JSONObject.toJSONString(liveWatchUserVO));
                 sendMsgVo.setNickName(fsUser.getNickname());
                 sendMsgVo.setAvatar(fsUser.getAvatar());
-                // 广播连接消息
-                broadcastWebMessage(liveId, JSONObject.toJSONString(R.ok().put("data", sendMsgVo)));
+                // 将上线消息加入队列
+                enqueueMessage(liveId, JSONObject.toJSONString(R.ok().put("data", sendMsgVo)), false);
             }
 
             // 缓存用户首次进入记录,过期时间4小时
@@ -290,6 +310,15 @@ public class WebSocketServer {
         // 初始化心跳时间
         heartbeatCache.put(session.getId(), System.currentTimeMillis());
 
+        // 如果有session,启动消费者线程
+        ConcurrentHashMap<Long, Session> tempRoom = getRoom(liveId);
+        List<Session> tempAdminRoom = getAdminRoom(liveId);
+        boolean hasSession = (tempRoom != null && !tempRoom.isEmpty()) ||
+                            (tempAdminRoom != null && !tempAdminRoom.isEmpty());
+        if (hasSession) {
+            startConsumerThread(liveId);
+        }
+
     }
 
     //关闭连接时调用
@@ -341,8 +370,8 @@ public class WebSocketServer {
             LiveWatchUser liveWatchUserVO = liveWatchUserService.close(fsUser,liveId, userId);
 
 
-            // 广播离开消息 添加一个概率问题 摇塞子,1-4 当为1的时候广播消息
-            if (1 == new Random().nextInt(10)) {
+            // 下线消息采样10%进入队列
+            if (random.nextDouble() < ENTRY_EXIT_SAMPLE_RATE) {
                 SendMsgVo sendMsgVo = new SendMsgVo();
                 sendMsgVo.setLiveId(liveId);
                 sendMsgVo.setUserId(userId);
@@ -352,7 +381,8 @@ public class WebSocketServer {
                 sendMsgVo.setData(JSONObject.toJSONString(liveWatchUserVO));
                 sendMsgVo.setNickName(fsUser.getNickname());
                 sendMsgVo.setAvatar(fsUser.getAvatar());
-                broadcastWebMessage(liveId, JSONObject.toJSONString(R.ok().put("data", sendMsgVo)));
+                // 将下线消息加入队列
+                enqueueMessage(liveId, JSONObject.toJSONString(R.ok().put("data", sendMsgVo)), false);
             }
 
         } else {
@@ -370,6 +400,9 @@ public class WebSocketServer {
         // 清理Session相关资源
         heartbeatCache.remove(session.getId());
         sessionLocks.remove(session.getId());
+
+        // 检查并清理空的直播间资源
+        cleanupEmptyRoom(liveId);
     }
 
     //收到客户端信息
@@ -379,6 +412,7 @@ public class WebSocketServer {
 
         long liveId = (long) userProperties.get("liveId");
         long userType = (long) userProperties.get("userType");
+        boolean isAdmin = false;
 
         SendMsgVo msg = JSONObject.parseObject(message, SendMsgVo.class);
         if(msg.isOn()) return;
@@ -447,8 +481,19 @@ public class WebSocketServer {
                     sendMessage(session, JSONObject.toJSONString(R.ok().put("data", msg)));
                     break;
                 case "sendMsg":
+                    // 参数校验
+                    if (liveMsgService == null) {
+                        log.error("[WebSocket-sendMsg] liveMsgService为null, liveId={}, userId={}", liveId, msg.getUserId());
+                        sendMessage(session, JSONObject.toJSONString(R.error("系统服务异常")));
+                        return;
+                    }
+
                     msg.setMsg(productionWordFilter.filter(msg.getMsg()).getFilteredText());
-                    if(StringUtils.isEmpty(msg.getMsg())) return;
+                    if(StringUtils.isEmpty(msg.getMsg())) {
+                        log.debug("[WebSocket-sendMsg] 消息内容为空, liveId={}, userId={}", liveId, msg.getUserId());
+                        return;
+                    }
+
                     liveMsg = new LiveMsg();
                     liveMsg.setLiveId(msg.getLiveId());
                     liveMsg.setUserId(msg.getUserId());
@@ -458,27 +503,48 @@ public class WebSocketServer {
                     liveMsg.setCreateTime(new Date());
 
                     if (userType == 0) {
-                        List<LiveWatchUser> liveWatchUser = liveWatchUserService.getByLiveIdAndUserId(msg.getLiveId(), msg.getUserId());
-                        if(!liveWatchUser.isEmpty() && liveWatchUser.get(0).getMsgStatus() == 1){
-                            sendMessage(session, JSONObject.toJSONString(R.error("你已被禁言")));
-                            return;
+                        try {
+                            List<LiveWatchUser> liveWatchUser = liveWatchUserService.getByLiveIdAndUserId(msg.getLiveId(), msg.getUserId());
+                            if(!liveWatchUser.isEmpty() && liveWatchUser.get(0).getMsgStatus() == 1){
+                                sendMessage(session, JSONObject.toJSONString(R.error("你已被禁言")));
+                                return;
+                            }
+                        } catch (Exception e) {
+                            log.error("[WebSocket-sendMsg] 检查禁言状态失败, liveId={}, userId={}, error={}",
+                                    liveId, msg.getUserId(), e.getMessage(), e);
                         }
 
-                        Map<String, Integer> flagMap = liveWatchUserService.getLiveFlagWithCache(liveId);
-                        Integer liveFlag = flagMap.get("liveFlag");
-                        Integer replayFlag = flagMap.get("replayFlag");
-                        liveMsg.setLiveFlag(liveFlag);
-                        liveMsg.setReplayFlag(replayFlag);
+                        try {
+                            Map<String, Integer> flagMap = liveWatchUserService.getLiveFlagWithCache(liveId);
+                            Integer liveFlag = flagMap.get("liveFlag");
+                            Integer replayFlag = flagMap.get("replayFlag");
+                            liveMsg.setLiveFlag(liveFlag);
+                            liveMsg.setReplayFlag(replayFlag);
+                        } catch (Exception e) {
+                            log.error("[WebSocket-sendMsg] 获取直播标志失败, liveId={}, error={}", liveId, e.getMessage(), e);
+                            // 设置默认值
+                            liveMsg.setLiveFlag(1);
+                            liveMsg.setReplayFlag(0);
+                        }
 
-                        liveMsgService.insertLiveMsg(liveMsg);
+                        try {
+                            liveMsgService.insertLiveMsg(liveMsg);
+                            log.debug("[WebSocket-sendMsg] 消息插入成功, liveId={}, userId={}, msgId={}",
+                                    liveId, msg.getUserId(), liveMsg.getMsgId());
+                        } catch (Exception e) {
+                            log.error("[WebSocket-sendMsg] 消息插入失败, liveId={}, userId={}, error={}",
+                                    liveId, msg.getUserId(), e.getMessage(), e);
+                        }
                     }
 
                     msg.setOn(true);
                     msg.setData(JSONObject.toJSONString(liveMsg));
 
-                    // 广播消息
-                    broadcastMessage(liveId, JSONObject.toJSONString(R.ok().put("data", msg)));
+                    // 将消息加入队列(普通用户消息)
+                    isAdmin = (userType == 1);
+                    enqueueMessage(liveId, JSONObject.toJSONString(R.ok().put("data", msg)), isAdmin);
                     break;
+
                 case "sendNormalMsg":
                     msg.setMsg(productionWordFilter.filter(msg.getMsg()).getFilteredText());
                     if(StringUtils.isEmpty(msg.getMsg())) return;
@@ -510,8 +576,7 @@ public class WebSocketServer {
                     msg.setOn(true);
                     msg.setData(JSONObject.toJSONString(liveMsg));
                     msg.setCmd("sendMsg");
-                    // 广播消息
-                    broadcastMessage(liveId, JSONObject.toJSONString(R.ok().put("data", msg)));
+                    enqueueMessage(liveId, JSONObject.toJSONString(R.ok().put("data", msg)), true);
                     break;
                 case "sendPopMsg":
                     msg.setMsg(productionWordFilter.filter(msg.getMsg()).getFilteredText());
@@ -524,8 +589,7 @@ public class WebSocketServer {
                     liveMsg.setMsg(msg.getMsg());
                     msg.setOn(true);
                     msg.setData(JSONObject.toJSONString(liveMsg));
-                    // 广播消息
-                    broadcastMessage(liveId, JSONObject.toJSONString(R.ok().put("data", msg)));
+                    enqueueMessage(liveId, JSONObject.toJSONString(R.ok().put("data", msg)), true);
                     break;
                 case "sendTopMsg":
                     msg.setMsg(productionWordFilter.filter(msg.getMsg()).getFilteredText());
@@ -539,8 +603,7 @@ public class WebSocketServer {
                     liveMsg.setEndTime(DateUtils.addMinutes(new Date(),msg.getDuration()).toString());
                     msg.setOn(true);
                     msg.setData(JSONObject.toJSONString(liveMsg));
-                    // 广播消息
-                    broadcastMessage(liveId, JSONObject.toJSONString(R.ok().put("data", msg)));
+                    enqueueMessage(liveId, JSONObject.toJSONString(R.ok().put("data", msg)), true);
                     // 放在当前活动里面
                     redisCache.deleteObject(String.format(LiveKeysConstant.LIVE_HOME_PAGE_CONFIG, liveId, TOP_MSG));
                     redisCache.setCacheObject(String.format(LiveKeysConstant.LIVE_HOME_PAGE_CONFIG, liveId, TOP_MSG), JSONObject.toJSONString(liveMsg));
@@ -550,13 +613,13 @@ public class WebSocketServer {
                     msg.setOn(true);
                     liveWatchUserService.updateGlobalVisible(liveId, msg.getStatus());
                     liveService.updateGlobalVisible(liveId, msg.getStatus());
-                    // 广播消息
-                    broadcastMessage(liveId, JSONObject.toJSONString(R.ok().put("data", msg)));
+                    // 管理员消息插队
+                    enqueueMessage(liveId, JSONObject.toJSONString(R.ok().put("data", msg)), true);
                     break;
                 case "singleVisible":
                     liveWatchUserService.updateSingleVisible(liveId, msg.getStatus(),msg.getUserId());
-                    // 广播消息
-                    broadcastMessage(liveId, JSONObject.toJSONString(R.ok().put("data", msg)));
+                    // 管理员消息插队
+                    enqueueMessage(liveId, JSONObject.toJSONString(R.ok().put("data", msg)), true);
                     break;
                 case "sendGift":
                     break;
@@ -595,7 +658,8 @@ public class WebSocketServer {
         sendMsgVo.setUserType(0L);
         sendMsgVo.setCmd("deleteMsg");
         sendMsgVo.setMsg(msg.getMsg());
-        broadcastMessage(liveId, JSONObject.toJSONString(R.ok().put("data", sendMsgVo)));
+        // 管理员消息插队
+        enqueueMessage(liveId, JSONObject.toJSONString(R.ok().put("data", sendMsgVo)), true);
     }
 
     private void processCoupon(long liveId, SendMsgVo msg) {
@@ -615,7 +679,8 @@ public class WebSocketServer {
         } else {
             redisCache.deleteObject(String.format(LiveKeysConstant.LIVE_COUPON_NUM , couponIssueId));
         }
-        broadcastMessage(liveId, JSONObject.toJSONString(R.ok().put("data", msg)));
+        // 管理员消息插队
+        enqueueMessage(liveId, JSONObject.toJSONString(R.ok().put("data", msg)), true);
     }
 
 
@@ -630,7 +695,8 @@ public class WebSocketServer {
         liveService.asyncToCacheLiveConfig(liveId);
         msg.setLiveId(liveId);
         msg.setData(JSONObject.toJSONString(liveGoods));
-        broadcastMessage(liveId, JSONObject.toJSONString(R.ok().put("data", msg)));
+        // 管理员消息插队
+        enqueueMessage(liveId, JSONObject.toJSONString(R.ok().put("data", msg)), true);
     }
 
     /**
@@ -644,7 +710,8 @@ public class WebSocketServer {
         if (Objects.nonNull(liveRedConf)) {
             liveService.asyncToCacheLiveConfig(liveId);
             msg.setData(JSONObject.toJSONString(liveRedConf));
-            broadcastMessage(liveId, JSONObject.toJSONString(R.ok().put("data", msg)));
+            // 管理员消息插队
+            enqueueMessage(liveId, JSONObject.toJSONString(R.ok().put("data", msg)), true);
         }
     }
 
@@ -659,7 +726,8 @@ public class WebSocketServer {
         if (Objects.nonNull(liveLotteryConf)) {
             liveService.asyncToCacheLiveConfig(liveId);
             msg.setData(JSONObject.toJSONString(liveLotteryConf));
-            broadcastMessage(liveId, JSONObject.toJSONString(R.ok().put("data", msg)));
+            // 管理员消息插队
+            enqueueMessage(liveId, JSONObject.toJSONString(R.ok().put("data", msg)), true);
         }
     }
 
@@ -748,7 +816,11 @@ public class WebSocketServer {
         if (session == null || !session.isOpen()) {
             return;
         }
-        session.getAsyncRemote().sendText(JSONObject.toJSONString(R.ok().put("data", sendMsgVo)));
+        try {
+            sendMessage(session, JSONObject.toJSONString(R.ok().put("data", sendMsgVo)));
+        } catch (IOException e) {
+            log.error(e.getMessage());
+        }
     }
 
     private void sendBlockMessage(Long liveId, Long userId) {
@@ -1139,7 +1211,8 @@ public class WebSocketServer {
 //                }
             }
             msg.setStatus(1);
-            broadcastMessage(task.getLiveId(), JSONObject.toJSONString(R.ok().put("data", msg)));
+            // 定时任务消息作为管理员消息插队
+            enqueueMessage(task.getLiveId(), JSONObject.toJSONString(R.ok().put("data", msg)), true);
         } catch (Exception e) {
             log.error("定时任务执行异常:{}", e.getMessage());
         }
@@ -1525,5 +1598,294 @@ public class WebSocketServer {
         }
     }
 
+    /**
+     * 消息队列包装类,支持优先级(管理员消息优先级更高)
+     */
+    private static class QueueMessage implements Comparable<QueueMessage> {
+        private final String message;
+        private final long timestamp;
+        private final int priority; // 0=普通消息, 1=管理员消息(优先级更高)
+        private final long sequence; // 序列号,用于相同优先级消息的FIFO排序
+        private final long sizeBytes; // 消息大小(字节数)
+
+        private static final AtomicLong sequenceGenerator = new AtomicLong(0);
+
+        public QueueMessage(String message, boolean isAdmin) {
+            this.message = message;
+            this.timestamp = System.currentTimeMillis();
+            this.priority = isAdmin ? 1 : 0;
+            this.sequence = sequenceGenerator.getAndIncrement();
+            // 计算消息大小(UTF-8编码)
+            this.sizeBytes = message != null ? message.getBytes(StandardCharsets.UTF_8).length : 0;
+        }
+
+        public String getMessage() {
+            return message;
+        }
+
+        public long getSizeBytes() {
+            return sizeBytes;
+        }
+
+        @Override
+        public int compareTo(QueueMessage other) {
+            // 优先级高的先处理(管理员消息)
+            int priorityCompare = Integer.compare(other.priority, this.priority);
+            if (priorityCompare != 0) {
+                return priorityCompare;
+            }
+            // 相同优先级按序列号排序(FIFO)
+            return Long.compare(this.sequence, other.sequence);
+        }
+    }
+
+    /**
+     * 获取或创建消息队列
+     */
+    private PriorityBlockingQueue<QueueMessage> getMessageQueue(Long liveId) {
+        return messageQueues.computeIfAbsent(liveId, k -> new PriorityBlockingQueue<>());
+    }
+
+    /**
+     * 启动消费者线程(如果还没有启动)
+     */
+    private void startConsumerThread(Long liveId) {
+        consumerRunningFlags.computeIfAbsent(liveId, k -> new AtomicBoolean(false));
+        AtomicBoolean runningFlag = consumerRunningFlags.get(liveId);
+
+        // 如果线程已经在运行,直接返回
+        if (runningFlag.get()) {
+            return;
+        }
+
+        // 尝试启动消费者线程
+        synchronized (consumerRunningFlags) {
+            if (runningFlag.compareAndSet(false, true)) {
+                Thread consumerThread = new Thread(() -> {
+                    PriorityBlockingQueue<QueueMessage> queue = getMessageQueue(liveId);
+                    log.info("[消息队列] 启动消费者线程, liveId={}", liveId);
+
+                    while (runningFlag.get()) {
+                        try {
+                            // 检查是否还有session,如果没有则退出
+                            ConcurrentHashMap<Long, Session> room = rooms.get(liveId);
+                            List<Session> adminRoom = adminRooms.get(liveId);
+
+                            boolean hasSession = (room != null && !room.isEmpty()) ||
+                                                (adminRoom != null && !adminRoom.isEmpty());
+
+                            if (!hasSession) {
+                                log.info("[消息队列] 直播间无session,停止消费者线程, liveId={}", liveId);
+                                break;
+                            }
+
+                            // 从队列中取消息,最多等待1秒
+                            QueueMessage queueMessage = queue.poll(1, TimeUnit.SECONDS);
+                            if (queueMessage != null) {
+                                // 更新队列大小(减少)
+                                AtomicLong currentSize = queueSizes.get(liveId);
+                                if (currentSize != null) {
+                                    currentSize.addAndGet(-queueMessage.getSizeBytes());
+                                }
+                                // 广播消息
+                                broadcastMessageFromQueue(liveId, queueMessage.getMessage());
+                            }
+                        } catch (InterruptedException e) {
+                            Thread.currentThread().interrupt();
+                            log.info("[消息队列] 消费者线程被中断, liveId={}", liveId);
+                            break;
+                        } catch (Exception e) {
+                            log.error("[消息队列] 消费消息异常, liveId={}", liveId, e);
+                        }
+                    }
+
+                    // 清理资源
+                    runningFlag.set(false);
+                    consumerThreads.remove(liveId);
+                    log.info("[消息队列] 消费者线程已停止, liveId={}", liveId);
+                }, "MessageConsumer-" + liveId);
+
+                consumerThread.setDaemon(true);
+                consumerThread.start();
+                consumerThreads.put(liveId, consumerThread);
+            }
+        }
+    }
+
+    /**
+     * 停止消费者线程
+     */
+    private void stopConsumerThread(Long liveId) {
+        AtomicBoolean runningFlag = consumerRunningFlags.get(liveId);
+        if (runningFlag != null) {
+            runningFlag.set(false);
+        }
+        Thread consumerThread = consumerThreads.remove(liveId);
+        if (consumerThread != null) {
+            consumerThread.interrupt();
+        }
+    }
+
+    /**
+     * 将消息加入队列
+     * @param liveId 直播间ID
+     * @param message 消息内容
+     * @param isAdmin 是否是管理员消息(管理员消息会插队)
+     * @return 是否成功加入队列
+     */
+    private boolean enqueueMessage(Long liveId, String message, boolean isAdmin) {
+        PriorityBlockingQueue<QueueMessage> queue = getMessageQueue(liveId);
+        AtomicLong currentSize = queueSizes.computeIfAbsent(liveId, k -> new AtomicLong(0));
+
+        // 计算新消息的大小
+        long messageSize = message != null ? message.getBytes(StandardCharsets.UTF_8).length : 0;
+
+        // 检查队列条数限制
+        if (!isAdmin && queue.size() >= MAX_QUEUE_SIZE) {
+            log.warn("[消息队列] 队列条数已满,丢弃消息, liveId={}, queueSize={}", liveId, queue.size());
+            return false;
+        }
+
+        // 检查队列大小限制(200MB)
+        long newTotalSize = currentSize.get() + messageSize;
+        if (newTotalSize > MAX_QUEUE_SIZE_BYTES) {
+            if (!isAdmin) {
+                // 普通消息超过大小限制,直接丢弃
+                log.warn("[消息队列] 队列大小超过限制,丢弃普通消息, liveId={}, currentSize={}MB, messageSize={}KB",
+                        liveId, currentSize.get() / (1024.0 * 1024.0), messageSize / 1024.0);
+                return false;
+            } else {
+                // 管理员消息:需要移除一些普通消息以腾出空间
+                long needToFree = newTotalSize - MAX_QUEUE_SIZE_BYTES;
+                long freedSize = removeMessagesToFreeSpace(queue, currentSize, needToFree, true);
+                if (freedSize < needToFree) {
+                    log.warn("[消息队列] 无法释放足够空间,管理员消息可能无法入队, liveId={}, needToFree={}KB, freed={}KB",
+                            liveId, needToFree / 1024.0, freedSize / 1024.0);
+                    // 即使空间不足,也尝试入队(可能会超过限制,但管理员消息优先级高)
+                }
+            }
+        }
+
+        // 如果是管理员消息且队列条数已满,移除一个普通消息
+        if (isAdmin && queue.size() >= MAX_QUEUE_SIZE) {
+            // 由于是优先级队列,普通消息(priority=0)会在队列末尾
+            // 尝试移除一个普通消息,为管理员消息腾出空间
+            QueueMessage removed = null;
+            Iterator<QueueMessage> iterator = queue.iterator();
+            while (iterator.hasNext()) {
+                QueueMessage msg = iterator.next();
+                if (msg.priority == 0) {
+                    removed = msg;
+                    break;
+                }
+            }
+            if (removed != null) {
+                queue.remove(removed);
+                currentSize.addAndGet(-removed.getSizeBytes());
+                log.debug("[消息队列] 管理员消息插队,移除普通消息, liveId={}", liveId);
+            } else {
+                // 如果没有普通消息,移除队列末尾的消息(可能是最早的管理员消息)
+                // 这种情况很少发生,因为管理员消息通常较少
+                log.warn("[消息队列] 队列条数已满且无普通消息可移除, liveId={}", liveId);
+            }
+        }
+
+        QueueMessage queueMessage = new QueueMessage(message, isAdmin);
+        queue.offer(queueMessage);
+        currentSize.addAndGet(messageSize);
+
+        // 如果有session,确保消费者线程在运行
+        ConcurrentHashMap<Long, Session> room = rooms.get(liveId);
+        List<Session> adminRoom = adminRooms.get(liveId);
+        boolean hasSession = (room != null && !room.isEmpty()) ||
+                            (adminRoom != null && !adminRoom.isEmpty());
+
+        if (hasSession) {
+            startConsumerThread(liveId);
+        }
+
+        return true;
+    }
+
+    /**
+     * 移除消息以释放空间
+     * @param queue 消息队列
+     * @param currentSize 当前队列大小(原子变量)
+     * @param needToFree 需要释放的空间(字节数)
+     * @param onlyRemoveNormal 是否只移除普通消息(true=只移除普通消息,false=可以移除任何消息)
+     * @return 实际释放的空间(字节数)
+     */
+    private long removeMessagesToFreeSpace(PriorityBlockingQueue<QueueMessage> queue,
+                                          AtomicLong currentSize,
+                                          long needToFree,
+                                          boolean onlyRemoveNormal) {
+        long freedSize = 0;
+        List<QueueMessage> toRemove = new ArrayList<>();
+
+        // 收集需要移除的消息(优先移除普通消息)
+        Iterator<QueueMessage> iterator = queue.iterator();
+        while (iterator.hasNext() && freedSize < needToFree) {
+            QueueMessage msg = iterator.next();
+            if (!onlyRemoveNormal || msg.priority == 0) {
+                toRemove.add(msg);
+                freedSize += msg.getSizeBytes();
+            }
+        }
+
+        // 如果只移除普通消息但空间还不够,可以移除管理员消息
+        if (onlyRemoveNormal && freedSize < needToFree) {
+            iterator = queue.iterator();
+            while (iterator.hasNext() && freedSize < needToFree) {
+                QueueMessage msg = iterator.next();
+                if (msg.priority == 1 && !toRemove.contains(msg)) {
+                    toRemove.add(msg);
+                    freedSize += msg.getSizeBytes();
+                }
+            }
+        }
+
+        // 移除消息并更新大小
+        for (QueueMessage msg : toRemove) {
+            if (queue.remove(msg)) {
+                currentSize.addAndGet(-msg.getSizeBytes());
+            }
+        }
+
+        if (freedSize > 0) {
+            log.info("[消息队列] 释放队列空间, removedCount={}, freedSize={}KB",
+                    toRemove.size(), freedSize / 1024.0);
+        }
+
+        return freedSize;
+    }
+
+    /**
+     * 从队列中消费消息并广播
+     */
+    private void broadcastMessageFromQueue(Long liveId, String message) {
+        broadcastMessage(liveId, message);
+    }
+
+    /**
+     * 检查并清理空的直播间资源
+     */
+    private void cleanupEmptyRoom(Long liveId) {
+        ConcurrentHashMap<Long, Session> room = rooms.get(liveId);
+        List<Session> adminRoom = adminRooms.get(liveId);
+
+        boolean hasSession = (room != null && !room.isEmpty()) ||
+                            (adminRoom != null && !adminRoom.isEmpty());
+
+        if (!hasSession) {
+            // 停止消费者线程
+            stopConsumerThread(liveId);
+            // 清理消息队列
+            messageQueues.remove(liveId);
+            consumerRunningFlags.remove(liveId);
+            queueSizes.remove(liveId);
+            log.info("[消息队列] 清理空直播间资源, liveId={}", liveId);
+        }
+    }
+
 }
 

+ 7 - 0
fs-live-app/src/main/resources/application.yml

@@ -2,6 +2,13 @@
 server:
   # 服务器的HTTP端口,默认为
   port: 7114
+  tomcat:
+    max-connections: 1000000
+    max-threads: 32
+    connection-timeout: 600000
+    accept-count: 10000
+  compression:
+    enabled: true
 
 # Spring配置
 spring:

+ 1 - 1
fs-service/pom.xml

@@ -123,7 +123,7 @@
         <dependency>
             <groupId>com.github.binarywang</groupId>
             <artifactId>weixin-java-pay</artifactId>
-            <version>4.7.2.B</version>
+            <version>4.8.0</version>
         </dependency>
         <dependency>
             <groupId>com.github.binarywang</groupId>

+ 17 - 0
fs-service/src/main/java/com/fs/app/service/AppPayService.java

@@ -0,0 +1,17 @@
+package com.fs.app.service;
+
+import com.fs.common.core.domain.R;
+import com.fs.his.param.FsPackageOrderDoPayParam;
+import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult;
+
+
+import java.util.Map;
+
+public interface AppPayService {
+
+
+    /**
+     * 微信支付回调
+     */
+    String wxNotify(WxPayOrderNotifyResult result);
+}

+ 64 - 0
fs-service/src/main/java/com/fs/app/service/impl/AppPayServiceImpl.java

@@ -0,0 +1,64 @@
+package com.fs.app.service.impl;
+
+import com.fs.app.service.AppPayService;
+import com.fs.his.service.IFsInquiryOrderService;
+import com.fs.his.service.IFsPackageOrderService;
+import com.fs.his.service.IFsStoreOrderService;
+import com.fs.hisStore.service.IFsStoreOrderScrmService;
+import com.fs.live.service.ILiveOrderService;
+import com.fs.system.mapper.SysConfigMapper;
+import com.github.binarywang.wxpay.bean.notify.WxPayNotifyResponse;
+import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Lazy;
+import org.springframework.stereotype.Service;
+
+@Slf4j
+@Service
+public class AppPayServiceImpl implements AppPayService {
+
+    @Autowired
+    private SysConfigMapper sysConfigMapper;
+    @Autowired
+    private IFsInquiryOrderService inquiryOrderService;
+    @Lazy
+    @Autowired
+    private IFsStoreOrderScrmService storeOrderService;
+
+    @Lazy
+    @Autowired
+    private ILiveOrderService liveOrderService;
+    @Autowired
+    private IFsPackageOrderService packageOrderService;
+
+    @Override
+    public String wxNotify(WxPayOrderNotifyResult result) {
+        log.info("微信回调参数: {}", result);
+        if (!"SUCCESS".equals(result.getReturnCode())){
+            return WxPayNotifyResponse.success("微信回调失败");
+        }
+
+        if (!"SUCCESS".equals(result.getResultCode())){
+            return WxPayNotifyResponse.success("交易失败");
+        }
+
+        String[] tradeNoArr = result.getOutTradeNo().split("-");
+        switch (tradeNoArr[0]) {
+            case "inquiry":
+                inquiryOrderService.payConfirm("", tradeNoArr[1],"","",1,result.getTransactionId(),"");
+                break;
+            case "store":
+                storeOrderService.payConfirm(1, Long.valueOf(tradeNoArr[1]),"","",result.getTransactionId(),"");
+
+            case "live":
+                liveOrderService.payConfirm(1, Long.valueOf(tradeNoArr[1]),"","",result.getTransactionId(),"");
+                break;
+            case "package":
+                packageOrderService.payConfirm("", tradeNoArr[1],"","",1,result.getTransactionId(),"");
+                break;
+        }
+        return WxPayNotifyResponse.success("OK");
+    }
+
+}

+ 8 - 0
fs-service/src/main/java/com/fs/his/config/AppConfig.java

@@ -2,6 +2,7 @@ package com.fs.his.config;
 
 import com.fs.course.vo.FsUserCourseVideoVO;
 import com.fs.his.domain.FsPackage;
+import com.fs.his.vo.GameVo;
 import lombok.Data;
 
 import java.util.List;
@@ -11,9 +12,16 @@ public class AppConfig {
     private String giftImage;
     private List<FsPackage> fsPackage;
     private Long courseId;
+    private Long tongueFlag;
+    private String appId;
     private List<FsUserCourseVideoVO> fsCourse;
     private Integer unbindLimit;
 
+    /**
+     * 游戏列表
+     */
+    private List<GameVo> gameList;
+
     private String corpId; //APP客服配置 企业主体id
     private String corpUrl; //APP客服配置 企业主体链接
     private Integer addIntegral; //玩一局游戏加多少积分

+ 1 - 0
fs-service/src/main/java/com/fs/his/enums/BusinessTypeEnum.java

@@ -8,6 +8,7 @@ import lombok.Getter;
 public enum BusinessTypeEnum {
     INTEGRAL_ORDER("integral", 6, "积分商城订单支付"),
     ORDER_ORDER("store", 8, "商城订单支付"),
+    LIVE_ORDER("live", 9, "直播订单支付"),
     ;
 
     private final String prefix;

+ 1 - 1
fs-service/src/main/java/com/fs/his/enums/PaymentMethodEnum.java

@@ -8,7 +8,7 @@ import lombok.Getter;
 public enum PaymentMethodEnum {
     MINIAPP_WECHAT("weixin"), // 小程序微信支付
     H5_WECHAT("微信"),      // H5微信支付
-    T_APP("t_app"),      // H5微信支付
+    WX_APP("wx_app"),      // H5微信支付
     T_NATIVE("t_native"),      // H5微信支付
     ALIPAY("alipay"),         // 支付宝支付
     H5_ALIPAY("alipay");       // H5支付宝支付

+ 5 - 0
fs-service/src/main/java/com/fs/his/param/FsIntegralOrderDoPayParam.java

@@ -10,4 +10,9 @@ public class FsIntegralOrderDoPayParam {
     private Long orderId;
 
     private Long userId;
+
+    /**
+     * 商品类型
+     */
+    private String type;
 }

+ 23 - 0
fs-service/src/main/java/com/fs/his/vo/GameVo.java

@@ -0,0 +1,23 @@
+package com.fs.his.vo;
+
+import lombok.Data;
+
+@Data
+public class GameVo {
+    private Long id;
+
+    /**
+     * 游戏图片
+     */
+    private String image;
+
+    /**
+     * 游戏名称
+     */
+    private String name;
+
+    /**
+     * 游戏链接
+     */
+    private String url;
+}

+ 3 - 0
fs-service/src/main/java/com/fs/hisStore/domain/FsStoreOrderScrm.java

@@ -354,6 +354,9 @@ public class FsStoreOrderScrm extends BaseEntity
     @TableField(exist = false)
     private String bankTransactionId;
 
+    @TableField(exist = false)
+    private Boolean isLive = false;
+
      // 是否审核,1-是,0-否
     private Integer isAudit;
 

+ 96 - 3
fs-service/src/main/java/com/fs/hisStore/service/impl/FsStoreOrderScrmServiceImpl.java

@@ -56,6 +56,7 @@ import com.fs.erp.dto.*;
 import com.fs.erp.dto.df.*;
 import com.fs.erp.mapper.FsErpFinishPushMapper;
 import com.fs.erp.service.IErpOrderService;
+import com.fs.his.config.AppConfig;
 import com.fs.his.config.FsSysConfig;
 import com.fs.his.domain.*;
 import com.fs.his.dto.FsPrescribeUsageDTO;
@@ -99,6 +100,9 @@ import com.fs.huifuPay.domain.HuifuCreateOrderResult;
 import com.fs.huifuPay.sdk.opps.core.request.V2TradePaymentScanpayRefundRequest;
 import com.fs.huifuPay.sdk.opps.core.utils.HuiFuUtils;
 import com.fs.huifuPay.service.HuiFuService;
+import com.fs.live.domain.LiveOrder;
+import com.fs.live.domain.LiveOrderPayment;
+import com.fs.live.mapper.LiveOrderMapper;
 import com.fs.pay.pay.dto.OrderQueryDTO;
 import com.fs.pay.pay.dto.RefundDTO;
 import com.fs.pay.service.IPayService;
@@ -108,6 +112,7 @@ import com.fs.hisStore.constants.StoreConstants;
 import com.fs.hisStore.domain.*;
 import com.fs.hisStore.enums.*;
 import com.fs.hisStore.service.*;
+import com.fs.system.domain.SysConfig;
 import com.fs.system.service.ISysConfigService;
 import com.fs.system.service.ISysDictTypeService;
 import com.fs.wx.miniapp.config.WxMaProperties;
@@ -127,7 +132,9 @@ import com.github.binarywang.wxpay.bean.result.WxPayRefundResult;
 import com.github.binarywang.wxpay.config.WxPayConfig;
 import com.github.binarywang.wxpay.exception.WxPayException;
 import com.github.binarywang.wxpay.service.WxPayService;
+import com.github.binarywang.wxpay.service.impl.WxPayServiceImpl;
 import com.google.common.base.Joiner;
+import com.google.gson.Gson;
 import lombok.Synchronized;
 import lombok.extern.slf4j.Slf4j;
 import me.chanjar.weixin.common.error.WxErrorException;
@@ -198,7 +205,8 @@ public class FsStoreOrderScrmServiceImpl implements IFsStoreOrderScrmService {
 
     @Autowired
     private FsUserAddressScrmMapper userAddressMapper;
-
+    @Autowired
+    private LiveOrderMapper liveOrderMapper;
     @Autowired
     private FsStoreOrderScrmMapper fsStoreOrderMapper;
 
@@ -2599,6 +2607,32 @@ public class FsStoreOrderScrmServiceImpl implements IFsStoreOrderScrmService {
                             return R.error(refund.getResp_desc());
                         }
 
+                    }else if ("wxApp".equals(payment.getPayMode()) && "wx_app".equals(payment.getPayTypeCode())) {
+                        // 处理微信退款
+                        WxPayService wxPayService = getWxPayService();
+                        WxPayRefundRequest refundRequest = new WxPayRefundRequest();
+                        refundRequest.setOutTradeNo("store-"+payment.getPayCode());
+                        refundRequest.setOutRefundNo("store-"+payment.getPayCode());
+                        refundRequest.setTotalFee(WxPayUnifiedOrderRequest.yuanToFen(payment.getPayMoney().toString()));
+                        refundRequest.setRefundFee(WxPayUnifiedOrderRequest.yuanToFen(payment.getPayMoney().toString()));
+                        try {
+                            WxPayRefundResult refundResult = wxPayService.refund(refundRequest);
+                            WxPayRefundQueryResult refundQueryResult = wxPayService.refundQuery("", refundResult.getOutTradeNo(), refundResult.getOutRefundNo(), refundResult.getRefundId());
+                            if (refundQueryResult != null && refundQueryResult.getResultCode().equals("SUCCESS")) {
+                                FsStorePaymentScrm paymentMap = new FsStorePaymentScrm();
+                                paymentMap.setPaymentId(payment.getPaymentId());
+                                paymentMap.setStatus(-1);
+                                paymentMap.setRefundTime(DateUtils.getNowDate());
+                                paymentMap.setRefundMoney(payment.getPayMoney());
+                                paymentService.updateFsStorePayment(paymentMap);
+                            } else {
+                                throw new CustomException("退款请求失败" + refundQueryResult.getReturnMsg());
+                            }
+
+                        } catch (WxPayException e) {
+                            throw new CustomException("退款请求失败" + e);
+                        }
+
                     } else {
                         TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
                         return R.error("支付类型异常");
@@ -2684,6 +2718,33 @@ public class FsStoreOrderScrmServiceImpl implements IFsStoreOrderScrmService {
 
     }
 
+    /**
+     * 封装微信参数
+     *
+     * @return
+     */
+    private WxPayService getWxPayService(){
+        SysConfig sysConfig = configService.selectConfigByConfigKey("app.config");
+        AppConfig config = new Gson().fromJson(sysConfig.getConfigValue(), AppConfig.class);
+        FsCoursePlaySourceConfig fsCoursePlaySourceConfig = fsCoursePlaySourceConfigMapper.selectCoursePlaySourceConfigByAppId(config.getAppId());
+        if (fsCoursePlaySourceConfig == null) {
+            throw new CustomException("未找到appId对应的小程序配置: " + config.getAppId());
+        }
+        MerchantAppConfig merchantAppConfig = merchantAppConfigMapper.selectMerchantAppConfigById(fsCoursePlaySourceConfig.getMerchantConfigId());
+        FsPayConfig payConfig1 = com.hc.openapi.tool.fastjson.JSON.parseObject(merchantAppConfig.getDataJson(), FsPayConfig.class);
+
+        WxPayConfig payConfig = new WxPayConfig();
+        payConfig.setAppId(payConfig1.getAppId());
+        payConfig.setMchId(payConfig1.getWxMchId());
+        payConfig.setMchKey(payConfig1.getWxMchKey());
+        payConfig.setSubAppId(StringUtils.trimToNull(null));
+        payConfig.setSubMchId(StringUtils.trimToNull(null));
+        payConfig.setKeyPath(null);
+        payConfig.setNotifyUrl(payConfig1.getNotifyUrlScrm());
+        WxPayServiceImpl payService = new WxPayServiceImpl();
+        payService.setConfig(payConfig);
+        return payService;
+    }
     @Override
     public R updateExpress(FsStoreOrderExpressEditParam param) {
         FsStoreOrderScrm order = fsStoreOrderMapper.selectFsStoreOrderById(param.getOrderId());
@@ -5694,7 +5755,7 @@ public class FsStoreOrderScrmServiceImpl implements IFsStoreOrderScrmService {
     @Transactional(rollbackFor = Exception.class)
     @Override
     public R payment(FsIntegralOrderDoPayParam param, PaymentMethodEnum paymentMethod) {
-        FsStoreOrderScrm order = fsStoreOrderMapper.selectFsStoreOrderById(param.getOrderId());
+        FsStoreOrderScrm order = buildPayment(param);
         if (Objects.isNull(order) || !order.getStatus().equals(0)
                 || order.getPayMoney().compareTo(BigDecimal.ZERO) <= 0){
             return R.error("非法操作");
@@ -5704,6 +5765,34 @@ public class FsStoreOrderScrmServiceImpl implements IFsStoreOrderScrmService {
         return storePaymentService.processPaymentScrm(payOrderParam);
     }
 
+    /**
+     * 综合参数
+     *
+     * @param param
+     * @return
+     */
+    public FsStoreOrderScrm buildPayment(FsIntegralOrderDoPayParam param){
+        if(param.getType().equals("live")){
+            LiveOrder liveOrder = liveOrderMapper.selectLiveOrderByOrderId(String.valueOf(param.getOrderId()));
+            if (ObjectUtil.isNotEmpty(liveOrder)){
+                FsStoreOrderScrm orderScrm = new FsStoreOrderScrm();
+                BeanUtils.copyProperties(liveOrder,orderScrm);
+                orderScrm.setId(liveOrder.getOrderId());
+                orderScrm.setUserId(Long.valueOf(liveOrder.getUserId()));
+                orderScrm.setIsLive(true);
+                return orderScrm;
+            }
+        }
+        if (param.getType().equals("store")){
+            FsStoreOrderScrm order = fsStoreOrderMapper.selectFsStoreOrderById(param.getOrderId());
+            if (ObjectUtil.isNotEmpty(order)){
+                return order;
+            }
+        }
+
+        return null;
+    }
+
 
     /**
      * 构建参数
@@ -5717,7 +5806,11 @@ public class FsStoreOrderScrmServiceImpl implements IFsStoreOrderScrmService {
         payOrderParam.setCompanyId(order.getCompanyId());
         payOrderParam.setCompanyUserId(order.getCompanyUserId());
         payOrderParam.setPaymentMethod(paymentMethod);
-        payOrderParam.setBusinessType(BusinessTypeEnum.ORDER_ORDER);
+        if (order.getIsLive()){
+            payOrderParam.setBusinessType(BusinessTypeEnum.LIVE_ORDER);
+        }else {
+            payOrderParam.setBusinessType(BusinessTypeEnum.ORDER_ORDER);
+        }
         return payOrderParam;
     }
 }

+ 115 - 15
fs-service/src/main/java/com/fs/hisStore/service/impl/FsStorePaymentScrmServiceImpl.java

@@ -41,6 +41,8 @@ import com.fs.course.domain.FsCourseRedPacketLog;
 import com.fs.course.mapper.FsCoursePlaySourceConfigMapper;
 import com.fs.course.mapper.FsCourseRedPacketLogMapper;
 import com.fs.course.service.IFsCourseRedPacketLogService;
+import com.fs.his.config.AppConfig;
+import com.fs.his.config.AppRedPacketConfig;
 import com.fs.his.domain.*;
 import com.fs.his.dto.PayConfigDTO;
 import com.fs.his.enums.PaymentMethodEnum;
@@ -58,8 +60,11 @@ import com.fs.huifuPay.domain.HuiFuCreateOrder;
 import com.fs.huifuPay.domain.HuifuCreateOrderResult;
 import com.fs.huifuPay.sdk.opps.core.utils.HuiFuUtils;
 import com.fs.huifuPay.service.HuiFuService;
+import com.fs.live.domain.LiveOrderPayment;
+import com.fs.live.mapper.LiveOrderPaymentMapper;
 import com.fs.pay.pay.dto.WxJspayDTO;
 import com.fs.hisStore.vo.FsStorePaymentStatisticsVO;
+import com.fs.system.domain.SysConfig;
 import com.fs.system.oss.CloudStorageService;
 import com.fs.system.oss.OSSFactory;
 import com.fs.system.service.ISysConfigService;
@@ -80,6 +85,7 @@ import com.github.binarywang.wxpay.exception.WxPayException;
 import com.github.binarywang.wxpay.service.TransferService;
 import com.github.binarywang.wxpay.service.WxPayService;
 import com.github.binarywang.wxpay.service.impl.WxPayServiceImpl;
+import com.google.gson.Gson;
 import com.hc.openapi.tool.fastjson.JSON;
 import lombok.extern.slf4j.Slf4j;
 import me.chanjar.weixin.common.error.WxErrorException;
@@ -117,6 +123,8 @@ public class FsStorePaymentScrmServiceImpl implements IFsStorePaymentScrmService
     @Autowired
     private FsStorePaymentScrmMapper fsStorePaymentMapper;
     @Autowired
+    private LiveOrderPaymentMapper liveOrderPaymentMapper;
+    @Autowired
     private IFsUserScrmService userService;
 
     @Autowired
@@ -1115,7 +1123,7 @@ public class FsStorePaymentScrmServiceImpl implements IFsStorePaymentScrmService
      */
     @Transactional(rollbackFor = Exception.class)
     @Override
-    public R processPaymentScrm(PayOrderParam payOrderParam) {
+    public R processPaymentScrm( PayOrderParam payOrderParam) {
         logger.info("发起支付 payOrderParam: {}", JSON.toJSONString(payOrderParam));
 
         if (payOrderParam.getAmount().compareTo(BigDecimal.ZERO) <= 0) {
@@ -1123,19 +1131,24 @@ public class FsStorePaymentScrmServiceImpl implements IFsStorePaymentScrmService
         }
 
         FsUserScrm user = userService.selectFsUserById(payOrderParam.getUserId());
-        if (Objects.isNull(user)){
+        if (Objects.isNull(user)) {
             throw new CustomException("用户不存在");
         }
-
-        FsPayConfig payConfig=new FsPayConfig();
+        String type = null;
+        FsPayConfig payConfig = new FsPayConfig();
+        if (PaymentMethodEnum.WX_APP == payOrderParam.getPaymentMethod()) {
+            String json = configService.selectConfigByKey("app.config");
+            AppConfig config = JSONUtil.toBean(json, AppConfig.class);
+            payOrderParam.setAppId(config.getAppId());
+            type = "wxApp";
+        }
         //支付宝可以不需要appid(在没有appid的情况下)【ps:小程序的支付宝没传appid 就G】
-        if ((PaymentMethodEnum.ALIPAY==payOrderParam.getPaymentMethod()
-                ||PaymentMethodEnum.T_NATIVE==payOrderParam.getPaymentMethod())
-                && StringUtils.isBlank(payOrderParam.getAppId())){
+        if ((PaymentMethodEnum.ALIPAY == payOrderParam.getPaymentMethod())
+                && StringUtils.isBlank(payOrderParam.getAppId())) {
             String json = configService.selectConfigByKey("his.pay");
             PayConfigDTO payConfigDTO = JSONUtil.toBean(json, PayConfigDTO.class);
             payConfig.setType(payConfigDTO.getType());
-        }else {
+        } else {
             if (StringUtils.isBlank(payOrderParam.getAppId())) {
                 throw new IllegalArgumentException("appId不能为空");
             }
@@ -1149,31 +1162,84 @@ public class FsStorePaymentScrmServiceImpl implements IFsStorePaymentScrmService
             }
 
             MerchantAppConfig merchantAppConfig = merchantAppConfigMapper.selectMerchantAppConfigById(fsCoursePlaySourceConfig.getMerchantConfigId());
-            payConfig =JSON.parseObject(merchantAppConfig.getDataJson(), FsPayConfig.class);
-            payConfig.setType(merchantAppConfig.getMerchantType());
+            payConfig = JSON.parseObject(merchantAppConfig.getDataJson(), FsPayConfig.class);
+            if (StringUtils.isNotEmpty(type)) {
+                payConfig.setType(type);
+            } else {
+                payConfig.setType(merchantAppConfig.getMerchantType());
+            }
             payConfig.setAppId(fsCoursePlaySourceConfig.getAppid());
 
             logger.debug("支付配置 his.pay: {}", payConfig);
         }
 
 
-
 //        FsPayConfig payConfig = JSONUtil.toBean(json, FsPayConfig.class);
 
         if (isWechatPayment(payOrderParam.getPaymentMethod())) {
             String openId = getOpenIdForPaymentMethod(user, payOrderParam.getPaymentMethod(), payConfig);
-            if (StringUtils.isBlank(openId)){
+            if (StringUtils.isBlank(openId)) {
                 throw new CustomException("用户OPENID不存在");
             }
         }
 
-        // 创建记录
-        FsStorePaymentScrm storePayment = createStorePaymentScrm(payConfig, user, payOrderParam);
+        // 创建记录 TODO 根据type创建支付
+        FsStorePaymentScrm storePayment = null;
+        if (payOrderParam.getBusinessType().getPrefix().equals("live")) {
+            LiveOrderPayment liveOrderPayment = createLiveStorePayment(payConfig, user, payOrderParam);
+            BeanUtils.copyProperties(liveOrderPayment, payOrderParam);
+        } else {
+            storePayment = createStorePaymentScrm(payConfig, user, payOrderParam);
+        }
 
         // 根据配置类型创建第三方支付订单
         return createThirdPartyPaymentScrm(payConfig, storePayment, user, payOrderParam);
     }
 
+
+    /**
+     * 直播订单支付信息
+     *
+     * @param payConfig
+     * @param user
+     * @param payOrderParam
+     * @return
+     */
+    private LiveOrderPayment createLiveStorePayment(FsPayConfig payConfig, FsUserScrm user, PayOrderParam payOrderParam) {
+        String payCode = OrderCodeUtils.getOrderSn();
+        if (StringUtils.isEmpty(payCode)) {
+            throw new CustomException("订单生成失败,请重试");
+        }
+
+        LiveOrderPayment storePayment = new LiveOrderPayment();
+        storePayment.setStatus(0);
+        storePayment.setAppId(payConfig.getAppId());
+        storePayment.setPayMode(payConfig.getType());
+        storePayment.setBusinessCode(payOrderParam.getOrderCode());
+        storePayment.setPayCode(payCode);
+        storePayment.setPayMoney(payOrderParam.getAmount());
+        storePayment.setCreateTime(new Date());
+        storePayment.setPayTypeCode(payOrderParam.getPaymentMethod().getDesc());
+        storePayment.setBusinessType(payOrderParam.getBusinessType().getCode());
+        storePayment.setCompanyId(payOrderParam.getCompanyId());
+        storePayment.setCompanyUserId(payOrderParam.getCompanyUserId());
+        storePayment.setRemark(payOrderParam.getBusinessType().getDesc());
+        storePayment.setStoreId(payOrderParam.getStoreId());
+        storePayment.setUserId(user.getUserId());
+        storePayment.setBusinessId(payOrderParam.getOrderId().toString());
+
+        // 设置openId(如果是微信支付)
+        if (isWechatPayment(payOrderParam.getPaymentMethod())) {
+            storePayment.setOpenId(getOpenIdForPaymentMethod(user, payOrderParam.getPaymentMethod(), payConfig));
+        }
+
+        if (liveOrderPaymentMapper.insertLiveOrderPayment(storePayment) <= 0) {
+            throw new CustomException("支付订单创建失败");
+        }
+
+        return storePayment;
+    }
+
     /**
      * 判断是否微信支付
      * @param method 支付类型
@@ -1219,6 +1285,8 @@ public class FsStorePaymentScrmServiceImpl implements IFsStorePaymentScrmService
         switch (payConfig.getType()) {
             case "wx":
                 return createWxPayment(storePayment, user, payOrderParam, payConfig);
+            case "wxApp":
+                return createWxAppPayment(storePayment, user, payOrderParam, payConfig);
             case "hf":
                 return createHfPayment(storePayment, user, payOrderParam, payConfig);
             default:
@@ -1262,7 +1330,6 @@ public class FsStorePaymentScrmServiceImpl implements IFsStorePaymentScrmService
             case ALIPAY:
             case H5_ALIPAY:
                 return "A_NATIVE";
-            case T_APP:
             case T_NATIVE:
                 return "T_NATIVE";
             default:
@@ -1311,6 +1378,38 @@ public class FsStorePaymentScrmServiceImpl implements IFsStorePaymentScrmService
         }
     }
 
+    /**
+     * 微信App支付
+     */
+    private R createWxAppPayment(FsStorePaymentScrm storePayment, FsUserScrm user, PayOrderParam payOrderParam, FsPayConfig payConfig) {
+        //创建微信订单
+        WxPayConfig wxPayConfig = buildWxPayConfig(payConfig);
+
+        wxPayConfig.setAppId(wxPayConfig.getAppId());
+        wxPayConfig.setMchId(wxPayConfig.getMchId());
+        wxPayConfig.setMchKey(wxPayConfig.getMchKey());
+        wxPayConfig.setSubAppId(StringUtils.trimToNull(null));
+        wxPayConfig.setSubMchId(StringUtils.trimToNull(null));
+        wxPayConfig.setKeyPath(null);
+        wxPayConfig.setNotifyUrl(wxPayConfig.getNotifyUrl());
+        wxPayService.setConfig(wxPayConfig);
+        WxPayUnifiedOrderRequest orderRequest = new WxPayUnifiedOrderRequest();
+        orderRequest.setBody(payOrderParam.getBusinessType().getDesc());
+        orderRequest.setOutTradeNo(payOrderParam.getBusinessType().getPrefix() + "-" + storePayment.getPayCode());
+        orderRequest.setTotalFee(WxPayUnifiedOrderRequest.yuanToFen(storePayment.getPayMoney().toString()));
+        orderRequest.setTradeType("APP");
+        orderRequest.setSpbillCreateIp(IpUtils.getIpAddr(ServletUtils.getRequest()));
+        orderRequest.setNotifyUrl(wxPayConfig.getNotifyUrl());
+        //调用统一下单接口,获取"预支付交易会话标识"
+        try {
+            Object result = wxPayService.createOrder(orderRequest);
+            return R.ok().put("data",result).put("type","wxApp").put("isPay",0);
+        } catch (WxPayException e) {
+            e.printStackTrace();
+            throw new CustomException("支付失败"+e.getMessage());
+        }
+    }
+
     /**
      * 构建微信支付配置
      */
@@ -1338,6 +1437,7 @@ public class FsStorePaymentScrmServiceImpl implements IFsStorePaymentScrmService
 
         FsStorePaymentScrm storePayment = new FsStorePaymentScrm();
         storePayment.setStatus(0);
+        storePayment.setAppId(payConfig.getAppId());
         storePayment.setPayMode(payConfig.getType());
         storePayment.setBusinessCode(payOrderParam.getOrderCode());
         storePayment.setPayCode(payCode);

+ 64 - 1
fs-service/src/main/java/com/fs/live/service/impl/LiveOrderServiceImpl.java

@@ -63,6 +63,7 @@ import com.fs.erp.domain.*;
 import com.fs.erp.dto.*;
 import com.fs.erp.mapper.FsErpFinishPushMapper;
 import com.fs.erp.service.IErpOrderService;
+import com.fs.his.config.AppConfig;
 import com.fs.his.config.FsSysConfig;
 import com.fs.his.domain.*;
 import com.fs.his.enums.FsStoreOrderLogEnum;
@@ -89,6 +90,9 @@ import com.fs.common.utils.SnowflakeUtil;
 import com.fs.huifuPay.sdk.opps.core.utils.HuiFuUtils;
 import com.fs.his.domain.MerchantAppConfig;
 import com.fs.his.domain.FsPayConfig;
+import com.fs.system.domain.SysConfig;
+import com.github.binarywang.wxpay.service.impl.WxPayServiceImpl;
+import com.google.gson.Gson;
 import org.springframework.aop.framework.AopContext;
 import com.fs.hisStore.service.IFsStoreProductPurchaseLimitScrmService;
 import com.fs.hisStore.domain.FsStoreProductPurchaseLimitScrm;
@@ -866,7 +870,13 @@ public class LiveOrderServiceImpl implements ILiveOrderService {
                         paymentMap.setTradeNo(tradeNo);
                         paymentMap.setBankSerialNo(bankSerialNo);
                         paymentMap.setBankTransactionId(bankTransactionId);
-                        paymentMap.setPayTypeCode("微信小程序支付");
+                        if (storePayment.getBusinessType().equals(8)){
+                            paymentMap.setPayTypeCode("微信支付");
+                        }else if (storePayment.getBusinessType().equals(9)){
+                            paymentMap.setPayTypeCode("微信app直播支付");
+                        }else {
+                            paymentMap.setPayTypeCode("微信小程序支付");
+                        }
                         liveOrderPaymentMapper.updateLiveOrderPayment(paymentMap);
                         order = baseMapper.selectFsUserVipOrderByOrderCode(storePayment.getBusinessCode());
                         if(order==null || !order.getStatus().equals(OrderInfoEnum.STATUS_0.getValue())){
@@ -1540,6 +1550,32 @@ public class LiveOrderServiceImpl implements ILiveOrderService {
                             TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
                             return R.error(refund != null ? refund.getResp_desc() : "退款失败");
                         }
+                    } else if ("wxApp".equals(payment.getPayMode()) && "wx_app".equals(payment.getPayTypeCode())) {
+                        // 处理微信退款
+                        WxPayService wxPayService = getWxPayService();
+                        WxPayRefundRequest refundRequest = new WxPayRefundRequest();
+                        refundRequest.setOutTradeNo("live-"+payment.getPayCode());
+                        refundRequest.setOutRefundNo("live-"+payment.getPayCode());
+                        refundRequest.setTotalFee(WxPayUnifiedOrderRequest.yuanToFen(payment.getPayMoney().toString()));
+                        refundRequest.setRefundFee(WxPayUnifiedOrderRequest.yuanToFen(payment.getPayMoney().toString()));
+                        try {
+                            WxPayRefundResult refundResult = wxPayService.refund(refundRequest);
+                            WxPayRefundQueryResult refundQueryResult = wxPayService.refundQuery("", refundResult.getOutTradeNo(), refundResult.getOutRefundNo(), refundResult.getRefundId());
+                            if (refundQueryResult != null && refundQueryResult.getResultCode().equals("SUCCESS")) {
+                                LiveOrderPayment paymentMap = new LiveOrderPayment();
+                                paymentMap.setPaymentId(payment.getPaymentId());
+                                paymentMap.setStatus(-1);
+                                paymentMap.setRefundTime(DateUtils.getNowDate());
+                                paymentMap.setRefundMoney(payment.getPayMoney());
+                                liveOrderPaymentMapper.updateLiveOrderPayment(paymentMap);
+                            } else {
+                                throw new CustomException("退款请求失败" + refundQueryResult.getReturnMsg());
+                            }
+
+                        } catch (WxPayException e) {
+                            throw new CustomException("退款请求失败" + e);
+                        }
+
                     } else {
                         TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
                         return R.error("支付类型异常");
@@ -1570,6 +1606,32 @@ public class LiveOrderServiceImpl implements ILiveOrderService {
         return R.ok();
     }
 
+
+
+
+    private WxPayService getWxPayService(){
+        SysConfig sysConfig = configService.selectConfigByConfigKey("app.config");
+        AppConfig config = new Gson().fromJson(sysConfig.getConfigValue(), AppConfig.class);
+        FsCoursePlaySourceConfig fsCoursePlaySourceConfig = fsCoursePlaySourceConfigMapper.selectCoursePlaySourceConfigByAppId(config.getAppId());
+        if (fsCoursePlaySourceConfig == null) {
+            throw new CustomException("未找到appId对应的小程序配置: " + config.getAppId());
+        }
+        MerchantAppConfig merchantAppConfig = merchantAppConfigMapper.selectMerchantAppConfigById(fsCoursePlaySourceConfig.getMerchantConfigId());
+        FsPayConfig payConfig1 = com.hc.openapi.tool.fastjson.JSON.parseObject(merchantAppConfig.getDataJson(), FsPayConfig.class);
+
+        WxPayConfig payConfig = new WxPayConfig();
+        payConfig.setAppId(payConfig1.getAppId());
+        payConfig.setMchId(payConfig1.getWxMchId());
+        payConfig.setMchKey(payConfig1.getWxMchKey());
+        payConfig.setSubAppId(StringUtils.trimToNull(null));
+        payConfig.setSubMchId(StringUtils.trimToNull(null));
+        payConfig.setKeyPath(null);
+        payConfig.setNotifyUrl(payConfig1.getNotifyUrlScrm());
+        WxPayServiceImpl payService = new WxPayServiceImpl();
+        payService.setConfig(payConfig);
+        return payService;
+    }
+
     @Override
     @Transactional(rollbackFor = Exception.class)
     public R refundOrderMoney(Long orderId, LiveAfterSales liveAfterSales) {
@@ -4408,6 +4470,7 @@ public class LiveOrderServiceImpl implements ILiveOrderService {
         liveOrder.setStatus(OrderInfoEnum.STATUS_0.getValue());
         liveOrder.setPayType("1");
         liveOrder.setTotalPrice(payPrice);
+        liveOrder.setPayMoney(payPrice);
         liveOrder.setPayPrice(payPrice.subtract(liveOrder.getDiscountMoney()));
         try {
             if (baseMapper.insertLiveOrder(liveOrder) > 0) {

+ 6 - 7
fs-service/src/main/resources/application-druid-zkzh.yml

@@ -128,15 +128,14 @@ spring:
                         config:
                             multi-statement-allow: true
 rocketmq:
-    name-server: rmq-1243b25nj.rocketmq.gz.public.tencenttdmq.com:8080 # RocketMQ NameServer 地址
+    name-server: rmq-16e437k44e.rocketmq.cd.qcloud.tencenttdmq.com:8080 # 替换为实际的
     producer:
-        group: my-producer-group
-        access-key: ak1243b25nj17d4b2dc1a03 # 替换为实际的 accessKey
-        secret-key: sk08a7ea1f9f4b0237 # 替换为实际的 secretKey
+        group: conversion-tracking-group
+        access-key: ak16e437k44e83e6c22f3b48 # 替换为实际的 accessKey
+        secret-key: ske995168f7b31620b # 替换为实际的 secretKey
     consumer:
-        group: test-group
-        access-key: ak1243b25nj17d4b2dc1a03 # 替换为实际的 accessKey
-        secret-key: sk08a7ea1f9f4b0237 # 替换为实际的 secretKey
+        access-key: ak16e437k44e83e6c22f3b48 # 替换为实际的 accessKey
+        secret-key: ske995168f7b31620b # 替换为实际的 secretKey
 cloud_host:
     company_name: 中康智慧
 openIM:

+ 84 - 0
fs-user-app/src/main/java/com/fs/app/controller/AppPayController.java

@@ -0,0 +1,84 @@
+package com.fs.app.controller;
+
+
+import cn.hutool.json.JSONUtil;
+import com.fs.app.service.AppPayService;
+import com.fs.common.exception.CustomException;
+import com.fs.common.utils.StringUtils;
+import com.fs.course.domain.FsCoursePlaySourceConfig;
+import com.fs.course.service.IFsCoursePlaySourceConfigService;
+import com.fs.his.config.AppConfig;
+import com.fs.his.domain.FsPayConfig;
+import com.fs.his.domain.MerchantAppConfig;
+import com.fs.his.service.IMerchantAppConfigService;
+import com.fs.system.domain.SysConfig;
+import com.fs.system.mapper.SysConfigMapper;
+import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult;
+import com.github.binarywang.wxpay.config.WxPayConfig;
+import com.github.binarywang.wxpay.exception.WxPayException;
+import com.github.binarywang.wxpay.service.WxPayService;
+import com.github.binarywang.wxpay.service.impl.WxPayServiceImpl;
+import com.google.gson.Gson;
+import com.hc.openapi.tool.fastjson.JSON;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+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 javax.servlet.http.HttpServletRequest;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+@Slf4j
+@Api("app支付接口")
+@RestController
+@RequestMapping("/appPay")
+public class AppPayController extends AppBaseController {
+
+    @Autowired
+    private AppPayService appPayService;
+
+    @Autowired
+    private IFsCoursePlaySourceConfigService fsCoursePlaySourceConfigService;
+   @Autowired
+    private IMerchantAppConfigService merchantAppConfigService;
+    @Autowired
+    private SysConfigMapper sysConfigMapper;
+
+
+    @ApiOperation("微信支付回调")
+    @PostMapping("/wxNotify")
+    public String wxNotify(@RequestBody String xmlData) throws WxPayException {
+        WxPayService payService = getWxPayService();
+        WxPayOrderNotifyResult result = payService.parseOrderNotifyResult(xmlData);
+        return appPayService.wxNotify(result);
+    }
+
+    private WxPayService getWxPayService(){
+        SysConfig sysConfig = sysConfigMapper.selectConfigByConfigKey("app.config");
+        AppConfig config = new Gson().fromJson(sysConfig.getConfigValue(), AppConfig.class);
+        FsCoursePlaySourceConfig fsCoursePlaySourceConfig = fsCoursePlaySourceConfigService.selectCoursePlaySourceConfigByAppId(config.getAppId());
+        if (fsCoursePlaySourceConfig == null) {
+            throw new CustomException("未找到appId对应的小程序配置: " + config.getAppId());
+        }
+        MerchantAppConfig merchantAppConfig = merchantAppConfigService.selectMerchantAppConfigById(fsCoursePlaySourceConfig.getMerchantConfigId());
+        FsPayConfig payConfig1 = JSON.parseObject(merchantAppConfig.getDataJson(), FsPayConfig.class);
+
+        WxPayConfig payConfig = new WxPayConfig();
+        payConfig.setAppId(payConfig1.getAppId());
+        payConfig.setMchId(payConfig1.getWxMchId());
+        payConfig.setMchKey(payConfig1.getWxMchKey());
+        payConfig.setSubAppId(StringUtils.trimToNull(null));
+        payConfig.setSubMchId(StringUtils.trimToNull(null));
+        payConfig.setKeyPath(null);
+        payConfig.setNotifyUrl(payConfig1.getNotifyUrlScrm());
+        WxPayServiceImpl payService = new WxPayServiceImpl();
+        payService.setConfig(payConfig);
+        return payService;
+    }
+}

+ 1 - 1
fs-user-app/src/main/java/com/fs/app/controller/app/AppController.java

@@ -69,6 +69,6 @@ public class AppController extends AppBaseController {
     @PostMapping("/wxPayment")
     public R wxPayment(@Validated @RequestBody FsIntegralOrderDoPayParam param) {
         param.setUserId(Long.parseLong(getUserId()));
-        return fsStoreOrderScrmService.payment(param, PaymentMethodEnum.T_NATIVE);
+        return fsStoreOrderScrmService.payment(param, PaymentMethodEnum.WX_APP);
     }
 }

+ 1 - 1
fs-user-app/src/main/java/com/fs/app/controller/live/LiveOrderController.java

@@ -343,7 +343,7 @@ public class LiveOrderController extends AppBaseController
         String userId= getUserId();
         log.info("开始创建订单,登录用户id:{}", userId);
         param.setUserId(userId);
-        return orderService.createStoreOrder(param);
+        return orderService.createLiveOrder(param);
     }
     @Login
     @ApiOperation("创建订单测试")