Java全栈项目--校园快递管理与配送系统(2)

源代码续

package com.campus.express.repository;

import com.campus.express.model.Notification;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;

import java.time.LocalDateTime;
import java.util.List;

/**
 * 通知数据访问接口
 */
@Repository
public interface NotificationRepository extends JpaRepository<Notification, Long> {

    /**
     * 根据接收者ID和接收者类型查询通知列表
     *
     * @param receiverId   接收者ID
     * @param receiverType 接收者类型
     * @param pageable     分页参数
     * @return 通知分页列表
     */
    Page<Notification> findByReceiverIdAndReceiverType(Long receiverId, Integer receiverType, Pageable pageable);

    /**
     * 根据接收者ID、接收者类型和已读状态查询通知列表
     *
     * @param receiverId   接收者ID
     * @param receiverType 接收者类型
     * @param readStatus   已读状态
     * @param pageable     分页参数
     * @return 通知分页列表
     */
    Page<Notification> findByReceiverIdAndReceiverTypeAndReadStatus(
            Long receiverId, Integer receiverType, Integer readStatus, Pageable pageable);

    /**
     * 根据通知类型查询通知列表
     *
     * @param type     通知类型
     * @param pageable 分页参数
     * @return 通知分页列表
     */
    Page<Notification> findByType(Integer type, Pageable pageable);

    /**
     * 根据通知渠道查询通知列表
     *
     * @param channel  通知渠道
     * @param pageable 分页参数
     * @return 通知分页列表
     */
    Page<Notification> findByChannel(Integer channel, Pageable pageable);

    /**
     * 根据发送状态查询通知列表
     *
     * @param sendStatus 发送状态
     * @param pageable   分页参数
     * @return 通知分页列表
     */
    Page<Notification> findBySendStatus(Integer sendStatus, Pageable pageable);

    /**
     * 根据创建时间范围查询通知列表
     *
     * @param startTime 开始时间
     * @param endTime   结束时间
     * @param pageable  分页参数
     * @return 通知分页列表
     */
    Page<Notification> findByCreatedTimeBetween(LocalDateTime startTime, LocalDateTime endTime, Pageable pageable);

    /**
     * 根据关联业务ID和关联业务类型查询通知列表
     *
     * @param businessId   关联业务ID
     * @param businessType 关联业务类型
     * @return 通知列表
     */
    List<Notification> findByBusinessIdAndBusinessType(Long businessId, Integer businessType);

    /**
     * 更新通知已读状态
     *
     * @param id         通知ID
     * @param readStatus 已读状态
     * @return 更新行数
     */
    @Modifying
    @Query("UPDATE Notification n SET n.readStatus = :readStatus, n.updatedTime = :updatedTime WHERE n.id = :id")
    int updateReadStatus(@Param("id") Long id, @Param("readStatus") Integer readStatus, @Param("updatedTime") LocalDateTime updatedTime);

    /**
     * 批量更新通知已读状态
     *
     * @param ids        通知ID列表
     * @param readStatus 已读状态
     * @return 更新行数
     */
    @Modifying
    @Query("UPDATE Notification n SET n.readStatus = :readStatus, n.updatedTime = :updatedTime WHERE n.id IN :ids")
    int batchUpdateReadStatus(@Param("ids") List<Long> ids, @Param("readStatus") Integer readStatus, @Param("updatedTime") LocalDateTime updatedTime);

    /**
     * 更新通知发送状态
     *
     * @param id         通知ID
     * @param sendStatus 发送状态
     * @param failReason 失败原因
     * @return 更新行数
     */
    @Modifying
    @Query("UPDATE Notification n SET n.sendStatus = :sendStatus, n.failReason = :failReason, n.updatedTime = :updatedTime WHERE n.id = :id")
    int updateSendStatus(@Param("id") Long id, @Param("sendStatus") Integer sendStatus, @Param("failReason") String failReason, @Param("updatedTime") LocalDateTime updatedTime);

    /**
     * 更新重试信息
     *
     * @param id            通知ID
     * @param retryCount    重试次数
     * @param nextRetryTime 下次重试时间
     * @return 更新行数
     */
    @Modifying
    @Query("UPDATE Notification n SET n.retryCount = :retryCount, n.nextRetryTime = :nextRetryTime, n.updatedTime = :updatedTime WHERE n.id = :id")
    int updateRetryInfo(@Param("id") Long id, @Param("retryCount") Integer retryCount, @Param("nextRetryTime") LocalDateTime nextRetryTime, @Param("updatedTime") LocalDateTime updatedTime);

    /**
     * 查询待重试的通知列表
     *
     * @param sendStatus    发送状态
     * @param nextRetryTime 下次重试时间
     * @return 通知列表
     */
    List<Notification> findBySendStatusAndNextRetryTimeLessThanEqual(Integer sendStatus, LocalDateTime nextRetryTime);

    /**
     * 统计接收者未读通知数量
     *
     * @param receiverId   接收者ID
     * @param receiverType 接收者类型
     * @param readStatus   已读状态
     * @return 未读通知数量
     */
    long countByReceiverIdAndReceiverTypeAndReadStatus(Long receiverId, Integer receiverType, Integer readStatus);

    /**
     * 统计通知数量
     *
     * @param type 通知类型
     * @return 通知数量
     */
    long countByType(Integer type);

    /**
     * 统计通知数量
     *
     * @param channel 通知渠道
     * @return 通知数量
     */
    long countByChannel(Integer channel);

    /**
     * 统计通知数量
     *
     * @param sendStatus 发送状态
     * @return 通知数量
     */
    long countBySendStatus(Integer sendStatus);
}

express-service\src\main\java\com\campus\express\repository\NotificationTemplateRepository.java

package com.campus.express.repository;

import com.campus.express.model.NotificationTemplate;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;

/**
 * 通知模板数据访问接口
 */
@Repository
public interface NotificationTemplateRepository extends JpaRepository<NotificationTemplate, Long> {

    /**
     * 根据模板编码查询模板
     *
     * @param code 模板编码
     * @return 模板对象
     */
    Optional<NotificationTemplate> findByCode(String code);

    /**
     * 根据模板名称查询模板列表
     *
     * @param name     模板名称
     * @param pageable 分页参数
     * @return 模板分页列表
     */
    Page<NotificationTemplate> findByNameContaining(String name, Pageable pageable);

    /**
     * 根据模板类型查询模板列表
     *
     * @param type     模板类型
     * @param pageable 分页参数
     * @return 模板分页列表
     */
    Page<NotificationTemplate> findByType(Integer type, Pageable pageable);

    /**
     * 根据适用渠道查询模板列表
     *
     * @param channel  适用渠道
     * @param pageable 分页参数
     * @return 模板分页列表
     */
    Page<NotificationTemplate> findByChannel(Integer channel, Pageable pageable);

    /**
     * 根据状态查询模板列表
     *
     * @param status   状态
     * @param pageable 分页参数
     * @return 模板分页列表
     */
    Page<NotificationTemplate> findByStatus(Integer status, Pageable pageable);

    /**
     * 根据模板类型和适用渠道查询模板列表
     *
     * @param type    模板类型
     * @param channel 适用渠道
     * @return 模板列表
     */
    List<NotificationTemplate> findByTypeAndChannel(Integer type, Integer channel);

    /**
     * 根据模板类型和状态查询模板列表
     *
     * @param type   模板类型
     * @param status 状态
     * @return 模板列表
     */
    List<NotificationTemplate> findByTypeAndStatus(Integer type, Integer status);

    /**
     * 根据适用渠道和状态查询模板列表
     *
     * @param channel 适用渠道
     * @param status  状态
     * @return 模板列表
     */
    List<NotificationTemplate> findByChannelAndStatus(Integer channel, Integer status);

    /**
     * 更新模板状态
     *
     * @param id         模板ID
     * @param status     状态
     * @param updatedTime 更新时间
     * @return 更新行数
     */
    @Modifying
    @Query("UPDATE NotificationTemplate t SET t.status = :status, t.updatedTime = :updatedTime WHERE t.id = :id")
    int updateStatus(@Param("id") Long id, @Param("status") Integer status, @Param("updatedTime") LocalDateTime updatedTime);

    /**
     * 检查模板编码是否存在
     *
     * @param code 模板编码
     * @return 是否存在
     */
    boolean existsByCode(String code);

    /**
     * 统计模板数量
     *
     * @param type 模板类型
     * @return 模板数量
     */
    long countByType(Integer type);

    /**
     * 统计模板数量
     *
     * @param channel 适用渠道
     * @return 模板数量
     */
    long countByChannel(Integer channel);

    /**
     * 统计模板数量
     *
     * @param status 状态
     * @return 模板数量
     */
    long countByStatus(Integer status);
}

express-service\src\main\java\com\campus\express\service\CabinetCellService.java

package com.campus.express.service;

import com.campus.express.dto.CabinetCellDTO;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;

import java.util.List;

/**
 * 快递柜格口服务接口
 */
public interface CabinetCellService {

    /**
     * 根据ID获取格口
     *
     * @param id 格口ID
     * @return 格口DTO
     */
    CabinetCellDTO getCellById(Long id);

    /**
     * 根据柜子ID和格口编号获取格口
     *
     * @param cabinetId 柜子ID
     * @param cellNumber 格口编号
     * @return 格口DTO
     */
    CabinetCellDTO getCellByCabinetIdAndCellNumber(Long cabinetId, String cellNumber);

    /**
     * 根据柜子ID获取格口列表
     *
     * @param cabinetId 柜子ID
     * @return 格口列表
     */
    List<CabinetCellDTO> getCellsByCabinetId(Long cabinetId);

    /**
     * 根据柜子ID和状态获取格口列表
     *
     * @param cabinetId 柜子ID
     * @param status 状态
     * @return 格口列表
     */
    List<CabinetCellDTO> getCellsByCabinetIdAndStatus(Long cabinetId, Integer status);

    /**
     * 根据快递ID获取格口
     *
     * @param expressId 快递ID
     * @return 格口DTO
     */
    CabinetCellDTO getCellByExpressId(Long expressId);

    /**
     * 根据柜子ID分页获取格口列表
     *
     * @param cabinetId 柜子ID
     * @param pageable 分页参数
     * @return 格口分页列表
     */
    Page<CabinetCellDTO> getCellsByCabinetIdPaged(Long cabinetId, Pageable pageable);

    /**
     * 更新格口状态
     *
     * @param id 格口ID
     * @param status 状态
     * @return 更新后的格口DTO
     */
    CabinetCellDTO updateCellStatus(Long id, Integer status);

    /**
     * 分配快递到格口
     *
     * @param cellId 格口ID
     * @param expressId 快递ID
     * @return 更新后的格口DTO
     */
    CabinetCellDTO assignExpressToCell(Long cellId, Long expressId);

    /**
     * 从格口取出快递
     *
     * @param cellId 格口ID
     * @return 更新后的格口DTO
     */
    CabinetCellDTO retrieveExpressFromCell(Long cellId);

    /**
     * 根据柜子ID和格口大小获取空闲格口
     *
     * @param cabinetId 柜子ID
     * @param size 格口大小
     * @return 空闲格口列表
     */
    List<CabinetCellDTO> getAvailableCellsByCabinetIdAndSize(Long cabinetId, Integer size);

    /**
     * 统计柜子中指定状态的格口数量
     *
     * @param cabinetId 柜子ID
     * @param status 状态
     * @return 格口数量
     */
    long countCellsByCabinetIdAndStatus(Long cabinetId, Integer status);

    /**
     * 统计柜子中指定大小的格口数量
     *
     * @param cabinetId 柜子ID
     * @param size 格口大小
     * @return 格口数量
     */
    long countCellsByCabinetIdAndSize(Long cabinetId, Integer size);

    /**
     * 创建格口
     *
     * @param cabinetId 柜子ID
     * @param cellNumber 格口编号
     * @param size 格口大小
     * @return 创建的格口DTO
     */
    CabinetCellDTO createCell(Long cabinetId, String cellNumber, Integer size);

    /**
     * 删除格口
     *
     * @param id 格口ID
     */
    void deleteCell(Long id);

    /**
     * 批量创建格口
     *
     * @param cabinetId 柜子ID
     * @param smallCount 小格口数量
     * @param mediumCount 中格口数量
     * @param largeCount 大格口数量
     * @return 创建的格口列表
     */
    List<CabinetCellDTO> batchCreateCells(Long cabinetId, Integer smallCount, Integer mediumCount, Integer largeCount);
}

express-service\src\main\java\com\campus\express\service\CabinetService.java

package com.campus.express.service;

import com.campus.express.dto.CabinetCreateRequest;
import com.campus.express.dto.CabinetDTO;
import com.campus.express.dto.CabinetUpdateRequest;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;

import java.util.List;

/**
 * 快递柜服务接口
 */
public interface CabinetService {

    /**
     * 创建快递柜
     *
     * @param request 创建快递柜请求
     * @return 快递柜DTO
     */
    CabinetDTO createCabinet(CabinetCreateRequest request);

    /**
     * 根据ID获取快递柜
     *
     * @param id 快递柜ID
     * @return 快递柜DTO
     */
    CabinetDTO getCabinetById(Long id);

    /**
     * 根据柜子编号获取快递柜
     *
     * @param cabinetNumber 柜子编号
     * @return 快递柜DTO
     */
    CabinetDTO getCabinetByNumber(String cabinetNumber);

    /**
     * 更新快递柜信息
     *
     * @param id 快递柜ID
     * @param request 更新快递柜请求
     * @return 更新后的快递柜DTO
     */
    CabinetDTO updateCabinet(Long id, CabinetUpdateRequest request);

    /**
     * 删除快递柜
     *
     * @param id 快递柜ID
     */
    void deleteCabinet(Long id);

    /**
     * 分页获取所有快递柜
     *
     * @param pageable 分页参数
     * @return 快递柜分页列表
     */
    Page<CabinetDTO> getAllCabinets(Pageable pageable);

    /**
     * 根据柜子名称或位置模糊查询快递柜列表
     *
     * @param keyword 关键字
     * @param pageable 分页参数
     * @return 快递柜分页列表
     */
    Page<CabinetDTO> searchCabinets(String keyword, Pageable pageable);

    /**
     * 根据状态获取快递柜列表
     *
     * @param status 状态
     * @param pageable 分页参数
     * @return 快递柜分页列表
     */
    Page<CabinetDTO> getCabinetsByStatus(Integer status, Pageable pageable);

    /**
     * 根据负责人ID获取快递柜列表
     *
     * @param managerId 负责人ID
     * @return 快递柜列表
     */
    List<CabinetDTO> getCabinetsByManagerId(Long managerId);

    /**
     * 查询可用格口数大于0的快递柜列表
     *
     * @param pageable 分页参数
     * @return 快递柜分页列表
     */
    Page<CabinetDTO> getAvailableCabinets(Pageable pageable);

    /**
     * 查询指定位置的快递柜列表
     *
     * @param location 位置
     * @param pageable 分页参数
     * @return 快递柜分页列表
     */
    Page<CabinetDTO> getCabinetsByLocation(String location, Pageable pageable);

    /**
     * 统计指定状态的快递柜数量
     *
     * @param status 状态
     * @return 快递柜数量
     */
    long countCabinetsByStatus(Integer status);

    /**
     * 统计指定负责人的快递柜数量
     *
     * @param managerId 负责人ID
     * @return 快递柜数量
     */
    long countCabinetsByManagerId(Long managerId);

    /**
     * 更新快递柜状态
     *
     * @param id 快递柜ID
     * @param status 状态
     * @return 更新后的快递柜DTO
     */
    CabinetDTO updateCabinetStatus(Long id, Integer status);

    /**
     * 分配快递柜负责人
     *
     * @param id 快递柜ID
     * @param managerId 负责人ID
     * @param managerName 负责人姓名
     * @param managerPhone 负责人电话
     * @return 更新后的快递柜DTO
     */
    CabinetDTO assignCabinetManager(Long id, Long managerId, String managerName, String managerPhone);

    /**
     * 更新快递柜可用格口数
     *
     * @param id 快递柜ID
     * @return 更新后的快递柜DTO
     */
    CabinetDTO updateAvailableCells(Long id);

    /**
     * 初始化快递柜格口
     *
     * @param cabinetId 快递柜ID
     * @param smallCount 小格口数量
     * @param mediumCount 中格口数量
     * @param largeCount 大格口数量
     */
    void initializeCabinetCells(Long cabinetId, Integer smallCount, Integer mediumCount, Integer largeCount);
}

express-service\src\main\java\com\campus\express\service\DeliveryAreaService.java

package com.campus.express.service;

import com.campus.express.dto.DeliveryAreaDTO;
import com.campus.express.dto.DeliveryAreaCreateRequest;
import com.campus.express.dto.DeliveryAreaUpdateRequest;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;

import java.util.List;

/**
 * 配送区域服务接口
 */
public interface DeliveryAreaService {

    /**
     * 创建配送区域
     *
     * @param request 创建配送区域请求
     * @return 配送区域DTO
     */
    DeliveryAreaDTO createArea(DeliveryAreaCreateRequest request);

    /**
     * 根据ID获取配送区域
     *
     * @param id 配送区域ID
     * @return 配送区域DTO
     */
    DeliveryAreaDTO getAreaById(Long id);

    /**
     * 根据区域代码获取配送区域
     *
     * @param areaCode 区域代码
     * @return 配送区域DTO
     */
    DeliveryAreaDTO getAreaByCode(String areaCode);

