huliguo
2025-07-31 7b1772169b274e87fe441923f0dbf5e25ee30a72
优化
1个文件已删除
15个文件已修改
1187 ■■■■ 已修改文件
pt-admin/src/main/resources/application-druid.yml 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-admin/src/main/resources/application.yml 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/domain/Courier.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/domain/Evaluation.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/object/dto/app/AddEvaluationDTO.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/object/dto/sys/AddCourierDTO.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/object/vo/sys/CourierSysDetailVO.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/object/vo/sys/FinanceStatisticsVO.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/object/vo/sys/OrderPageListVO.java 19 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/service/impl/AppUserServiceImpl.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/service/impl/EvaluationServiceImpl.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/service/impl/OrderServiceImpl.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/java/com/ruoyi/errand/utils/DeliveryWebSocket.java 213 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/resources/mapper/CourierMapper.xml 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-errand/src/main/resources/mapper/OrderMapper.xml 19 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
sql/chongzhou.sql 893 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pt-admin/src/main/resources/application-druid.yml
@@ -7,8 +7,10 @@
            # 主库数据源
            master:
                url: jdbc:mysql://47.109.140.106:3306/paotui?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
#                url: jdbc:mysql://localhost:3306/paotui?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
                username: root
                password: weier0306
#                password: 123456
            # 从库数据源
            slave:
                # 从数据源开关/默认关闭
pt-admin/src/main/resources/application.yml
@@ -16,7 +16,7 @@
# 开发环境配置
server:
  # 服务器的HTTP端口,默认为8080
  port: 8086
  port: 8080
  servlet:
    # 应用的访问路径
    context-path: /
@@ -76,6 +76,7 @@
    database: 0
    # 密码
    password: weier0306
#    password: 123456
    # 连接超时时间
    timeout: 10s
    jedis:
@@ -103,7 +104,7 @@
  # 令牌密钥
  secret: abcdefghijklmnopqrstuvwxyz
  # 令牌有效期(默认1440分钟,24小时)
  expireTime: 1440
  expireTime: 43200
## MyBatis配置
#mybatis:
@@ -151,6 +152,7 @@
  mchId: 1719862902
  key: 5Kb8zX9qR2TdF7Yw3vHnJgLp6sA4cE1M
  callbackPath: http://47.109.140.106:8080
#  callbackPath: http://221.182.45.100:8086
  certPath: classpath:cert/apiclient_cert.p12
  RASPath:
ali:
pt-errand/src/main/java/com/ruoyi/errand/domain/Courier.java
@@ -43,6 +43,10 @@
    @TableField("back_view")
    private String backView;
    @ApiModelProperty("工作照")
    @TableField("work_img")
    private String workImg;
    @ApiModelProperty("状态:0-禁用,1-启用")
    @TableField("status")
    private Integer status;
pt-errand/src/main/java/com/ruoyi/errand/domain/Evaluation.java
@@ -46,4 +46,8 @@
    @TableField("update_time")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime updateTime;
    @ApiModelProperty("评价图片,多个逗号相隔")
    @TableField("img_url")
    private String imgUrl;
}
pt-errand/src/main/java/com/ruoyi/errand/object/dto/app/AddEvaluationDTO.java
@@ -31,4 +31,7 @@
    @ApiModelProperty("评价内容")
    private String content;
    @ApiModelProperty("评价图片,多个逗号相隔")
    private String imgUrl;
}
pt-errand/src/main/java/com/ruoyi/errand/object/dto/sys/AddCourierDTO.java
@@ -27,4 +27,7 @@
    @ApiModelProperty("背面照地址")
    private String backView;
    @ApiModelProperty("工作照")
    private String workImg;
}
pt-errand/src/main/java/com/ruoyi/errand/object/vo/sys/CourierSysDetailVO.java
@@ -37,6 +37,9 @@
    @ApiModelProperty("背面照地址")
    private String backView;
    @ApiModelProperty("工作照")
    private String workImg;
    @ApiModelProperty("当天接单量")
    private Integer today=0;
