package com.ruoyi.errand.utils; import com.alibaba.fastjson2.JSONObject; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.ruoyi.common.exception.ServiceException; import com.ruoyi.errand.domain.AppUser; import com.ruoyi.errand.domain.CommunityCourier; import com.ruoyi.errand.domain.Order; import com.ruoyi.errand.mapper.AppUserMapper; import com.ruoyi.errand.mapper.CommunityCourierMapper; import com.ruoyi.errand.mapper.OrderMapper; import com.ruoyi.errand.object.vo.app.CourierOrderListVO; import io.jsonwebtoken.Claims; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.stereotype.Component; import javax.annotation.Resource; import javax.websocket.*; import javax.websocket.server.PathParam; import javax.websocket.server.ServerEndpoint; import java.io.IOException; import java.util.List; import java.util.Objects; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; @ServerEndpoint("/ws/delivery") @Component @Slf4j public class DeliveryWebSocket { private static final ConcurrentHashMap deliveryPersonSessions = new ConcurrentHashMap<>(); private static RedisTemplate redisTemplate; @Autowired public void setRedisTemplate(RedisTemplate redisTemplate) { DeliveryWebSocket.redisTemplate = redisTemplate; } private static AppUserMapper appUserMapper; private static OrderMapper orderMapper; private static CommunityCourierMapper communityCourierMapper; @Autowired public void setAppUserMapper(AppUserMapper appUserMapper) { DeliveryWebSocket.appUserMapper = appUserMapper; } @Autowired public void setOrderMapper(OrderMapper orderMapper) { DeliveryWebSocket.orderMapper = orderMapper; } @Autowired public void setCommunityCourierMapper(CommunityCourierMapper communityCourierMapper) { DeliveryWebSocket.communityCourierMapper = communityCourierMapper; } @OnOpen public void onOpen(Session session) { try { String token = getTokenFromSession(session); String userId = validateToken(token); if (userId == null) { closeSessionWithReason(session, "Invalid token"); return; } deliveryPersonSessions.put(userId, session); checkPendingNotifications(userId); sendWaitOrderNum(userId); } catch (Exception e) { handleSessionError(session, "Connection error", e); } } @OnClose public void onClose(Session session) { try { String token = getTokenFromSession(session); String userId = validateToken(token); if (userId != null) { deliveryPersonSessions.remove(userId); } } catch (Exception e) { log.info("关闭失败:{}",e.getMessage()); } } @OnError public void onError(Session session, Throwable error) { handleSessionError(session, "WebSocket error", error); } // Helper Methods private String getTokenFromSession(Session session) { return session.getRequestParameterMap().get("Authorization").stream().findFirst().orElse(null); } private String validateToken(String token) { if (token == null || !token.startsWith("app:")) { return null; } try { String realToken = token.substring(4); Claims claims = JwtUtil.parseJWT(realToken); return claims.get("userId").toString(); } catch (Exception e) { return null; } } private AppUser getAuthenticatedAppUser(String userId) { return appUserMapper.selectById(userId); // return (AppUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); } private void validateCourierStatus(Session session, AppUser appUser) throws IOException { if (appUser.getCourierId() == null) { sendMessageToSession(session, "您已不是跑腿员"); throw new IllegalStateException("您已不是跑腿员"); } } private CommunityCourier getCommunityCourier(AppUser appUser) { return communityCourierMapper.selectOne( new LambdaQueryWrapper() .eq(CommunityCourier::getCourierId, appUser.getCourierId()) ); } private void validateCommunityBinding(Session session, CommunityCourier courier) throws IOException { if (courier == null) { sendMessageToSession(session, "您还未绑定需代办的小区"); throw new IllegalStateException("您还未绑定需代办的小区"); } } private long countAvailableOrders(CommunityCourier courier) { return orderMapper.selectCount( new LambdaQueryWrapper() .eq(Order::getDelFlag, 0) .eq(Order::getOrderStatus, 1) .eq(Order::getPayStatus, 2) .eq(Order::getCommunityId, courier.getCommunityId()) ); } public void sendWaitOrderNum(String userId) { Session session = deliveryPersonSessions.get(userId); AppUser appUser = getAuthenticatedAppUser(userId); CommunityCourier courier = getCommunityCourier(appUser); try { validateCommunityBinding(session, courier); } catch (IOException e) { log.info("校验绑定跑腿员失败:{},跑腿员id:{}",e.getMessage(),courier.getCourierId()); } long orderCount = countAvailableOrders(courier); JSONObject message = new JSONObject(); message.put("orderNum", orderCount); sendNotification(userId, message.toJSONString()); } private void checkPendingNotifications(String userId) { String key = "delivery:notification:" + userId; List notifications = redisTemplate.opsForList().range(key, 0, -1); if (notifications != null && !notifications.isEmpty()) { notifications.forEach(notification -> sendNotification(userId, notification.toString()) ); redisTemplate.delete(key); } } public static void sendNotification(String userId, String message) { Session session = deliveryPersonSessions.get(userId); if (session != null && session.isOpen()) { sendMessageToSession(session, message); } else { String key = "delivery:notification:" + userId; redisTemplate.opsForList().rightPush(key, message); redisTemplate.expire(key, 1, TimeUnit.DAYS); } } private static void sendMessageToSession(Session session, String message) { try { session.getBasicRemote().sendText(message); } catch (IOException e) { log.info("发送消息错误:{},消息内容:{}",e.getMessage(),message); } } private void closeSessionWithReason(Session session, String reason) { try { session.close(new CloseReason(CloseReason.CloseCodes.VIOLATED_POLICY, reason)); } catch (IOException e) { log.info("关闭session错误:{}",e.getMessage()); } } private void handleSessionError(Session session, String message, Throwable error) { try { sendMessageToSession(session, message); closeSessionWithReason(session, message); } finally { String token = getTokenFromSession(session); String userId = validateToken(token); if (userId != null) { deliveryPersonSessions.remove(userId); } } } }