    /**
     * 更新配送区域信息
     *
     * @param id 配送区域ID
     * @param request 更新配送区域请求
     * @return 更新后的配送区域DTO
     */
    DeliveryAreaDTO updateArea(Long id, DeliveryAreaUpdateRequest request);

    /**
     * 删除配送区域
     *
     * @param id 配送区域ID
     */
    void deleteArea(Long id);

    /**
     * 分页获取所有配送区域
     *
     * @param pageable 分页参数
     * @return 配送区域分页列表
     */
    Page<DeliveryAreaDTO> getAllAreas(Pageable pageable);

    /**
     * 根据名称模糊查询配送区域
     *
     * @param name 名称关键字
     * @param pageable 分页参数
     * @return 配送区域分页列表
     */
    Page<DeliveryAreaDTO> searchAreasByName(String name, Pageable pageable);

    /**
     * 根据状态获取配送区域列表
     *
     * @param status 状态
     * @param pageable 分页参数
     * @return 配送区域分页列表
     */
    Page<DeliveryAreaDTO> getAreasByStatus(Integer status, Pageable pageable);

    /**
     * 根据父区域ID获取子区域列表
     *
     * @param parentId 父区域ID
     * @return 配送区域列表
     */
    List<DeliveryAreaDTO> getAreasByParentId(Long parentId);

    /**
     * 根据区域级别获取配送区域列表
     *
     * @param level 区域级别
     * @return 配送区域列表
     */
    List<DeliveryAreaDTO> getAreasByLevel(Integer level);

    /**
     * 更新配送区域状态
     *
     * @param id 配送区域ID
     * @param status 状态
     * @return 更新后的配送区域DTO
     */
    DeliveryAreaDTO updateAreaStatus(Long id, Integer status);

    /**
     * 获取区域树结构
     *
     * @return 区域树结构
     */
    List<DeliveryAreaDTO> getAreaTree();

    /**
     * 根据坐标获取所属区域
     *
     * @param longitude 经度
     * @param latitude 纬度
     * @return 配送区域DTO
     */
    DeliveryAreaDTO getAreaByCoordinates(Double longitude, Double latitude);

    /**
     * 判断坐标是否在指定区域内
     *
     * @param areaId 区域ID
     * @param longitude 经度
     * @param latitude 纬度
     * @return 是否在区域内
     */
    boolean isCoordinateInArea(Long areaId, Double longitude, Double latitude);

    /**
     * 计算两个坐标点之间的距离
     *
     * @param startLongitude 起点经度
     * @param startLatitude 起点纬度
     * @param endLongitude 终点经度
     * @param endLatitude 终点纬度
     * @return 距离(米)
     */
    double calculateDistance(Double startLongitude, Double startLatitude, Double endLongitude, Double endLatitude);

    /**
     * 获取指定区域的配送费用
     *
     * @param areaId 区域ID
     * @return 配送费用
     */
    Double getDeliveryFeeByArea(Long areaId);

    /**
     * 统计指定状态的配送区域数量
     *
     * @param status 状态
     * @return 配送区域数量
     */
    long countAreasByStatus(Integer status);

    /**
     * 统计指定级别的配送区域数量
     *
     * @param level 区域级别
     * @return 配送区域数量
     */
    long countAreasByLevel(Integer level);
}

express-service\src\main\java\com\campus\express\service\DeliveryRouteService.java

package com.campus.express.service;

import com.campus.express.dto.DeliveryRouteDTO;
import com.campus.express.dto.DeliveryRouteCreateRequest;
import com.campus.express.dto.DeliveryRouteUpdateRequest;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;

import java.util.List;

/**
 * 配送路线服务接口
 */
public interface DeliveryRouteService {

    /**
     * 创建配送路线
     *
     * @param request 创建配送路线请求
     * @return 配送路线DTO
     */
    DeliveryRouteDTO createRoute(DeliveryRouteCreateRequest request);

    /**
     * 根据ID获取配送路线
     *
     * @param id 配送路线ID
     * @return 配送路线DTO
     */
    DeliveryRouteDTO getRouteById(Long id);

    /**
     * 更新配送路线信息
     *
     * @param id 配送路线ID
     * @param request 更新配送路线请求
     * @return 更新后的配送路线DTO
     */
    DeliveryRouteDTO updateRoute(Long id, DeliveryRouteUpdateRequest request);

    /**
     * 删除配送路线
     *
     * @param id 配送路线ID
     */
    void deleteRoute(Long id);

    /**
     * 分页获取所有配送路线
     *
     * @param pageable 分页参数
     * @return 配送路线分页列表
     */
    Page<DeliveryRouteDTO> getAllRoutes(Pageable pageable);

    /**
     * 根据名称模糊查询配送路线
     *
     * @param name 名称关键字
     * @param pageable 分页参数
     * @return 配送路线分页列表
     */
    Page<DeliveryRouteDTO> searchRoutesByName(String name, Pageable pageable);

    /**
     * 根据状态获取配送路线列表
     *
     * @param status 状态
     * @param pageable 分页参数
     * @return 配送路线分页列表
     */
    Page<DeliveryRouteDTO> getRoutesByStatus(Integer status, Pageable pageable);

    /**
     * 根据区域获取配送路线列表
     *
     * @param area 区域
     * @return 配送路线列表
     */
    List<DeliveryRouteDTO> getRoutesByArea(String area);

    /**
     * 根据配送员ID获取配送路线列表
     *
     * @param courierId 配送员ID
     * @return 配送路线列表
     */
    List<DeliveryRouteDTO> getRoutesByCourierId(Long courierId);

    /**
     * 分配配送员到路线
     *
     * @param routeId 配送路线ID
     * @param courierId 配送员ID
     * @param courierName 配送员姓名
     * @return 更新后的配送路线DTO
     */
    DeliveryRouteDTO assignCourierToRoute(Long routeId, Long courierId, String courierName);

    /**
     * 更新配送路线状态
     *
     * @param routeId 配送路线ID
     * @param status 状态
     * @return 更新后的配送路线DTO
     */
    DeliveryRouteDTO updateRouteStatus(Long routeId, Integer status);

    /**
     * 根据区域和状态获取配送路线列表
     *
     * @param area 区域
     * @param status 状态
     * @return 配送路线列表
     */
    List<DeliveryRouteDTO> getRoutesByAreaAndStatus(String area, Integer status);

    /**
     * 获取最优配送路线
     *
     * @param startPoint 起点
     * @param endPoint 终点
     * @param waypoints 途经点
     * @return 配送路线DTO
     */
    DeliveryRouteDTO getOptimalRoute(String startPoint, String endPoint, List<String> waypoints);

    /**
     * 统计指定状态的配送路线数量
     *
     * @param status 状态
     * @return 配送路线数量
     */
    long countRoutesByStatus(Integer status);

    /**
     * 统计指定区域的配送路线数量
     *
     * @param area 区域
     * @return 配送路线数量
     */
    long countRoutesByArea(String area);

    /**
     * 统计指定配送员的配送路线数量
     *
     * @param courierId 配送员ID
     * @return 配送路线数量
     */
    long countRoutesByCourierId(Long courierId);
}

express-service\src\main\java\com\campus\express\service\DeliveryService.java

package com.campus.express.service;

import com.campus.express.dto.DeliveryDTO;
import com.campus.express.dto.DeliveryCreateRequest;
import com.campus.express.dto.DeliveryUpdateRequest;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;

import java.time.LocalDateTime;
import java.util.List;

/**
 * 配送服务接口
 */
public interface DeliveryService {

    /**
     * 创建配送任务
     *
     * @param request 创建配送任务请求
     * @return 配送任务DTO
     */
    DeliveryDTO createDelivery(DeliveryCreateRequest request);

    /**
     * 根据ID获取配送任务
     *
     * @param id 配送任务ID
     * @return 配送任务DTO
     */
    DeliveryDTO getDeliveryById(Long id);

    /**
     * 更新配送任务信息
     *
     * @param id 配送任务ID
     * @param request 更新配送任务请求
     * @return 更新后的配送任务DTO
     */
    DeliveryDTO updateDelivery(Long id, DeliveryUpdateRequest request);

    /**
     * 删除配送任务
     *
     * @param id 配送任务ID
     */
    void deleteDelivery(Long id);

    /**
     * 分页获取所有配送任务
     *
     * @param pageable 分页参数
     * @return 配送任务分页列表
     */
    Page<DeliveryDTO> getAllDeliveries(Pageable pageable);

    /**
     * 根据配送员ID获取配送任务列表
     *
     * @param courierId 配送员ID
     * @param pageable 分页参数
     * @return 配送任务分页列表
     */
    Page<DeliveryDTO> getDeliveriesByCourierId(Long courierId, Pageable pageable);

    /**
     * 根据状态获取配送任务列表
     *
     * @param status 状态
     * @param pageable 分页参数
     * @return 配送任务分页列表
     */
    Page<DeliveryDTO> getDeliveriesByStatus(Integer status, Pageable pageable);

    /**
     * 根据快递ID获取配送任务
     *
     * @param expressId 快递ID
     * @return 配送任务DTO
     */
    DeliveryDTO getDeliveryByExpressId(Long expressId);

    /**
     * 根据配送区域获取配送任务列表
     *
     * @param area 配送区域
     * @param pageable 分页参数
     * @return 配送任务分页列表
     */
    Page<DeliveryDTO> getDeliveriesByArea(String area, Pageable pageable);

    /**
     * 根据创建时间范围获取配送任务列表
     *
     * @param startTime 开始时间
     * @param endTime 结束时间
     * @param pageable 分页参数
     * @return 配送任务分页列表
     */
    Page<DeliveryDTO> getDeliveriesByCreatedTimeBetween(LocalDateTime startTime, LocalDateTime endTime, Pageable pageable);

    /**
     * 根据计划配送时间范围获取配送任务列表
     *
     * @param startTime 开始时间
     * @param endTime 结束时间
     * @param pageable 分页参数
     * @return 配送任务分页列表
     */
    Page<DeliveryDTO> getDeliveriesByPlannedDeliveryTimeBetween(LocalDateTime startTime, LocalDateTime endTime, Pageable pageable);

    /**
     * 分配配送员
     *
     * @param deliveryId 配送任务ID
     * @param courierId 配送员ID
     * @param courierName 配送员姓名
     * @param courierPhone 配送员电话
     * @return 更新后的配送任务DTO
     */
    DeliveryDTO assignCourier(Long deliveryId, Long courierId, String courierName, String courierPhone);

    /**
     * 更新配送任务状态
     *
     * @param deliveryId 配送任务ID
     * @param status 状态
     * @param remark 备注
     * @return 更新后的配送任务DTO
     */
    DeliveryDTO updateDeliveryStatus(Long deliveryId, Integer status, String remark);

    /**
     * 开始配送
     *
     * @param deliveryId 配送任务ID
     * @return 更新后的配送任务DTO
     */
    DeliveryDTO startDelivery(Long deliveryId);

    /**
     * 完成配送
     *
     * @param deliveryId 配送任务ID
     * @param deliveryCode 配送验证码
     * @return 更新后的配送任务DTO
     */
    DeliveryDTO completeDelivery(Long deliveryId, String deliveryCode);

    /**
     * 取消配送
     *
     * @param deliveryId 配送任务ID
     * @param reason 取消原因
     * @return 更新后的配送任务DTO
     */
    DeliveryDTO cancelDelivery(Long deliveryId, String reason);

    /**
     * 生成配送验证码
     *
     * @param deliveryId 配送任务ID
     * @return 配送验证码
     */
    String generateDeliveryCode(Long deliveryId);

    /**
     * 验证配送验证码
     *
     * @param deliveryId 配送任务ID
     * @param deliveryCode 配送验证码
     * @return 是否有效
     */
    boolean verifyDeliveryCode(Long deliveryId, String deliveryCode);

    /**
     * 统计配送员的配送任务数量
     *
     * @param courierId 配送员ID
     * @return 配送任务数量
     */
    long countDeliveriesByCourierId(Long courierId);

    /**
     * 统计指定状态的配送任务数量
     *
     * @param status 状态
     * @return 配送任务数量
     */
    long countDeliveriesByStatus(Integer status);

    /**
     * 统计指定区域的配送任务数量
     *
     * @param area 配送区域
     * @return 配送任务数量
     */
    long countDeliveriesByArea(String area);

    /**
     * 统计指定时间范围内的配送任务数量
     *
     * @param startTime 开始时间
     * @param endTime 结束时间
     * @return 配送任务数量
     */
    long countDeliveriesByCreatedTimeBetween(LocalDateTime startTime, LocalDateTime endTime);
}

express-service\src\main\java\com\campus\express\service\ExpressService.java

package com.campus.express.service;

import com.campus.express.dto.ExpressCreateRequest;
import com.campus.express.dto.ExpressDTO;
import com.campus.express.dto.ExpressUpdateRequest;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;

import java.time.LocalDateTime;
import java.util.List;

/**
 * 快递服务接口
 */
public interface ExpressService {

    /**
     * 创建快递
     *
     * @param request 创建快递请求
     * @return 快递DTO
     */
    ExpressDTO createExpress(ExpressCreateRequest request);

    /**
     * 根据ID获取快递
     *
     * @param id 快递ID
     * @return 快递DTO
     */
    ExpressDTO getExpressById(Long id);

    /**
     * 根据快递单号获取快递
     *
     * @param trackingNumber 快递单号
     * @return 快递DTO
     */
    ExpressDTO getExpressByTrackingNumber(String trackingNumber);

    /**
     * 更新快递信息
     *
     * @param id 快递ID
     * @param request 更新快递请求
     * @return 更新后的快递DTO
     */
    ExpressDTO updateExpress(Long id, ExpressUpdateRequest request);

    /**
     * 删除快递
     *
     * @param id 快递ID
     */
    void deleteExpress(Long id);

    /**
     * 分页获取所有快递
     *
     * @param pageable 分页参数
     * @return 快递分页列表
     */
    Page<ExpressDTO> getAllExpress(Pageable pageable);

    /**
     * 根据用户ID获取快递列表
     *
     * @param userId 用户ID
     * @param pageable 分页参数
     * @return 快递分页列表
     */
    Page<ExpressDTO> getExpressByUserId(Long userId, Pageable pageable);

    /**
     * 根据快递员ID获取快递列表
     *
     * @param courierId 快递员ID
     * @param pageable 分页参数
     * @return 快递分页列表
     */
    Page<ExpressDTO> getExpressByCourierId(Long courierId, Pageable pageable);

    /**
     * 根据柜子ID获取快递列表
     *
     * @param cabinetId 柜子ID
     * @param pageable 分页参数
     * @return 快递分页列表
     */
    Page<ExpressDTO> getExpressByCabinetId(Long cabinetId, Pageable pageable);

    /**
     * 根据状态获取快递列表
     *
     * @param status 状态
     * @param pageable 分页参数
     * @return 快递分页列表
     */
    Page<ExpressDTO> getExpressByStatus(Integer status, Pageable pageable);

    /**
     * 根据收件人姓名或电话模糊查询快递列表
     *
     * @param keyword 关键字
     * @param pageable 分页参数
     * @return 快递分页列表
     */
    Page<ExpressDTO> searchExpressByRecipient(String keyword, Pageable pageable);

    /**
     * 分配快递员
     *
     * @param expressId 快递ID
     * @param courierId 快递员ID
     * @param courierName 快递员姓名
     * @param courierPhone 快递员电话
     * @return 更新后的快递DTO
     */
    ExpressDTO assignCourier(Long expressId, Long courierId, String courierName, String courierPhone);

    /**
     * 分配快递柜
     *
     * @param expressId 快递ID
     * @param cabinetId 柜子ID
     * @param cellNumber 格口编号
     * @return 更新后的快递DTO
     */
    ExpressDTO assignCabinet(Long expressId, Long cabinetId, String cellNumber);

    /**
     * 更新快递状态
     *
     * @param expressId 快递ID
     * @param status 状态
     * @param operatorId 操作人ID
     * @param operatorType 操作人类型
     * @param operatorName 操作人姓名
     * @param description 操作描述
     * @return 更新后的快递DTO
     */
    ExpressDTO updateExpressStatus(Long expressId, Integer status, Long operatorId, Integer operatorType, String operatorName, String description);

    /**
     * 签收快递
     *
     * @param expressId 快递ID
     * @param userId 用户ID
     * @param userName 用户姓名
     * @return 更新后的快递DTO
     */
    ExpressDTO signExpress(Long expressId, Long userId, String userName);

    /**
     * 统计用户的快递数量
     *
     * @param userId 用户ID
     * @return 快递数量
     */
    long countExpressByUserId(Long userId);

    /**
     * 统计快递员的快递数量
     *
     * @param courierId 快递员ID
     * @return 快递数量
     */
    long countExpressByCourierId(Long courierId);

    /**
     * 统计指定状态的快递数量
     *
     * @param status 状态
     * @return 快递数量
     */
    long countExpressByStatus(Integer status);

    /**
     * 查询指定时间范围内创建的快递列表
     *
     * @param startTime 开始时间
     * @param endTime 结束时间
     * @param pageable 分页参数
     * @return 快递分页列表
     */
    Page<ExpressDTO> getExpressByCreatedTimeBetween(LocalDateTime startTime, LocalDateTime endTime, Pageable pageable);

    /**
     * 生成取件码
     *
     * @param expressId 快递ID
     * @return 取件码
     */
    String generatePickupCode(Long expressId);