pt-errand/src/main/java/com/ruoyi/errand/object/vo/sys/FinanceStatisticsVO.java
@@ -14,9 +14,7 @@
public class FinanceStatisticsVO {
    @ApiModelProperty("订单id")
    private Integer id;
    @Excel(name = "金额")
    @ApiModelProperty("金额")
    private BigDecimal money;
    @Excel(name = "下单时间",width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
    @ApiModelProperty("下单时间")
@@ -31,6 +29,10 @@
    @ApiModelProperty("支付方式  0-在线支付 1-会员支付")
    private Integer payMethod;
    @Excel(name = "金额")
    @ApiModelProperty("金额")
    private BigDecimal money;
    @Excel(name = "订单编号")
    @ApiModelProperty("订单编号")
    private String orderNumber;
pt-errand/src/main/java/com/ruoyi/errand/object/vo/sys/OrderPageListVO.java
@@ -21,10 +21,6 @@
    @ApiModelProperty("订单编号")
    private String orderNumber;
    @Excel(name = "下单时间",width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
    @ApiModelProperty("下单时间")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime orderTime;
    @Excel(name = "支付金额")
    @ApiModelProperty("支付金额")
@@ -51,13 +47,28 @@
    @ApiModelProperty("跑腿员联系电话")
    private String courierPhone;
    @Excel(name = "下单时间",width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
    @ApiModelProperty("下单时间")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime orderTime;
    @Excel(name = "完成时间",width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
    @ApiModelProperty("完成时间")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime finishTime;
    @Excel(name = "完成时长")
    @ApiModelProperty("完成时长")
    private Integer duration;
    @Excel(name = "订单状态", readConverterExp = "1=待确认,2=进行中,3=已取消,4=已完成,5=已评价")
    @ApiModelProperty("订单状态 1待确认2进行中3已取消4已完成5已评价")
    private Integer orderStatus;
    @Excel(name = "评价星级")
    @ApiModelProperty("评分(0.5-5.0,支持半星)")
    private Integer rating;
}
pt-errand/src/main/java/com/ruoyi/errand/service/impl/AppUserServiceImpl.java
@@ -123,11 +123,11 @@
    @Override
    public R<LoginVO> mobileLogin(MobileLoginDTO mobileLogin) {
        String code = redisService.getCacheObject(mobileLogin.getPhone());
        if(!"999999".equals(mobileLogin.getCode())){
//        if(!"999999".equals(mobileLogin.getCode())){
            if(null == code || !code.equals(mobileLogin.getCode())){
                throw new ServiceException("验证码错误");
            }
        }
//        }
        //查看用户是否存在
        AppUser appUser = this.getOne(new LambdaQueryWrapper<AppUser>().eq(AppUser::getPhone, mobileLogin.getPhone())
                .ne(AppUser::getStatus,AppUserStatusConstant.LOGOUT)
pt-errand/src/main/java/com/ruoyi/errand/service/impl/EvaluationServiceImpl.java
@@ -64,6 +64,7 @@
        evaluation.setRating(addEvaluationDTO.getRating());
        evaluation.setContent(addEvaluationDTO.getContent());
        evaluation.setCreateTime(LocalDateTime.now());
        evaluation.setImgUrl(addEvaluationDTO.getImgUrl());
        this.save(evaluation);
    }
@@ -76,6 +77,7 @@
        evaluation.setRating(addEvaluationDTO.getRating());
        evaluation.setContent(addEvaluationDTO.getContent());
        evaluation.setUpdateTime(LocalDateTime.now());
        evaluation.setImgUrl(addEvaluationDTO.getImgUrl());
        this.updateById(evaluation);
    }
}
pt-errand/src/main/java/com/ruoyi/errand/service/impl/OrderServiceImpl.java
@@ -267,6 +267,7 @@
        message.put("orderId", order.getId());
        message.put("orderTime", order.getOrderTime());
        deliveryWebSocket.sendNotification(deliveryPersonId.toString(), message.toJSONString());
        deliveryWebSocket.sendWaitOrderNum(deliveryPersonId.toString());
    }
    @Override
pt-errand/src/main/java/com/ruoyi/errand/utils/DeliveryWebSocket.java
@@ -1,17 +1,28 @@
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.CloseReason;
import javax.websocket.OnClose;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
@@ -22,10 +33,10 @@
@ServerEndpoint("/ws/delivery")
@Component
public class    DeliveryWebSocket {
@Slf4j
public class DeliveryWebSocket {
    private static ConcurrentHashMap<String, Session> deliveryPersonSessions = new ConcurrentHashMap<>();
    private static final ConcurrentHashMap<String, Session> deliveryPersonSessions = new ConcurrentHashMap<>();
    private static  RedisTemplate redisTemplate;
    @Autowired
@@ -33,24 +44,68 @@
        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) {
        // 从查询参数获取token
        String token = session.getRequestParameterMap().get("token").stream().findFirst().orElse(null);
        // 验证token
        String userId=validateToken(token);
        if (null==userId) {
            try {
                session.close(new CloseReason(CloseReason.CloseCodes.VIOLATED_POLICY, "Invalid token"));
            } catch (IOException e) {
                e.printStackTrace();
            }
            return;
        }
        try {
            String token = getTokenFromSession(session);
            String userId = validateToken(token);
        deliveryPersonSessions.put(userId, session);
        checkPendingNotifications(userId);
            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) {
@@ -58,58 +113,116 @@
            return null;
        }
        try {
            //解析token 获取userid,再查询到AppUser
            String realToken = token.substring(4);
            Claims claims = JwtUtil.parseJWT(realToken);
            String userId = claims.get("userId").toString();
            return userId;
            return claims.get("userId").toString();
        } catch (Exception e) {
            return null;
        }
    }
    @OnClose
    public void onClose(Session session) {
        String token = session.getRequestParameterMap().get("token").stream().findFirst().orElse(null);
        // 验证token
        String userId=validateToken(token);
        if (null==userId) {
            try {
                session.close(new CloseReason(CloseReason.CloseCodes.VIOLATED_POLICY, "Invalid token"));
            } catch (IOException e) {
                e.printStackTrace();
            }
            return;
        }
        deliveryPersonSessions.remove(userId);
    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<CommunityCourier>()
                        .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<Order>()
                        .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<Object> notifications = redisTemplate.opsForList().range(key, 0, -1);
        if (notifications != null && !notifications.isEmpty()) {
            for (Object notification : notifications) {
                sendNotification(userId, notification.toString());
            }
            notifications.forEach(notification ->
                    sendNotification(userId, notification.toString())
            );
            redisTemplate.delete(key);
        }
    }
    public  void sendNotification(String userId, String message) {
    public static void sendNotification(String userId, String message) {
        Session session = deliveryPersonSessions.get(userId);
        if (session != null && session.isOpen()) {
            try {
                session.getBasicRemote().sendText(message);
            } catch (IOException e) {
                e.printStackTrace();
            }
            sendMessageToSession(session, message);
        } else {
            // 用户不在线,存入Redis
            String key = "delivery:notification:" + userId;
            redisTemplate.opsForList().rightPush(key, message);
            // 设置过期时间,比如1天
            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);
            }
        }
    }
}
pt-errand/src/main/resources/mapper/CourierMapper.xml
@@ -96,7 +96,8 @@
            tc.create_time as createTime,
            tc.status as status,
            tc.front_view as frontView,
            tc.back_view as backView
            tc.back_view as backView,
            tc.work_img as workImg
        from t_courier tc
                 left join t_community_courier tcc on tc.id = tcc.courier_id
                 left join t_community tcm on tcc.community_id = tcm.id
pt-errand/src/main/resources/mapper/OrderMapper.xml
@@ -243,10 +243,18 @@
            tau.name as appUserName,
            tau.phone as appUserPhone,
            tc.name as courierName,
            tc.phone as courierPhone
            tc.phone as courierPhone,
            te.rating as rating,
            CASE
                WHEN o.finish_time IS NOT NULL THEN
                TIMESTAMPDIFF(MINUTE, o.order_time, o.finish_time)
            ELSE NULL
            END as duration
        from t_order o
        inner join t_app_user tau on o.app_user_id = tau.id
        left join t_courier tc on o.courier_id = tc.id
        left join t_evaluation te on o.id = te.order_id and te.type = 1
        where
            o.del_flag=0
        and o.pay_status=2
@@ -398,10 +406,17 @@
        tau.name as appUserName,
        tau.phone as appUserPhone,
        tc.name as courierName,
        tc.phone as courierPhone
        tc.phone as courierPhone,
        te.rating as rating,
        CASE
        WHEN o.finish_time IS NOT NULL THEN
        TIMESTAMPDIFF(MINUTE, o.order_time, o.finish_time)
        ELSE 0
        END as duration
        from t_order o
        inner join t_app_user tau on o.app_user_id = tau.id
        left join t_courier tc on o.courier_id = tc.id
        left join t_evaluation te on o.id = te.order_id on te.type = 1
        where
        o.del_flag=0
        and o.pay_status=2
sql/chongzhou.sql
File was deleted