    /**
     * 验证取件码
     *
     * @param expressId 快递ID
     * @param pickupCode 取件码
     * @return 是否有效
     */
    boolean verifyPickupCode(Long expressId, String pickupCode);
}

express-service\src\main\java\com\campus\express\service\ExpressTrackingService.java

package com.campus.express.service;

import com.campus.express.dto.ExpressTrackingDTO;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;

import java.time.LocalDateTime;
import java.util.List;

/**
 * 快递跟踪记录服务接口
 */
public interface ExpressTrackingService {

    /**
     * 创建快递跟踪记录
     *
     * @param expressId 快递ID
     * @param trackingNumber 快递单号
     * @param operationType 操作类型
     * @param description 操作描述
     * @param operatorId 操作人ID
     * @param operatorType 操作人类型
     * @param operatorName 操作人姓名
     * @param location 操作地点
     * @param remark 备注
     * @return 跟踪记录DTO
     */
    ExpressTrackingDTO createTracking(Long expressId, String trackingNumber, Integer operationType, 
                                      String description, Long operatorId, Integer operatorType, 
                                      String operatorName, String location, String remark);

    /**
     * 根据ID获取跟踪记录
     *
     * @param id 跟踪记录ID
     * @return 跟踪记录DTO
     */
    ExpressTrackingDTO getTrackingById(Long id);

    /**
     * 根据快递ID获取跟踪记录列表
     *
     * @param expressId 快递ID
     * @return 跟踪记录列表
     */
    List<ExpressTrackingDTO> getTrackingsByExpressId(Long expressId);

    /**
     * 根据快递单号获取跟踪记录列表
     *
     * @param trackingNumber 快递单号
     * @return 跟踪记录列表
     */
    List<ExpressTrackingDTO> getTrackingsByTrackingNumber(String trackingNumber);

    /**
     * 根据操作类型获取跟踪记录列表
     *
     * @param operationType 操作类型
     * @param pageable 分页参数
     * @return 跟踪记录分页列表
     */
    Page<ExpressTrackingDTO> getTrackingsByOperationType(Integer operationType, Pageable pageable);

    /**
     * 根据操作人ID获取跟踪记录列表
     *
     * @param operatorId 操作人ID
     * @param pageable 分页参数
     * @return 跟踪记录分页列表
     */
    Page<ExpressTrackingDTO> getTrackingsByOperatorId(Long operatorId, Pageable pageable);

    /**
     * 根据操作人类型获取跟踪记录列表
     *
     * @param operatorType 操作人类型
     * @param pageable 分页参数
     * @return 跟踪记录分页列表
     */
    Page<ExpressTrackingDTO> getTrackingsByOperatorType(Integer operatorType, Pageable pageable);

    /**
     * 获取指定时间范围内的跟踪记录列表
     *
     * @param startTime 开始时间
     * @param endTime 结束时间
     * @param pageable 分页参数
     * @return 跟踪记录分页列表
     */
    Page<ExpressTrackingDTO> getTrackingsByTimeRange(LocalDateTime startTime, LocalDateTime endTime, Pageable pageable);

    /**
     * 根据快递ID和操作类型获取跟踪记录
     *
     * @param expressId 快递ID
     * @param operationType 操作类型
     * @return 跟踪记录列表
     */
    List<ExpressTrackingDTO> getTrackingsByExpressIdAndOperationType(Long expressId, Integer operationType);

    /**
     * 统计指定快递的跟踪记录数量
     *
     * @param expressId 快递ID
     * @return 跟踪记录数量
     */
    long countTrackingsByExpressId(Long expressId);

    /**
     * 统计指定操作类型的跟踪记录数量
     *
     * @param operationType 操作类型
     * @return 跟踪记录数量
     */
    long countTrackingsByOperationType(Integer operationType);

    /**
     * 删除跟踪记录
     *
     * @param id 跟踪记录ID
     */
    void deleteTracking(Long id);

    /**
     * 获取最新的跟踪记录
     *
     * @param expressId 快递ID
     * @return 最新的跟踪记录
     */
    ExpressTrackingDTO getLatestTracking(Long expressId);

    /**
     * 分页获取所有跟踪记录
     *
     * @param pageable 分页参数
     * @return 跟踪记录分页列表
     */
    Page<ExpressTrackingDTO> getAllTrackings(Pageable pageable);
}

express-service\src\main\java\com\campus\express\service\NotificationSender.java

package com.campus.express.service;

import com.campus.express.model.Notification;

/**
 * 通知发送器接口
 * 定义通知发送的通用方法
 */
public interface NotificationSender {

    /**
     * 发送通知
     *
     * @param notification 通知对象
     * @return 是否发送成功
     */
    boolean send(Notification notification);
    
    /**
     * 获取支持的通知渠道
     *
     * @return 通知渠道
     */
    int getSupportedChannel();
}

express-service\src\main\java\com\campus\express\service\NotificationService.java

package com.campus.express.service;

import com.campus.express.dto.NotificationCreateRequest;
import com.campus.express.dto.NotificationDTO;
import com.campus.express.dto.NotificationSendRequest;
import com.campus.express.dto.NotificationUpdateRequest;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;

/**
 * 通知服务接口
 */
public interface NotificationService {

    /**
     * 创建通知
     *
     * @param request 创建通知请求
     * @return 通知DTO
     */
    NotificationDTO createNotification(NotificationCreateRequest request);

    /**
     * 根据ID查询通知
     *
     * @param id 通知ID
     * @return 通知DTO
     */
    NotificationDTO getNotificationById(Long id);

    /**
     * 更新通知
     *
     * @param id      通知ID
     * @param request 更新通知请求
     * @return 更新后的通知DTO
     */
    NotificationDTO updateNotification(Long id, NotificationUpdateRequest request);

    /**
     * 删除通知
     *
     * @param id 通知ID
     */
    void deleteNotification(Long id);

    /**
     * 分页查询通知列表
     *
     * @param pageable 分页参数
     * @return 通知DTO分页列表
     */
    Page<NotificationDTO> getNotifications(Pageable pageable);

    /**
     * 根据接收者ID和接收者类型分页查询通知列表
     *
     * @param receiverId   接收者ID
     * @param receiverType 接收者类型
     * @param pageable     分页参数
     * @return 通知DTO分页列表
     */
    Page<NotificationDTO> getNotificationsByReceiver(Long receiverId, Integer receiverType, Pageable pageable);

    /**
     * 根据接收者ID、接收者类型和已读状态分页查询通知列表
     *
     * @param receiverId   接收者ID
     * @param receiverType 接收者类型
     * @param readStatus   已读状态
     * @param pageable     分页参数
     * @return 通知DTO分页列表
     */
    Page<NotificationDTO> getNotificationsByReceiverAndReadStatus(
            Long receiverId, Integer receiverType, Integer readStatus, Pageable pageable);

    /**
     * 根据通知类型分页查询通知列表
     *
     * @param type     通知类型
     * @param pageable 分页参数
     * @return 通知DTO分页列表
     */
    Page<NotificationDTO> getNotificationsByType(Integer type, Pageable pageable);

    /**
     * 根据通知渠道分页查询通知列表
     *
     * @param channel  通知渠道
     * @param pageable 分页参数
     * @return 通知DTO分页列表
     */
    Page<NotificationDTO> getNotificationsByChannel(Integer channel, Pageable pageable);

    /**
     * 根据发送状态分页查询通知列表
     *
     * @param sendStatus 发送状态
     * @param pageable   分页参数
     * @return 通知DTO分页列表
     */
    Page<NotificationDTO> getNotificationsBySendStatus(Integer sendStatus, Pageable pageable);

    /**
     * 根据创建时间范围分页查询通知列表
     *
     * @param startTime 开始时间
     * @param endTime   结束时间
     * @param pageable  分页参数
     * @return 通知DTO分页列表
     */
    Page<NotificationDTO> getNotificationsByCreatedTimeBetween(
            LocalDateTime startTime, LocalDateTime endTime, Pageable pageable);

    /**
     * 根据关联业务ID和关联业务类型查询通知列表
     *
     * @param businessId   关联业务ID
     * @param businessType 关联业务类型
     * @return 通知DTO列表
     */
    List<NotificationDTO> getNotificationsByBusiness(Long businessId, Integer businessType);

    /**
     * 发送通知
     *
     * @param request 发送通知请求
     * @return 通知DTO列表
     */
    List<NotificationDTO> sendNotification(NotificationSendRequest request);

    /**
     * 标记通知为已读
     *
     * @param id 通知ID
     * @return 更新后的通知DTO
     */
    NotificationDTO markAsRead(Long id);

    /**
     * 批量标记通知为已读
     *
     * @param ids 通知ID列表
     * @return 更新行数
     */
    int batchMarkAsRead(List<Long> ids);

    /**
     * 标记接收者的所有通知为已读
     *
     * @param receiverId   接收者ID
     * @param receiverType 接收者类型
     * @return 更新行数
     */
    int markAllAsRead(Long receiverId, Integer receiverType);

    /**
     * 重试发送失败的通知
     *
     * @param id 通知ID
     * @return 更新后的通知DTO
     */
    NotificationDTO retrySendNotification(Long id);

    /**
     * 统计接收者未读通知数量
     *
     * @param receiverId   接收者ID
     * @param receiverType 接收者类型
     * @return 未读通知数量
     */
    long countUnreadNotifications(Long receiverId, Integer receiverType);

    /**
     * 统计通知数量
     *
     * @param params 查询参数
     * @return 通知数量
     */
    Map<String, Long> countNotifications(Map<String, Object> params);
}

express-service\src\main\java\com\campus\express\service\NotificationTemplateService.java

package com.campus.express.service;

import com.campus.express.dto.NotificationTemplateCreateRequest;
import com.campus.express.dto.NotificationTemplateDTO;
import com.campus.express.dto.NotificationTemplateUpdateRequest;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;

import java.util.List;
import java.util.Map;

/**
 * 通知模板服务接口
 */
public interface NotificationTemplateService {

    /**
     * 创建通知模板
     *
     * @param request 创建通知模板请求
     * @return 通知模板DTO
     */
    NotificationTemplateDTO createNotificationTemplate(NotificationTemplateCreateRequest request);

    /**
     * 根据ID查询通知模板
     *
     * @param id 通知模板ID
     * @return 通知模板DTO
     */
    NotificationTemplateDTO getNotificationTemplateById(Long id);

    /**
     * 根据模板编码查询通知模板
     *
     * @param code 模板编码
     * @return 通知模板DTO
     */
    NotificationTemplateDTO getNotificationTemplateByCode(String code);

    /**
     * 更新通知模板
     *
     * @param id      通知模板ID
     * @param request 更新通知模板请求
     * @return 更新后的通知模板DTO
     */
    NotificationTemplateDTO updateNotificationTemplate(Long id, NotificationTemplateUpdateRequest request);

    /**
     * 删除通知模板
     *
     * @param id 通知模板ID
     */
    void deleteNotificationTemplate(Long id);

    /**
     * 分页查询通知模板列表
     *
     * @param pageable 分页参数
     * @return 通知模板DTO分页列表
     */
    Page<NotificationTemplateDTO> getNotificationTemplates(Pageable pageable);

    /**
     * 根据模板名称分页查询通知模板列表
     *
     * @param name     模板名称
     * @param pageable 分页参数
     * @return 通知模板DTO分页列表
     */
    Page<NotificationTemplateDTO> getNotificationTemplatesByName(String name, Pageable pageable);

    /**
     * 根据模板类型分页查询通知模板列表
     *
     * @param type     模板类型
     * @param pageable 分页参数
     * @return 通知模板DTO分页列表
     */
    Page<NotificationTemplateDTO> getNotificationTemplatesByType(Integer type, Pageable pageable);

    /**
     * 根据适用渠道分页查询通知模板列表
     *
     * @param channel  适用渠道
     * @param pageable 分页参数
     * @return 通知模板DTO分页列表
     */
    Page<NotificationTemplateDTO> getNotificationTemplatesByChannel(Integer channel, Pageable pageable);

    /**
     * 根据状态分页查询通知模板列表
     *
     * @param status   状态
     * @param pageable 分页参数
     * @return 通知模板DTO分页列表
     */
    Page<NotificationTemplateDTO> getNotificationTemplatesByStatus(Integer status, Pageable pageable);

    /**
     * 根据模板类型和适用渠道查询通知模板列表
     *
     * @param type    模板类型
     * @param channel 适用渠道
     * @return 通知模板DTO列表
     */
    List<NotificationTemplateDTO> getNotificationTemplatesByTypeAndChannel(Integer type, Integer channel);

    /**
     * 根据模板类型和状态查询通知模板列表
     *
     * @param type   模板类型
     * @param status 状态
     * @return 通知模板DTO列表
     */
    List<NotificationTemplateDTO> getNotificationTemplatesByTypeAndStatus(Integer type, Integer status);

    /**
     * 根据适用渠道和状态查询通知模板列表
     *
     * @param channel 适用渠道
     * @param status  状态
     * @return 通知模板DTO列表
     */
    List<NotificationTemplateDTO> getNotificationTemplatesByChannelAndStatus(Integer channel, Integer status);

    /**
     * 更新通知模板状态
     *
     * @param id     通知模板ID
     * @param status 状态
     * @return 更新后的通知模板DTO
     */
    NotificationTemplateDTO updateTemplateStatus(Long id, Integer status);

    /**
     * 检查模板编码是否存在
     *
     * @param code 模板编码
     * @return 是否存在
     */
    boolean isTemplateCodeExists(String code);

    /**
     * 渲染模板内容
     *
     * @param templateCode 模板编码
     * @param params       模板参数
     * @return 渲染后的内容
     */
    Map<String, String> renderTemplate(String templateCode, Map<String, Object> params);

    /**
     * 统计通知模板数量
     *
     * @param params 查询参数
     * @return 通知模板数量
     */
    Map<String, Long> countNotificationTemplates(Map<String, Object> params);
}

express-service\src\main\java\com\campus\express\service\impl\CabinetCellServiceImpl.java

package com.campus.express.service.impl;

import com.campus.express.dto.CabinetCellDTO;
import com.campus.express.exception.ResourceNotFoundException;
import com.campus.express.model.CabinetCell;
import com.campus.express.repository.CabinetCellRepository;
import com.campus.express.service.CabinetCellService;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 快递柜格口服务实现类
 */
@Service
@RequiredArgsConstructor
public class CabinetCellServiceImpl implements CabinetCellService {

    private final CabinetCellRepository cabinetCellRepository;

    /**
     * 根据ID获取格口
     *
     * @param id 格口ID
     * @return 格口DTO
     */
    @Override
    public CabinetCellDTO getCellById(Long id) {
        CabinetCell cell = cabinetCellRepository.findById(id)
                .orElseThrow(() -> new ResourceNotFoundException("格口不存在,ID: " + id));
        return convertToDTO(cell);
    }

    /**
     * 根据柜子ID和格口编号获取格口
     *
     * @param cabinetId 柜子ID
     * @param cellNumber 格口编号
     * @return 格口DTO
     */
    @Override
    public CabinetCellDTO getCellByCabinetIdAndCellNumber(Long cabinetId, String cellNumber) {
        CabinetCell cell = cabinetCellRepository.findByCabinetIdAndCellNumber(cabinetId, cellNumber)
                .orElseThrow(() -> new ResourceNotFoundException("格口不存在,柜子ID: " + cabinetId + ",格口编号: " + cellNumber));
        return convertToDTO(cell);
    }

    /**
     * 根据柜子ID获取格口列表
     *
     * @param cabinetId 柜子ID
     * @return 格口列表
     */
    @Override
    public List<CabinetCellDTO> getCellsByCabinetId(Long cabinetId) {
        return cabinetCellRepository.findByCabinetId(cabinetId).stream()
                .map(this::convertToDTO)
                .collect(Collectors.toList());
    }

    /**
     * 根据柜子ID和状态获取格口列表
     *
     * @param cabinetId 柜子ID
     * @param status 状态
     * @return 格口列表
     */
    @Override
    public List<CabinetCellDTO> getCellsByCabinetIdAndStatus(Long cabinetId, Integer status) {
        return cabinetCellRepository.findByCabinetIdAndStatus(cabinetId, status).stream()
                .map(this::convertToDTO)
                .collect(Collectors.toList());
    }

    /**
     * 根据快递ID获取格口
     *
     * @param expressId 快递ID
     * @return 格口DTO
     */
    @Override
    public CabinetCellDTO getCellByExpressId(Long expressId) {
        CabinetCell cell = cabinetCellRepository.findByExpressId(expressId)
                .orElseThrow(() -> new ResourceNotFoundException("未找到存放该快递的格口,快递ID: " + expressId));
        return convertToDTO(cell);
    }

    /**
     * 根据柜子ID分页获取格口列表
     *
     * @param cabinetId 柜子ID
     * @param pageable 分页参数
     * @return 格口分页列表
     */
    @Override
    public Page<CabinetCellDTO> getCellsByCabinetIdPaged(Long cabinetId, Pageable pageable) {
        return cabinetCellRepository.findByCabinetId(cabinetId, pageable).map(this::convertToDTO);
    }

    /**
     * 更新格口状态
     *
     * @param id 格口ID
     * @param status 状态
     * @return 更新后的格口DTO
     */
    @Override
    @Transactional
    public CabinetCellDTO updateCellStatus(Long id, Integer status) {
        CabinetCell cell = cabinetCellRepository.findById(id)
                .orElseThrow(() -> new ResourceNotFoundException("格口不存在,ID: " + id));

        cell.setStatus(status);
        CabinetCell updatedCell = cabinetCellRepository.save(cell);
        return convertToDTO(updatedCell);
    }

    /**
     * 分配快递到格口
     *
     * @param cellId 格口ID
     * @param expressId 快递ID
     * @return 更新后的格口DTO
     */
    @Override
    @Transactional
    public CabinetCellDTO assignExpressToCell(Long cellId, Long expressId) {
        CabinetCell cell = cabinetCellRepository.findById(cellId)
                .orElseThrow(() -> new ResourceNotFoundException("格口不存在,ID: " + cellId));

        if (cell.getStatus() != 0) {
            throw new IllegalStateException("格口不可用,当前状态: " + getStatusDesc(cell.getStatus()));
        }

        cell.setExpressId(expressId);
        cell.setStatus(1); // 占用
        cell.setStorageTime(LocalDateTime.now());

        CabinetCell updatedCell = cabinetCellRepository.save(cell);
        return convertToDTO(updatedCell);
    }

    /**
     * 从格口取出快递
     *
     * @param cellId 格口ID
     * @return 更新后的格口DTO
     */
    @Override
    @Transactional
    public CabinetCellDTO retrieveExpressFromCell(Long cellId) {
        CabinetCell cell = cabinetCellRepository.findById(cellId)
                .orElseThrow(() -> new ResourceNotFoundException("格口不存在,ID: " + cellId));

        if (cell.getStatus() != 1) {
            throw new IllegalStateException("格口中没有快递,当前状态: " + getStatusDesc(cell.getStatus()));
        }

        cell.setExpressId(null);
        cell.setStatus(0); // 空闲
        cell.setRetrievalTime(LocalDateTime.now());

        CabinetCell updatedCell = cabinetCellRepository.save(cell);
        return convertToDTO(updatedCell);
    }

    /**
     * 根据柜子ID和格口大小获取空闲格口
     *
     * @param cabinetId 柜子ID
     * @param size 格口大小
     * @return 空闲格口列表
     */
    @Override
    public List<CabinetCellDTO> getAvailableCellsByCabinetIdAndSize(Long cabinetId, Integer size) {
        return cabinetCellRepository.findByCabinetIdAndSizeAndStatus(cabinetId, size, 0).stream()
                .map(this::convertToDTO)
                .collect(Collectors.toList());
    }

    /**
     * 统计柜子中指定状态的格口数量
     *
     * @param cabinetId 柜子ID
     * @param status 状态
     * @return 格口数量
     */
    @Override
    public long countCellsByCabinetIdAndStatus(Long cabinetId, Integer status) {
        return cabinetCellRepository.countByCabinetIdAndStatus(cabinetId, status);
    }

    /**
     * 统计柜子中指定大小的格口数量
     *
     * @param cabinetId 柜子ID
     * @param size 格口大小
     * @return 格口数量
     */
    @Override
    public long countCellsByCabinetIdAndSize(Long cabinetId, Integer size) {
        return cabinetCellRepository.countByCabinetIdAndSize(cabinetId, size);
    }

    /**
     * 创建格口
     *
     * @param cabinetId 柜子ID
     * @param cellNumber 格口编号
     * @param size 格口大小
     * @return 创建的格口DTO
     */
    @Override
    @Transactional
    public CabinetCellDTO createCell(Long cabinetId, String cellNumber, Integer size) {
        // 检查格口编号是否已存在
        if (cabinetCellRepository.findByCabinetIdAndCellNumber(cabinetId, cellNumber).isPresent()) {
            throw new IllegalArgumentException("格口编号已存在,柜子ID: " + cabinetId + ",格口编号: " + cellNumber);
        }

        CabinetCell cell = CabinetCell.builder()
                .cabinetId(cabinetId)
                .cellNumber(cellNumber)
                .size(size)
                .status(0) // 空闲
                .build();

        CabinetCell savedCell = cabinetCellRepository.save(cell);
        return convertToDTO(savedCell);
    }

    /**
     * 删除格口
     *
     * @param id 格口ID
     */
    @Override
    @Transactional
    public void deleteCell(Long id) {
        if (!cabinetCellRepository.existsById(id)) {
            throw new ResourceNotFoundException("格口不存在,ID: " + id);
        }
        cabinetCellRepository.deleteById(id);
    }

    /**
     * 批量创建格口
     *
     * @param cabinetId 柜子ID
     * @param smallCount 小格口数量
     * @param mediumCount 中格口数量
     * @param largeCount 大格口数量
     * @return 创建的格口列表
     */
    @Override
    @Transactional
    public List<CabinetCellDTO> batchCreateCells(Long cabinetId, Integer smallCount, Integer mediumCount, Integer largeCount) {
        List<CabinetCell> cells = new ArrayList<>();
        int cellIndex = 1;

        // 创建小格口
        for (int i = 0; i < smallCount; i++) {
            String cellNumber = String.format("S%03d", cellIndex++);
            CabinetCell cell = CabinetCell.builder()
                    .cabinetId(cabinetId)
                    .cellNumber(cellNumber)
                    .size(0) // 小
                    .status(0) // 空闲
                    .build();
            cells.add(cell);
        }

        // 创建中格口
        cellIndex = 1;
        for (int i = 0; i < mediumCount; i++) {
            String cellNumber = String.format("M%03d", cellIndex++);
            CabinetCell cell = CabinetCell.builder()
                    .cabinetId(cabinetId)
                    .cellNumber(cellNumber)
                    .size(1) // 中
                    .status(0) // 空闲
                    .build();
            cells.add(cell);
        }

        // 创建大格口
        cellIndex = 1;
        for (int i = 0; i < largeCount; i++) {
            String cellNumber = String.format("L%03d", cellIndex++);
            CabinetCell cell = CabinetCell.builder()
                    .cabinetId(cabinetId)
                    .cellNumber(cellNumber)
                    .size(2) // 大
                    .status(0) // 空闲
                    .build();
            cells.add(cell);
        }

        List<CabinetCell> savedCells = cabinetCellRepository.saveAll(cells);
        return savedCells.stream().map(this::convertToDTO).collect(Collectors.toList());
    }

    /**
     * 将CabinetCell实体转换为CabinetCellDTO
     *
     * @param cell 格口实体
     * @return 格口DTO
     */
    private CabinetCellDTO convertToDTO(CabinetCell cell) {
        return CabinetCellDTO.builder()
                .id(cell.getId())
                .cabinetId(cell.getCabinetId())
                .cellNumber(cell.getCellNumber())
                .size(cell.getSize())
                .sizeDesc(getSizeDesc(cell.getSize()))
                .status(cell.getStatus())
                .statusDesc(getStatusDesc(cell.getStatus()))
                .expressId(cell.getExpressId())
                .storageTime(cell.getStorageTime())
                .retrievalTime(cell.getRetrievalTime())
                .remark(cell.getRemark())
                .createdTime(cell.getCreatedTime())
                .updatedTime(cell.getUpdatedTime())
                .build();
    }

    /**
     * 获取大小描述
     *
     * @param size 大小码
     * @return 大小描述
     */
    private String getSizeDesc(Integer size) {
        if (size == null) {
            return null;
        }
        switch (size) {
            case 0:
                return "小";
            case 1:
                return "中";
            case 2:
                return "大";
            default:
                return "未知大小";
        }
    }

    /**
     * 获取状态描述
     *
     * @param status 状态码
     * @return 状态描述
     */
    private String getStatusDesc(Integer status) {
        if (status == null) {
            return null;
        }
        switch (status) {
            case 0:
                return "空闲";
            case 1:
                return "占用";
            case 2:
                return "故障";
            default:
                return "未知状态";
        }
    }
}

express-service\src\main\java\com\campus\express\service\impl\CabinetServiceImpl.java

package com.campus.express.service.impl;

import com.campus.express.dto.CabinetCreateRequest;
import com.campus.express.dto.CabinetDTO;
import com.campus.express.dto.CabinetUpdateRequest;
import com.campus.express.exception.ResourceNotFoundException;
import com.campus.express.model.Cabinet;
import com.campus.express.repository.CabinetRepository;
import com.campus.express.service.CabinetCellService;
import com.campus.express.service.CabinetService;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.stream.Collectors;

/**
 * 快递柜服务实现类
 */
@Service
@RequiredArgsConstructor
public class CabinetServiceImpl implements CabinetService {

    private final CabinetRepository cabinetRepository;
    private final CabinetCellService cabinetCellService;

    /**
     * 创建快递柜
     *
     * @param request 创建快递柜请求
     * @return 快递柜DTO
     */
    @Override
    @Transactional
    public CabinetDTO createCabinet(CabinetCreateRequest request) {
        // 检查柜子编号是否已存在
        if (cabinetRepository.findByCabinetNumber(request.getCabinetNumber()).isPresent()) {
            throw new IllegalArgumentException("柜子编号已存在: " + request.getCabinetNumber());
        }

        // 计算总格口数
        int totalCells = 0;
        if (request.getSmallCellCount() != null) {
            totalCells += request.getSmallCellCount();
        }
        if (request.getMediumCellCount() != null) {
            totalCells += request.getMediumCellCount();
        }
        if (request.getLargeCellCount() != null) {
            totalCells += request.getLargeCellCount();
        }

        // 如果未指定格口数量,使用总格口数
        if (totalCells == 0) {
            totalCells = request.getTotalCells();
        }

        Cabinet cabinet = Cabinet.builder()
                .cabinetNumber(request.getCabinetNumber())
                .name(request.getName())
                .location(request.getLocation())
                .status(request.getStatus())
                .totalCells(totalCells)
                .availableCells(totalCells)
                .managerId(request.getManagerId())
                .managerName(request.getManagerName())
                .managerPhone(request.getManagerPhone())
                .remark(request.getRemark())
                .build();

        Cabinet savedCabinet = cabinetRepository.save(cabinet);

        // 初始化格口
        initializeCabinetCells(
                savedCabinet.getId(),
                request.getSmallCellCount() != null ? request.getSmallCellCount() : 0,
                request.getMediumCellCount() != null ? request.getMediumCellCount() : 0,
                request.getLargeCellCount() != null ? request.getLargeCellCount() : totalCells
        );

        return convertToDTO(savedCabinet);
    }

    /**
     * 根据ID获取快递柜
     *
     * @param id 快递柜ID
     * @return 快递柜DTO
     */
    @Override
    public CabinetDTO getCabinetById(Long id) {
        Cabinet cabinet = cabinetRepository.findById(id)
                .orElseThrow(() -> new ResourceNotFoundException("快递柜不存在,ID: " + id));
        return convertToDTO(cabinet);
    }

    /**
     * 根据柜子编号获取快递柜
     *
     * @param cabinetNumber 柜子编号
     * @return 快递柜DTO
     */
    @Override
    public CabinetDTO getCabinetByNumber(String cabinetNumber) {
        Cabinet cabinet = cabinetRepository.findByCabinetNumber(cabinetNumber)
                .orElseThrow(() -> new ResourceNotFoundException("快递柜不存在,编号: " + cabinetNumber));
        return convertToDTO(cabinet);
    }

    /**
     * 更新快递柜信息
     *
     * @param id 快递柜ID
     * @param request 更新快递柜请求
     * @return 更新后的快递柜DTO
     */
    @Override
    @Transactional
    public CabinetDTO updateCabinet(Long id, CabinetUpdateRequest request) {
        Cabinet cabinet = cabinetRepository.findById(id)
                .orElseThrow(() -> new ResourceNotFoundException("快递柜不存在,ID: " + id));

        if (request.getName() != null) {
            cabinet.setName(request.getName());
        }
        if (request.getLocation() != null) {
            cabinet.setLocation(request.getLocation());
        }
        if (request.getStatus() != null) {
            cabinet.setStatus(request.getStatus());
        }
        if (request.getManagerId() != null) {
            cabinet.setManagerId(request.getManagerId());
            cabinet.setManagerName(request.getManagerName());
            cabinet.setManagerPhone(request.getManagerPhone());
        }
        if (request.getRemark() != null) {
            cabinet.setRemark(request.getRemark());
        }

        Cabinet updatedCabinet = cabinetRepository.save(cabinet);
        return convertToDTO(updatedCabinet);
    }

    /**
     * 删除快递柜
     *
     * @param id 快递柜ID
     */
    @Override
    @Transactional
    public void deleteCabinet(Long id) {
        if (!cabinetRepository.existsById(id)) {
            throw new ResourceNotFoundException("快递柜不存在,ID: " + id);
        }
        cabinetRepository.deleteById(id);
    }

    /**
     * 分页获取所有快递柜
     *
     * @param pageable 分页参数
     * @return 快递柜分页列表
     */
    @Override
    public Page<CabinetDTO> getAllCabinets(Pageable pageable) {
        return cabinetRepository.findAll(pageable).map(this::convertToDTO);
    }

    /**
     * 根据柜子名称或位置模糊查询快递柜列表
     *
     * @param keyword 关键字
     * @param pageable 分页参数
     * @return 快递柜分页列表
     */
    @Override
    public Page<CabinetDTO> searchCabinets(String keyword, Pageable pageable) {
        return cabinetRepository.findByNameOrLocationContaining(keyword, pageable).map(this::convertToDTO);
    }

    /**
     * 根据状态获取快递柜列表
     *
     * @param status 状态
     * @param pageable 分页参数
     * @return 快递柜分页列表
     */
    @Override
    public Page<CabinetDTO> getCabinetsByStatus(Integer status, Pageable pageable) {
        return cabinetRepository.findByStatus(status, pageable).map(this::convertToDTO);
    }

    /**
     * 根据负责人ID获取快递柜列表
     *
     * @param managerId 负责人ID
     * @return 快递柜列表
     */
    @Override
    public List<CabinetDTO> getCabinetsByManagerId(Long managerId) {
        return cabinetRepository.findByManagerId(managerId).stream()
                .map(this::convertToDTO)
                .collect(Collectors.toList());
    }

    /**
     * 查询可用格口数大于0的快递柜列表
     *
     * @param pageable 分页参数
     * @return 快递柜分页列表
     */
    @Override
    public Page<CabinetDTO> getAvailableCabinets(Pageable pageable) {
        return cabinetRepository.findByAvailableCellsGreaterThan(0, pageable).map(this::convertToDTO);
    }

    /**
     * 查询指定位置的快递柜列表
     *
     * @param location 位置
     * @param pageable 分页参数
     * @return 快递柜分页列表
     */
    @Override
    public Page<CabinetDTO> getCabinetsByLocation(String location, Pageable pageable) {
        return cabinetRepository.findByLocationContaining(location, pageable).map(this::convertToDTO);
    }

    /**
     * 统计指定状态的快递柜数量
     *
     * @param status 状态
     * @return 快递柜数量
     */
    @Override
    public long countCabinetsByStatus(Integer status) {
        return cabinetRepository.countByStatus(status);
    }

    /**
     * 统计指定负责人的快递柜数量
     *
     * @param managerId 负责人ID
     * @return 快递柜数量
     */
    @Override
    public long countCabinetsByManagerId(Long managerId) {
        return cabinetRepository.countByManagerId(managerId);
    }

    /**
     * 更新快递柜状态
     *
     * @param id 快递柜ID
     * @param status 状态
     * @return 更新后的快递柜DTO
     */
    @Override
    @Transactional
    public CabinetDTO updateCabinetStatus(Long id, Integer status) {
        Cabinet cabinet = cabinetRepository.findById(id)
                .orElseThrow(() -> new ResourceNotFoundException("快递柜不存在,ID: " + id));

        cabinet.setStatus(status);
        Cabinet updatedCabinet = cabinetRepository.save(cabinet);
        return convertToDTO(updatedCabinet);
    }

    /**
     * 分配快递柜负责人
     *
     * @param id 快递柜ID
     * @param managerId 负责人ID
     * @param managerName 负责人姓名
     * @param managerPhone 负责人电话
     * @return 更新后的快递柜DTO
     */
    @Override
    @Transactional
    public CabinetDTO assignCabinetManager(Long id, Long managerId, String managerName, String managerPhone) {
        Cabinet cabinet = cabinetRepository.findById(id)
                .orElseThrow(() -> new ResourceNotFoundException("快递柜不存在,ID: " + id));

        cabinet.setManagerId(managerId);
        cabinet.setManagerName(managerName);
        cabinet.setManagerPhone(managerPhone);

        Cabinet updatedCabinet = cabinetRepository.save(cabinet);
        return convertToDTO(updatedCabinet);
    }

    /**
     * 更新快递柜可用格口数
     *
     * @param id 快递柜ID
     * @return 更新后的快递柜DTO
     */
    @Override
    @Transactional
    public CabinetDTO updateAvailableCells(Long id) {
        Cabinet cabinet = cabinetRepository.findById(id)
                .orElseThrow(() -> new ResourceNotFoundException("快递柜不存在,ID: " + id));

        // 查询空闲格口数量
        long availableCells = cabinetCellService.countCellsByCabinetIdAndStatus(id, 0);
        cabinet.setAvailableCells((int) availableCells);

        Cabinet updatedCabinet = cabinetRepository.save(cabinet);
        return convertToDTO(updatedCabinet);
    }

    /**
     * 初始化快递柜格口
     *
     * @param cabinetId 快递柜ID
     * @param smallCount 小格口数量
     * @param mediumCount 中格口数量
     * @param largeCount 大格口数量
     */
    @Override
    @Transactional
    public void initializeCabinetCells(Long cabinetId, Integer smallCount, Integer mediumCount, Integer largeCount) {
        cabinetCellService.batchCreateCells(cabinetId, smallCount, mediumCount, largeCount);
    }

    /**
     * 将Cabinet实体转换为CabinetDTO
     *
     * @param cabinet 快递柜实体
     * @return 快递柜DTO
     */
    private CabinetDTO convertToDTO(Cabinet cabinet) {
        return CabinetDTO.builder()
                .id(cabinet.getId())
                .cabinetNumber(cabinet.getCabinetNumber())
                .name(cabinet.getName())
                .location(cabinet.getLocation())
                .status(cabinet.getStatus())
                .statusDesc(getStatusDesc(cabinet.getStatus()))
                .totalCells(cabinet.getTotalCells())
                .availableCells(cabinet.getAvailableCells())
                .smallCellCount((int) cabinetCellService.countCellsByCabinetIdAndSize(cabinet.getId(), 0))
                .mediumCellCount((int) cabinetCellService.countCellsByCabinetIdAndSize(cabinet.getId(), 1))
                .largeCellCount((int) cabinetCellService.countCellsByCabinetIdAndSize(cabinet.getId(), 2))
                .managerId(cabinet.getManagerId())
                .managerName(cabinet.getManagerName())
                .managerPhone(cabinet.getManagerPhone())
                .remark(cabinet.getRemark())
                .createdTime(cabinet.getCreatedTime())
                .updatedTime(cabinet.getUpdatedTime())
                .build();
    }

    /**
     * 获取状态描述
     *
     * @param status 状态码
     * @return 状态描述
     */
    private String getStatusDesc(Integer status) {
        if (status == null) {
            return null;
        }
        switch (status) {
            case 0:
                return "停用";
            case 1:
                return "启用";
            default:
                return "未知状态";
        }
    }
}

express-service\src\main\java\com\campus\express\service\impl\DeliveryAreaServiceImpl.java

package com.campus.express.service.impl;

import com.campus.express.dto.DeliveryAreaCreateRequest;
import com.campus.express.dto.DeliveryAreaDTO;
import com.campus.express.dto.DeliveryAreaUpdateRequest;
import com.campus.express.exception.ResourceNotFoundException;
import com.campus.express.model.DeliveryArea;
import com.campus.express.repository.DeliveryAreaRepository;
import com.campus.express.service.DeliveryAreaService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 配送区域服务实现类
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class DeliveryAreaServiceImpl implements DeliveryAreaService {

    private final DeliveryAreaRepository deliveryAreaRepository;

    /**
     * 创建配送区域
     *
     * @param request 创建配送区域请求
     * @return 配送区域DTO
     */
    @Override
    @Transactional
    public DeliveryAreaDTO createDeliveryArea(DeliveryAreaCreateRequest request) {
        log.info("Creating delivery area with name: {}", request.getName());
        
        DeliveryArea deliveryArea = new DeliveryArea();
        BeanUtils.copyProperties(request, deliveryArea);
        
        // 设置默认值
        if (deliveryArea.getStatus() == null) {
            deliveryArea.setStatus(1); // 默认启用
        }
        
        deliveryArea.setCreatedTime(LocalDateTime.now());
        deliveryArea.setUpdatedTime(LocalDateTime.now());
        
        DeliveryArea savedArea = deliveryAreaRepository.save(deliveryArea);
        log.info("Delivery area created with ID: {}", savedArea.getId());
        
        return convertToDTO(savedArea);
    }

    /**
     * 根据ID查询配送区域
     *
     * @param id 配送区域ID
     * @return 配送区域DTO
     */
    @Override
    public DeliveryAreaDTO getDeliveryAreaById(Long id) {
        log.info("Getting delivery area by ID: {}", id);
        
        DeliveryArea deliveryArea = deliveryAreaRepository.findById(id)
                .orElseThrow(() -> new ResourceNotFoundException("Delivery area not found with id: " + id));
        
        DeliveryAreaDTO dto = convertToDTO(deliveryArea);
        
        // 如果是父区域,加载子区域
        if (deliveryArea.getLevel() != null && deliveryArea.getLevel() < 3) {
            List<DeliveryArea> children = deliveryAreaRepository.findByParentId(deliveryArea.getId());
            if (!children.isEmpty()) {
                dto.setChildren(children.stream()
                        .map(this::convertToDTO)
                        .collect(Collectors.toList()));
            }
        }
        
        return dto;
    }

    /**
     * 根据区域代码查询配送区域
     *
     * @param areaCode 区域代码
     * @return 配送区域DTO
     */
    @Override
    public DeliveryAreaDTO getDeliveryAreaByAreaCode(String areaCode) {
        log.info("Getting delivery area by area code: {}", areaCode);
        
        DeliveryArea deliveryArea = deliveryAreaRepository.findByAreaCode(areaCode)
                .orElseThrow(() -> new ResourceNotFoundException("Delivery area not found with area code: " + areaCode));
        
        return convertToDTO(deliveryArea);
    }

    /**
     * 更新配送区域
     *
     * @param id      配送区域ID
     * @param request 更新配送区域请求
     * @return 更新后的配送区域DTO
     */
    @Override
    @Transactional
    public DeliveryAreaDTO updateDeliveryArea(Long id, DeliveryAreaUpdateRequest request) {
        log.info("Updating delivery area with ID: {}", id);
        
        DeliveryArea deliveryArea = deliveryAreaRepository.findById(id)
                .orElseThrow(() -> new ResourceNotFoundException("Delivery area not found with id: " + id));
        
        // 只更新非空字段
        if (request.getName() != null) {
            deliveryArea.setName(request.getName());
        }
        if (request.getParentId() != null) {
            deliveryArea.setParentId(request.getParentId());
        }
        if (request.getLevel() != null) {
            deliveryArea.setLevel(request.getLevel());
        }
        if (request.getBoundary() != null) {
            deliveryArea.setBoundary(request.getBoundary());
        }
        if (request.getCenterLongitude() != null) {
            deliveryArea.setCenterLongitude(request.getCenterLongitude());
        }
        if (request.getCenterLatitude() != null) {
            deliveryArea.setCenterLatitude(request.getCenterLatitude());
        }
        if (request.getDeliveryFee() != null) {
            deliveryArea.setDeliveryFee(request.getDeliveryFee());
        }
        if (request.getEstimatedDeliveryTime() != null) {
            deliveryArea.setEstimatedDeliveryTime(request.getEstimatedDeliveryTime());
        }
        if (request.getStatus() != null) {
            deliveryArea.setStatus(request.getStatus());
        }
        if (request.getDescription() != null) {
            deliveryArea.setDescription(request.getDescription());
        }
        if (request.getRemark() != null) {
            deliveryArea.setRemark(request.getRemark());
        }
        
        deliveryArea.setUpdatedTime(LocalDateTime.now());
        
        DeliveryArea updatedArea = deliveryAreaRepository.save(deliveryArea);
        log.info("Delivery area updated with ID: {}", updatedArea.getId());
        
        return convertToDTO(updatedArea);
    }

    /**
     * 删除配送区域
     *
     * @param id 配送区域ID
     */
    @Override
    @Transactional
    public void deleteDeliveryArea(Long id) {
        log.info("Deleting delivery area with ID: {}", id);
        
        DeliveryArea deliveryArea = deliveryAreaRepository.findById(id)
                .orElseThrow(() -> new ResourceNotFoundException("Delivery area not found with id: " + id));
        
        // 检查是否有子区域
        long childCount = deliveryAreaRepository.countByParentId(id);
        if (childCount > 0) {
            throw new IllegalStateException("Cannot delete area with children. Please delete children first.");
        }
        
        deliveryAreaRepository.deleteById(id);
        log.info("Delivery area deleted with ID: {}", id);
    }

    /**
     * 分页查询配送区域列表
     *
     * @param pageable 分页参数
     * @return 配送区域DTO分页列表
     */
    @Override
    public Page<DeliveryAreaDTO> getDeliveryAreas(Pageable pageable) {
        log.info("Getting delivery areas with pagination: {}", pageable);
        
        Page<DeliveryArea> areaPage = deliveryAreaRepository.findAll(pageable);
        List<DeliveryAreaDTO> areaDTOs = areaPage.getContent().stream()
                .map(this::convertToDTO)
                .collect(Collectors.toList());
        
        return new PageImpl<>(areaDTOs, pageable, areaPage.getTotalElements());
    }

    /**
     * 根据名称关键字分页查询配送区域列表
     *
     * @param name     名称关键字
     * @param pageable 分页参数
     * @return 配送区域DTO分页列表
     */
    @Override
    public Page<DeliveryAreaDTO> getDeliveryAreasByName(String name, Pageable pageable) {
        log.info("Getting delivery areas by name: {} with pagination: {}", name, pageable);
        
        Page<DeliveryArea> areaPage = deliveryAreaRepository.findByNameContaining(name, pageable);
        List<DeliveryAreaDTO> areaDTOs = areaPage.getContent().stream()
                .map(this::convertToDTO)
                .collect(Collectors.toList());
        
        return new PageImpl<>(areaDTOs, pageable, areaPage.getTotalElements());
    }

    /**
     * 根据状态分页查询配送区域列表
     *
     * @param status   状态
     * @param pageable 分页参数
     * @return 配送区域DTO分页列表
     */
    @Override
    public Page<DeliveryAreaDTO> getDeliveryAreasByStatus(Integer status, Pageable pageable) {
        log.info("Getting delivery areas by status: {} with pagination: {}", status, pageable);
        
        Page<DeliveryArea> areaPage = deliveryAreaRepository.findByStatus(status, pageable);
        List<DeliveryAreaDTO> areaDTOs = areaPage.getContent().stream()
                .map(this::convertToDTO)
                .collect(Collectors.toList());
        
        return new PageImpl<>(areaDTOs, pageable, areaPage.getTotalElements());
    }

    /**
     * 根据级别查询配送区域列表
     *
     * @param level 级别
     * @return 配送区域DTO列表
     */
    @Override
    public List<DeliveryAreaDTO> getDeliveryAreasByLevel(Integer level) {
        log.info("Getting delivery areas by level: {}", level);
        
        List<DeliveryArea> areas = deliveryAreaRepository.findByLevel(level);
        return areas.stream()
                .map(this::convertToDTO)
                .collect(Collectors.toList());
    }

    /**
     * 根据父区域ID查询子区域列表
     *
     * @param parentId 父区域ID
     * @return 配送区域DTO列表
     */
    @Override
    public List<DeliveryAreaDTO> getDeliveryAreasByParentId(Long parentId) {
        log.info("Getting delivery areas by parent ID: {}", parentId);
        
        List<DeliveryArea> areas = deliveryAreaRepository.findByParentId(parentId);
        return areas.stream()
                .map(this::convertToDTO)
                .collect(Collectors.toList());
    }

    /**
     * 获取区域树
     *
     * @return 区域树
     */
    @Override
    public List<DeliveryAreaDTO> getDeliveryAreaTree() {
        log.info("Getting delivery area tree");
        
        // 获取所有顶级区域(校区级别)
        List<DeliveryArea> topAreas = deliveryAreaRepository.findByLevel(1);
        List<DeliveryAreaDTO> result = new ArrayList<>();
        
        for (DeliveryArea topArea : topAreas) {
            DeliveryAreaDTO topDto = convertToDTO(topArea);
            
            // 获取二级区域(楼栋级别)
            List<DeliveryArea> secondLevelAreas = deliveryAreaRepository.findByParentId(topArea.getId());
            List<DeliveryAreaDTO> secondLevelDtos = new ArrayList<>();
            
            for (DeliveryArea secondArea : secondLevelAreas) {
                DeliveryAreaDTO secondDto = convertToDTO(secondArea);
                
                // 获取三级区域(具体位置级别)
                List<DeliveryArea> thirdLevelAreas = deliveryAreaRepository.findByParentId(secondArea.getId());
                List<DeliveryAreaDTO> thirdLevelDtos = thirdLevelAreas.stream()
                        .map(this::convertToDTO)
                        .collect(Collectors.toList());
                
                secondDto.setChildren(thirdLevelDtos);
                secondLevelDtos.add(secondDto);
            }
            
            topDto.setChildren(secondLevelDtos);
            result.add(topDto);
        }
        
        return result;
    }

    /**
     * 更新配送区域状态
     *
     * @param id     配送区域ID
     * @param status 状态
     * @return 更新后的配送区域DTO
     */
    @Override
    @Transactional
    public DeliveryAreaDTO updateAreaStatus(Long id, Integer status) {
        log.info("Updating delivery area status with ID: {}, status: {}", id, status);
        
        DeliveryArea deliveryArea = deliveryAreaRepository.findById(id)
                .orElseThrow(() -> new ResourceNotFoundException("Delivery area not found with id: " + id));
        
        deliveryArea.setStatus(status);
        deliveryArea.setUpdatedTime(LocalDateTime.now());
        
        DeliveryArea updatedArea = deliveryAreaRepository.save(deliveryArea);
        log.info("Delivery area status updated with ID: {}, status: {}", updatedArea.getId(), updatedArea.getStatus());
        
        return convertToDTO(updatedArea);
    }

    /**
     * 统计配送区域数量
     *
     * @param params 查询参数
     * @return 配送区域数量
     */
    @Override
    public Map<String, Long> countDeliveryAreas(Map<String, Object> params) {
        log.info("Counting delivery areas with params: {}", params);
        
        // 初始化统计结果
        Map<String, Long> result = Map.of(
                "total", deliveryAreaRepository.count(),
                "active", deliveryAreaRepository.countByStatus(1), // 启用
                "inactive", deliveryAreaRepository.countByStatus(0), // 停用
                "level1", deliveryAreaRepository.countByLevel(1), // 校区级别
                "level2", deliveryAreaRepository.countByLevel(2), // 楼栋级别
                "level3", deliveryAreaRepository.countByLevel(3) // 具体位置级别
        );
        
        return result;
    }

    /**
     * 将实体对象转换为DTO对象
     *
     * @param deliveryArea 配送区域实体
     * @return 配送区域DTO
     */
    private DeliveryAreaDTO convertToDTO(DeliveryArea deliveryArea) {
        DeliveryAreaDTO dto = new DeliveryAreaDTO();
        BeanUtils.copyProperties(deliveryArea, dto);
        
        // 设置状态描述
        if (deliveryArea.getStatus() != null) {
            switch (deliveryArea.getStatus()) {
                case 0:
                    dto.setStatusDesc("停用");
                    break;
                case 1:
                    dto.setStatusDesc("启用");
                    break;
                default:
                    dto.setStatusDesc("未知状态");
            }
        }
        
        // 设置级别描述
        if (deliveryArea.getLevel() != null) {
            switch (deliveryArea.getLevel()) {
                case 1:
                    dto.setLevelDesc("校区");
                    break;
                case 2:
                    dto.setLevelDesc("楼栋");
                    break;
                case 3:
                    dto.setLevelDesc("具体位置");
                    break;
                default:
                    dto.setLevelDesc("未知级别");
            }
        }
        
        // 如果有父区域ID,查询父区域名称
        if (deliveryArea.getParentId() != null) {
            deliveryAreaRepository.findById(deliveryArea.getParentId())
                    .ifPresent(parent -> dto.setParentName(parent.getName()));
        }
        
        return dto;
    }
}

express-service\src\main\java\com\campus\express\service\impl\DeliveryRouteServiceImpl.java

package com.campus.express.service.impl;

import com.campus.express.dto.DeliveryRouteCreateRequest;
import com.campus.express.dto.DeliveryRouteDTO;
import com.campus.express.dto.DeliveryRouteUpdateRequest;
import com.campus.express.exception.ResourceNotFoundException;
import com.campus.express.model.DeliveryRoute;
import com.campus.express.repository.DeliveryRouteRepository;
import com.campus.express.service.DeliveryRouteService;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 配送路线服务实现类
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class DeliveryRouteServiceImpl implements DeliveryRouteService {

    private final DeliveryRouteRepository deliveryRouteRepository;
    private final ObjectMapper objectMapper;

    /**
     * 创建配送路线
     *
     * @param request 创建配送路线请求
     * @return 配送路线DTO
     */
    @Override
    @Transactional
    public DeliveryRouteDTO createDeliveryRoute(DeliveryRouteCreateRequest request) {
        log.info("Creating delivery route with name: {}", request.getName());
        
        DeliveryRoute deliveryRoute = new DeliveryRoute();
        BeanUtils.copyProperties(request, deliveryRoute);
        
        // 设置默认值
        if (deliveryRoute.getStatus() == null) {
            deliveryRoute.setStatus(1); // 默认启用
        }
        
        deliveryRoute.setCreatedTime(LocalDateTime.now());
        deliveryRoute.setUpdatedTime(LocalDateTime.now());
        
        // 处理途经点
        if (request.getWaypoints() != null && !request.getWaypoints().isEmpty()) {
            try {
                deliveryRoute.setWaypoints(objectMapper.writeValueAsString(request.getWaypoints()));
            } catch (JsonProcessingException e) {
                log.error("Failed to serialize waypoints", e);
                deliveryRoute.setWaypoints("[]");
            }
        } else {
            deliveryRoute.setWaypoints("[]");
        }
        
        DeliveryRoute savedRoute = deliveryRouteRepository.save(deliveryRoute);
        log.info("Delivery route created with ID: {}", savedRoute.getId());
        
        return convertToDTO(savedRoute);
    }

    /**
     * 根据ID查询配送路线
     *
     * @param id 配送路线ID
     * @return 配送路线DTO
     */
    @Override
    public DeliveryRouteDTO getDeliveryRouteById(Long id) {
        log.info("Getting delivery route by ID: {}", id);
        
        DeliveryRoute deliveryRoute = deliveryRouteRepository.findById(id)
                .orElseThrow(() -> new ResourceNotFoundException("Delivery route not found with id: " + id));
        
        return convertToDTO(deliveryRoute);
    }

    /**
     * 根据路线编号查询配送路线
     *
     * @param routeNumber 路线编号
     * @return 配送路线DTO
     */
    @Override
    public DeliveryRouteDTO getDeliveryRouteByRouteNumber(String routeNumber) {
        log.info("Getting delivery route by route number: {}", routeNumber);
        
        DeliveryRoute deliveryRoute = deliveryRouteRepository.findByRouteNumber(routeNumber)
                .orElseThrow(() -> new ResourceNotFoundException("Delivery route not found with route number: " + routeNumber));
        
        return convertToDTO(deliveryRoute);
    }

    /**
     * 更新配送路线
     *
     * @param id      配送路线ID
     * @param request 更新配送路线请求
     * @return 更新后的配送路线DTO
     */
    @Override
    @Transactional
    public DeliveryRouteDTO updateDeliveryRoute(Long id, DeliveryRouteUpdateRequest request) {
        log.info("Updating delivery route with ID: {}", id);
        
        DeliveryRoute deliveryRoute = deliveryRouteRepository.findById(id)
                .orElseThrow(() -> new ResourceNotFoundException("Delivery route not found with id: " + id));
        
        // 只更新非空字段
        if (request.getName() != null) {
            deliveryRoute.setName(request.getName());
        }
        if (request.getStartPoint() != null) {
            deliveryRoute.setStartPoint(request.getStartPoint());
        }
        if (request.getStartLongitude() != null) {
            deliveryRoute.setStartLongitude(request.getStartLongitude());
        }
        if (request.getStartLatitude() != null) {
            deliveryRoute.setStartLatitude(request.getStartLatitude());
        }
        if (request.getEndPoint() != null) {
            deliveryRoute.setEndPoint(request.getEndPoint());
        }
        if (request.getEndLongitude() != null) {
            deliveryRoute.setEndLongitude(request.getEndLongitude());
        }
        if (request.getEndLatitude() != null) {
            deliveryRoute.setEndLatitude(request.getEndLatitude());
        }
        if (request.getWaypoints() != null) {
            try {
                deliveryRoute.setWaypoints(objectMapper.writeValueAsString(request.getWaypoints()));
            } catch (JsonProcessingException e) {
                log.error("Failed to serialize waypoints", e);
            }
        }
        if (request.getArea() != null) {
            deliveryRoute.setArea(request.getArea());
        }
        if (request.getAreaCode() != null) {
            deliveryRoute.setAreaCode(request.getAreaCode());
        }
        if (request.getDistance() != null) {
            deliveryRoute.setDistance(request.getDistance());
        }
        if (request.getEstimatedDuration() != null) {
            deliveryRoute.setEstimatedDuration(request.getEstimatedDuration());
        }
        if (request.getCourierId() != null) {
            deliveryRoute.setCourierId(request.getCourierId());
        }
        if (request.getCourierName() != null) {
            deliveryRoute.setCourierName(request.getCourierName());
        }
        if (request.getStatus() != null) {
            deliveryRoute.setStatus(request.getStatus());
        }
        if (request.getDescription() != null) {
            deliveryRoute.setDescription(request.getDescription());
        }
        if (request.getRouteType() != null) {
            deliveryRoute.setRouteType(request.getRouteType());
        }
        if (request.getRemark() != null) {
            deliveryRoute.setRemark(request.getRemark());
        }
        
        deliveryRoute.setUpdatedTime(LocalDateTime.now());
        
        DeliveryRoute updatedRoute = deliveryRouteRepository.save(deliveryRoute);
        log.info("Delivery route updated with ID: {}", updatedRoute.getId());
        
        return convertToDTO(updatedRoute);
    }

    /**
     * 删除配送路线
     *
     * @param id 配送路线ID
     */
    @Override
    @Transactional
    public void deleteDeliveryRoute(Long id) {
        log.info("Deleting delivery route with ID: {}", id);
        
        if (!deliveryRouteRepository.existsById(id)) {
            throw new ResourceNotFoundException("Delivery route not found with id: " + id);
        }
        
        deliveryRouteRepository.deleteById(id);
        log.info("Delivery route deleted with ID: {}", id);
    }

    /**
     * 分页查询配送路线列表
     *
     * @param pageable 分页参数
     * @return 配送路线DTO分页列表
     */
    @Override
    public Page<DeliveryRouteDTO> getDeliveryRoutes(Pageable pageable) {
        log.info("Getting delivery routes with pagination: {}", pageable);
        
        Page<DeliveryRoute> routePage = deliveryRouteRepository.findAll(pageable);
        List<DeliveryRouteDTO> routeDTOs = routePage.getContent().stream()
                .map(this::convertToDTO)
                .collect(Collectors.toList());
        
        return new PageImpl<>(routeDTOs, pageable, routePage.getTotalElements());
    }

    /**
     * 根据名称关键字分页查询配送路线列表
     *
     * @param name     名称关键字
     * @param pageable 分页参数
     * @return 配送路线DTO分页列表
     */
    @Override
    public Page<DeliveryRouteDTO> getDeliveryRoutesByName(String name, Pageable pageable) {
        log.info("Getting delivery routes by name: {} with pagination: {}", name, pageable);
        
        Page<DeliveryRoute> routePage = deliveryRouteRepository.findByNameContaining(name, pageable);
        List<DeliveryRouteDTO> routeDTOs = routePage.getContent().stream()
                .map(this::convertToDTO)
                .collect(Collectors.toList());
        
        return new PageImpl<>(routeDTOs, pageable, routePage.getTotalElements());
    }

    /**
     * 根据状态分页查询配送路线列表
     *
     * @param status   状态
     * @param pageable 分页参数
     * @return 配送路线DTO分页列表
     */
    @Override
    public Page<DeliveryRouteDTO> getDeliveryRoutesByStatus(Integer status, Pageable pageable) {
        log.info("Getting delivery routes by status: {} with pagination: {}", status, pageable);
        
        Page<DeliveryRoute> routePage = deliveryRouteRepository.findByStatus(status, pageable);
        List<DeliveryRouteDTO> routeDTOs = routePage.getContent().stream()
                .map(this::convertToDTO)
                .collect(Collectors.toList());
        
        return new PageImpl<>(routeDTOs, pageable, routePage.getTotalElements());
    }

    /**
     * 根据区域查询配送路线列表
     *
     * @param area 区域
     * @return 配送路线DTO列表
     */
    @Override
    public List<DeliveryRouteDTO> getDeliveryRoutesByArea(String area) {
        log.info("Getting delivery routes by area: {}", area);
        
        List<DeliveryRoute> routes = deliveryRouteRepository.findByArea(area);
        return routes.stream()
                .map(this::convertToDTO)
                .collect(Collectors.toList());
    }

    /**
     * 根据配送员ID查询配送路线列表
     *
     * @param courierId 配送员ID
     * @return 配送路线DTO列表
     */
    @Override
    public List<DeliveryRouteDTO> getDeliveryRoutesByCourierId(Long courierId) {
        log.info("Getting delivery routes by courier ID: {}", courierId);
        
        List<DeliveryRoute> routes = deliveryRouteRepository.findByCourierId(courierId);
        return routes.stream()
                .map(this::convertToDTO)
                .collect(Collectors.toList());
    }

    /**
     * 分配配送员
     *
     * @param id          配送路线ID
     * @param courierId   配送员ID
     * @param courierName 配送员姓名
     * @return 更新后的配送路线DTO
     */
    @Override
    @Transactional
    public DeliveryRouteDTO assignCourier(Long id, Long courierId, String courierName) {
        log.info("Assigning courier to delivery route with ID: {}, courier ID: {}", id, courierId);
        
        DeliveryRoute deliveryRoute = deliveryRouteRepository.findById(id)
                .orElseThrow(() -> new ResourceNotFoundException("Delivery route not found with id: " + id));
        
        deliveryRoute.setCourierId(courierId);
        deliveryRoute.setCourierName(courierName);
        deliveryRoute.setUpdatedTime(LocalDateTime.now());
        
        DeliveryRoute updatedRoute = deliveryRouteRepository.save(deliveryRoute);
        log.info("Courier assigned to delivery route with ID: {}", updatedRoute.getId());
        
        return convertToDTO(updatedRoute);
    }

    /**
     * 更新配送路线状态
     *
     * @param id     配送路线ID
     * @param status 状态
     * @return 更新后的配送路线DTO
     */
    @Override
    @Transactional
    public DeliveryRouteDTO updateRouteStatus(Long id, Integer status) {
        log.info("Updating delivery route status with ID: {}, status: {}", id, status);
        
        DeliveryRoute deliveryRoute = deliveryRouteRepository.findById(id)
                .orElseThrow(() -> new ResourceNotFoundException("Delivery route not found with id: " + id));
        
        deliveryRoute.setStatus(status);
        deliveryRoute.setUpdatedTime(LocalDateTime.now());
        
        DeliveryRoute updatedRoute = deliveryRouteRepository.save(deliveryRoute);
        log.info("Delivery route status updated with ID: {}, status: {}", updatedRoute.getId(), updatedRoute.getStatus());
        
        return convertToDTO(updatedRoute);
    }

    /**
     * 统计配送路线数量
     *
     * @param params 查询参数
     * @return 配送路线数量
     */
    @Override
    public Map<String, Long> countDeliveryRoutes(Map<String, Object> params) {
        log.info("Counting delivery routes with params: {}", params);
        
        // 初始化统计结果
        Map<String, Long> result = Map.of(
                "total", deliveryRouteRepository.count(),
                "active", deliveryRouteRepository.countByStatus(1), // 启用
                "inactive", deliveryRouteRepository.countByStatus(0) // 停用
        );
        
        return result;
    }

    /**
     * 将实体对象转换为DTO对象
     *
     * @param deliveryRoute 配送路线实体
     * @return 配送路线DTO
     */
    private DeliveryRouteDTO convertToDTO(DeliveryRoute deliveryRoute) {
        DeliveryRouteDTO dto = new DeliveryRouteDTO();
        BeanUtils.copyProperties(deliveryRoute, dto);
        
        // 处理途经点
        List<DeliveryRouteDTO.WaypointDTO> waypoints = new ArrayList<>();
        if (deliveryRoute.getWaypoints() != null && !deliveryRoute.getWaypoints().isEmpty()) {
            try {
                waypoints = objectMapper.readValue(
                        deliveryRoute.getWaypoints(),
                        objectMapper.getTypeFactory().constructCollectionType(
                                List.class, DeliveryRouteDTO.WaypointDTO.class)
                );
            } catch (JsonProcessingException e) {
                log.error("Failed to deserialize waypoints", e);
            }
        }
        dto.setWaypoints(waypoints);
        
        // 设置状态描述
        if (deliveryRoute.getStatus() != null) {
            switch (deliveryRoute.getStatus()) {
                case 0:
                    dto.setStatusDesc("停用");
                    break;
                case 1:
                    dto.setStatusDesc("启用");
                    break;
                default:
                    dto.setStatusDesc("未知状态");
            }
        }
        
        // 设置路线类型描述
        if (deliveryRoute.getRouteType() != null) {
            switch (deliveryRoute.getRouteType()) {
                case 0:
                    dto.setRouteTypeDesc("步行");
                    break;
                case 1:
                    dto.setRouteTypeDesc("自行车");
                    break;
                case 2:
                    dto.setRouteTypeDesc("电动车");
                    break;
                case 3:
                    dto.setRouteTypeDesc("汽车");
                    break;
                default:
                    dto.setRouteTypeDesc("未知类型");
            }
        }
        
        return dto;
    }
}

express-service\src\main\java\com\campus\express\service\impl\DeliveryServiceImpl.java

package com.campus.express.service.impl;

import com.campus.express.dto.DeliveryCreateRequest;
import com.campus.express.dto.DeliveryDTO;
import com.campus.express.dto.DeliveryUpdateRequest;
import com.campus.express.exception.ResourceNotFoundException;
import com.campus.express.model.Delivery;
import com.campus.express.repository.DeliveryRepository;
import com.campus.express.service.DeliveryService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.stream.Collectors;

/**
 * 配送服务实现类
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class DeliveryServiceImpl implements DeliveryService {

    private final DeliveryRepository deliveryRepository;
    private final Random random = new Random();

    /**
     * 创建配送任务
     *
     * @param request 创建配送任务请求
     * @return 配送任务DTO
     */
    @Override
    @Transactional
    public DeliveryDTO createDelivery(DeliveryCreateRequest request) {
        log.info("Creating delivery task for express ID: {}", request.getExpressId());
        
        Delivery delivery = new Delivery();
        BeanUtils.copyProperties(request, delivery);
        
        // 设置默认值
        delivery.setStatus(0); // 待分配状态
        delivery.setCreatedTime(LocalDateTime.now());
        delivery.setUpdatedTime(LocalDateTime.now());
        
        // 如果没有设置优先级,默认为普通优先级
        if (delivery.getPriority() == null) {
            delivery.setPriority(0);
        }
        
        // 生成配送验证码
        delivery.setDeliveryCode(generateDeliveryCode());
        
        Delivery savedDelivery = deliveryRepository.save(delivery);
        log.info("Delivery task created with ID: {}", savedDelivery.getId());
        
        return convertToDTO(savedDelivery);
    }

    /**
     * 根据ID查询配送任务
     *
     * @param id 配送任务ID
     * @return 配送任务DTO
     */
    @Override
    public DeliveryDTO getDeliveryById(Long id) {
        log.info("Getting delivery task by ID: {}", id);
        
        Delivery delivery = deliveryRepository.findById(id)
                .orElseThrow(() -> new ResourceNotFoundException("Delivery not found with id: " + id));
        
        return convertToDTO(delivery);
    }

    /**
     * 根据快递ID查询配送任务
     *
     * @param expressId 快递ID
     * @return 配送任务DTO
     */
    @Override
    public DeliveryDTO getDeliveryByExpressId(Long expressId) {
        log.info("Getting delivery task by express ID: {}", expressId);
        
        Delivery delivery = deliveryRepository.findByExpressId(expressId)
                .orElseThrow(() -> new ResourceNotFoundException("Delivery not found with express id: " + expressId));
        
        return convertToDTO(delivery);
    }

    /**
     * 更新配送任务
     *
     * @param id      配送任务ID
     * @param request 更新配送任务请求
     * @return 更新后的配送任务DTO
     */
    @Override
    @Transactional
    public DeliveryDTO updateDelivery(Long id, DeliveryUpdateRequest request) {
        log.info("Updating delivery task with ID: {}", id);
        
        Delivery delivery = deliveryRepository.findById(id)
                .orElseThrow(() -> new ResourceNotFoundException("Delivery not found with id: " + id));
        
        // 只更新非空字段
        if (request.getCourierId() != null) {
            delivery.setCourierId(request.getCourierId());
        }
        if (request.getCourierName() != null) {
            delivery.setCourierName(request.getCourierName());
        }
        if (request.getCourierPhone() != null) {
            delivery.setCourierPhone(request.getCourierPhone());
        }
        if (request.getRecipientName() != null) {
            delivery.setRecipientName(request.getRecipientName());
        }
        if (request.getRecipientPhone() != null) {
            delivery.setRecipientPhone(request.getRecipientPhone());
        }
        if (request.getRecipientAddress() != null) {
            delivery.setRecipientAddress(request.getRecipientAddress());
        }
        if (request.getDeliveryArea() != null) {
            delivery.setDeliveryArea(request.getDeliveryArea());
        }
        if (request.getAreaCode() != null) {
            delivery.setAreaCode(request.getAreaCode());
        }
        if (request.getRouteId() != null) {
            delivery.setRouteId(request.getRouteId());
        }
        if (request.getRouteName() != null) {
            delivery.setRouteName(request.getRouteName());
        }
        if (request.getPlannedDeliveryTime() != null) {
            delivery.setPlannedDeliveryTime(request.getPlannedDeliveryTime());
        }
        if (request.getActualDeliveryTime() != null) {
            delivery.setActualDeliveryTime(request.getActualDeliveryTime());
        }
        if (request.getCompletedTime() != null) {
            delivery.setCompletedTime(request.getCompletedTime());
        }
        if (request.getDeliveryCode() != null) {
            delivery.setDeliveryCode(request.getDeliveryCode());
        }
        if (request.getStatus() != null) {
            delivery.setStatus(request.getStatus());
        }
        if (request.getPriority() != null) {
            delivery.setPriority(request.getPriority());
        }
        if (request.getDeliveryFee() != null) {
            delivery.setDeliveryFee(request.getDeliveryFee());
        }
        if (request.getDistance() != null) {
            delivery.setDistance(request.getDistance());
        }
        if (request.getEstimatedDuration() != null) {
            delivery.setEstimatedDuration(request.getEstimatedDuration());
        }
        if (request.getActualDuration() != null) {
            delivery.setActualDuration(request.getActualDuration());
        }
        if (request.getCancelReason() != null) {
            delivery.setCancelReason(request.getCancelReason());
        }
        if (request.getRemark() != null) {
            delivery.setRemark(request.getRemark());
        }
        
        delivery.setUpdatedTime(LocalDateTime.now());
        
        Delivery updatedDelivery = deliveryRepository.save(delivery);
        log.info("Delivery task updated with ID: {}", updatedDelivery.getId());
        
        return convertToDTO(updatedDelivery);
    }

    /**
     * 删除配送任务
     *
     * @param id 配送任务ID
     */
    @Override
    @Transactional
    public void deleteDelivery(Long id) {
        log.info("Deleting delivery task with ID: {}", id);
        
        if (!deliveryRepository.existsById(id)) {
            throw new ResourceNotFoundException("Delivery not found with id: " + id);
        }
        
        deliveryRepository.deleteById(id);
        log.info("Delivery task deleted with ID: {}", id);
    }

    /**
     * 分页查询配送任务列表
     *
     * @param pageable 分页参数
     * @return 配送任务DTO分页列表
     */
    @Override
    public Page<DeliveryDTO> getDeliveries(Pageable pageable) {
        log.info("Getting delivery tasks with pagination: {}", pageable);
        
        Page<Delivery> deliveryPage = deliveryRepository.findAll(pageable);
        List<DeliveryDTO> deliveryDTOs = deliveryPage.getContent().stream()
                .map(this::convertToDTO)
                .collect(Collectors.toList());
        
        return new PageImpl<>(deliveryDTOs, pageable, deliveryPage.getTotalElements());
    }

    /**
     * 根据状态分页查询配送任务列表
     *
     * @param status   状态
     * @param pageable 分页参数
     * @return 配送任务DTO分页列表
     */
    @Override
    public Page<DeliveryDTO> getDeliveriesByStatus(Integer status, Pageable pageable) {
        log.info("Getting delivery tasks by status: {} with pagination: {}", status, pageable);
        
        Page<Delivery> deliveryPage = deliveryRepository.findByStatus(status, pageable);
        List<DeliveryDTO> deliveryDTOs = deliveryPage.getContent().stream()
                .map(this::convertToDTO)
                .collect(Collectors.toList());
        
        return new PageImpl<>(deliveryDTOs, pageable, deliveryPage.getTotalElements());
    }

    /**
     * 根据配送员ID分页查询配送任务列表
     *
     * @param courierId 配送员ID
     * @param pageable  分页参数
     * @return 配送任务DTO分页列表
     */
    @Override
    public Page<DeliveryDTO> getDeliveriesByCourierId(Long courierId, Pageable pageable) {
        log.info("Getting delivery tasks by courier ID: {} with pagination: {}", courierId, pageable);
        
        Page<Delivery> deliveryPage = deliveryRepository.findByCourierId(courierId, pageable);
        List<DeliveryDTO> deliveryDTOs = deliveryPage.getContent().stream()
                .map(this::convertToDTO)
                .collect(Collectors.toList());
        
        return new PageImpl<>(deliveryDTOs, pageable, deliveryPage.getTotalElements());
    }

    /**
     * 根据配送区域分页查询配送任务列表
     *
     * @param area     配送区域
     * @param pageable 分页参数
     * @return 配送任务DTO分页列表
     */
    @Override
    public Page<DeliveryDTO> getDeliveriesByArea(String area, Pageable pageable) {
        log.info("Getting delivery tasks by area: {} with pagination: {}", area, pageable);
        
        Page<Delivery> deliveryPage = deliveryRepository.findByDeliveryAreaContaining(area, pageable);
        List<DeliveryDTO> deliveryDTOs = deliveryPage.getContent().stream()
                .map(this::convertToDTO)
                .collect(Collectors.toList());
        
        return new PageImpl<>(deliveryDTOs, pageable, deliveryPage.getTotalElements());
    }

    /**
     * 根据创建时间范围分页查询配送任务列表
     *
     * @param startTime 开始时间
     * @param endTime   结束时间
     * @param pageable  分页参数
     * @return 配送任务DTO分页列表
     */
    @Override
    public Page<DeliveryDTO> getDeliveriesByCreatedTimeBetween(LocalDateTime startTime, LocalDateTime endTime, Pageable pageable) {
        log.info("Getting delivery tasks by created time between: {} and {} with pagination: {}", startTime, endTime, pageable);
        
        Page<Delivery> deliveryPage = deliveryRepository.findByCreatedTimeBetween(startTime, endTime, pageable);
        List<DeliveryDTO> deliveryDTOs = deliveryPage.getContent().stream()
                .map(this::convertToDTO)
                .collect(Collectors.toList());
        
        return new PageImpl<>(deliveryDTOs, pageable, deliveryPage.getTotalElements());
    }

    /**
     * 分配配送员
     *
     * @param id        配送任务ID
     * @param courierId 配送员ID
     * @param courierName 配送员姓名
     * @param courierPhone 配送员电话
     * @return 更新后的配送任务DTO
     */
    @Override
    @Transactional
    public DeliveryDTO assignCourier(Long id, Long courierId, String courierName, String courierPhone) {
        log.info("Assigning courier to delivery task with ID: {}, courier ID: {}", id, courierId);
        
        Delivery delivery = deliveryRepository.findById(id)
                .orElseThrow(() -> new ResourceNotFoundException("Delivery not found with id: " + id));
        
        delivery.setCourierId(courierId);
        delivery.setCourierName(courierName);
        delivery.setCourierPhone(courierPhone);
        delivery.setStatus(1); // 待配送状态
        delivery.setUpdatedTime(LocalDateTime.now());
        
        Delivery updatedDelivery = deliveryRepository.save(delivery);
        log.info("Courier assigned to delivery task with ID: {}", updatedDelivery.getId());
        
        return convertToDTO(updatedDelivery);
    }

    /**
     * 更新配送任务状态
     *
     * @param id     配送任务ID
     * @param status 状态
     * @return 更新后的配送任务DTO
     */
    @Override
    @Transactional
    public DeliveryDTO updateDeliveryStatus(Long id, Integer status) {
        log.info("Updating delivery task status with ID: {}, status: {}", id, status);
        
        Delivery delivery = deliveryRepository.findById(id)
                .orElseThrow(() -> new ResourceNotFoundException("Delivery not found with id: " + id));
        
        delivery.setStatus(status);
        delivery.setUpdatedTime(LocalDateTime.now());
        
        // 根据状态更新相关时间
        if (status == 2) { // 配送中
            delivery.setActualDeliveryTime(LocalDateTime.now());
        } else if (status == 4) { // 已完成
            delivery.setCompletedTime(LocalDateTime.now());
            
            // 计算实际配送时长(分钟)
            if (delivery.getActualDeliveryTime() != null) {
                long minutes = java.time.Duration.between(delivery.getActualDeliveryTime(), LocalDateTime.now()).toMinutes();
                delivery.setActualDuration((int) minutes);
            }
        }
        
        Delivery updatedDelivery = deliveryRepository.save(delivery);
        log.info("Delivery task status updated with ID: {}, status: {}", updatedDelivery.getId(), updatedDelivery.getStatus());
        
        return convertToDTO(updatedDelivery);
    }

    /**
     * 取消配送任务
     *
     * @param id     配送任务ID
     * @param reason 取消原因
     * @return 更新后的配送任务DTO
     */
    @Override
    @Transactional
    public DeliveryDTO cancelDelivery(Long id, String reason) {
        log.info("Cancelling delivery task with ID: {}, reason: {}", id, reason);
        
        Delivery delivery = deliveryRepository.findById(id)
                .orElseThrow(() -> new ResourceNotFoundException("Delivery not found with id: " + id));
        
        delivery.setStatus(5); // 已取消状态
        delivery.setCancelReason(reason);
        delivery.setUpdatedTime(LocalDateTime.now());
        
        Delivery updatedDelivery = deliveryRepository.save(delivery);
        log.info("Delivery task cancelled with ID: {}", updatedDelivery.getId());
        
        return convertToDTO(updatedDelivery);
    }

    /**
     * 生成配送验证码
     *
     * @return 配送验证码
     */
    @Override
    public String generateDeliveryCode() {
        // 生成6位数字验证码
        StringBuilder code = new StringBuilder();
        for (int i = 0; i < 6; i++) {
            code.append(random.nextInt(10));
        }
        return code.toString();
    }

    /**
     * 验证配送验证码
     *
     * @param id   配送任务ID
     * @param code 验证码
     * @return 是否验证成功
     */
    @Override
    public boolean verifyDeliveryCode(Long id, String code) {
        log.info("Verifying delivery code for delivery task with ID: {}", id);
        
        Delivery delivery = deliveryRepository.findById(id)
                .orElseThrow(() -> new ResourceNotFoundException("Delivery not found with id: " + id));
        
        return delivery.getDeliveryCode().equals(code);
    }

    /**
     * 统计配送任务数量
     *
     * @param params 查询参数
     * @return 配送任务数量
     */
    @Override
    public Map<String, Long> countDeliveries(Map<String, Object> params) {
        log.info("Counting delivery tasks with params: {}", params);
        
        // 初始化统计结果
        Map<String, Long> result = Map.of(
                "total", deliveryRepository.count(),
                "pending", deliveryRepository.countByStatus(0), // 待分配
                "assigned", deliveryRepository.countByStatus(1), // 待配送
                "delivering", deliveryRepository.countByStatus(2), // 配送中
                "delivered", deliveryRepository.countByStatus(3), // 已送达
                "completed", deliveryRepository.countByStatus(4), // 已完成
                "cancelled", deliveryRepository.countByStatus(5) // 已取消
        );
        
        return result;
    }

    /**
     * 将实体对象转换为DTO对象
     *
     * @param delivery 配送任务实体
     * @return 配送任务DTO
     */
    private DeliveryDTO convertToDTO(Delivery delivery) {
        DeliveryDTO dto = new DeliveryDTO();
        BeanUtils.copyProperties(delivery, dto);
        
        // 设置状态描述
        switch (delivery.getStatus()) {
            case 0:
                dto.setStatusDesc("待分配");
                break;
            case 1:
                dto.setStatusDesc("待配送");
                break;
            case 2:
                dto.setStatusDesc("配送中");
                break;
            case 3:
                dto.setStatusDesc("已送达");
                break;
            case 4:
                dto.setStatusDesc("已完成");
                break;
            case 5:
                dto.setStatusDesc("已取消");
                break;
            default:
                dto.setStatusDesc("未知状态");
        }
        
        // 设置优先级描述
        switch (delivery.getPriority()) {
            case 0:
                dto.setPriorityDesc("普通");
                break;
            case 1:
                dto.setPriorityDesc("优先");
                break;
            case 2:
                dto.setPriorityDesc("加急");
                break;
            default:
                dto.setPriorityDesc("未知优先级");
        }
        
        return dto;
    }
}

express-service\src\main\java\com\campus\express\service\impl\ExpressServiceImpl.java

package com.campus.express.service.impl;

import com.campus.express.dto.ExpressCreateRequest;
import com.campus.express.dto.ExpressDTO;
import com.campus.express.dto.ExpressUpdateRequest;
import com.campus.express.exception.ResourceNotFoundException;
import com.campus.express.model.Express;
import com.campus.express.repository.ExpressRepository;
import com.campus.express.service.ExpressService;
import com.campus.express.service.ExpressTrackingService;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.Random;

/**
 * 快递服务实现类
 */
@Service
@RequiredArgsConstructor
public class ExpressServiceImpl implements ExpressService {

    private final ExpressRepository expressRepository;
    private final ExpressTrackingService expressTrackingService;
    private static final Random random = new Random();

    /**
     * 创建快递
     *
     * @param request 创建快递请求
     * @return 快递DTO
     */
    @Override
    @Transactional
    public ExpressDTO createExpress(ExpressCreateRequest request) {
        Express express = Express.builder()
                .trackingNumber(request.getTrackingNumber())
                .company(request.getCompany())
                .recipientName(request.getRecipientName())
                .recipientPhone(request.getRecipientPhone())
                .recipientAddress(request.getRecipientAddress())
                .description(request.getDescription())
                .weight(request.getWeight())
                .status(0) // 待揽收
                .courierId(request.getCourierId())
                .courierName(request.getCourierName())
                .courierPhone(request.getCourierPhone())
                .userId(request.getUserId())
                .remark(request.getRemark())
                .build();

        Express savedExpress = expressRepository.save(express);

        // 创建跟踪记录
        expressTrackingService.createTracking(
                savedExpress.getId(),
                savedExpress.getTrackingNumber(),
                0, // 创建
                "快递已创建",
                null, // 系统操作
                0, // 系统
                "系统",
                null,
                null
        );

        return convertToDTO(savedExpress);
    }

    /**
     * 根据ID获取快递
     *
     * @param id 快递ID
     * @return 快递DTO
     */
    @Override
    public ExpressDTO getExpressById(Long id) {
        Express express = expressRepository.findById(id)
                .orElseThrow(() -> new ResourceNotFoundException("快递不存在,ID: " + id));
        return convertToDTO(express);
    }

    /**
     * 根据快递单号获取快递
     *
     * @param trackingNumber 快递单号
     * @return 快递DTO
     */
    @Override
    public ExpressDTO getExpressByTrackingNumber(String trackingNumber) {
        Express express = expressRepository.findByTrackingNumber(trackingNumber)
                .orElseThrow(() -> new ResourceNotFoundException("快递不存在,单号: " + trackingNumber));
        return convertToDTO(express);
    }

    /**
     * 更新快递信息
     *
     * @param id 快递ID
     * @param request 更新快递请求
     * @return 更新后的快递DTO
     */
    @Override
    @Transactional
    public ExpressDTO updateExpress(Long id, ExpressUpdateRequest request) {
        Express express = expressRepository.findById(id)
                .orElseThrow(() -> new ResourceNotFoundException("快递不存在,ID: " + id));

        if (request.getCompany() != null) {
            express.setCompany(request.getCompany());
        }
        if (request.getRecipientName() != null) {
            express.setRecipientName(request.getRecipientName());
        }
        if (request.getRecipientPhone() != null) {
            express.setRecipientPhone(request.getRecipientPhone());
        }
        if (request.getRecipientAddress() != null) {
            express.setRecipientAddress(request.getRecipientAddress());
        }
        if (request.getDescription() != null) {
            express.setDescription(request.getDescription());
        }
        if (request.getWeight() != null) {
            express.setWeight(request.getWeight());
        }
        if (request.getStatus() != null) {
            express.setStatus(request.getStatus());
        }
        if (request.getCourierId() != null) {
            express.setCourierId(request.getCourierId());
            express.setCourierName(request.getCourierName());
            express.setCourierPhone(request.getCourierPhone());
        }
        if (request.getUserId() != null) {
            express.setUserId(request.getUserId());
        }
        if (request.getCabinetId() != null) {
            express.setCabinetId(request.getCabinetId());
        }
        if (request.getCabinetCell() != null) {
            express.setCabinetCell(request.getCabinetCell());
        }
        if (request.getPickupCode() != null) {
            express.setPickupCode(request.getPickupCode());
        }
        if (request.getRemark() != null) {
            express.setRemark(request.getRemark());
        }

        Express updatedExpress = expressRepository.save(express);
        return convertToDTO(updatedExpress);
    }

    /**
     * 删除快递
     *
     * @param id 快递ID
     */
    @Override
    @Transactional
    public void deleteExpress(Long id) {
        if (!expressRepository.existsById(id)) {
            throw new ResourceNotFoundException("快递不存在,ID: " + id);
        }
        expressRepository.deleteById(id);
    }

    /**
     * 分页获取所有快递
     *
     * @param pageable 分页参数
     * @return 快递分页列表
     */
    @Override
    public Page<ExpressDTO> getAllExpress(Pageable pageable) {
        return expressRepository.findAll(pageable).map(this::convertToDTO);
    }

    /**
     * 根据用户ID获取快递列表
     *
     * @param userId 用户ID
     * @param pageable 分页参数
     * @return 快递分页列表
     */
    @Override
    public Page<ExpressDTO> getExpressByUserId(Long userId, Pageable pageable) {
        return expressRepository.findByUserId(userId, pageable).map(this::convertToDTO);
    }

    /**
     * 根据快递员ID获取快递列表
     *
     * @param courierId 快递员ID
     * @param pageable 分页参数
     * @return 快递分页列表
     */
    @Override
    public Page<ExpressDTO> getExpressByCourierId(Long courierId, Pageable pageable) {
        return expressRepository.findByCourierId(courierId, pageable).map(this::convertToDTO);
    }

    /**
     * 根据柜子ID获取快递列表
     *
     * @param cabinetId 柜子ID
     * @param pageable 分页参数
     * @return 快递分页列表
     */
    @Override
    public Page<ExpressDTO> getExpressByCabinetId(Long cabinetId, Pageable pageable) {
        return expressRepository.findByCabinetId(cabinetId, pageable).map(this::convertToDTO);
    }

    /**
     * 根据状态获取快递列表
     *
     * @param status 状态
     * @param pageable 分页参数
     * @return 快递分页列表
     */
    @Override
    public Page<ExpressDTO> getExpressByStatus(Integer status, Pageable pageable) {
        return expressRepository.findByStatus(status, pageable).map(this::convertToDTO);
    }

    /**
     * 根据收件人姓名或电话模糊查询快递列表
     *
     * @param keyword 关键字
     * @param pageable 分页参数
     * @return 快递分页列表
     */
    @Override
    public Page<ExpressDTO> searchExpressByRecipient(String keyword, Pageable pageable) {
        return expressRepository.findByRecipientNameOrPhoneContaining(keyword, pageable).map(this::convertToDTO);
    }

    /**
     * 分配快递员
     *
     * @param expressId 快递ID
     * @param courierId 快递员ID
     * @param courierName 快递员姓名
     * @param courierPhone 快递员电话
     * @return 更新后的快递DTO
     */
    @Override
    @Transactional
    public ExpressDTO assignCourier(Long expressId, Long courierId, String courierName, String courierPhone) {
        Express express = expressRepository.findById(expressId)
                .orElseThrow(() -> new ResourceNotFoundException("快递不存在,ID: " + expressId));

        express.setCourierId(courierId);
        express.setCourierName(courierName);
        express.setCourierPhone(courierPhone);

        // 创建跟踪记录
        expressTrackingService.createTracking(
                express.getId(),
                express.getTrackingNumber(),
                1, // 揽收
                "快递已分配给快递员: " + courierName,
                courierId,
                1, // 快递员
                courierName,
                null,
                null
        );

        Express updatedExpress = expressRepository.save(express);
        return convertToDTO(updatedExpress);
    }

    /**
     * 分配快递柜
     *
     * @param expressId 快递ID
     * @param cabinetId 柜子ID
     * @param cellNumber 格口编号
     * @return 更新后的快递DTO
     */
    @Override
    @Transactional
    public ExpressDTO assignCabinet(Long expressId, Long cabinetId, String cellNumber) {
        Express express = expressRepository.findById(expressId)
                .orElseThrow(() -> new ResourceNotFoundException("快递不存在,ID: " + expressId));

        express.setCabinetId(cabinetId);
        express.setCabinetCell(cellNumber);
        express.setPickupCode(generatePickupCode(expressId));
        express.setStatus(3); // 已送达
        express.setDeliveryTime(LocalDateTime.now());

        // 创建跟踪记录
        expressTrackingService.createTracking(
                express.getId(),
                express.getTrackingNumber(),
                3, // 送达
                "快递已存入柜子,取件码: " + express.getPickupCode(),
                express.getCourierId(),
                1, // 快递员
                express.getCourierName(),
                null,
                null
        );

        Express updatedExpress = expressRepository.save(express);
        return convertToDTO(updatedExpress);
    }

    /**
     * 更新快递状态
     *
     * @param expressId 快递ID
     * @param status 状态
     * @param operatorId 操作人ID
     * @param operatorType 操作人类型
     * @param operatorName 操作人姓名
     * @param description 操作描述
     * @return 更新后的快递DTO
     */
    @Override
    @Transactional
    public ExpressDTO updateExpressStatus(Long expressId, Integer status, Long operatorId, Integer operatorType, String operatorName, String description) {
        Express express = expressRepository.findById(expressId)
                .orElseThrow(() -> new ResourceNotFoundException("快递不存在,ID: " + expressId));

        express.setStatus(status);

        // 根据状态设置相应的时间
        if (status == 1) { // 已揽收
            express.setCollectionTime(LocalDateTime.now());
        } else if (status == 3) { // 已送达
            express.setDeliveryTime(LocalDateTime.now());
        } else if (status == 4) { // 已签收
            express.setSignTime(LocalDateTime.now());
        }

        // 创建跟踪记录
        expressTrackingService.createTracking(
                express.getId(),
                express.getTrackingNumber(),
                status,
                description,
                operatorId,
                operatorType,
                operatorName,
                null,
                null
        );

        Express updatedExpress = expressRepository.save(express);
        return convertToDTO(updatedExpress);
    }

    /**
     * 签收快递
     *
     * @param expressId 快递ID
     * @param userId 用户ID
     * @param userName 用户姓名
     * @return 更新后的快递DTO
     */
    @Override
    @Transactional
    public ExpressDTO signExpress(Long expressId, Long userId, String userName) {
        Express express = expressRepository.findById(expressId)
                .orElseThrow(() -> new ResourceNotFoundException("快递不存在,ID: " + expressId));

        express.setStatus(4); // 已签收
        express.setSignTime(LocalDateTime.now());

        // 创建跟踪记录
        expressTrackingService.createTracking(
                express.getId(),
                express.getTrackingNumber(),
                4, // 签收
                "快递已被签收",
                userId,
                2, // 用户
                userName,
                null,
                null
        );

        Express updatedExpress = expressRepository.save(express);
        return convertToDTO(updatedExpress);
    }

    /**
     * 统计用户的快递数量
     *
     * @param userId 用户ID
     * @return 快递数量
     */
    @Override
    public long countExpressByUserId(Long userId) {
        return expressRepository.countByUserId(userId);
    }

    /**
     * 统计快递员的快递数量
     *
     * @param courierId 快递员ID
     * @return 快递数量
     */
    @Override
    public long countExpressByCourierId(Long courierId) {
        return expressRepository.countByCourierId(courierId);
    }

    /**
     * 统计指定状态的快递数量
     *
     * @param status 状态
     * @return 快递数量
     */
    @Override
    public long countExpressByStatus(Integer status) {
        return expressRepository.countByStatus(status);
    }

    /**
     * 查询指定时间范围内创建的快递列表
     *
     * @param startTime 开始时间
     * @param endTime 结束时间
     * @param pageable 分页参数
     * @return 快递分页列表
     */
    @Override
    public Page<ExpressDTO> getExpressByCreatedTimeBetween(LocalDateTime startTime, LocalDateTime endTime, Pageable pageable) {
        return expressRepository.findByCreatedTimeBetween(startTime, endTime, pageable).map(this::convertToDTO);
    }

    /**
     * 生成取件码
     *
     * @param expressId 快递ID
     * @return 取件码
     */
    @Override
    public String generatePickupCode(Long expressId) {
        // 生成6位数字取件码
        StringBuilder code = new StringBuilder();
        for (int i = 0; i < 6; i++) {
            code.append(random.nextInt(10));
        }
        return code.toString();
    }

    /**
     * 验证取件码
     *
     * @param expressId 快递ID
     * @param pickupCode 取件码
     * @return 是否有效
     */
    @Override
    public boolean verifyPickupCode(Long expressId, String pickupCode) {
        Express express = expressRepository.findById(expressId)
                .orElseThrow(() -> new ResourceNotFoundException("快递不存在,ID: " + expressId));
        return express.getPickupCode() != null && express.getPickupCode().equals(pickupCode);
    }

    /**
     * 将Express实体转换为ExpressDTO
     *
     * @param express 快递实体
     * @return 快递DTO
     */
    private ExpressDTO convertToDTO(Express express) {
        return ExpressDTO.builder()
                .id(express.getId())
                .trackingNumber(express.getTrackingNumber())
                .company(express.getCompany())
                .recipientName(express.getRecipientName())
                .recipientPhone(express.getRecipientPhone())
                .recipientAddress(express.getRecipientAddress())
                .description(express.getDescription())
                .weight(express.getWeight())
                .status(express.getStatus())
                .statusDesc(getStatusDesc(express.getStatus()))
                .courierId(express.getCourierId())
                .courierName(express.getCourierName())
                .courierPhone(express.getCourierPhone())
                .userId(express.getUserId())
                .cabinetId(express.getCabinetId())
                .cabinetName(express.getCabinetName())
                .cabinetLocation(express.getCabinetLocation())
                .cabinetCell(express.getCabinetCell())
                .pickupCode(express.getPickupCode())
                .collectionTime(express.getCollectionTime())
                .deliveryTime(express.getDeliveryTime())
                .signTime(express.getSignTime())
                .remark(express.getRemark())
                .createdTime(express.getCreatedTime())
                .updatedTime(express.getUpdatedTime())
                .build();
    }

    /**
     * 获取状态描述
     *
     * @param status 状态码
     * @return 状态描述
     */
    private String getStatusDesc(Integer status) {
        if (status == null) {
            return null;
        }
        switch (status) {
            case 0:
                return "待揽收";
            case 1:
                return "已揽收";
            case 2:
                return "配送中";
            case 3:
                return "已送达";
            case 4:
                return "已签收";
            case 5:
                return "异常";
            default:
                return "未知状态";
        }
    }
}

express-service\src\main\java\com\campus\express\service\impl\ExpressTrackingServiceImpl.java

package com.campus.express.service.impl;

import com.campus.express.dto.ExpressTrackingDTO;
import com.campus.express.exception.ResourceNotFoundException;
import com.campus.express.model.ExpressTracking;
import com.campus.express.repository.ExpressTrackingRepository;
import com.campus.express.service.ExpressTrackingService;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 快递跟踪记录服务实现类
 */
@Service
@RequiredArgsConstructor
public class ExpressTrackingServiceImpl implements ExpressTrackingService {

    private final ExpressTrackingRepository expressTrackingRepository;

    /**
     * 创建跟踪记录
     *
     * @param expressId 快递ID
     * @param trackingNumber 快递单号
     * @param operationType 操作类型
     * @param description 描述
     * @param operatorId 操作人ID
     * @param operatorType 操作人类型
     * @param operatorName 操作人姓名
     * @param location 位置
     * @param remark 备注
     * @return 跟踪记录DTO
     */
    @Override
    @Transactional
    public ExpressTrackingDTO createTracking(Long expressId, String trackingNumber, Integer operationType, 
                                             String description, Long operatorId, Integer operatorType, 
                                             String operatorName, String location, String remark) {
        ExpressTracking tracking = ExpressTracking.builder()
                .expressId(expressId)
                .trackingNumber(trackingNumber)
                .operationType(operationType)
                .description(description)
                .operatorId(operatorId)
                .operatorType(operatorType)
                .operatorName(operatorName)
                .location(location)
                .remark(remark)
                .build();

        ExpressTracking savedTracking = expressTrackingRepository.save(tracking);
        return convertToDTO(savedTracking);
    }

    /**
     * 根据ID获取跟踪记录
     *
     * @param id 跟踪记录ID
     * @return 跟踪记录DTO
     */
    @Override
    public ExpressTrackingDTO getTrackingById(Long id) {
        ExpressTracking tracking = expressTrackingRepository.findById(id)
                .orElseThrow(() -> new ResourceNotFoundException("跟踪记录不存在,ID: " + id));
        return convertToDTO(tracking);
    }

    /**
     * 根据快递ID获取跟踪记录列表
     *
     * @param expressId 快递ID
     * @return 跟踪记录列表
     */
    @Override
    public List<ExpressTrackingDTO> getTrackingsByExpressId(Long expressId) {
        return expressTrackingRepository.findByExpressIdOrderByCreatedTimeDesc(expressId).stream()
                .map(this::convertToDTO)
                .collect(Collectors.toList());
    }

    /**
     * 根据快递单号获取跟踪记录列表
     *
     * @param trackingNumber 快递单号
     * @return 跟踪记录列表
     */
    @Override
    public List<ExpressTrackingDTO> getTrackingsByTrackingNumber(String trackingNumber) {
        return expressTrackingRepository.findByTrackingNumberOrderByCreatedTimeDesc(trackingNumber).stream()
                .map(this::convertToDTO)
                .collect(Collectors.toList());
    }

    /**
     * 根据操作类型获取跟踪记录列表
     *
     * @param operationType 操作类型
     * @param pageable 分页参数
     * @return 跟踪记录分页列表
     */
    @Override
    public Page<ExpressTrackingDTO> getTrackingsByOperationType(Integer operationType, Pageable pageable) {
        return expressTrackingRepository.findByOperationType(operationType, pageable).map(this::convertToDTO);
    }

    /**
     * 根据操作人ID获取跟踪记录列表
     *
     * @param operatorId 操作人ID
     * @param pageable 分页参数
     * @return 跟踪记录分页列表
     */
    @Override
    public Page<ExpressTrackingDTO> getTrackingsByOperatorId(Long operatorId, Pageable pageable) {
        return expressTrackingRepository.findByOperatorId(operatorId, pageable).map(this::convertToDTO);
    }

    /**
     * 根据操作人类型获取跟踪记录列表
     *
     * @param operatorType 操作人类型
     * @param pageable 分页参数
     * @return 跟踪记录分页列表
     */
    @Override
    public Page<ExpressTrackingDTO> getTrackingsByOperatorType(Integer operatorType, Pageable pageable) {
        return expressTrackingRepository.findByOperatorType(operatorType, pageable).map(this::convertToDTO);
    }

    /**
     * 根据创建时间范围获取跟踪记录列表
     *
     * @param startTime 开始时间
     * @param endTime 结束时间
     * @param pageable 分页参数
     * @return 跟踪记录分页列表
     */
    @Override
    public Page<ExpressTrackingDTO> getTrackingsByCreatedTimeBetween(LocalDateTime startTime, LocalDateTime endTime, Pageable pageable) {
        return expressTrackingRepository.findByCreatedTimeBetween(startTime, endTime, pageable).map(this::convertToDTO);
    }

    /**
     * 统计指定快递的跟踪记录数量
     *
     * @param expressId 快递ID
     * @return 跟踪记录数量
     */
    @Override
    public long countTrackingsByExpressId(Long expressId) {
        return expressTrackingRepository.countByExpressId(expressId);
    }

    /**
     * 统计指定操作类型的跟踪记录数量
     *
     * @param operationType 操作类型
     * @return 跟踪记录数量
     */
    @Override
    public long countTrackingsByOperationType(Integer operationType) {
        return expressTrackingRepository.countByOperationType(operationType);
    }

    /**
     * 统计指定操作人的跟踪记录数量
     *
     * @param operatorId 操作人ID
     * @return 跟踪记录数量
     */
    @Override
    public long countTrackingsByOperatorId(Long operatorId) {
        return expressTrackingRepository.countByOperatorId(operatorId);
    }

    /**
     * 统计指定时间范围内的跟踪记录数量
     *
     * @param startTime 开始时间
     * @param endTime 结束时间
     * @return 跟踪记录数量
     */
    @Override
    public long countTrackingsByCreatedTimeBetween(LocalDateTime startTime, LocalDateTime endTime) {
        return expressTrackingRepository.countByCreatedTimeBetween(startTime, endTime);
    }

    /**
     * 删除跟踪记录
     *
     * @param id 跟踪记录ID
     */
    @Override
    @Transactional
    public void deleteTracking(Long id) {
        if (!expressTrackingRepository.existsById(id)) {
            throw new ResourceNotFoundException("跟踪记录不存在,ID: " + id);
        }
        expressTrackingRepository.deleteById(id);
    }

    /**
     * 将ExpressTracking实体转换为ExpressTrackingDTO
     *
     * @param tracking 跟踪记录实体
     * @return 跟踪记录DTO
     */
    private ExpressTrackingDTO convertToDTO(ExpressTracking tracking) {
        return ExpressTrackingDTO.builder()
                .id(tracking.getId())
                .expressId(tracking.getExpressId())
                .trackingNumber(tracking.getTrackingNumber())
                .operationType(tracking.getOperationType())
                .operationTypeDesc(getOperationTypeDesc(tracking.getOperationType()))
                .description(tracking.getDescription())
                .operatorId(tracking.getOperatorId())
                .operatorType(tracking.getOperatorType())
                .operatorTypeDesc(getOperatorTypeDesc(tracking.getOperatorType()))
                .operatorName(tracking.getOperatorName())
                .location(tracking.getLocation())
                .remark(tracking.getRemark())
                .createdTime(tracking.getCreatedTime())
                .build();
    }

    /**
     * 获取操作类型描述
     *
     * @param operationType 操作类型码
     * @return 操作类型描述
     */
    private String getOperationTypeDesc(Integer operationType) {
        if (operationType == null) {
            return null;
        }
        switch (operationType) {
            case 0:
                return "创建";
            case 1:
                return "揽收";
            case 2:
                return "运输";
            case 3:
                return "送达";
            case 4:
                return "签收";
            case 5:
                return "异常";
            default:
                return "未知操作";
        }
    }

    /**
     * 获取操作人类型描述
     *
     * @param operatorType 操作人类型码
     * @return 操作人类型描述
     */
    private String getOperatorTypeDesc(Integer operatorType) {
        if (operatorType == null) {
            return null;
        }
        switch (operatorType) {
            case 0:
                return "系统";
            case 1:
                return "快递员";
            case 2:
                return "用户";
            case 3:
                return "管理员";
            default:
                return "未知类型";
        }
    }
}

express-service\src\main\java\com\campus\express\service\impl\InAppNotificationSender.java

package com.campus.express.service.impl;

import com.campus.express.model.Notification;
import com.campus.express.service.NotificationSender;
import com.campus.express.util.NotificationUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

/**
 * 站内信通知发送器
 */
@Service
@Slf4j
public class InAppNotificationSender implements NotificationSender {

    /**
     * 发送站内信通知
     *
     * @param notification 通知对象
     * @return 是否发送成功
     */
    @Override
    public boolean send(Notification notification) {
        if (notification == null) {
            log.error("Notification is null");
            return false;
        }

        if (notification.getChannel() != getSupportedChannel()) {
            log.error("Unsupported notification channel: {}", notification.getChannel());
            return false;
        }

        try {
            log.info("Sending in-app notification: {} to receiver: {}", notification.getTitle(), notification.getReceiverId());
            
            // 实际项目中,这里应该将通知保存到数据库,并通过WebSocket等方式推送给在线用户
            // 这里只是模拟实现,直接返回成功
            
            log.info("In-app notification sent successfully: {}", notification.getId());
            return true;
        } catch (Exception e) {
            log.error("Failed to send in-app notification: {}", e.getMessage(), e);
            return false;
        }
    }

    /**
     * 获取支持的通知渠道
     *
     * @return 通知渠道
     */
    @Override
    public int getSupportedChannel() {
        return NotificationUtils.NotificationChannel.IN_APP;
    }
}

express-service\src\main\java\com\campus\express\service\impl\NotificationServiceImpl.java

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

天天进步2015

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值