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

引言

随着电子商务的快速发展,校园内的快递业务量呈现爆发式增长。传统的快递管理模式已经难以满足高校师生的需求,各种问题层出不穷:快递积压、取件难、配送慢、信息不透明等。本文将详细介绍一个基于Java全栈技术的校园快递管理与配送系统的设计与实现,旨在提供一个高效、便捷、智能的校园快递解决方案。

系统概述

校园快递管理与配送系统是一个面向高校的综合性快递服务平台,主要服务对象包括学生、教职工、快递员和系统管理员。系统采用Java全栈技术栈开发,包括前端、后端、数据库和移动端应用,实现了快递信息管理、智能派件、路径规划、用户互动等功能。

技术栈选择

前端技术

  • Vue.js 3:采用组合式API,提供响应式UI界面
  • Element Plus:基于Vue 3的组件库,提供丰富的UI组件
  • Axios:处理HTTP请求
  • ECharts:数据可视化图表
  • 高德地图API:实现校园地图和路径规划

后端技术

  • Spring Boot 2.7:简化Spring应用开发
  • Spring Security:认证和授权框架
  • Spring Data JPA:简化数据库访问
  • Spring Cloud:微服务架构支持
  • JWT:无状态身份验证

数据库

  • MySQL 8.0:关系型数据库
  • Redis:缓存和会话管理
  • MongoDB:存储非结构化数据,如用户反馈

移动端

  • Android原生:Java开发
  • iOS原生:Swift开发
  • Flutter:跨平台解决方案

DevOps

  • Git:版本控制
  • Jenkins:CI/CD
  • Docker:容器化部署
  • Kubernetes:容器编排

系统架构

系统采用前后端分离的微服务架构,主要分为以下几个部分:

  1. 用户服务:负责用户注册、登录、认证和授权
  2. 快递服务:管理快递信息,包括录入、查询、更新和删除
  3. 配送服务:负责快递派送、路径规划和配送状态跟踪
  4. 通知服务:处理系统消息推送,如短信、邮件和应用内通知
  5. 数据分析服务:收集和分析系统数据,生成报表和可视化图表
  6. API网关:统一接口管理,处理跨域请求和负载均衡

数据库设计

系统数据库主要包含以下核心表:

  1. 用户表(user):存储用户基本信息
  2. 快递表(express):存储快递基本信息
  3. 快递柜表(locker):存储快递柜信息
  4. 配送任务表(delivery_task):存储配送任务信息
  5. 配送路径表(delivery_route):存储配送路径信息
  6. 通知表(notification):存储系统通知信息
  7. 反馈表(feedback):存储用户反馈信息

用户表(user)设计

CREATE TABLE `user` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `username` varchar(50) NOT NULL COMMENT '用户名',
  `password` varchar(100) NOT NULL COMMENT '密码',
  `real_name` varchar(50) DEFAULT NULL COMMENT '真实姓名',
  `phone` varchar(20) NOT NULL COMMENT '手机号',
  `email` varchar(100) DEFAULT NULL COMMENT '邮箱',
  `user_type` tinyint NOT NULL COMMENT '用户类型:1-学生,2-教职工,3-快递员,4-管理员',
  `student_id` varchar(20) DEFAULT NULL COMMENT '学号',
  `department` varchar(50) DEFAULT NULL COMMENT '院系/部门',
  `dormitory` varchar(50) DEFAULT NULL COMMENT '宿舍号',
  `avatar` varchar(255) DEFAULT NULL COMMENT '头像URL',
  `status` tinyint NOT NULL DEFAULT '1' COMMENT '状态:0-禁用,1-启用',
  `created_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `updated_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_username` (`username`),
  UNIQUE KEY `uk_phone` (`phone`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表';

快递表(express)设计

CREATE TABLE `express` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `express_code` varchar(50) NOT NULL COMMENT '快递单号',
  `express_company` varchar(50) NOT NULL COMMENT '快递公司',
  `sender_name` varchar(50) NOT NULL COMMENT '寄件人姓名',
  `sender_phone` varchar(20) NOT NULL COMMENT '寄件人电话',
  `receiver_id` bigint NOT NULL COMMENT '收件人ID',
  `receiver_name` varchar(50) NOT NULL COMMENT '收件人姓名',
  `receiver_phone` varchar(20) NOT NULL COMMENT '收件人电话',
  `receiver_address` varchar(255) NOT NULL COMMENT '收件地址',
  `express_type` tinyint NOT NULL COMMENT '快递类型:1-小件,2-中件,3-大件',
  `express_status` tinyint NOT NULL COMMENT '快递状态:1-待入库,2-已入库,3-待配送,4-配送中,5-已送达,6-已取件,7-异常',
  `locker_id` bigint DEFAULT NULL COMMENT '快递柜ID',
  `locker_cell_no` varchar(20) DEFAULT NULL COMMENT '快递柜格口号',
  `arrival_time` datetime DEFAULT NULL COMMENT '到达时间',
  `delivery_time` datetime DEFAULT NULL COMMENT '送达时间',
  `pickup_time` datetime DEFAULT NULL COMMENT '取件时间',
  `remark` varchar(255) DEFAULT NULL COMMENT '备注',
  `created_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `updated_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_express_code` (`express_code`),
  KEY `idx_receiver_id` (`receiver_id`),
  KEY `idx_express_status` (`express_status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='快递表';

核心功能实现

1. 用户认证与授权

采用Spring Security + JWT实现无状态认证,代码示例:

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserDetailsServiceImpl userDetailsService;
    
    @Autowired
    private JwtAuthenticationEntryPoint unauthorizedHandler;

    @Bean
    public JwtAuthenticationFilter jwtAuthenticationFilter() {
        return new JwtAuthenticationFilter();
    }

    @Override
    public void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService)
            .passwordEncoder(passwordEncoder());
    }

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.cors().and().csrf().disable()
            .exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and()
            .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
            .authorizeRequests()
            .antMatchers("/api/auth/**").permitAll()
            .antMatchers("/api/public/**").permitAll()
            .anyRequest().authenticated();

        http.addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
    }
}

2. 快递信息管理

快递服务实现了快递信息的CRUD操作,包括录入、查询、更新和删除功能。

@Service
public class ExpressServiceImpl implements ExpressService {

    @Autowired
    private ExpressRepository expressRepository;
    
    @Autowired
    private UserRepository userRepository;
    
    @Autowired
    private NotificationService notificationService;
    
    @Override
    public ExpressDTO createExpress(ExpressCreateRequest request) {
        // 验证收件人是否存在
        User receiver = userRepository.findById(request.getReceiverId())
                .orElseThrow(() -> new ResourceNotFoundException("User", "id", request.getReceiverId()));
        
        // 创建快递实体
        Express express = new Express();
        express.setExpressCode(request.getExpressCode());
        express.setExpressCompany(request.getExpressCompany());
        express.setSenderName(request.getSenderName());
        express.setSenderPhone(request.getSenderPhone());
        express.setReceiverId(receiver.getId());
        express.setReceiverName(receiver.getRealName());
        express.setReceiverPhone(receiver.getPhone());
        express.setReceiverAddress(request.getReceiverAddress());
        express.setExpressType(request.getExpressType());
        express.setExpressStatus(ExpressStatus.WAITING_STORAGE.getCode());
        express.setRemark(request.getRemark());
        
        // 保存快递信息
        Express savedExpress = expressRepository.save(express);
        
        // 发送通知给收件人
        notificationService.sendExpressArrivalNotification(savedExpress);
        
        return ExpressMapper.INSTANCE.toDTO(savedExpress);
    }
    
    @Override
    public Page<ExpressDTO> getExpressList(ExpressQueryRequest request, Pageable pageable) {
        Specification<Express> spec = (root, query, criteriaBuilder) -> {
            List<Predicate> predicates = new ArrayList<>();
            
            if (StringUtils.hasText(request.getExpressCode())) {
                predicates.add(criteriaBuilder.like(root.get("expressCode"), "%" + request.getExpressCode() + "%"));
            }
            
            if (StringUtils.hasText(request.getExpressCompany())) {
                predicates.add(criteriaBuilder.equal(root.get("expressCompany"), request.getExpressCompany()));
            }
            
            if (request.getReceiverId() != null) {
                predicates.add(criteriaBuilder.equal(root.get("receiverId"), request.getReceiverId()));
            }
            
            if (request.getExpressStatus() != null) {
                predicates.add(criteriaBuilder.equal(root.get("expressStatus"), request.getExpressStatus()));
            }
            
            return criteriaBuilder.and(predicates.toArray(new Predicate[0]));
        };
        
        Page<Express> expressPage = expressRepository.findAll(spec, pageable);
        return expressPage.map(ExpressMapper.INSTANCE::toDTO);
    }
    
    // 其他方法实现...
}

3. 智能配送系统

配送服务实现了智能派件、路径规划和配送状态跟踪功能。

@Service
public class DeliveryServiceImpl implements DeliveryService {

    @Autowired
    private DeliveryTaskRepository deliveryTaskRepository;
    
    @Autowired
    private ExpressRepository expressRepository;
    
    @Autowired
    private UserRepository userRepository;
    
    @Autowired
    private RouteOptimizationService routeOptimizationService;
    
    @Override
    @Transactional
    public DeliveryTaskDTO createDeliveryTask(DeliveryTaskCreateRequest request) {
        // 验证快递员是否存在
        User courier = userRepository.findById(request.getCourierId())
                .orElseThrow(() -> new ResourceNotFoundException("User", "id", request.getCourierId()));
        
        // 验证用户类型是否为快递员
        if (courier.getUserType() != UserType.COURIER.getCode()) {
            throw new BadRequestException("User is not a courier");
        }
        
        // 创建配送任务
        DeliveryTask task = new DeliveryTask();
        task.setCourierId(courier.getId());
        task.setTaskDate(LocalDate.now());
        task.setTaskStatus(DeliveryTaskStatus.CREATED.getCode());
        
        // 保存配送任务
        DeliveryTask savedTask = deliveryTaskRepository.save(task);
        
        // 分配快递到配送任务
        List<Express> expressList = expressRepository.findAllById(request.getExpressIds());
        for (Express express : expressList) {
            express.setExpressStatus(ExpressStatus.DELIVERING.getCode());
            express.setDeliveryTaskId(savedTask.getId());
        }
        expressRepository.saveAll(expressList);
        
        // 生成最优配送路径
        List<DeliveryPoint> deliveryPoints = expressList.stream()
                .map(this::convertToDeliveryPoint)
                .collect(Collectors.toList());
        
        List<DeliveryPoint> optimizedRoute = routeOptimizationService.optimizeRoute(deliveryPoints);
        
        // 保存配送路径
        saveDeliveryRoute(savedTask.getId(), optimizedRoute);
        
        return DeliveryTaskMapper.INSTANCE.toDTO(savedTask);
    }
    
    // 其他方法实现...
}

4. 移动端应用

为了方便用户使用,系统提供了移动端应用,包括Android和iOS版本。以下是Android版本的部分代码示例:

public class ExpressListActivity extends AppCompatActivity {

    private RecyclerView recyclerView;
    private ExpressAdapter adapter;
    private SwipeRefreshLayout swipeRefreshLayout;
    private ExpressViewModel viewModel;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_express_list);
        
        // 初始化视图
        recyclerView = findViewById(R.id.recycler_view);
        swipeRefreshLayout = findViewById(R.id.swipe_refresh_layout);
        
        // 设置布局管理器
        recyclerView.setLayoutManager(new LinearLayoutManager(this));
        
        // 初始化适配器
        adapter = new ExpressAdapter(new ArrayList<>());
        recyclerView.setAdapter(adapter);
        
        // 初始化ViewModel
        viewModel = new ViewModelProvider(this).get(ExpressViewModel.class);
        
        // 观察数据变化
        viewModel.getExpressList().observe(this, expressItems -> {
            adapter.updateData(expressItems);
            swipeRefreshLayout.setRefreshing(false);
        });
        
        // 设置下拉刷新监听器
        swipeRefreshLayout.setOnRefreshListener(() -> {
            viewModel.refreshExpressList();
        });
        
        // 加载数据
        viewModel.loadExpressList();
    }
}

系统部署

系统采用Docker容器化部署,使用Kubernetes进行容器编排,实现高可用和弹性伸缩。

# docker-compose.yml
version: '3'
services:
  mysql:
    image: mysql:8.0
    ports:
      - "3306:3306"
    environment:
      - MYSQL_ROOT_PASSWORD=root
      - MYSQL_DATABASE=campus_express
    volumes:
      - mysql-data:/var/lib/mysql
    networks:
      - app-network

  redis:
    image: redis:6.2
    ports:
      - "6379:6379"
    volumes:
      - redis-data:/data
    networks:
      - app-network

  api-gateway:
    image: campus-express/api-gateway:latest
    ports:
      - "8080:8080"
    depends_on:
      - user-service
      - express-service
      - delivery-service
      - notification-service
    networks:
      - app-network

  user-service:
    image: campus-express/user-service:latest
    depends_on:
      - mysql
      - redis
    networks:
      - app-network

  express-service:
    image: campus-express/express-service:latest
    depends_on:
      - mysql
    networks:
      - app-network

  delivery-service:
    image: campus-express/delivery-service:latest
    depends_on:
      - mysql
    networks:
      - app-network

  notification-service:
    image: campus-express/notification-service:latest
    depends_on:
      - mysql
      - redis
    networks:
      - app-network

networks:
  app-network:
    driver: bridge

volumes:
  mysql-data:
  redis-data:

系统性能优化

为了提高系统性能,采取了以下优化措施:

  1. 数据库优化

    • 合理设计索引
    • 使用读写分离
    • 实现分库分表
    • 优化SQL语句
  2. 缓存优化

    • 使用Redis缓存热点数据
    • 实现多级缓存策略
    • 合理设置缓存过期时间
  3. JVM优化

    • 调整堆内存大小
    • 选择合适的垃圾收集器
    • 优化JVM参数
  4. 前端优化

    • 使用懒加载
    • 压缩静态资源
    • 使用CDN加速
    • 实现前端缓存

系统安全性

系统安全性是校园快递管理系统的重要考虑因素,主要采取了以下安全措施:

  1. 认证与授权

    • 基于JWT的无状态认证
    • 细粒度的权限控制
    • 防止CSRF攻击
  2. 数据安全

    • 敏感数据加密存储
    • 传输数据HTTPS加密
    • 防止SQL注入攻击
  3. 接口安全

    • 接口限流
    • 防止重放攻击
    • 参数验证
  4. 日志审计

    • 记录用户操作日志
    • 系统异常监控
    • 安全事件告警

系统测试

系统测试采用多层次测试策略,包括单元测试、集成测试、性能测试和安全测试。

@SpringBootTest
public class ExpressServiceTest {

    @Autowired
    private ExpressService expressService;
    
    @MockBean
    private ExpressRepository expressRepository;
    
    @MockBean
    private UserRepository userRepository;
    
    @MockBean
    private NotificationService notificationService;
    
    @Test
    public void testCreateExpress() {
        // 准备测试数据
        User receiver = new User();
        receiver.setId(1L);
        receiver.setRealName("张三");
        receiver.setPhone("13800138000");
        
        Express express = new Express();
        express.setId(1L);
        express.setExpressCode("SF1234567890");
        express.setExpressCompany("顺丰速运");
        express.setReceiverId(1L);
        express.setReceiverName("张三");
        express.setReceiverPhone("13800138000");
        
        ExpressCreateRequest request = new ExpressCreateRequest();
        request.setExpressCode("SF1234567890");
        request.setExpressCompany("顺丰速运");
        request.setSenderName("李四");
        request.setSenderPhone("13900139000");
        request.setReceiverId(1L);
        request.setReceiverAddress("第一宿舍楼101");
        request.setExpressType(1);
        
        // 设置Mock行为
        when(userRepository.findById(1L)).thenReturn(Optional.of(receiver));
        when(expressRepository.save(any(Express.class))).thenReturn(express);
        
        // 执行测试
        ExpressDTO result = expressService.createExpress(request);
        
        // 验证结果
        assertNotNull(result);
        assertEquals("SF1234567890", result.getExpressCode());
        assertEquals("顺丰速运", result.getExpressCompany());
        assertEquals(1L, result.getReceiverId().longValue());
        
        // 验证交互
        verify(userRepository).findById(1L);
        verify(expressRepository).save(any(Express.class));
        verify(notificationService).sendExpressArrivalNotification(any(Express.class));
    }
}

未来展望

校园快递管理与配送系统还有很大的发展空间,未来可以考虑以下几个方向:

  1. 引入人工智能

    • 智能客服
    • 配送路径优化
    • 需求预测
  2. 物联网集成

    • 智能快递柜
    • 无人配送车
    • 实时定位追踪
  3. 大数据分析

    • 用户行为分析
    • 配送效率优化
    • 智能决策支持
  4. 生态系统扩展

    • 校园电商集成
    • 社交功能
    • 校园服务平台

总结

本文详细介绍了基于Java全栈技术的校园快递管理与配送系统的设计与实现。该系统采用前后端分离的微服务架构,实现了快递信息管理、智能派件、路径规划、用户互动等功能,为高校师生提供了高效、便捷、智能的校园快递解决方案。

通过该系统的实施,可以有效解决校园快递积压、取件难、配送慢、信息不透明等问题,提高校园快递服务质量,改善师生用户体验。

源代码

Directory Content Summary

Source Directory: ./campus-express-system

Directory Structure

campus-express-system/
  express-service/
    pom.xml
    src/
      main/
        java/
          com/
            campus/
              express/
                ExpressServiceApplication.java
                controller/
                  DeliveryAreaController.java
                  DeliveryController.java
                  DeliveryRouteController.java
                  NotificationController.java
                  NotificationTemplateController.java
                dto/
                  ApiResponse.java
                  CabinetCellDTO.java
                  CabinetCreateRequest.java
                  CabinetDTO.java
                  CabinetUpdateRequest.java
                  DeliveryAreaCreateRequest.java
                  DeliveryAreaDTO.java
                  DeliveryAreaUpdateRequest.java
                  DeliveryCreateRequest.java
                  DeliveryDTO.java
                  DeliveryRouteCreateRequest.java
                  DeliveryRouteDTO.java
                  DeliveryRouteUpdateRequest.java
                  DeliveryUpdateRequest.java
                  ExpressCreateRequest.java
                  ExpressDTO.java
                  ExpressTrackingDTO.java
                  ExpressUpdateRequest.java
                  NotificationCreateRequest.java
                  NotificationDTO.java
                  NotificationSendRequest.java
                  NotificationTemplateCreateRequest.java
                  NotificationTemplateDTO.java
                  NotificationTemplateUpdateRequest.java
                  NotificationUpdateRequest.java
                exception/
                  BusinessException.java
                  ErrorResponse.java
                  GlobalExceptionHandler.java
                  ResourceNotFoundException.java
                model/
                  Cabinet.java
                  CabinetCell.java
                  Delivery.java
                  DeliveryArea.java
                  DeliveryRoute.java
                  Express.java
                  ExpressTracking.java
                  Notification.java
                  NotificationTemplate.java
                repository/
                  CabinetCellRepository.java
                  CabinetRepository.java
                  DeliveryAreaRepository.java
                  DeliveryRepository.java
                  DeliveryRouteRepository.java
                  ExpressRepository.java
                  ExpressTrackingRepository.java
                  NotificationRepository.java
                  NotificationTemplateRepository.java
                service/
                  CabinetCellService.java
                  CabinetService.java
                  DeliveryAreaService.java
                  DeliveryRouteService.java
                  DeliveryService.java
                  ExpressService.java
                  ExpressTrackingService.java
                  NotificationSender.java
                  NotificationService.java
                  NotificationTemplateService.java
                  impl/
                    CabinetCellServiceImpl.java
                    CabinetServiceImpl.java
                    DeliveryAreaServiceImpl.java
                    DeliveryRouteServiceImpl.java
                    DeliveryServiceImpl.java
                    ExpressServiceImpl.java
                    ExpressTrackingServiceImpl.java
                    InAppNotificationSender.java
                    NotificationServiceImpl.java
                    NotificationTemplateServiceImpl.java
                util/
                  NotificationUtils.java
        resources/
          application.yml
          db/
            data.sql
            schema.sql
  express-ui/
    package.json
    public/
      static/
    src/
      App.vue
      main.js
      api/
        notification.js
      assets/
      components/
        Notification/
          index.vue
          NotificationList.vue
          NotificationManager.js
      layout/
        index.vue
        components/
          AppMain.vue
          Navbar.vue
          Sidebar/
            index.vue
            Item.vue
            Link.vue
            Logo.vue
            SidebarItem.vue
          TagsView/
            index.vue
            ScrollPane.vue
      plugins/
        notification.js
      router/
        index.js
      store/
      utils/
        notification.js
      views/
        delivery/
        express/
        login/
        notification/
          dashboard.vue
          detail.vue
          history.vue
          list.vue
          my-notifications.vue
          send.vue
          settings.vue
          statistics.vue
          template-editor.vue
          template.vue
        system/
  user-service/
    pom.xml
    src/
      main/
        java/
          com/
            campus/
              express/
                user/
                  UserServiceApplication.java
                  config/
                    CorsConfig.java
                    DataInitializer.java
                    JwtAuthenticationEntryPoint.java
                    JwtAuthenticationFilter.java
                    SecurityConfig.java
                  controller/
                    AuthController.java
                    RoleController.java
                    UserController.java
                  dto/
                    ApiResponse.java
                    JwtResponse.java
                    LoginRequest.java
                    PasswordChangeRequest.java
                    SignupRequest.java
                    UserDTO.java
                  exception/
                    BadRequestException.java
                    GlobalExceptionHandler.java
                    ResourceNotFoundException.java
                  model/
                    Role.java
                    User.java
                  repository/
                    RoleRepository.java
                    UserRepository.java
                  service/
                    SecurityService.java
                    UserDetailsServiceImpl.java
                    UserService.java
                    UserServiceImpl.java
                  util/
                    JwtUtils.java
        resources/
          application.yml
    target/
      classes/
        application.yml
        com/
          campus/
            express/
              user/
                config/
                controller/
                dto/
                exception/
                model/
                repository/
                service/
                util/
      generated-sources/
        annotations/
      generated-test-sources/
        test-annotations/
      test-classes/

File Contents

express-service\pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.8</version>
        <relativePath/>
    </parent>
    
    <groupId>com.campus.express</groupId>
    <artifactId>express-service</artifactId>
    <version>1.0.0</version>
    <name>express-service</name>
    <description>Express Service for Campus Express Management System</description>
    
    <properties>
        <java.version>11</java.version>
        <spring-cloud.version>2021.0.5</spring-cloud.version>
        <jjwt.version>0.11.5</jjwt.version>
    </properties>
    
    <dependencies>
        <!-- Spring Boot -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-validation</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        
        <!-- Spring Cloud -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        
        <!-- Database -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.28</version>
        </dependency>
        
        <!-- JWT -->
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt-api</artifactId>
            <version>${jjwt.version}</version>
        </dependency>
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt-impl</artifactId>
            <version>${jjwt.version}</version>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt-jackson</artifactId>
            <version>${jjwt.version}</version>
            <scope>runtime</scope>
        </dependency>
        
        <!-- Lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        
        <!-- Test -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
    
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
    
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

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

package com.campus.express;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.openfeign.EnableFeignClients;

/**
 * 快递服务应用程序入口
 */
@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients
public class ExpressServiceApplication {

    public static void main(String[] args) {
        SpringApplication.run(ExpressServiceApplication.class, args);
    }
}

express-service\src\main\java\com\campus\express\controller\DeliveryAreaController.java

package com.campus.express.controller;

import com.campus.express.dto.DeliveryAreaCreateRequest;
import com.campus.express.dto.DeliveryAreaDTO;
import com.campus.express.dto.DeliveryAreaUpdateRequest;
import com.campus.express.service.DeliveryAreaService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import javax.validation.Valid;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 配送区域控制器
 */
@RestController
@RequestMapping("/api/delivery-areas")
@RequiredArgsConstructor
@Slf4j
public class DeliveryAreaController {

    private final DeliveryAreaService deliveryAreaService;

    /**
     * 创建配送区域
     *
     * @param request 创建配送区域请求
     * @return 配送区域DTO
     */
    @PostMapping
    public ResponseEntity<DeliveryAreaDTO> createDeliveryArea(@Valid @RequestBody DeliveryAreaCreateRequest request) {
        log.info("REST request to create delivery area: {}", request);
        DeliveryAreaDTO result = deliveryAreaService.createDeliveryArea(request);
        return ResponseEntity.status(HttpStatus.CREATED).body(result);
    }

    /**
     * 根据ID查询配送区域
     *
     * @param id 配送区域ID
     * @return 配送区域DTO
     */
    @GetMapping("/{id}")
    public ResponseEntity<DeliveryAreaDTO> getDeliveryAreaById(@PathVariable Long id) {
        log.info("REST request to get delivery area by ID: {}", id);
        DeliveryAreaDTO result = deliveryAreaService.getDeliveryAreaById(id);
        return ResponseEntity.ok(result);
    }

    /**
     * 根据区域代码查询配送区域
     *
     * @param areaCode 区域代码
     * @return 配送区域DTO
     */
    @GetMapping("/area-code/{areaCode}")
    public ResponseEntity<DeliveryAreaDTO> getDeliveryAreaByAreaCode(@PathVariable String areaCode) {
        log.info("REST request to get delivery area by area code: {}", areaCode);
        DeliveryAreaDTO result = deliveryAreaService.getDeliveryAreaByAreaCode(areaCode);
        return ResponseEntity.ok(result);
    }

    /**
     * 更新配送区域
     *
     * @param id      配送区域ID
     * @param request 更新配送区域请求
     * @return 更新后的配送区域DTO
     */
    @PutMapping("/{id}")
    public ResponseEntity<DeliveryAreaDTO> updateDeliveryArea(
            @PathVariable Long id,
            @Valid @RequestBody DeliveryAreaUpdateRequest request) {
        log.info("REST request to update delivery area with ID: {}, request: {}", id, request);
        DeliveryAreaDTO result = deliveryAreaService.updateDeliveryArea(id, request);
        return ResponseEntity.ok(result);
    }

    /**
     * 删除配送区域
     *
     * @param id 配送区域ID
     * @return 无内容响应
     */
    @DeleteMapping("/{id}")
    public ResponseEntity<Void> deleteDeliveryArea(@PathVariable Long id) {
        log.info("REST request to delete delivery area with ID: {}", id);
        deliveryAreaService.deleteDeliveryArea(id);
        return ResponseEntity.noContent().build();
    }

    /**
     * 分页查询配送区域列表
     *
     * @param pageable 分页参数
     * @return 配送区域DTO分页列表
     */
    @GetMapping
    public ResponseEntity<Page<DeliveryAreaDTO>> getDeliveryAreas(Pageable pageable) {
        log.info("REST request to get delivery areas with pagination: {}", pageable);
        Page<DeliveryAreaDTO> result = deliveryAreaService.getDeliveryAreas(pageable);
        return ResponseEntity.ok(result);
    }

    /**
     * 根据名称关键字分页查询配送区域列表
     *
     * @param name     名称关键字
     * @param pageable 分页参数
     * @return 配送区域DTO分页列表
     */
    @GetMapping("/search")
    public ResponseEntity<Page<DeliveryAreaDTO>> getDeliveryAreasByName(
            @RequestParam String name,
            Pageable pageable) {
        log.info("REST request to get delivery areas by name: {} with pagination: {}", name, pageable);
        Page<DeliveryAreaDTO> result = deliveryAreaService.getDeliveryAreasByName(name, pageable);
        return ResponseEntity.ok(result);
    }

    /**
     * 根据状态分页查询配送区域列表
     *
     * @param status   状态
     * @param pageable 分页参数
     * @return 配送区域DTO分页列表
     */
    @GetMapping("/status/{status}")
    public ResponseEntity<Page<DeliveryAreaDTO>> getDeliveryAreasByStatus(
            @PathVariable Integer status,
            Pageable pageable) {
        log.info("REST request to get delivery areas by status: {} with pagination: {}", status, pageable);
        Page<DeliveryAreaDTO> result = deliveryAreaService.getDeliveryAreasByStatus(status, pageable);
        return ResponseEntity.ok(result);
    }

    /**
     * 根据级别查询配送区域列表
     *
     * @param level 级别
     * @return 配送区域DTO列表
     */
    @GetMapping("/level/{level}")
    public ResponseEntity<List<DeliveryAreaDTO>> getDeliveryAreasByLevel(@PathVariable Integer level) {
        log.info("REST request to get delivery areas by level: {}", level);
        List<DeliveryAreaDTO> result = deliveryAreaService.getDeliveryAreasByLevel(level);
        return ResponseEntity.ok(result);
    }

    /**
     * 根据父区域ID查询子区域列表
     *
     * @param parentId 父区域ID
     * @return 配送区域DTO列表
     */
    @GetMapping("/parent/{parentId}")
    public ResponseEntity<List<DeliveryAreaDTO>> getDeliveryAreasByParentId(@PathVariable Long parentId) {
        log.info("REST request to get delivery areas by parent ID: {}", parentId);
        List<DeliveryAreaDTO> result = deliveryAreaService.getDeliveryAreasByParentId(parentId);
        return ResponseEntity.ok(result);
    }

    /**
     * 获取区域树
     *
     * @return 区域树
     */
    @GetMapping("/tree")
    public ResponseEntity<List<DeliveryAreaDTO>> getDeliveryAreaTree() {
        log.info("REST request to get delivery area tree");
        List<DeliveryAreaDTO> result = deliveryAreaService.getDeliveryAreaTree();
        return ResponseEntity.ok(result);
    }

    /**
     * 更新配送区域状态
     *
     * @param id     配送区域ID
     * @param status 状态
     * @return 更新后的配送区域DTO
     */
    @PutMapping("/{id}/status/{status}")
    public ResponseEntity<DeliveryAreaDTO> updateAreaStatus(
            @PathVariable Long id,
            @PathVariable Integer status) {
        log.info("REST request to update delivery area status with ID: {}, status: {}", id, status);
        DeliveryAreaDTO result = deliveryAreaService.updateAreaStatus(id, status);
        return ResponseEntity.ok(result);
    }

    /**
     * 统计配送区域数量
     *
     * @return 配送区域数量
     */
    @GetMapping("/count")
    public ResponseEntity<Map<String, Long>> countDeliveryAreas() {
        log.info("REST request to count delivery areas");
        Map<String, Long> result = deliveryAreaService.countDeliveryAreas(new HashMap<>());
        return ResponseEntity.ok(result);
    }
}

express-service\src\main\java\com\campus\express\controller\DeliveryController.java

package com.campus.express.controller;

import com.campus.express.dto.DeliveryCreateRequest;
import com.campus.express.dto.DeliveryDTO;
import com.campus.express.dto.DeliveryUpdateRequest;
import com.campus.express.service.DeliveryService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import javax.validation.Valid;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;

/**
 * 配送任务控制器
 */
@RestController
@RequestMapping("/api/deliveries")
@RequiredArgsConstructor
@Slf4j
public class DeliveryController {

    private final DeliveryService deliveryService;

    /**
     * 创建配送任务
     *
     * @param request 创建配送任务请求
     * @return 配送任务DTO
     */
    @PostMapping
    public ResponseEntity<DeliveryDTO> createDelivery(@Valid @RequestBody DeliveryCreateRequest request) {
        log.info("REST request to create delivery task: {}", request);
        DeliveryDTO result = deliveryService.createDelivery(request);
        return ResponseEntity.status(HttpStatus.CREATED).body(result);
    }

    /**
     * 根据ID查询配送任务
     *
     * @param id 配送任务ID
     * @return 配送任务DTO
     */
    @GetMapping("/{id}")
    public ResponseEntity<DeliveryDTO> getDeliveryById(@PathVariable Long id) {
        log.info("REST request to get delivery task by ID: {}", id);
        DeliveryDTO result = deliveryService.getDeliveryById(id);
        return ResponseEntity.ok(result);
    }

    /**
     * 根据快递ID查询配送任务
     *
     * @param expressId 快递ID
     * @return 配送任务DTO
     */
    @GetMapping("/express/{expressId}")
    public ResponseEntity<DeliveryDTO> getDeliveryByExpressId(@PathVariable Long expressId) {
        log.info("REST request to get delivery task by express ID: {}", expressId);
        DeliveryDTO result = deliveryService.getDeliveryByExpressId(expressId);
        return ResponseEntity.ok(result);
    }

    /**
     * 更新配送任务
     *
     * @param id      配送任务ID
     * @param request 更新配送任务请求
     * @return 更新后的配送任务DTO
     */
    @PutMapping("/{id}")
    public ResponseEntity<DeliveryDTO> updateDelivery(
            @PathVariable Long id,
            @Valid @RequestBody DeliveryUpdateRequest request) {
        log.info("REST request to update delivery task with ID: {}, request: {}", id, request);
        DeliveryDTO result = deliveryService.updateDelivery(id, request);
        return ResponseEntity.ok(result);
    }

    /**
     * 删除配送任务
     *
     * @param id 配送任务ID
     * @return 无内容响应
     */
    @DeleteMapping("/{id}")
    public ResponseEntity<Void> deleteDelivery(@PathVariable Long id) {
        log.info("REST request to delete delivery task with ID: {}", id);
        deliveryService.deleteDelivery(id);
        return ResponseEntity.noContent().build();
    }

    /**
     * 分页查询配送任务列表
     *
     * @param pageable 分页参数
     * @return 配送任务DTO分页列表
     */
    @GetMapping
    public ResponseEntity<Page<DeliveryDTO>> getDeliveries(Pageable pageable) {
        log.info("REST request to get delivery tasks with pagination: {}", pageable);
        Page<DeliveryDTO> result = deliveryService.getDeliveries(pageable);
        return ResponseEntity.ok(result);
    }

    /**
     * 根据状态分页查询配送任务列表
     *
     * @param status   状态
     * @param pageable 分页参数
     * @return 配送任务DTO分页列表
     */
    @GetMapping("/status/{status}")
    public ResponseEntity<Page<DeliveryDTO>> getDeliveriesByStatus(
            @PathVariable Integer status,
            Pageable pageable) {
        log.info("REST request to get delivery tasks by status: {} with pagination: {}", status, pageable);
        Page<DeliveryDTO> result = deliveryService.getDeliveriesByStatus(status, pageable);
        return ResponseEntity.ok(result);
    }

    /**
     * 根据配送员ID分页查询配送任务列表
     *
     * @param courierId 配送员ID
     * @param pageable  分页参数
     * @return 配送任务DTO分页列表
     */
    @GetMapping("/courier/{courierId}")
    public ResponseEntity<Page<DeliveryDTO>> getDeliveriesByCourierId(
            @PathVariable Long courierId,
            Pageable pageable) {
        log.info("REST request to get delivery tasks by courier ID: {} with pagination: {}", courierId, pageable);
        Page<DeliveryDTO> result = deliveryService.getDeliveriesByCourierId(courierId, pageable);
        return ResponseEntity.ok(result);
    }

    /**
     * 根据配送区域分页查询配送任务列表
     *
     * @param area     配送区域
     * @param pageable 分页参数
     * @return 配送任务DTO分页列表
     */
    @GetMapping("/area")
    public ResponseEntity<Page<DeliveryDTO>> getDeliveriesByArea(
            @RequestParam String area,
            Pageable pageable) {
        log.info("REST request to get delivery tasks by area: {} with pagination: {}", area, pageable);
        Page<DeliveryDTO> result = deliveryService.getDeliveriesByArea(area, pageable);
        return ResponseEntity.ok(result);
    }

    /**
     * 根据创建时间范围分页查询配送任务列表
     *
     * @param startTime 开始时间
     * @param endTime   结束时间
     * @param pageable  分页参数
     * @return 配送任务DTO分页列表
     */
    @GetMapping("/created-time")
    public ResponseEntity<Page<DeliveryDTO>> getDeliveriesByCreatedTimeBetween(
            @RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime startTime,
            @RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime endTime,
            Pageable pageable) {
        log.info("REST request to get delivery tasks by created time between: {} and {} with pagination: {}", startTime, endTime, pageable);
        Page<DeliveryDTO> result = deliveryService.getDeliveriesByCreatedTimeBetween(startTime, endTime, pageable);
        return ResponseEntity.ok(result);
    }

    /**
     * 分配配送员
     *
     * @param id        配送任务ID
     * @param courierId 配送员ID
     * @param courierName 配送员姓名
     * @param courierPhone 配送员电话
     * @return 更新后的配送任务DTO
     */
    @PutMapping("/{id}/assign-courier")
    public ResponseEntity<DeliveryDTO> assignCourier(
            @PathVariable Long id,
            @RequestParam Long courierId,
            @RequestParam String courierName,
            @RequestParam String courierPhone) {
        log.info("REST request to assign courier to delivery task with ID: {}, courier ID: {}", id, courierId);
        DeliveryDTO result = deliveryService.assignCourier(id, courierId, courierName, courierPhone);
        return ResponseEntity.ok(result);
    }

    /**
     * 更新配送任务状态
     *
     * @param id     配送任务ID
     * @param status 状态
     * @return 更新后的配送任务DTO
     */
    @PutMapping("/{id}/status/{status}")
    public ResponseEntity<DeliveryDTO> updateDeliveryStatus(
            @PathVariable Long id,
            @PathVariable Integer status) {
        log.info("REST request to update delivery task status with ID: {}, status: {}", id, status);
        DeliveryDTO result = deliveryService.updateDeliveryStatus(id, status);
        return ResponseEntity.ok(result);
    }

    /**
     * 取消配送任务
     *
     * @param id     配送任务ID
     * @param reason 取消原因
     * @return 更新后的配送任务DTO
     */
    @PutMapping("/{id}/cancel")
    public ResponseEntity<DeliveryDTO> cancelDelivery(
            @PathVariable Long id,
            @RequestParam String reason) {
        log.info("REST request to cancel delivery task with ID: {}, reason: {}", id, reason);
        DeliveryDTO result = deliveryService.cancelDelivery(id, reason);
        return ResponseEntity.ok(result);
    }

    /**
     * 生成配送验证码
     *
     * @return 配送验证码
     */
    @GetMapping("/generate-code")
    public ResponseEntity<Map<String, String>> generateDeliveryCode() {
        log.info("REST request to generate delivery code");
        String code = deliveryService.generateDeliveryCode();
        Map<String, String> result = new HashMap<>();
        result.put("code", code);
        return ResponseEntity.ok(result);
    }

    /**
     * 验证配送验证码
     *
     * @param id   配送任务ID
     * @param code 验证码
     * @return 是否验证成功
     */
    @GetMapping("/{id}/verify-code")
    public ResponseEntity<Map<String, Boolean>> verifyDeliveryCode(
            @PathVariable Long id,
            @RequestParam String code) {
        log.info("REST request to verify delivery code for delivery task with ID: {}", id);
        boolean verified = deliveryService.verifyDeliveryCode(id, code);
        Map<String, Boolean> result = new HashMap<>();
        result.put("verified", verified);
        return ResponseEntity.ok(result);
    }

    /**
     * 统计配送任务数量
     *
     * @return 配送任务数量
     */
    @GetMapping("/count")
    public ResponseEntity<Map<String, Long>> countDeliveries() {
        log.info("REST request to count delivery tasks");
        Map<String, Long> result = deliveryService.countDeliveries(new HashMap<>());
        return ResponseEntity.ok(result);
    }
}

express-service\src\main\java\com\campus\express\controller\DeliveryRouteController.java

package com.campus.express.controller;

import com.campus.express.dto.DeliveryRouteCreateRequest;
import com.campus.express.dto.DeliveryRouteDTO;
import com.campus.express.dto.DeliveryRouteUpdateRequest;
import com.campus.express.service.DeliveryRouteService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import javax.validation.Valid;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 配送路线控制器
 */
@RestController
@RequestMapping("/api/delivery-routes")
@RequiredArgsConstructor
@Slf4j
public class DeliveryRouteController {

    private final DeliveryRouteService deliveryRouteService;

    /**
     * 创建配送路线
     *
     * @param request 创建配送路线请求
     * @return 配送路线DTO
     */
    @PostMapping
    public ResponseEntity<DeliveryRouteDTO> createDeliveryRoute(@Valid @RequestBody DeliveryRouteCreateRequest request) {
        log.info("REST request to create delivery route: {}", request);
        DeliveryRouteDTO result = deliveryRouteService.createDeliveryRoute(request);
        return ResponseEntity.status(HttpStatus.CREATED).body(result);
    }

    /**
     * 根据ID查询配送路线
     *
     * @param id 配送路线ID
     * @return 配送路线DTO
     */
    @GetMapping("/{id}")
    public ResponseEntity<DeliveryRouteDTO> getDeliveryRouteById(@PathVariable Long id) {
        log.info("REST request to get delivery route by ID: {}", id);
        DeliveryRouteDTO result = deliveryRouteService.getDeliveryRouteById(id);
        return ResponseEntity.ok(result);
    }

    /**
     * 根据路线编号查询配送路线
     *
     * @param routeNumber 路线编号
     * @return 配送路线DTO
     */
    @GetMapping("/route-number/{routeNumber}")
    public ResponseEntity<DeliveryRouteDTO> getDeliveryRouteByRouteNumber(@PathVariable String routeNumber) {
        log.info("REST request to get delivery route by route number: {}", routeNumber);
        DeliveryRouteDTO result = deliveryRouteService.getDeliveryRouteByRouteNumber(routeNumber);
        return ResponseEntity.ok(result);
    }

    /**
     * 更新配送路线
     *
     * @param id      配送路线ID
     * @param request 更新配送路线请求
     * @return 更新后的配送路线DTO
     */
    @PutMapping("/{id}")
    public ResponseEntity<DeliveryRouteDTO> updateDeliveryRoute(
            @PathVariable Long id,
            @Valid @RequestBody DeliveryRouteUpdateRequest request) {
        log.info("REST request to update delivery route with ID: {}, request: {}", id, request);
        DeliveryRouteDTO result = deliveryRouteService.updateDeliveryRoute(id, request);
        return ResponseEntity.ok(result);
    }

    /**
     * 删除配送路线
     *
     * @param id 配送路线ID
     * @return 无内容响应
     */
    @DeleteMapping("/{id}")
    public ResponseEntity<Void> deleteDeliveryRoute(@PathVariable Long id) {
        log.info("REST request to delete delivery route with ID: {}", id);
        deliveryRouteService.deleteDeliveryRoute(id);
        return ResponseEntity.noContent().build();
    }

    /**
     * 分页查询配送路线列表
     *
     * @param pageable 分页参数
     * @return 配送路线DTO分页列表
     */
    @GetMapping
    public ResponseEntity<Page<DeliveryRouteDTO>> getDeliveryRoutes(Pageable pageable) {
        log.info("REST request to get delivery routes with pagination: {}", pageable);
        Page<DeliveryRouteDTO> result = deliveryRouteService.getDeliveryRoutes(pageable);
        return ResponseEntity.ok(result);
    }

    /**
     * 根据名称关键字分页查询配送路线列表
     *
     * @param name     名称关键字
     * @param pageable 分页参数
     * @return 配送路线DTO分页列表
     */
    @GetMapping("/search")
    public ResponseEntity<Page<DeliveryRouteDTO>> getDeliveryRoutesByName(
            @RequestParam String name,
            Pageable pageable) {
        log.info("REST request to get delivery routes by name: {} with pagination: {}", name, pageable);
        Page<DeliveryRouteDTO> result = deliveryRouteService.getDeliveryRoutesByName(name, pageable);
        return ResponseEntity.ok(result);
    }

    /**
     * 根据状态分页查询配送路线列表
     *
     * @param status   状态
     * @param pageable 分页参数
     * @return 配送路线DTO分页列表
     */
    @GetMapping("/status/{status}")
    public ResponseEntity<Page<DeliveryRouteDTO>> getDeliveryRoutesByStatus(
            @PathVariable Integer status,
            Pageable pageable) {
        log.info("REST request to get delivery routes by status: {} with pagination: {}", status, pageable);
        Page<DeliveryRouteDTO> result = deliveryRouteService.getDeliveryRoutesByStatus(status, pageable);
        return ResponseEntity.ok(result);
    }

    /**
     * 根据区域查询配送路线列表
     *
     * @param area 区域
     * @return 配送路线DTO列表
     */
    @GetMapping("/area/{area}")
    public ResponseEntity<List<DeliveryRouteDTO>> getDeliveryRoutesByArea(@PathVariable String area) {
        log.info("REST request to get delivery routes by area: {}", area);
        List<DeliveryRouteDTO> result = deliveryRouteService.getDeliveryRoutesByArea(area);
        return ResponseEntity.ok(result);
    }

    /**
     * 根据配送员ID查询配送路线列表
     *
     * @param courierId 配送员ID
     * @return 配送路线DTO列表
     */
    @GetMapping("/courier/{courierId}")
    public ResponseEntity<List<DeliveryRouteDTO>> getDeliveryRoutesByCourierId(@PathVariable Long courierId) {
        log.info("REST request to get delivery routes by courier ID: {}", courierId);
        List<DeliveryRouteDTO> result = deliveryRouteService.getDeliveryRoutesByCourierId(courierId);
        return ResponseEntity.ok(result);
    }

    /**
     * 分配配送员
     *
     * @param id          配送路线ID
     * @param courierId   配送员ID
     * @param courierName 配送员姓名
     * @return 更新后的配送路线DTO
     */
    @PutMapping("/{id}/assign-courier")
    public ResponseEntity<DeliveryRouteDTO> assignCourier(
            @PathVariable Long id,
            @RequestParam Long courierId,
            @RequestParam String courierName) {
        log.info("REST request to assign courier to delivery route with ID: {}, courier ID: {}", id, courierId);
        DeliveryRouteDTO result = deliveryRouteService.assignCourier(id, courierId, courierName);
        return ResponseEntity.ok(result);
    }

    /**
     * 更新配送路线状态
     *
     * @param id     配送路线ID
     * @param status 状态
     * @return 更新后的配送路线DTO
     */
    @PutMapping("/{id}/status/{status}")
    public ResponseEntity<DeliveryRouteDTO> updateRouteStatus(
            @PathVariable Long id,
            @PathVariable Integer status) {
        log.info("REST request to update delivery route status with ID: {}, status: {}", id, status);
        DeliveryRouteDTO result = deliveryRouteService.updateRouteStatus(id, status);
        return ResponseEntity.ok(result);
    }

    /**
     * 统计配送路线数量
     *
     * @return 配送路线数量
     */
    @GetMapping("/count")
    public ResponseEntity<Map<String, Long>> countDeliveryRoutes() {
        log.info("REST request to count delivery routes");
        Map<String, Long> result = deliveryRouteService.countDeliveryRoutes(new HashMap<>());
        return ResponseEntity.ok(result);
    }
}

express-service\src\main\java\com\campus\express\controller\NotificationController.java

package com.campus.express.controller;

import com.campus.express.dto.*;
import com.campus.express.service.NotificationService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.web.PageableDefault;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import javax.validation.Valid;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;

/**
 * 通知管理控制器
 */
@RestController
@RequestMapping("/api/notifications")
@RequiredArgsConstructor
@Slf4j
public class NotificationController {

    private final NotificationService notificationService;

    /**
     * 创建通知
     *
     * @param request 创建通知请求
     * @return 通知DTO
     */
    @PostMapping
    public ResponseEntity<NotificationDTO> createNotification(@Valid @RequestBody NotificationCreateRequest request) {
        log.info("Creating notification: {}", request);
        NotificationDTO notification = notificationService.createNotification(request);
        return new ResponseEntity<>(notification, HttpStatus.CREATED);
    }

    /**
     * 根据ID查询通知
     *
     * @param id 通知ID
     * @return 通知DTO
     */
    @GetMapping("/{id}")
    public ResponseEntity<NotificationDTO> getNotificationById(@PathVariable Long id) {
        log.info("Getting notification by ID: {}", id);
        NotificationDTO notification = notificationService.getNotificationById(id);
        return ResponseEntity.ok(notification);
    }

    /**
     * 更新通知
     *
     * @param id      通知ID
     * @param request 更新通知请求
     * @return 更新后的通知DTO
     */
    @PutMapping("/{id}")
    public ResponseEntity<NotificationDTO> updateNotification(
            @PathVariable Long id, @Valid @RequestBody NotificationUpdateRequest request) {
        log.info("Updating notification with ID: {}", id);
        NotificationDTO notification = notificationService.updateNotification(id, request);
        return ResponseEntity.ok(notification);
    }

    /**
     * 删除通知
     *
     * @param id 通知ID
     * @return 无内容响应
     */
    @DeleteMapping("/{id}")
    public ResponseEntity<Void> deleteNotification(@PathVariable Long id) {
        log.info("Deleting notification with ID: {}", id);
        notificationService.deleteNotification(id);
        return ResponseEntity.noContent().build();
    }

    /**
     * 分页查询通知列表
     *
     * @param pageable 分页参数
     * @return 通知DTO分页列表
     */
    @GetMapping
    public ResponseEntity<Page<NotificationDTO>> getNotifications(
            @PageableDefault(size = 10, sort = "createdTime,desc") Pageable pageable) {
        log.info("Getting notifications with pagination: {}", pageable);
        Page<NotificationDTO> notifications = notificationService.getNotifications(pageable);
        return ResponseEntity.ok(notifications);
    }

    /**
     * 根据接收者ID和接收者类型分页查询通知列表
     *
     * @param receiverId   接收者ID
     * @param receiverType 接收者类型
     * @param pageable     分页参数
     * @return 通知DTO分页列表
     */
    @GetMapping("/receiver")
    public ResponseEntity<Page<NotificationDTO>> getNotificationsByReceiver(
            @RequestParam Long receiverId,
            @RequestParam Integer receiverType,
            @PageableDefault(size = 10, sort = "createdTime,desc") Pageable pageable) {
        log.info("Getting notifications by receiver ID: {} and receiver type: {}", receiverId, receiverType);
        Page<NotificationDTO> notifications = notificationService.getNotificationsByReceiver(
                receiverId, receiverType, pageable);
        return ResponseEntity.ok(notifications);
    }

    /**
     * 根据接收者ID、接收者类型和已读状态分页查询通知列表
     *
     * @param receiverId   接收者ID
     * @param receiverType 接收者类型
     * @param readStatus   已读状态
     * @param pageable     分页参数
     * @return 通知DTO分页列表
     */
    @GetMapping("/receiver/status")
    public ResponseEntity<Page<NotificationDTO>> getNotificationsByReceiverAndReadStatus(
            @RequestParam Long receiverId,
            @RequestParam Integer receiverType,
            @RequestParam Integer readStatus,
            @PageableDefault(size = 10, sort = "createdTime,desc") Pageable pageable) {
        log.info("Getting notifications by receiver ID: {}, receiver type: {} and read status: {}", 
                receiverId, receiverType, readStatus);
        Page<NotificationDTO> notifications = notificationService.getNotificationsByReceiverAndReadStatus(
                receiverId, receiverType, readStatus, pageable);
        return ResponseEntity.ok(notifications);
    }

    /**
     * 根据通知类型分页查询通知列表
     *
     * @param type     通知类型
     * @param pageable 分页参数
     * @return 通知DTO分页列表
     */
    @GetMapping("/type/{type}")
    public ResponseEntity<Page<NotificationDTO>> getNotificationsByType(
            @PathVariable Integer type,
            @PageableDefault(size = 10, sort = "createdTime,desc") Pageable pageable) {
        log.info("Getting notifications by type: {}", type);
        Page<NotificationDTO> notifications = notificationService.getNotificationsByType(type, pageable);
        return ResponseEntity.ok(notifications);
    }

    /**
     * 根据通知渠道分页查询通知列表
     *
     * @param channel  通知渠道
     * @param pageable 分页参数
     * @return 通知DTO分页列表
     */
    @GetMapping("/channel/{channel}")
    public ResponseEntity<Page<NotificationDTO>> getNotificationsByChannel(
            @PathVariable Integer channel,
            @PageableDefault(size = 10, sort = "createdTime,desc") Pageable pageable) {
        log.info("Getting notifications by channel: {}", channel);
        Page<NotificationDTO> notifications = notificationService.getNotificationsByChannel(channel, pageable);
        return ResponseEntity.ok(notifications);
    }

    /**
     * 根据发送状态分页查询通知列表
     *
     * @param sendStatus 发送状态
     * @param pageable   分页参数
     * @return 通知DTO分页列表
     */
    @GetMapping("/send-status/{sendStatus}")
    public ResponseEntity<Page<NotificationDTO>> getNotificationsBySendStatus(
            @PathVariable Integer sendStatus,
            @PageableDefault(size = 10, sort = "createdTime,desc") Pageable pageable) {
        log.info("Getting notifications by send status: {}", sendStatus);
        Page<NotificationDTO> notifications = notificationService.getNotificationsBySendStatus(sendStatus, pageable);
        return ResponseEntity.ok(notifications);
    }

    /**
     * 根据创建时间范围分页查询通知列表
     *
     * @param startTime 开始时间
     * @param endTime   结束时间
     * @param pageable  分页参数
     * @return 通知DTO分页列表
     */
    @GetMapping("/time-range")
    public ResponseEntity<Page<NotificationDTO>> getNotificationsByCreatedTimeBetween(
            @RequestParam LocalDateTime startTime,
            @RequestParam LocalDateTime endTime,
            @PageableDefault(size = 10, sort = "createdTime,desc") Pageable pageable) {
        log.info("Getting notifications by created time between: {} and {}", startTime, endTime);
        Page<NotificationDTO> notifications = notificationService.getNotificationsByCreatedTimeBetween(
                startTime, endTime, pageable);
        return ResponseEntity.ok(notifications);
    }

    /**
     * 根据关联业务ID和关联业务类型查询通知列表
     *
     * @param businessId   关联业务ID
     * @param businessType 关联业务类型
     * @return 通知DTO列表
     */
    @GetMapping("/business")
    public ResponseEntity<List<NotificationDTO>> getNotificationsByBusiness(
            @RequestParam Long businessId, @RequestParam Integer businessType) {
        log.info("Getting notifications by business ID: {} and business type: {}", businessId, businessType);
        List<NotificationDTO> notifications = notificationService.getNotificationsByBusiness(businessId, businessType);
        return ResponseEntity.ok(notifications);
    }

    /**
     * 发送通知
     *
     * @param request 发送通知请求
     * @return 通知DTO列表
     */
    @PostMapping("/send")
    public ResponseEntity<List<NotificationDTO>> sendNotification(@Valid @RequestBody NotificationSendRequest request) {
        log.info("Sending notification: {}", request);
        List<NotificationDTO> notifications = notificationService.sendNotification(request);
        return new ResponseEntity<>(notifications, HttpStatus.CREATED);
    }

    /**
     * 标记通知为已读
     *
     * @param id 通知ID
     * @return 更新后的通知DTO
     */
    @PutMapping("/{id}/read")
    public ResponseEntity<NotificationDTO> markAsRead(@PathVariable Long id) {
        log.info("Marking notification as read with ID: {}", id);
        NotificationDTO notification = notificationService.markAsRead(id);
        return ResponseEntity.ok(notification);
    }

    /**
     * 批量标记通知为已读
     *
     * @param ids 通知ID列表
     * @return 更新行数
     */
    @PutMapping("/batch-read")
    public ResponseEntity<Integer> batchMarkAsRead(@RequestBody List<Long> ids) {
        log.info("Batch marking notifications as read with IDs: {}", ids);
        int updated = notificationService.batchMarkAsRead(ids);
        return ResponseEntity.ok(updated);
    }

    /**
     * 标记接收者的所有通知为已读
     *
     * @param receiverId   接收者ID
     * @param receiverType 接收者类型
     * @return 更新行数
     */
    @PutMapping("/mark-all-read")
    public ResponseEntity<Integer> markAllAsRead(
            @RequestParam Long receiverId, @RequestParam Integer receiverType) {
        log.info("Marking all notifications as read for receiver ID: {} and receiver type: {}", 
                receiverId, receiverType);
        int updated = notificationService.markAllAsRead(receiverId, receiverType);
        return ResponseEntity.ok(updated);
    }

    /**
     * 重试发送失败的通知
     *
     * @param id 通知ID
     * @return 更新后的通知DTO
     */
    @PutMapping("/{id}/retry")
    public ResponseEntity<NotificationDTO> retrySendNotification(@PathVariable Long id) {
        log.info("Retrying to send notification with ID: {}", id);
        NotificationDTO notification = notificationService.retrySendNotification(id);
        return ResponseEntity.ok(notification);
    }

    /**
     * 统计接收者未读通知数量
     *
     * @param receiverId   接收者ID
     * @param receiverType 接收者类型
     * @return 未读通知数量
     */
    @GetMapping("/count/unread")
    public ResponseEntity<Long> countUnreadNotifications(
            @RequestParam Long receiverId, @RequestParam Integer receiverType) {
        log.info("Counting unread notifications for receiver ID: {} and receiver type: {}", 
                receiverId, receiverType);
        long count = notificationService.countUnreadNotifications(receiverId, receiverType);
        return ResponseEntity.ok(count);
    }

    /**
     * 统计通知数量
     *
     * @param params 查询参数
     * @return 通知数量
     */
    @GetMapping("/count")
    public ResponseEntity<Map<String, Long>> countNotifications(@RequestParam Map<String, Object> params) {
        log.info("Counting notifications with params: {}", params);
        Map<String, Long> counts = notificationService.countNotifications(params);
        return ResponseEntity.ok(counts);
    }
}

express-service\src\main\java\com\campus\express\controller\NotificationTemplateController.java

package com.campus.express.controller;

import com.campus.express.dto.NotificationTemplateCreateRequest;
import com.campus.express.dto.NotificationTemplateDTO;
import com.campus.express.dto.NotificationTemplateUpdateRequest;
import com.campus.express.service.NotificationTemplateService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.web.PageableDefault;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import javax.validation.Valid;
import java.util.List;
import java.util.Map;

/**
 * 通知模板管理控制器
 */
@RestController
@RequestMapping("/api/notification-templates")
@RequiredArgsConstructor
@Slf4j
public class NotificationTemplateController {

    private final NotificationTemplateService notificationTemplateService;

    /**
     * 创建通知模板
     *
     * @param request 创建通知模板请求
     * @return 通知模板DTO
     */
    @PostMapping
    public ResponseEntity<NotificationTemplateDTO> createNotificationTemplate(
            @Valid @RequestBody NotificationTemplateCreateRequest request) {
        log.info("Creating notification template: {}", request);
        NotificationTemplateDTO template = notificationTemplateService.createNotificationTemplate(request);
        return new ResponseEntity<>(template, HttpStatus.CREATED);
    }

    /**
     * 根据ID查询通知模板
     *
     * @param id 通知模板ID
     * @return 通知模板DTO
     */
    @GetMapping("/{id}")
    public ResponseEntity<NotificationTemplateDTO> getNotificationTemplateById(@PathVariable Long id) {
        log.info("Getting notification template by ID: {}", id);
        NotificationTemplateDTO template = notificationTemplateService.getNotificationTemplateById(id);
        return ResponseEntity.ok(template);
    }

    /**
     * 根据模板编码查询通知模板
     *
     * @param code 模板编码
     * @return 通知模板DTO
     */
    @GetMapping("/code/{code}")
    public ResponseEntity<NotificationTemplateDTO> getNotificationTemplateByCode(@PathVariable String code) {
        log.info("Getting notification template by code: {}", code);
        NotificationTemplateDTO template = notificationTemplateService.getNotificationTemplateByCode(code);
        return ResponseEntity.ok(template);
    }

    /**
     * 更新通知模板
     *
     * @param id      通知模板ID
     * @param request 更新通知模板请求
     * @return 更新后的通知模板DTO
     */
    @PutMapping("/{id}")
    public ResponseEntity<NotificationTemplateDTO> updateNotificationTemplate(
            @PathVariable Long id, @Valid @RequestBody NotificationTemplateUpdateRequest request) {
        log.info("Updating notification template with ID: {}", id);
        NotificationTemplateDTO template = notificationTemplateService.updateNotificationTemplate(id, request);
        return ResponseEntity.ok(template);
    }

    /**
     * 删除通知模板
     *
     * @param id 通知模板ID
     * @return 无内容响应
     */
    @DeleteMapping("/{id}")
    public ResponseEntity<Void> deleteNotificationTemplate(@PathVariable Long id) {
        log.info("Deleting notification template with ID: {}", id);
        notificationTemplateService.deleteNotificationTemplate(id);
        return ResponseEntity.noContent().build();
    }

    /**
     * 分页查询通知模板列表
     *
     * @param pageable 分页参数
     * @return 通知模板DTO分页列表
     */
    @GetMapping
    public ResponseEntity<Page<NotificationTemplateDTO>> getNotificationTemplates(
            @PageableDefault(size = 10, sort = "createdTime,desc") Pageable pageable) {
        log.info("Getting notification templates with pagination: {}", pageable);
        Page<NotificationTemplateDTO> templates = notificationTemplateService.getNotificationTemplates(pageable);
        return ResponseEntity.ok(templates);
    }

    /**
     * 根据模板名称分页查询通知模板列表
     *
     * @param name     模板名称
     * @param pageable 分页参数
     * @return 通知模板DTO分页列表
     */
    @GetMapping("/name")
    public ResponseEntity<Page<NotificationTemplateDTO>> getNotificationTemplatesByName(
            @RequestParam String name,
            @PageableDefault(size = 10, sort = "createdTime,desc") Pageable pageable) {
        log.info("Getting notification templates by name: {}", name);
        Page<NotificationTemplateDTO> templates = notificationTemplateService.getNotificationTemplatesByName(name, pageable);
        return ResponseEntity.ok(templates);
    }

    /**
     * 根据模板类型分页查询通知模板列表
     *
     * @param type     模板类型
     * @param pageable 分页参数
     * @return 通知模板DTO分页列表
     */
    @GetMapping("/type/{type}")
    public ResponseEntity<Page<NotificationTemplateDTO>> getNotificationTemplatesByType(
            @PathVariable Integer type,
            @PageableDefault(size = 10, sort = "createdTime,desc") Pageable pageable) {
        log.info("Getting notification templates by type: {}", type);
        Page<NotificationTemplateDTO> templates = notificationTemplateService.getNotificationTemplatesByType(type, pageable);
        return ResponseEntity.ok(templates);
    }

    /**
     * 根据适用渠道分页查询通知模板列表
     *
     * @param channel  适用渠道
     * @param pageable 分页参数
     * @return 通知模板DTO分页列表
     */
    @GetMapping("/channel/{channel}")
    public ResponseEntity<Page<NotificationTemplateDTO>> getNotificationTemplatesByChannel(
            @PathVariable Integer channel,
            @PageableDefault(size = 10, sort = "createdTime,desc") Pageable pageable) {
        log.info("Getting notification templates by channel: {}", channel);
        Page<NotificationTemplateDTO> templates = notificationTemplateService.getNotificationTemplatesByChannel(channel, pageable);
        return ResponseEntity.ok(templates);
    }

    /**
     * 根据状态分页查询通知模板列表
     *
     * @param status   状态
     * @param pageable 分页参数
     * @return 通知模板DTO分页列表
     */
    @GetMapping("/status/{status}")
    public ResponseEntity<Page<NotificationTemplateDTO>> getNotificationTemplatesByStatus(
            @PathVariable Integer status,
            @PageableDefault(size = 10, sort = "createdTime,desc") Pageable pageable) {
        log.info("Getting notification templates by status: {}", status);
        Page<NotificationTemplateDTO> templates = notificationTemplateService.getNotificationTemplatesByStatus(status, pageable);
        return ResponseEntity.ok(templates);
    }

    /**
     * 根据模板类型和适用渠道查询通知模板列表
     *
     * @param type    模板类型
     * @param channel 适用渠道
     * @return 通知模板DTO列表
     */
    @GetMapping("/type/{type}/channel/{channel}")
    public ResponseEntity<List<NotificationTemplateDTO>> getNotificationTemplatesByTypeAndChannel(
            @PathVariable Integer type, @PathVariable Integer channel) {
        log.info("Getting notification templates by type: {} and channel: {}", type, channel);
        List<NotificationTemplateDTO> templates = notificationTemplateService.getNotificationTemplatesByTypeAndChannel(type, channel);
        return ResponseEntity.ok(templates);
    }

    /**
     * 根据模板类型和状态查询通知模板列表
     *
     * @param type   模板类型
     * @param status 状态
     * @return 通知模板DTO列表
     */
    @GetMapping("/type/{type}/status/{status}")
    public ResponseEntity<List<NotificationTemplateDTO>> getNotificationTemplatesByTypeAndStatus(
            @PathVariable Integer type, @PathVariable Integer status) {
        log.info("Getting notification templates by type: {} and status: {}", type, status);
        List<NotificationTemplateDTO> templates = notificationTemplateService.getNotificationTemplatesByTypeAndStatus(type, status);
        return ResponseEntity.ok(templates);
    }

    /**
     * 根据适用渠道和状态查询通知模板列表
     *
     * @param channel 适用渠道
     * @param status  状态
     * @return 通知模板DTO列表
     */
    @GetMapping("/channel/{channel}/status/{status}")
    public ResponseEntity<List<NotificationTemplateDTO>> getNotificationTemplatesByChannelAndStatus(
            @PathVariable Integer channel, @PathVariable Integer status) {
        log.info("Getting notification templates by channel: {} and status: {}", channel, status);
        List<NotificationTemplateDTO> templates = notificationTemplateService.getNotificationTemplatesByChannelAndStatus(channel, status);
        return ResponseEntity.ok(templates);
    }

    /**
     * 更新通知模板状态
     *
     * @param id     通知模板ID
     * @param status 状态
     * @return 更新后的通知模板DTO
     */
    @PutMapping("/{id}/status/{status}")
    public ResponseEntity<NotificationTemplateDTO> updateTemplateStatus(
            @PathVariable Long id, @PathVariable Integer status) {
        log.info("Updating notification template status with ID: {} to status: {}", id, status);
        NotificationTemplateDTO template = notificationTemplateService.updateTemplateStatus(id, status);
        return ResponseEntity.ok(template);
    }

    /**
     * 检查模板编码是否存在
     *
     * @param code 模板编码
     * @return 是否存在
     */
    @GetMapping("/check-code/{code}")
    public ResponseEntity<Boolean> isTemplateCodeExists(@PathVariable String code) {
        log.info("Checking if template code exists: {}", code);
        boolean exists = notificationTemplateService.isTemplateCodeExists(code);
        return ResponseEntity.ok(exists);
    }

    /**
     * 渲染模板内容
     *
     * @param templateCode 模板编码
     * @param params       模板参数
     * @return 渲染后的内容
     */
    @PostMapping("/render/{templateCode}")
    public ResponseEntity<Map<String, String>> renderTemplate(
            @PathVariable String templateCode, @RequestBody Map<String, Object> params) {
        log.info("Rendering template with code: {} and params: {}", templateCode, params);
        Map<String, String> renderedContent = notificationTemplateService.renderTemplate(templateCode, params);
        return ResponseEntity.ok(renderedContent);
    }

    /**
     * 统计通知模板数量
     *
     * @param params 查询参数
     * @return 通知模板数量
     */
    @GetMapping("/count")
    public ResponseEntity<Map<String, Long>> countNotificationTemplates(@RequestParam Map<String, Object> params) {
        log.info("Counting notification templates with params: {}", params);
        Map<String, Long> counts = notificationTemplateService.countNotificationTemplates(params);
        return ResponseEntity.ok(counts);
    }
}

express-service\src\main\java\com\campus\express\dto\ApiResponse.java

package com.campus.express.dto;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.time.LocalDateTime;

/**
 * API响应包装类
 */
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ApiResponse<T> {
    
    private Integer code;
    private String message;
    private T data;
    private LocalDateTime timestamp;
    
    /**
     * 创建成功响应
     * 
     * @param data 响应数据
     * @param <T> 数据类型
     * @return API响应对象
     */
    public static <T> ApiResponse<T> success(T data) {
        return ApiResponse.<T>builder()
                .code(200)
                .message("操作成功")
                .data(data)
                .timestamp(LocalDateTime.now())
                .build();
    }
    
    /**
     * 创建成功响应(无数据)
     * 
     * @param <T> 数据类型
     * @return API响应对象
     */
    public static <T> ApiResponse<T> success() {
        return ApiResponse.<T>builder()
                .code(200)
                .message("操作成功")
                .timestamp(LocalDateTime.now())
                .build();
    }
    
    /**
     * 创建成功响应(自定义消息)
     * 
     * @param message 响应消息
     * @param data 响应数据
     * @param <T> 数据类型
     * @return API响应对象
     */
    public static <T> ApiResponse<T> success(String message, T data) {
        return ApiResponse.<T>builder()
                .code(200)
                .message(message)
                .data(data)
                .timestamp(LocalDateTime.now())
                .build();
    }
    
    /**
     * 创建错误响应
     * 
     * @param code 错误码
     * @param message 错误消息
     * @param <T> 数据类型
     * @return API响应对象
     */
    public static <T> ApiResponse<T> error(Integer code, String message) {
        return ApiResponse.<T>builder()
                .code(code)
                .message(message)
                .timestamp(LocalDateTime.now())
                .build();
    }
    
    /**
     * 创建错误响应(400 Bad Request)
     * 
     * @param message 错误消息
     * @param <T> 数据类型
     * @return API响应对象
     */
    public static <T> ApiResponse<T> badRequest(String message) {
        return error(400, message);
    }
    
    /**
     * 创建错误响应(401 Unauthorized)
     * 
     * @param message 错误消息
     * @param <T> 数据类型
     * @return API响应对象
     */
    public static <T> ApiResponse<T> unauthorized(String message) {
        return error(401, message);
    }
    
    /**
     * 创建错误响应(403 Forbidden)
     * 
     * @param message 错误消息
     * @param <T> 数据类型
     * @return API响应对象
     */
    public static <T> ApiResponse<T> forbidden(String message) {
        return error(403, message);
    }
    
    /**
     * 创建错误响应(404 Not Found)
     * 
     * @param message 错误消息
     * @param <T> 数据类型
     * @return API响应对象
     */
    public static <T> ApiResponse<T> notFound(String message) {
        return error(404, message);
    }
    
    /**
     * 创建错误响应(500 Internal Server Error)
     * 
     * @param message 错误消息
     * @param <T> 数据类型
     * @return API响应对象
     */
    public static <T> ApiResponse<T> serverError(String message) {
        return error(500, message);
    }
}

express-service\src\main\java\com\campus\express\dto\CabinetCellDTO.java

package com.campus.express.dto;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import java.time.LocalDateTime;

/**
 * 快递柜格口数据传输对象
 */
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class CabinetCellDTO {

    private Long id;

    /**
     * 柜子ID
     */
    @NotNull(message = "柜子ID不能为空")
    private Long cabinetId;

    /**
     * 柜子名称
     */
    private String cabinetName;

    /**
     * 格口编号
     */
    @NotBlank(message = "格口编号不能为空")
    @Size(max = 20, message = "格口编号长度不能超过20个字符")
    private String cellNumber;

    /**
     * 格口大小:0-小,1-中,2-大
     */
    @NotNull(message = "格口大小不能为空")
    private Integer size;

    /**
     * 格口大小描述
     */
    private String sizeDesc;

    /**
     * 格口状态:0-空闲,1-占用,2-故障
     */
    @NotNull(message = "格口状态不能为空")
    private Integer status;

    /**
     * 格口状态描述
     */
    private String statusDesc;

    /**
     * 当前存放的快递ID
     */
    private Long expressId;

    /**
     * 当前存放的快递单号
     */
    private String trackingNumber;

    /**
     * 存入时间
     */
    private LocalDateTime storageTime;

    /**
     * 取出时间
     */
    private LocalDateTime retrievalTime;

    /**
     * 备注
     */
    @Size(max = 255, message = "备注长度不能超过255个字符")
    private String remark;

    /**
     * 创建时间
     */
    private LocalDateTime createdTime;

    /**
     * 更新时间
     */
    private LocalDateTime updatedTime;
}

express-service\src\main\java\com\campus\express\dto\CabinetCreateRequest.java

package com.campus.express.dto;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;

/**
 * 创建快递柜请求DTO
 */
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class CabinetCreateRequest {

    /**
     * 柜子编号
     */
    @NotBlank(message = "柜子编号不能为空")
    @Size(max = 50, message = "柜子编号长度不能超过50个字符")
    private String cabinetNumber;

    /**
     * 柜子名称
     */
    @NotBlank(message = "柜子名称不能为空")
    @Size(max = 100, message = "柜子名称长度不能超过100个字符")
    private String name;

    /**
     * 柜子位置
     */
    @NotBlank(message = "柜子位置不能为空")
    @Size(max = 255, message = "柜子位置长度不能超过255个字符")
    private String location;

    /**
     * 柜子状态:0-停用,1-启用
     */
    @NotNull(message = "柜子状态不能为空")
    private Integer status;

    /**
     * 总格口数
     */
    @NotNull(message = "总格口数不能为空")
    private Integer totalCells;

    /**
     * 小格口数量
     */
    private Integer smallCellCount;

    /**
     * 中格口数量
     */
    private Integer mediumCellCount;

    /**
     * 大格口数量
     */
    private Integer largeCellCount;

    /**
     * 负责人ID
     */
    private Long managerId;

    /**
     * 负责人姓名
     */
    @Size(max = 50, message = "负责人姓名长度不能超过50个字符")
    private String managerName;

    /**
     * 负责人电话
     */
    @Size(max = 20, message = "负责人电话长度不能超过20个字符")
    private String managerPhone;

    /**
     * 备注
     */
    @Size(max = 255, message = "备注长度不能超过255个字符")
    private String remark;
}

express-service\src\main\java\com\campus\express\dto\CabinetDTO.java

package com.campus.express.dto;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import java.time.LocalDateTime;

/**
 * 快递柜数据传输对象
 */
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class CabinetDTO {

    private Long id;

    /**
     * 柜子编号
     */
    @NotBlank(message = "柜子编号不能为空")
    @Size(max = 50, message = "柜子编号长度不能超过50个字符")
    private String cabinetNumber;

    /**
     * 柜子名称
     */
    @NotBlank(message = "柜子名称不能为空")
    @Size(max = 100, message = "柜子名称长度不能超过100个字符")
    private String name;

    /**
     * 柜子位置
     */
    @NotBlank(message = "柜子位置不能为空")
    @Size(max = 255, message = "柜子位置长度不能超过255个字符")
    private String location;

    /**
     * 柜子状态:0-停用,1-启用
     */
    @NotNull(message = "柜子状态不能为空")
    private Integer status;

    /**
     * 柜子状态描述
     */
    private String statusDesc;

    /**
     * 总格口数
     */
    @NotNull(message = "总格口数不能为空")
    private Integer totalCells;

    /**
     * 可用格口数
     */
    @NotNull(message = "可用格口数不能为空")
    private Integer availableCells;

    /**
     * 小格口数量
     */
    private Integer smallCellCount;

    /**
     * 中格口数量
     */
    private Integer mediumCellCount;

    /**
     * 大格口数量
     */
    private Integer largeCellCount;

    /**
     * 负责人ID
     */
    private Long managerId;

    /**
     * 负责人姓名
     */
    @Size(max = 50, message = "负责人姓名长度不能超过50个字符")
    private String managerName;

    /**
     * 负责人电话
     */
    @Size(max = 20, message = "负责人电话长度不能超过20个字符")
    private String managerPhone;

    /**
     * 备注
     */
    @Size(max = 255, message = "备注长度不能超过255个字符")
    private String remark;

    /**
     * 创建时间
     */
    private LocalDateTime createdTime;

    /**
     * 更新时间
     */
    private LocalDateTime updatedTime;
}

express-service\src\main\java\com\campus\express\dto\CabinetUpdateRequest.java

package com.campus.express.dto;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import javax.validation.constraints.Size;

/**
 * 更新快递柜请求DTO
 */
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class CabinetUpdateRequest {

    /**
     * 柜子名称
     */
    @Size(max = 100, message = "柜子名称长度不能超过100个字符")
    private String name;

    /**
     * 柜子位置
     */
    @Size(max = 255, message = "柜子位置长度不能超过255个字符")
    private String location;

    /**
     * 柜子状态:0-停用,1-启用
     */
    private Integer status;

    /**
     * 负责人ID
     */
    private Long managerId;

    /**
     * 负责人姓名
     */
    @Size(max = 50, message = "负责人姓名长度不能超过50个字符")
    private String managerName;

    /**
     * 负责人电话
     */
    @Size(max = 20, message = "负责人电话长度不能超过20个字符")
    private String managerPhone;

    /**
     * 备注
     */
    @Size(max = 255, message = "备注长度不能超过255个字符")
    private String remark;
}

express-service\src\main\java\com\campus\express\dto\DeliveryAreaCreateRequest.java

package com.campus.express.dto;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;

/**
 * 创建配送区域请求
 */
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class DeliveryAreaCreateRequest {
    
    /**
     * 区域代码
     */
    @NotBlank(message = "区域代码不能为空")
    private String areaCode;
    
    /**
     * 区域名称
     */
    @NotBlank(message = "区域名称不能为空")
    private String name;
    
    /**
     * 父区域ID
     */
    private Long parentId;
    
    /**
     * 区域级别(1-校区,2-楼栋,3-具体位置)
     */
    @NotNull(message = "区域级别不能为空")
    private Integer level;
    
    /**
     * 区域边界(GeoJSON格式)
     */
    private String boundary;
    
    /**
     * 中心点坐标(经度)
     */
    @NotNull(message = "中心点经度不能为空")
    private Double centerLongitude;
    
    /**
     * 中心点坐标(纬度)
     */
    @NotNull(message = "中心点纬度不能为空")
    private Double centerLatitude;
    
    /**
     * 配送费用
     */
    private Double deliveryFee;
    
    /**
     * 预计配送时间(分钟)
     */
    private Integer estimatedDeliveryTime;
    
    /**
     * 状态(0-停用,1-启用)
     */
    private Integer status;
    
    /**
     * 区域描述
     */
    private String description;
    
    /**
     * 备注
     */
    private String remark;
}

express-service\src\main\java\com\campus\express\dto\DeliveryAreaDTO.java

package com.campus.express.dto;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

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

/**
 * 配送区域数据传输对象
 */
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class DeliveryAreaDTO {
    
    /**
     * 配送区域ID
     */
    private Long id;
    
    /**
     * 区域代码
     */
    private String areaCode;
    
    /**
     * 区域名称
     */
    private String name;
    
    /**
     * 父区域ID
     */
    private Long parentId;
    
    /**
     * 父区域名称
     */
    private String parentName;
    
    /**
     * 区域级别(1-校区,2-楼栋,3-具体位置)
     */
    private Integer level;
    
    /**
     * 区域级别描述
     */
    private String levelDesc;
    
    /**
     * 区域边界(GeoJSON格式)
     */
    private String boundary;
    
    /**
     * 中心点坐标(经度)
     */
    private Double centerLongitude;
    
    /**
     * 中心点坐标(纬度)
     */
    private Double centerLatitude;
    
    /**
     * 配送费用
     */
    private Double deliveryFee;
    
    /**
     * 预计配送时间(分钟)
     */
    private Integer estimatedDeliveryTime;
    
    /**
     * 状态(0-停用,1-启用)
     */
    private Integer status;
    
    /**
     * 状态描述
     */
    private String statusDesc;
    
    /**
     * 区域描述
     */
    private String description;
    
    /**
     * 子区域列表
     */
    private List<DeliveryAreaDTO> children;
    
    /**
     * 备注
     */
    private String remark;
    
    /**
     * 创建时间
     */
    private LocalDateTime createdTime;
    
    /**
     * 更新时间
     */
    private LocalDateTime updatedTime;
}

express-service\src\main\java\com\campus\express\dto\DeliveryAreaUpdateRequest.java

package com.campus.express.dto;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * 更新配送区域请求
 */
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class DeliveryAreaUpdateRequest {
    
    /**
     * 区域名称
     */
    private String name;
    
    /**
     * 父区域ID
     */
    private Long parentId;
    
    /**
     * 区域级别(1-校区,2-楼栋,3-具体位置)
     */
    private Integer level;
    
    /**
     * 区域边界(GeoJSON格式)
     */
    private String boundary;
    
    /**
     * 中心点坐标(经度)
     */
    private Double centerLongitude;
    
    /**
     * 中心点坐标(纬度)
     */
    private Double centerLatitude;
    
    /**
     * 配送费用
     */
    private Double deliveryFee;
    
    /**
     * 预计配送时间(分钟)
     */
    private Integer estimatedDeliveryTime;
    
    /**
     * 状态(0-停用,1-启用)
     */
    private Integer status;
    
    /**
     * 区域描述
     */
    private String description;
    
    /**
     * 备注
     */
    private String remark;
}

express-service\src\main\java\com\campus\express\dto\DeliveryCreateRequest.java

package com.campus.express.dto;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.time.LocalDateTime;

/**
 * 创建配送任务请求
 */
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class DeliveryCreateRequest {
    
    /**
     * 快递ID
     */
    @NotNull(message = "快递ID不能为空")
    private Long expressId;
    
    /**
     * 快递单号
     */
    @NotBlank(message = "快递单号不能为空")
    private String trackingNumber;
    
    /**
     * 配送员ID
     */
    private Long courierId;
    
    /**
     * 配送员姓名
     */
    private String courierName;
    
    /**
     * 配送员电话
     */
    private String courierPhone;
    
    /**
     * 收件人姓名
     */
    @NotBlank(message = "收件人姓名不能为空")
    private String recipientName;
    
    /**
     * 收件人电话
     */
    @NotBlank(message = "收件人电话不能为空")
    private String recipientPhone;
    
    /**
     * 收件人地址
     */
    @NotBlank(message = "收件人地址不能为空")
    private String recipientAddress;
    
    /**
     * 配送区域
     */
    @NotBlank(message = "配送区域不能为空")
    private String deliveryArea;
    
    /**
     * 配送区域代码
     */
    private String areaCode;
    
    /**
     * 配送路线ID
     */
    private Long routeId;
    
    /**
     * 计划配送时间
     */
    private LocalDateTime plannedDeliveryTime;
    
    /**
     * 配送优先级(0-普通,1-优先,2-加急)
     */
    private Integer priority;
    
    /**
     * 配送费用
     */
    private Double deliveryFee;
    
    /**
     * 配送距离(米)
     */
    private Double distance;
    
    /**
     * 预计配送时长(分钟)
     */
    private Integer estimatedDuration;
    
    /**
     * 备注
     */
    private String remark;
}

express-service\src\main\java\com\campus\express\dto\DeliveryDTO.java

package com.campus.express.dto;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.time.LocalDateTime;

/**
 * 配送任务数据传输对象
 */
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class DeliveryDTO {
    
    /**
     * 配送任务ID
     */
    private Long id;
    
    /**
     * 快递ID
     */
    private Long expressId;
    
    /**
     * 快递单号
     */
    private String trackingNumber;
    
    /**
     * 配送员ID
     */
    private Long courierId;
    
    /**
     * 配送员姓名
     */
    private String courierName;
    
    /**
     * 配送员电话
     */
    private String courierPhone;
    
    /**
     * 收件人姓名
     */
    private String recipientName;
    
    /**
     * 收件人电话
     */
    private String recipientPhone;
    
    /**
     * 收件人地址
     */
    private String recipientAddress;
    
    /**
     * 配送区域
     */
    private String deliveryArea;
    
    /**
     * 配送区域代码
     */
    private String areaCode;
    
    /**
     * 配送路线ID
     */
    private Long routeId;
    
    /**
     * 配送路线名称
     */
    private String routeName;
    
    /**
     * 计划配送时间
     */
    private LocalDateTime plannedDeliveryTime;
    
    /**
     * 实际配送时间
     */
    private LocalDateTime actualDeliveryTime;
    
    /**
     * 配送完成时间
     */
    private LocalDateTime completedTime;
    
    /**
     * 配送验证码
     */
    private String deliveryCode;
    
    /**
     * 配送状态(0-待分配,1-待配送,2-配送中,3-已送达,4-已完成,5-已取消)
     */
    private Integer status;
    
    /**
     * 配送状态描述
     */
    private String statusDesc;
    
    /**
     * 配送优先级(0-普通,1-优先,2-加急)
     */
    private Integer priority;
    
    /**
     * 配送优先级描述
     */
    private String priorityDesc;
    
    /**
     * 配送费用
     */
    private Double deliveryFee;
    
    /**
     * 配送距离(米)
     */
    private Double distance;
    
    /**
     * 预计配送时长(分钟)
     */
    private Integer estimatedDuration;
    
    /**
     * 实际配送时长(分钟)
     */
    private Integer actualDuration;
    
    /**
     * 取消原因
     */
    private String cancelReason;
    
    /**
     * 备注
     */
    private String remark;
    
    /**
     * 创建时间
     */
    private LocalDateTime createdTime;
    
    /**
     * 更新时间
     */
    private LocalDateTime updatedTime;
}

express-service\src\main\java\com\campus\express\dto\DeliveryRouteCreateRequest.java

package com.campus.express.dto;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.util.List;

/**
 * 创建配送路线请求
 */
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class DeliveryRouteCreateRequest {
    
    /**
     * 路线编号
     */
    @NotBlank(message = "路线编号不能为空")
    private String routeNumber;
    
    /**
     * 路线名称
     */
    @NotBlank(message = "路线名称不能为空")
    private String name;
    
    /**
     * 起点
     */
    @NotBlank(message = "起点不能为空")
    private String startPoint;
    
    /**
     * 起点坐标(经度)
     */
    @NotNull(message = "起点经度不能为空")
    private Double startLongitude;
    
    /**
     * 起点坐标(纬度)
     */
    @NotNull(message = "起点纬度不能为空")
    private Double startLatitude;
    
    /**
     * 终点
     */
    @NotBlank(message = "终点不能为空")
    private String endPoint;
    
    /**
     * 终点坐标(经度)
     */
    @NotNull(message = "终点经度不能为空")
    private Double endLongitude;
    
    /**
     * 终点坐标(纬度)
     */
    @NotNull(message = "终点纬度不能为空")
    private Double endLatitude;
    
    /**
     * 途经点列表
     */
    private List<WaypointDTO> waypoints;
    
    /**
     * 配送区域
     */
    @NotBlank(message = "配送区域不能为空")
    private String area;
    
    /**
     * 区域代码
     */
    private String areaCode;
    
    /**
     * 路线距离(米)
     */
    private Double distance;
    
    /**
     * 预计时长(分钟)
     */
    private Integer estimatedDuration;
    
    /**
     * 配送员ID
     */
    private Long courierId;
    
    /**
     * 配送员姓名
     */
    private String courierName;
    
    /**
     * 状态(0-停用,1-启用)
     */
    private Integer status;
    
    /**
     * 路线描述
     */
    private String description;
    
    /**
     * 路线类型(0-步行,1-自行车,2-电动车,3-汽车)
     */
    private Integer routeType;
    
    /**
     * 备注
     */
    private String remark;
    
    /**
     * 途经点数据传输对象
     */
    @Data
    @Builder
    @NoArgsConstructor
    @AllArgsConstructor
    public static class WaypointDTO {
        
        /**
         * 途经点名称
         */
        @NotBlank(message = "途经点名称不能为空")
        private String name;
        
        /**
         * 途经点坐标(经度)
         */
        @NotNull(message = "途经点经度不能为空")
        private Double longitude;
        
        /**
         * 途经点坐标(纬度)
         */
        @NotNull(message = "途经点纬度不能为空")
        private Double latitude;
        
        /**
         * 途经点顺序
         */
        @NotNull(message = "途经点顺序不能为空")
        private Integer sequence;
    }
}

express-service\src\main\java\com\campus\express\dto\DeliveryRouteDTO.java

package com.campus.express.dto;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

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

/**
 * 配送路线数据传输对象
 */
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class DeliveryRouteDTO {
    
    /**
     * 配送路线ID
     */
    private Long id;
    
    /**
     * 路线编号
     */
    private String routeNumber;
    
    /**
     * 路线名称
     */
    private String name;
    
    /**
     * 起点
     */
    private String startPoint;
    
    /**
     * 起点坐标(经度)
     */
    private Double startLongitude;
    
    /**
     * 起点坐标(纬度)
     */
    private Double startLatitude;
    
    /**
     * 终点
     */
    private String endPoint;
    
    /**
     * 终点坐标(经度)
     */
    private Double endLongitude;
    
    /**
     * 终点坐标(纬度)
     */
    private Double endLatitude;
    
    /**
     * 途经点列表
     */
    private List<WaypointDTO> waypoints;
    
    /**
     * 配送区域
     */
    private String area;
    
    /**
     * 区域代码
     */
    private String areaCode;
    
    /**
     * 路线距离(米)
     */
    private Double distance;
    
    /**
     * 预计时长(分钟)
     */
    private Integer estimatedDuration;
    
    /**
     * 配送员ID
     */
    private Long courierId;
    
    /**
     * 配送员姓名
     */
    private String courierName;
    
    /**
     * 状态(0-停用,1-启用)
     */
    private Integer status;
    
    /**
     * 状态描述
     */
    private String statusDesc;
    
    /**
     * 路线描述
     */
    private String description;
    
    /**
     * 路线类型(0-步行,1-自行车,2-电动车,3-汽车)
     */
    private Integer routeType;
    
    /**
     * 路线类型描述
     */
    private String routeTypeDesc;
    
    /**
     * 备注
     */
    private String remark;
    
    /**
     * 创建时间
     */
    private LocalDateTime createdTime;
    
    /**
     * 更新时间
     */
    private LocalDateTime updatedTime;
    
    /**
     * 途经点数据传输对象
     */
    @Data
    @Builder
    @NoArgsConstructor
    @AllArgsConstructor
    public static class WaypointDTO {
        
        /**
         * 途经点名称
         */
        private String name;
        
        /**
         * 途经点坐标(经度)
         */
        private Double longitude;
        
        /**
         * 途经点坐标(纬度)
         */
        private Double latitude;
        
        /**
         * 途经点顺序
         */
        private Integer sequence;
    }
}

express-service\src\main\java\com\campus\express\dto\DeliveryRouteUpdateRequest.java

package com.campus.express.dto;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.List;

/**
 * 更新配送路线请求
 */
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class DeliveryRouteUpdateRequest {
    
    /**
     * 路线名称
     */
    private String name;
    
    /**
     * 起点
     */
    private String startPoint;
    
    /**
     * 起点坐标(经度)
     */
    private Double startLongitude;
    
    /**
     * 起点坐标(纬度)
     */
    private Double startLatitude;
    
    /**
     * 终点
     */
    private String endPoint;
    
    /**
     * 终点坐标(经度)
     */
    private Double endLongitude;
    
    /**
     * 终点坐标(纬度)
     */
    private Double endLatitude;
    
    /**
     * 途经点列表
     */
    private List<DeliveryRouteCreateRequest.WaypointDTO> waypoints;
    
    /**
     * 配送区域
     */
    private String area;
    
    /**
     * 区域代码
     */
    private String areaCode;
    
    /**
     * 路线距离(米)
     */
    private Double distance;
    
    /**
     * 预计时长(分钟)
     */
    private Integer estimatedDuration;
    
    /**
     * 配送员ID
     */
    private Long courierId;
    
    /**
     * 配送员姓名
     */
    private String courierName;
    
    /**
     * 状态(0-停用,1-启用)
     */
    private Integer status;
    
    /**
     * 路线描述
     */
    private String description;
    
    /**
     * 路线类型(0-步行,1-自行车,2-电动车,3-汽车)
     */
    private Integer routeType;
    
    /**
     * 备注
     */
    private String remark;
}

express-service\src\main\java\com\campus\express\dto\DeliveryUpdateRequest.java

package com.campus.express.dto;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.time.LocalDateTime;

/**
 * 更新配送任务请求
 */
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class DeliveryUpdateRequest {
    
    /**
     * 配送员ID
     */
    private Long courierId;
    
    /**
     * 配送员姓名
     */
    private String courierName;
    
    /**
     * 配送员电话
     */
    private String courierPhone;
    
    /**
     * 收件人姓名
     */
    private String recipientName;
    
    /**
     * 收件人电话
     */
    private String recipientPhone;
    
    /**
     * 收件人地址
     */
    private String recipientAddress;
    
    /**
     * 配送区域
     */
    private String deliveryArea;
    
    /**
     * 配送区域代码
     */
    private String areaCode;
    
    /**
     * 配送路线ID
     */
    private Long routeId;
    
    /**
     * 配送路线名称
     */
    private String routeName;
    
    /**
     * 计划配送时间
     */
    private LocalDateTime plannedDeliveryTime;
    
    /**
     * 实际配送时间
     */
    private LocalDateTime actualDeliveryTime;
    
    /**
     * 配送完成时间
     */
    private LocalDateTime completedTime;
    
    /**
     * 配送验证码
     */
    private String deliveryCode;
    
    /**
     * 配送状态(0-待分配,1-待配送,2-配送中,3-已送达,4-已完成,5-已取消)
     */
    private Integer status;
    
    /**
     * 配送优先级(0-普通,1-优先,2-加急)
     */
    private Integer priority;
    
    /**
     * 配送费用
     */
    private Double deliveryFee;
    
    /**
     * 配送距离(米)
     */
    private Double distance;
    
    /**
     * 预计配送时长(分钟)
     */
    private Integer estimatedDuration;
    
    /**
     * 实际配送时长(分钟)
     */
    private Integer actualDuration;
    
    /**
     * 取消原因
     */
    private String cancelReason;
    
    /**
     * 备注
     */
    private String remark;
}

express-service\src\main\java\com\campus\express\dto\ExpressCreateRequest.java

package com.campus.express.dto;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;

/**
 * 创建快递请求DTO
 */
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ExpressCreateRequest {

    /**
     * 快递单号
     */
    @NotBlank(message = "快递单号不能为空")
    @Size(max = 50, message = "快递单号长度不能超过50个字符")
    private String trackingNumber;

    /**
     * 快递公司
     */
    @NotBlank(message = "快递公司不能为空")
    @Size(max = 50, message = "快递公司长度不能超过50个字符")
    private String company;

    /**
     * 收件人姓名
     */
    @NotBlank(message = "收件人姓名不能为空")
    @Size(max = 50, message = "收件人姓名长度不能超过50个字符")
    private String recipientName;

    /**
     * 收件人电话
     */
    @NotBlank(message = "收件人电话不能为空")
    @Size(max = 20, message = "收件人电话长度不能超过20个字符")
    private String recipientPhone;

    /**
     * 收件人地址(宿舍楼栋)
     */
    @NotBlank(message = "收件人地址不能为空")
    @Size(max = 255, message = "收件人地址长度不能超过255个字符")
    private String recipientAddress;

    /**
     * 快递描述
     */
    @Size(max = 255, message = "快递描述长度不能超过255个字符")
    private String description;

    /**
     * 快递重量(克)
     */
    private Integer weight;

    /**
     * 快递员ID
     */
    private Long courierId;

    /**
     * 快递员姓名
     */
    private String courierName;

    /**
     * 快递员电话
     */
    private String courierPhone;

    /**
     * 用户ID(收件人)
     */
    private Long userId;

    /**
     * 备注
     */
    @Size(max = 255, message = "备注长度不能超过255个字符")
    private String remark;
}

express-service\src\main\java\com\campus\express\dto\ExpressDTO.java

package com.campus.express.dto;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import java.time.LocalDateTime;

/**
 * 快递数据传输对象
 */
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ExpressDTO {

    private Long id;

    /**
     * 快递单号
     */
    @NotBlank(message = "快递单号不能为空")
    @Size(max = 50, message = "快递单号长度不能超过50个字符")
    private String trackingNumber;

    /**
     * 快递公司
     */
    @NotBlank(message = "快递公司不能为空")
    @Size(max = 50, message = "快递公司长度不能超过50个字符")
    private String company;

    /**
     * 收件人姓名
     */
    @NotBlank(message = "收件人姓名不能为空")
    @Size(max = 50, message = "收件人姓名长度不能超过50个字符")
    private String recipientName;

    /**
     * 收件人电话
     */
    @NotBlank(message = "收件人电话不能为空")
    @Size(max = 20, message = "收件人电话长度不能超过20个字符")
    private String recipientPhone;

    /**
     * 收件人地址(宿舍楼栋)
     */
    @NotBlank(message = "收件人地址不能为空")
    @Size(max = 255, message = "收件人地址长度不能超过255个字符")
    private String recipientAddress;

    /**
     * 快递描述
     */
    @Size(max = 255, message = "快递描述长度不能超过255个字符")
    private String description;

    /**
     * 快递重量(克)
     */
    private Integer weight;

    /**
     * 快递状态:0-待揽收,1-已揽收,2-配送中,3-已送达,4-已签收,5-异常
     */
    @NotNull(message = "快递状态不能为空")
    private Integer status;

    /**
     * 快递状态描述
     */
    private String statusDesc;

    /**
     * 快递员ID
     */
    private Long courierId;

    /**
     * 快递员姓名
     */
    private String courierName;

    /**
     * 快递员电话
     */
    private String courierPhone;

    /**
     * 用户ID(收件人)
     */
    private Long userId;

    /**
     * 柜子ID
     */
    private Long cabinetId;

    /**
     * 柜子名称
     */
    private String cabinetName;

    /**
     * 柜子位置
     */
    private String cabinetLocation;

    /**
     * 柜子格口号
     */
    private String cabinetCell;

    /**
     * 取件码
     */
    private String pickupCode;

    /**
     * 揽收时间
     */
    private LocalDateTime collectionTime;

    /**
     * 送达时间
     */
    private LocalDateTime deliveryTime;

    /**
     * 签收时间
     */
    private LocalDateTime signTime;

    /**
     * 备注
     */
    @Size(max = 255, message = "备注长度不能超过255个字符")
    private String remark;

    /**
     * 创建时间
     */
    private LocalDateTime createdTime;

    /**
     * 更新时间
     */
    private LocalDateTime updatedTime;
}

express-service\src\main\java\com\campus\express\dto\ExpressTrackingDTO.java

package com.campus.express.dto;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import java.time.LocalDateTime;

/**
 * 快递跟踪记录数据传输对象
 */
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ExpressTrackingDTO {

    private Long id;

    /**
     * 快递ID
     */
    @NotNull(message = "快递ID不能为空")
    private Long expressId;

    /**
     * 快递单号
     */
    @NotBlank(message = "快递单号不能为空")
    @Size(max = 50, message = "快递单号长度不能超过50个字符")
    private String trackingNumber;

    /**
     * 操作类型:0-创建,1-揽收,2-配送中,3-送达,4-签收,5-异常
     */
    @NotNull(message = "操作类型不能为空")
    private Integer operationType;

    /**
     * 操作类型描述
     */
    private String operationTypeDesc;

    /**
     * 操作描述
     */
    @NotBlank(message = "操作描述不能为空")
    @Size(max = 255, message = "操作描述长度不能超过255个字符")
    private String description;

    /**
     * 操作人ID
     */
    private Long operatorId;

    /**
     * 操作人类型:0-系统,1-快递员,2-用户
     */
    @NotNull(message = "操作人类型不能为空")
    private Integer operatorType;

    /**
     * 操作人类型描述
     */
    private String operatorTypeDesc;

    /**
     * 操作人姓名
     */
    @Size(max = 50, message = "操作人姓名长度不能超过50个字符")
    private String operatorName;

    /**
     * 操作地点
     */
    @Size(max = 255, message = "操作地点长度不能超过255个字符")
    private String location;

    /**
     * 备注
     */
    @Size(max = 255, message = "备注长度不能超过255个字符")
    private String remark;

    /**
     * 创建时间(操作时间)
     */
    private LocalDateTime createdTime;
}

express-service\src\main\java\com\campus\express\dto\ExpressUpdateRequest.java

package com.campus.express.dto;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import javax.validation.constraints.Size;

/**
 * 更新快递请求DTO
 */
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ExpressUpdateRequest {

    /**
     * 快递公司
     */
    @Size(max = 50, message = "快递公司长度不能超过50个字符")
    private String company;

    /**
     * 收件人姓名
     */
    @Size(max = 50, message = "收件人姓名长度不能超过50个字符")
    private String recipientName;

    /**
     * 收件人电话
     */
    @Size(max = 20, message = "收件人电话长度不能超过20个字符")
    private String recipientPhone;

    /**
     * 收件人地址(宿舍楼栋)
     */
    @Size(max = 255, message = "收件人地址长度不能超过255个字符")
    private String recipientAddress;

    /**
     * 快递描述
     */
    @Size(max = 255, message = "快递描述长度不能超过255个字符")
    private String description;

    /**
     * 快递重量(克)
     */
    private Integer weight;

    /**
     * 快递状态:0-待揽收,1-已揽收,2-配送中,3-已送达,4-已签收,5-异常
     */
    private Integer status;

    /**
     * 快递员ID
     */
    private Long courierId;

    /**
     * 快递员姓名
     */
    private String courierName;

    /**
     * 快递员电话
     */
    private String courierPhone;

    /**
     * 用户ID(收件人)
     */
    private Long userId;

    /**
     * 柜子ID
     */
    private Long cabinetId;

    /**
     * 柜子格口号
     */
    private String cabinetCell;

    /**
     * 取件码
     */
    private String pickupCode;

    /**
     * 备注
     */
    @Size(max = 255, message = "备注长度不能超过255个字符")
    private String remark;
}

express-service\src\main\java\com\campus\express\dto\NotificationCreateRequest.java

package com.campus.express.dto;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;

/**
 * 创建通知请求
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
public class NotificationCreateRequest {

    /**
     * 通知标题
     */
    @NotBlank(message = "通知标题不能为空")
    @Size(max = 100, message = "通知标题长度不能超过100个字符")
    private String title;

    /**
     * 通知内容
     */
    @NotBlank(message = "通知内容不能为空")
    @Size(max = 500, message = "通知内容长度不能超过500个字符")
    private String content;

    /**
     * 通知类型:1-系统通知,2-快递通知,3-配送通知,4-活动通知
     */
    @NotNull(message = "通知类型不能为空")
    private Integer type;

    /**
     * 通知渠道:1-站内信,2-短信,3-邮件,4-推送
     */
    @NotNull(message = "通知渠道不能为空")
    private Integer channel;

    /**
     * 接收者ID
     */
    @NotNull(message = "接收者ID不能为空")
    private Long receiverId;

    /**
     * 接收者类型:1-用户,2-配送员,3-管理员
     */
    @NotNull(message = "接收者类型不能为空")
    private Integer receiverType;

    /**
     * 发送者ID
     */
    private Long senderId;

    /**
     * 发送者类型:1-系统,2-用户,3-配送员,4-管理员
     */
    private Integer senderType = 1; // 默认为系统发送

    /**
     * 关联业务ID
     */
    private Long businessId;

    /**
     * 关联业务类型:1-快递,2-配送,3-活动
     */
    private Integer businessType;

    /**
     * 备注
     */
    @Size(max = 200, message = "备注长度不能超过200个字符")
    private String remark;
}

express-service\src\main\java\com\campus\express\dto\NotificationDTO.java

package com.campus.express.dto;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.time.LocalDateTime;

/**
 * 通知DTO
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
public class NotificationDTO {

    /**
     * 通知ID
     */
    private Long id;

    /**
     * 通知标题
     */
    private String title;

    /**
     * 通知内容
     */
    private String content;

    /**
     * 通知类型:1-系统通知,2-快递通知,3-配送通知,4-活动通知
     */
    private Integer type;

    /**
     * 通知类型描述
     */
    private String typeDesc;

    /**
     * 通知渠道:1-站内信,2-短信,3-邮件,4-推送
     */
    private Integer channel;

    /**
     * 通知渠道描述
     */
    private String channelDesc;

    /**
     * 接收者ID
     */
    private Long receiverId;

    /**
     * 接收者类型:1-用户,2-配送员,3-管理员
     */
    private Integer receiverType;

    /**
     * 接收者类型描述
     */
    private String receiverTypeDesc;

    /**
     * 发送者ID
     */
    private Long senderId;

    /**
     * 发送者类型:1-系统,2-用户,3-配送员,4-管理员
     */
    private Integer senderType;

    /**
     * 发送者类型描述
     */
    private String senderTypeDesc;

    /**
     * 关联业务ID
     */
    private Long businessId;

    /**
     * 关联业务类型:1-快递,2-配送,3-活动
     */
    private Integer businessType;

    /**
     * 关联业务类型描述
     */
    private String businessTypeDesc;

    /**
     * 是否已读:0-未读,1-已读
     */
    private Integer readStatus;

    /**
     * 是否已读描述
     */
    private String readStatusDesc;

    /**
     * 是否发送成功:0-失败,1-成功
     */
    private Integer sendStatus;

    /**
     * 是否发送成功描述
     */
    private String sendStatusDesc;

    /**
     * 失败原因
     */
    private String failReason;

    /**
     * 重试次数
     */
    private Integer retryCount;

    /**
     * 下次重试时间
     */
    private LocalDateTime nextRetryTime;

    /**
     * 创建时间
     */
    private LocalDateTime createdTime;

    /**
     * 更新时间
     */
    private LocalDateTime updatedTime;

    /**
     * 备注
     */
    private String remark;
}

express-service\src\main\java\com\campus\express\dto\NotificationSendRequest.java

package com.campus.express.dto;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import java.util.List;
import java.util.Map;

/**
 * 发送通知请求
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
public class NotificationSendRequest {

    /**
     * 通知标题(如果不使用模板,则必填)
     */
    @Size(max = 100, message = "通知标题长度不能超过100个字符")
    private String title;

    /**
     * 通知内容(如果不使用模板,则必填)
     */
    @Size(max = 500, message = "通知内容长度不能超过500个字符")
    private String content;

    /**
     * 模板编码(如果使用模板,则必填)
     */
    private String templateCode;

    /**
     * 模板参数(如果使用模板,则必填)
     */
    private Map<String, Object> templateParams;

    /**
     * 通知类型:1-系统通知,2-快递通知,3-配送通知,4-活动通知
     */
    @NotNull(message = "通知类型不能为空")
    private Integer type;

    /**
     * 通知渠道:1-站内信,2-短信,3-邮件,4-推送
     */
    @NotNull(message = "通知渠道不能为空")
    private Integer channel;

    /**
     * 接收者ID列表
     */
    @NotEmpty(message = "接收者ID列表不能为空")
    private List<Long> receiverIds;

    /**
     * 接收者类型:1-用户,2-配送员,3-管理员
     */
    @NotNull(message = "接收者类型不能为空")
    private Integer receiverType;

    /**
     * 发送者ID
     */
    private Long senderId;

    /**
     * 发送者类型:1-系统,2-用户,3-配送员,4-管理员
     */
    private Integer senderType = 1; // 默认为系统发送

    /**
     * 关联业务ID
     */
    private Long businessId;

    /**
     * 关联业务类型:1-快递,2-配送,3-活动
     */
    private Integer businessType;

    /**
     * 是否立即发送:true-是,false-否(加入队列)
     */
    private Boolean sendImmediately = true;

    /**
     * 备注
     */
    @Size(max = 200, message = "备注长度不能超过200个字符")
    private String remark;
}

express-service\src\main\java\com\campus\express\dto\NotificationTemplateCreateRequest.java

package com.campus.express.dto;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Pattern;
import javax.validation.constraints.Size;

/**
 * 创建通知模板请求
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
public class NotificationTemplateCreateRequest {

    /**
     * 模板编码
     */
    @NotBlank(message = "模板编码不能为空")
    @Size(max = 50, message = "模板编码长度不能超过50个字符")
    @Pattern(regexp = "^[A-Z][A-Z0-9_]*$", message = "模板编码必须以大写字母开头,只能包含大写字母、数字和下划线")
    private String code;

    /**
     * 模板名称
     */
    @NotBlank(message = "模板名称不能为空")
    @Size(max = 100, message = "模板名称长度不能超过100个字符")
    private String name;

    /**
     * 模板标题
     */
    @NotBlank(message = "模板标题不能为空")
    @Size(max = 100, message = "模板标题长度不能超过100个字符")
    private String title;

    /**
     * 模板内容
     */
    @NotBlank(message = "模板内容不能为空")
    @Size(max = 1000, message = "模板内容长度不能超过1000个字符")
    private String content;

    /**
     * 模板类型:1-系统通知,2-快递通知,3-配送通知,4-活动通知
     */
    @NotNull(message = "模板类型不能为空")
    private Integer type;

    /**
     * 适用渠道:1-站内信,2-短信,3-邮件,4-推送
     */
    @NotNull(message = "适用渠道不能为空")
    private Integer channel;

    /**
     * 状态:0-停用,1-启用
     */
    @NotNull(message = "状态不能为空")
    private Integer status;

    /**
     * 备注
     */
    @Size(max = 200, message = "备注长度不能超过200个字符")
    private String remark;
}

express-service\src\main\java\com\campus\express\dto\NotificationTemplateDTO.java

package com.campus.express.dto;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.time.LocalDateTime;

/**
 * 通知模板DTO
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
public class NotificationTemplateDTO {

    /**
     * 模板ID
     */
    private Long id;

    /**
     * 模板编码
     */
    private String code;

    /**
     * 模板名称
     */
    private String name;

    /**
     * 模板标题
     */
    private String title;

    /**
     * 模板内容
     */
    private String content;

    /**
     * 模板类型:1-系统通知,2-快递通知,3-配送通知,4-活动通知
     */
    private Integer type;

    /**
     * 模板类型描述
     */
    private String typeDesc;

    /**
     * 适用渠道:1-站内信,2-短信,3-邮件,4-推送
     */
    private Integer channel;

    /**
     * 适用渠道描述
     */
    private String channelDesc;

    /**
     * 状态:0-停用,1-启用
     */
    private Integer status;

    /**
     * 状态描述
     */
    private String statusDesc;

    /**
     * 创建时间
     */
    private LocalDateTime createdTime;

    /**
     * 更新时间
     */
    private LocalDateTime updatedTime;

    /**
     * 备注
     */
    private String remark;
}

express-service\src\main\java\com\campus\express\dto\NotificationTemplateUpdateRequest.java

package com.campus.express.dto;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import javax.validation.constraints.Size;

/**
 * 更新通知模板请求
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
public class NotificationTemplateUpdateRequest {

    /**
     * 模板名称
     */
    @Size(max = 100, message = "模板名称长度不能超过100个字符")
    private String name;

    /**
     * 模板标题
     */
    @Size(max = 100, message = "模板标题长度不能超过100个字符")
    private String title;

    /**
     * 模板内容
     */
    @Size(max = 1000, message = "模板内容长度不能超过1000个字符")
    private String content;

    /**
     * 模板类型:1-系统通知,2-快递通知,3-配送通知,4-活动通知
     */
    private Integer type;

    /**
     * 适用渠道:1-站内信,2-短信,3-邮件,4-推送
     */
    private Integer channel;

    /**
     * 状态:0-停用,1-启用
     */
    private Integer status;

    /**
     * 备注
     */
    @Size(max = 200, message = "备注长度不能超过200个字符")
    private String remark;
}

express-service\src\main\java\com\campus\express\dto\NotificationUpdateRequest.java

package com.campus.express.dto;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import javax.validation.constraints.Size;

/**
 * 更新通知请求
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
public class NotificationUpdateRequest {

    /**
     * 通知标题
     */
    @Size(max = 100, message = "通知标题长度不能超过100个字符")
    private String title;

    /**
     * 通知内容
     */
    @Size(max = 500, message = "通知内容长度不能超过500个字符")
    private String content;

    /**
     * 通知类型:1-系统通知,2-快递通知,3-配送通知,4-活动通知
     */
    private Integer type;

    /**
     * 通知渠道:1-站内信,2-短信,3-邮件,4-推送
     */
    private Integer channel;

    /**
     * 是否已读:0-未读,1-已读
     */
    private Integer readStatus;

    /**
     * 是否发送成功:0-失败,1-成功
     */
    private Integer sendStatus;

    /**
     * 失败原因
     */
    @Size(max = 200, message = "失败原因长度不能超过200个字符")
    private String failReason;

    /**
     * 重试次数
     */
    private Integer retryCount;

    /**
     * 备注
     */
    @Size(max = 200, message = "备注长度不能超过200个字符")
    private String remark;
}

express-service\src\main\java\com\campus\express\exception\BusinessException.java

package com.campus.express.exception;

import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;

/**
 * 业务异常
 */
@ResponseStatus(HttpStatus.BAD_REQUEST)
public class BusinessException extends RuntimeException {

    public BusinessException(String message) {
        super(message);
    }

    public BusinessException(String message, Throwable cause) {
        super(message, cause);
    }
}

express-service\src\main\java\com\campus\express\exception\ErrorResponse.java

package com.campus.express.exception;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.time.LocalDateTime;

/**
 * 错误响应类
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class ErrorResponse {
    
    /**
     * HTTP 状态码
     */
    private int status;
    
    /**
     * 错误消息
     */
    private String message;
    
    /**
     * 请求路径
     */
    private String path;
    
    /**
     * 时间戳
     */
    private LocalDateTime timestamp;
}

express-service\src\main\java\com\campus\express\exception\GlobalExceptionHandler.java

package com.campus.express.exception;

import lombok.extern.slf4j.Slf4j;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.context.request.WebRequest;

import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * 全局异常处理器
 */
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {

    /**
     * 处理资源未找到异常
     */
    @ExceptionHandler(ResourceNotFoundException.class)
    public ResponseEntity<ErrorResponse> handleResourceNotFoundException(
            ResourceNotFoundException ex, WebRequest request) {
        log.error("Resource not found exception: {}", ex.getMessage());
        
        ErrorResponse errorResponse = new ErrorResponse(
                HttpStatus.NOT_FOUND.value(),
                ex.getMessage(),
                request.getDescription(false),
                LocalDateTime.now()
        );
        
        return new ResponseEntity<>(errorResponse, HttpStatus.NOT_FOUND);
    }

    /**
     * 处理业务异常
     */
    @ExceptionHandler(BusinessException.class)
    public ResponseEntity<ErrorResponse> handleBusinessException(
            BusinessException ex, WebRequest request) {
        log.error("Business exception: {}", ex.getMessage());
        
        ErrorResponse errorResponse = new ErrorResponse(
                HttpStatus.BAD_REQUEST.value(),
                ex.getMessage(),
                request.getDescription(false),
                LocalDateTime.now()
        );
        
        return new ResponseEntity<>(errorResponse, HttpStatus.BAD_REQUEST);
    }

    /**
     * 处理参数校验异常 (Bean Validation)
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity<ErrorResponse> handleMethodArgumentNotValidException(
            MethodArgumentNotValidException ex, WebRequest request) {
        log.error("Validation exception: {}", ex.getMessage());
        
        BindingResult bindingResult = ex.getBindingResult();
        Map<String, String> errors = new HashMap<>();
        
        for (FieldError fieldError : bindingResult.getFieldErrors()) {
            errors.put(fieldError.getField(), fieldError.getDefaultMessage());
        }
        
        String errorMessage = errors.entrySet().stream()
                .map(entry -> entry.getKey() + ": " + entry.getValue())
                .collect(Collectors.joining(", "));
        
        ErrorResponse errorResponse = new ErrorResponse(
                HttpStatus.BAD_REQUEST.value(),
                "Validation failed: " + errorMessage,
                request.getDescription(false),
                LocalDateTime.now()
        );
        
        return new ResponseEntity<>(errorResponse, HttpStatus.BAD_REQUEST);
    }

    /**
     * 处理约束违反异常
     */
    @ExceptionHandler(ConstraintViolationException.class)
    public ResponseEntity<ErrorResponse> handleConstraintViolationException(
            ConstraintViolationException ex, WebRequest request) {
        log.error("Constraint violation exception: {}", ex.getMessage());
        
        Set<ConstraintViolation<?>> violations = ex.getConstraintViolations();
        String errorMessage = violations.stream()
                .map(violation -> violation.getPropertyPath() + ": " + violation.getMessage())
                .collect(Collectors.joining(", "));
        
        ErrorResponse errorResponse = new ErrorResponse(
                HttpStatus.BAD_REQUEST.value(),
                "Validation failed: " + errorMessage,
                request.getDescription(false),
                LocalDateTime.now()
        );
        
        return new ResponseEntity<>(errorResponse, HttpStatus.BAD_REQUEST);
    }

    /**
     * 处理数据完整性违反异常
     */
    @ExceptionHandler(DataIntegrityViolationException.class)
    public ResponseEntity<ErrorResponse> handleDataIntegrityViolationException(
            DataIntegrityViolationException ex, WebRequest request) {
        log.error("Data integrity violation exception: {}", ex.getMessage());
        
        ErrorResponse errorResponse = new ErrorResponse(
                HttpStatus.CONFLICT.value(),
                "Database error: " + ex.getMostSpecificCause().getMessage(),
                request.getDescription(false),
                LocalDateTime.now()
        );
        
        return new ResponseEntity<>(errorResponse, HttpStatus.CONFLICT);
    }

    /**
     * 处理所有其他异常
     */
    @ExceptionHandler(Exception.class)
    public ResponseEntity<ErrorResponse> handleGlobalException(
            Exception ex, WebRequest request) {
        log.error("Global exception: {}", ex.getMessage(), ex);
        
        ErrorResponse errorResponse = new ErrorResponse(
                HttpStatus.INTERNAL_SERVER_ERROR.value(),
                "An unexpected error occurred: " + ex.getMessage(),
                request.getDescription(false),
                LocalDateTime.now()
        );
        
        return new ResponseEntity<>(errorResponse, HttpStatus.INTERNAL_SERVER_ERROR);
    }
}

express-service\src\main\java\com\campus\express\exception\ResourceNotFoundException.java

package com.campus.express.exception;

import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;

/**
 * 资源未找到异常
 */
@ResponseStatus(HttpStatus.NOT_FOUND)
public class ResourceNotFoundException extends RuntimeException {

    public ResourceNotFoundException(String message) {
        super(message);
    }

    public ResourceNotFoundException(String message, Throwable cause) {
        super(message, cause);
    }
}

express-service\src\main\java\com\campus\express\model\Cabinet.java

package com.campus.express.model;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.UpdateTimestamp;

import javax.persistence.*;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import java.time.LocalDateTime;

/**
 * 快递柜实体类
 */
@Data
@Entity
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Table(name = "cabinet")
public class Cabinet {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    /**
     * 柜子编号
     */
    @NotBlank
    @Size(max = 50)
    @Column(unique = true, nullable = false)
    private String cabinetNumber;

    /**
     * 柜子名称
     */
    @NotBlank
    @Size(max = 100)
    @Column(nullable = false)
    private String name;

    /**
     * 柜子位置
     */
    @NotBlank
    @Size(max = 255)
    @Column(nullable = false)
    private String location;

    /**
     * 柜子状态:0-停用,1-启用
     */
    @NotNull
    @Column(nullable = false)
    private Integer status;

    /**
     * 总格口数
     */
    @NotNull
    @Column(nullable = false)
    private Integer totalCells;

    /**
     * 可用格口数
     */
    @NotNull
    @Column(nullable = false)
    private Integer availableCells;

    /**
     * 负责人ID
     */
    private Long managerId;

    /**
     * 负责人姓名
     */
    @Size(max = 50)
    private String managerName;

    /**
     * 负责人电话
     */
    @Size(max = 20)
    private String managerPhone;

    /**
     * 备注
     */
    @Size(max = 255)
    private String remark;

    /**
     * 创建时间
     */
    @CreationTimestamp
    @Column(nullable = false, updatable = false)
    private LocalDateTime createdTime;

    /**
     * 更新时间
     */
    @UpdateTimestamp
    @Column(nullable = false)
    private LocalDateTime updatedTime;
}

express-service\src\main\java\com\campus\express\model\CabinetCell.java

package com.campus.express.model;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.UpdateTimestamp;

import javax.persistence.*;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import java.time.LocalDateTime;

/**
 * 快递柜格口实体类
 */
@Data
@Entity
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Table(name = "cabinet_cell")
public class CabinetCell {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    /**
     * 柜子ID
     */
    @NotNull
    @Column(nullable = false)
    private Long cabinetId;

    /**
     * 格口编号
     */
    @NotBlank
    @Size(max = 20)
    @Column(nullable = false)
    private String cellNumber;

    /**
     * 格口大小:0-小,1-中,2-大
     */
    @NotNull
    @Column(nullable = false)
    private Integer size;

    /**
     * 格口状态:0-空闲,1-占用,2-故障
     */
    @NotNull
    @Column(nullable = false)
    private Integer status;

    /**
     * 当前存放的快递ID
     */
    private Long expressId;

    /**
     * 存入时间
     */
    private LocalDateTime storageTime;

    /**
     * 取出时间
     */
    private LocalDateTime retrievalTime;

    /**
     * 备注
     */
    @Size(max = 255)
    private String remark;

    /**
     * 创建时间
     */
    @CreationTimestamp
    @Column(nullable = false, updatable = false)
    private LocalDateTime createdTime;

    /**
     * 更新时间
     */
    @UpdateTimestamp
    @Column(nullable = false)
    private LocalDateTime updatedTime;
}

express-service\src\main\java\com\campus\express\model\Delivery.java

package com.campus.express.model;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.time.LocalDateTime;

/**
 * 配送任务实体类
 */
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Delivery {
    
    /**
     * 配送任务ID
     */
    private Long id;
    
    /**
     * 快递ID
     */
    private Long expressId;
    
    /**
     * 快递单号
     */
    private String trackingNumber;
    
    /**
     * 配送员ID
     */
    private Long courierId;
    
    /**
     * 配送员姓名
     */
    private String courierName;
    
    /**
     * 配送员电话
     */
    private String courierPhone;
    
    /**
     * 收件人姓名
     */
    private String recipientName;
    
    /**
     * 收件人电话
     */
    private String recipientPhone;
    
    /**
     * 收件人地址
     */
    private String recipientAddress;
    
    /**
     * 配送区域
     */
    private String deliveryArea;
    
    /**
     * 配送区域代码
     */
    private String areaCode;
    
    /**
     * 配送路线ID
     */
    private Long routeId;
    
    /**
     * 配送路线名称
     */
    private String routeName;
    
    /**
     * 计划配送时间
     */
    private LocalDateTime plannedDeliveryTime;
    
    /**
     * 实际配送时间
     */
    private LocalDateTime actualDeliveryTime;
    
    /**
     * 配送完成时间
     */
    private LocalDateTime completedTime;
    
    /**
     * 配送验证码
     */
    private String deliveryCode;
    
    /**
     * 配送状态(0-待分配,1-待配送,2-配送中,3-已送达,4-已完成,5-已取消)
     */
    private Integer status;
    
    /**
     * 配送优先级(0-普通,1-优先,2-加急)
     */
    private Integer priority;
    
    /**
     * 配送费用
     */
    private Double deliveryFee;
    
    /**
     * 配送距离(米)
     */
    private Double distance;
    
    /**
     * 预计配送时长(分钟)
     */
    private Integer estimatedDuration;
    
    /**
     * 实际配送时长(分钟)
     */
    private Integer actualDuration;
    
    /**
     * 取消原因
     */
    private String cancelReason;
    
    /**
     * 备注
     */
    private String remark;
    
    /**
     * 创建时间
     */
    private LocalDateTime createdTime;
    
    /**
     * 更新时间
     */
    private LocalDateTime updatedTime;
}

express-service\src\main\java\com\campus\express\model\DeliveryArea.java

package com.campus.express.model;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.time.LocalDateTime;

/**
 * 配送区域实体类
 */
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class DeliveryArea {
    
    /**
     * 配送区域ID
     */
    private Long id;
    
    /**
     * 区域代码
     */
    private String areaCode;
    
    /**
     * 区域名称
     */
    private String name;
    
    /**
     * 父区域ID
     */
    private Long parentId;
    
    /**
     * 区域级别(1-校区,2-楼栋,3-具体位置)
     */
    private Integer level;
    
    /**
     * 区域边界(GeoJSON格式存储)
     */
    private String boundary;
    
    /**
     * 中心点坐标(经度)
     */
    private Double centerLongitude;
    
    /**
     * 中心点坐标(纬度)
     */
    private Double centerLatitude;
    
    /**
     * 配送费用
     */
    private Double deliveryFee;
    
    /**
     * 预计配送时间(分钟)
     */
    private Integer estimatedDeliveryTime;
    
    /**
     * 状态(0-停用,1-启用)
     */
    private Integer status;
    
    /**
     * 区域描述
     */
    private String description;
    
    /**
     * 备注
     */
    private String remark;
    
    /**
     * 创建时间
     */
    private LocalDateTime createdTime;
    
    /**
     * 更新时间
     */
    private LocalDateTime updatedTime;
}

express-service\src\main\java\com\campus\express\model\DeliveryRoute.java

package com.campus.express.model;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.time.LocalDateTime;

/**
 * 配送路线实体类
 */
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class DeliveryRoute {
    
    /**
     * 配送路线ID
     */
    private Long id;
    
    /**
     * 路线编号
     */
    private String routeNumber;
    
    /**
     * 路线名称
     */
    private String name;
    
    /**
     * 起点
     */
    private String startPoint;
    
    /**
     * 起点坐标(经度)
     */
    private Double startLongitude;
    
    /**
     * 起点坐标(纬度)
     */
    private Double startLatitude;
    
    /**
     * 终点
     */
    private String endPoint;
    
    /**
     * 终点坐标(经度)
     */
    private Double endLongitude;
    
    /**
     * 终点坐标(纬度)
     */
    private Double endLatitude;
    
    /**
     * 途经点(JSON格式存储)
     */
    private String waypoints;
    
    /**
     * 配送区域
     */
    private String area;
    
    /**
     * 区域代码
     */
    private String areaCode;
    
    /**
     * 路线距离(米)
     */
    private Double distance;
    
    /**
     * 预计时长(分钟)
     */
    private Integer estimatedDuration;
    
    /**
     * 配送员ID
     */
    private Long courierId;
    
    /**
     * 配送员姓名
     */
    private String courierName;
    
    /**
     * 状态(0-停用,1-启用)
     */
    private Integer status;
    
    /**
     * 路线描述
     */
    private String description;
    
    /**
     * 路线类型(0-步行,1-自行车,2-电动车,3-汽车)
     */
    private Integer routeType;
    
    /**
     * 备注
     */
    private String remark;
    
    /**
     * 创建时间
     */
    private LocalDateTime createdTime;
    
    /**
     * 更新时间
     */
    private LocalDateTime updatedTime;
}

express-service\src\main\java\com\campus\express\model\Express.java

package com.campus.express.model;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.UpdateTimestamp;

import javax.persistence.*;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import java.time.LocalDateTime;

/**
 * 快递实体类
 */
@Data
@Entity
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Table(name = "express")
public class Express {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    /**
     * 快递单号
     */
    @NotBlank
    @Size(max = 50)
    @Column(unique = true, nullable = false)
    private String trackingNumber;

    /**
     * 快递公司
     */
    @NotBlank
    @Size(max = 50)
    @Column(nullable = false)
    private String company;

    /**
     * 收件人姓名
     */
    @NotBlank
    @Size(max = 50)
    @Column(nullable = false)
    private String recipientName;

    /**
     * 收件人电话
     */
    @NotBlank
    @Size(max = 20)
    @Column(nullable = false)
    private String recipientPhone;

    /**
     * 收件人地址(宿舍楼栋)
     */
    @NotBlank
    @Size(max = 255)
    @Column(nullable = false)
    private String recipientAddress;

    /**
     * 快递描述
     */
    @Size(max = 255)
    private String description;

    /**
     * 快递重量(克)
     */
    private Integer weight;

    /**
     * 快递状态:0-待揽收,1-已揽收,2-配送中,3-已送达,4-已签收,5-异常
     */
    @NotNull
    @Column(nullable = false)
    private Integer status;

    /**
     * 快递员ID
     */
    private Long courierId;

    /**
     * 用户ID(收件人)
     */
    private Long userId;

    /**
     * 柜子ID
     */
    private Long cabinetId;

    /**
     * 柜子格口号
     */
    private String cabinetCell;

    /**
     * 取件码
     */
    @Size(max = 10)
    private String pickupCode;

    /**
     * 揽收时间
     */
    private LocalDateTime collectionTime;

    /**
     * 送达时间
     */
    private LocalDateTime deliveryTime;

    /**
     * 签收时间
     */
    private LocalDateTime signTime;

    /**
     * 备注
     */
    @Size(max = 255)
    private String remark;

    /**
     * 创建时间
     */
    @CreationTimestamp
    @Column(nullable = false, updatable = false)
    private LocalDateTime createdTime;

    /**
     * 更新时间
     */
    @UpdateTimestamp
    @Column(nullable = false)
    private LocalDateTime updatedTime;
}

express-service\src\main\java\com\campus\express\model\ExpressTracking.java

package com.campus.express.model;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.hibernate.annotations.CreationTimestamp;

import javax.persistence.*;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import java.time.LocalDateTime;

/**
 * 快递跟踪记录实体类
 */
@Data
@Entity
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Table(name = "express_tracking")
public class ExpressTracking {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    /**
     * 快递ID
     */
    @NotNull
    @Column(nullable = false)
    private Long expressId;

    /**
     * 快递单号
     */
    @NotBlank
    @Size(max = 50)
    @Column(nullable = false)
    private String trackingNumber;

    /**
     * 操作类型:0-创建,1-揽收,2-配送中,3-送达,4-签收,5-异常
     */
    @NotNull
    @Column(nullable = false)
    private Integer operationType;

    /**
     * 操作描述
     */
    @NotBlank
    @Size(max = 255)
    @Column(nullable = false)
    private String description;

    /**
     * 操作人ID
     */
    private Long operatorId;

    /**
     * 操作人类型:0-系统,1-快递员,2-用户
     */
    @NotNull
    @Column(nullable = false)
    private Integer operatorType;

    /**
     * 操作人姓名
     */
    @Size(max = 50)
    private String operatorName;

    /**
     * 操作地点
     */
    @Size(max = 255)
    private String location;

    /**
     * 备注
     */
    @Size(max = 255)
    private String remark;

    /**
     * 创建时间(操作时间)
     */
    @CreationTimestamp
    @Column(nullable = false, updatable = false)
    private LocalDateTime createdTime;
}

express-service\src\main\java\com\campus\express\model\Notification.java

package com.campus.express.model;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import javax.persistence.*;
import java.time.LocalDateTime;

/**
 * 通知实体类
 */
@Entity
@Table(name = "notification")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Notification {

    /**
     * 通知ID
     */
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    /**
     * 通知标题
     */
    @Column(nullable = false, length = 100)
    private String title;

    /**
     * 通知内容
     */
    @Column(nullable = false, length = 500)
    private String content;

    /**
     * 通知类型:1-系统通知,2-快递通知,3-配送通知,4-活动通知
     */
    @Column(nullable = false)
    private Integer type;

    /**
     * 通知渠道:1-站内信,2-短信,3-邮件,4-推送
     */
    @Column(nullable = false)
    private Integer channel;

    /**
     * 接收者ID
     */
    @Column(nullable = false)
    private Long receiverId;

    /**
     * 接收者类型:1-用户,2-配送员,3-管理员
     */
    @Column(nullable = false)
    private Integer receiverType;

    /**
     * 发送者ID
     */
    private Long senderId;

    /**
     * 发送者类型:1-系统,2-用户,3-配送员,4-管理员
     */
    private Integer senderType;

    /**
     * 关联业务ID
     */
    private Long businessId;

    /**
     * 关联业务类型:1-快递,2-配送,3-活动
     */
    private Integer businessType;

    /**
     * 是否已读:0-未读,1-已读
     */
    @Column(nullable = false)
    private Integer readStatus;

    /**
     * 是否发送成功:0-失败,1-成功
     */
    @Column(nullable = false)
    private Integer sendStatus;

    /**
     * 失败原因
     */
    @Column(length = 200)
    private String failReason;

    /**
     * 重试次数
     */
    private Integer retryCount;

    /**
     * 下次重试时间
     */
    private LocalDateTime nextRetryTime;

    /**
     * 创建时间
     */
    @Column(nullable = false)
    private LocalDateTime createdTime;

    /**
     * 更新时间
     */
    @Column(nullable = false)
    private LocalDateTime updatedTime;

    /**
     * 备注
     */
    @Column(length = 200)
    private String remark;
}

express-service\src\main\java\com\campus\express\model\NotificationTemplate.java

package com.campus.express.model;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import javax.persistence.*;
import java.time.LocalDateTime;

/**
 * 通知模板实体类
 */
@Entity
@Table(name = "notification_template")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class NotificationTemplate {

    /**
     * 模板ID
     */
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    /**
     * 模板编码
     */
    @Column(nullable = false, unique = true, length = 50)
    private String code;

    /**
     * 模板名称
     */
    @Column(nullable = false, length = 100)
    private String name;

    /**
     * 模板标题
     */
    @Column(nullable = false, length = 100)
    private String title;

    /**
     * 模板内容
     */
    @Column(nullable = false, length = 1000)
    private String content;

    /**
     * 模板类型:1-系统通知,2-快递通知,3-配送通知,4-活动通知
     */
    @Column(nullable = false)
    private Integer type;

    /**
     * 适用渠道:1-站内信,2-短信,3-邮件,4-推送
     */
    @Column(nullable = false)
    private Integer channel;

    /**
     * 状态:0-停用,1-启用
     */
    @Column(nullable = false)
    private Integer status;

    /**
     * 创建时间
     */
    @Column(nullable = false)
    private LocalDateTime createdTime;

    /**
     * 更新时间
     */
    @Column(nullable = false)
    private LocalDateTime updatedTime;

    /**
     * 备注
     */
    @Column(length = 200)
    private String remark;
}

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

package com.campus.express.repository;

import com.campus.express.model.CabinetCell;
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.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;

import java.util.List;
import java.util.Optional;

/**
 * 快递柜格口数据访问接口
 */
@Repository
public interface CabinetCellRepository extends JpaRepository<CabinetCell, Long>, JpaSpecificationExecutor<CabinetCell> {

    /**
     * 根据柜子ID和格口编号查询格口
     *
     * @param cabinetId 柜子ID
     * @param cellNumber 格口编号
     * @return 格口信息
     */
    Optional<CabinetCell> findByCabinetIdAndCellNumber(Long cabinetId, String cellNumber);

    /**
     * 根据柜子ID查询格口列表
     *
     * @param cabinetId 柜子ID
     * @return 格口列表
     */
    List<CabinetCell> findByCabinetId(Long cabinetId);

    /**
     * 根据柜子ID和状态查询格口列表
     *
     * @param cabinetId 柜子ID
     * @param status 状态
     * @return 格口列表
     */
    List<CabinetCell> findByCabinetIdAndStatus(Long cabinetId, Integer status);

    /**
     * 根据快递ID查询格口
     *
     * @param expressId 快递ID
     * @return 格口信息
     */
    Optional<CabinetCell> findByExpressId(Long expressId);

    /**
     * 根据柜子ID分页查询格口列表
     *
     * @param cabinetId 柜子ID
     * @param pageable 分页参数
     * @return 格口分页列表
     */
    Page<CabinetCell> findByCabinetId(Long cabinetId, Pageable pageable);

    /**
     * 根据柜子ID和格口大小查询空闲格口列表
     *
     * @param cabinetId 柜子ID
     * @param size 格口大小
     * @param status 格口状态(0-空闲)
     * @return 格口列表
     */
    List<CabinetCell> findByCabinetIdAndSizeAndStatus(Long cabinetId, Integer size, Integer status);

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

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

    /**
     * 查询指定柜子中的空闲格口数量
     *
     * @param cabinetId 柜子ID
     * @param status 状态(0-空闲)
     * @return 空闲格口数量
     */
    @Query("SELECT COUNT(c) FROM CabinetCell c WHERE c.cabinetId = :cabinetId AND c.status = :status")
    long countAvailableCells(@Param("cabinetId") Long cabinetId, @Param("status") Integer status);
}

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

package com.campus.express.repository;

import com.campus.express.model.Cabinet;
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.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;

import java.util.List;
import java.util.Optional;

/**
 * 快递柜数据访问接口
 */
@Repository
public interface CabinetRepository extends JpaRepository<Cabinet, Long>, JpaSpecificationExecutor<Cabinet> {

    /**
     * 根据柜子编号查询柜子
     *
     * @param cabinetNumber 柜子编号
     * @return 柜子信息
     */
    Optional<Cabinet> findByCabinetNumber(String cabinetNumber);

    /**
     * 根据柜子名称或位置模糊查询柜子列表
     *
     * @param keyword 关键字
     * @param pageable 分页参数
     * @return 柜子分页列表
     */
    @Query("SELECT c FROM Cabinet c WHERE c.name LIKE %:keyword% OR c.location LIKE %:keyword%")
    Page<Cabinet> findByNameOrLocationContaining(@Param("keyword") String keyword, Pageable pageable);

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

    /**
     * 根据负责人ID查询柜子列表
     *
     * @param managerId 负责人ID
     * @return 柜子列表
     */
    List<Cabinet> findByManagerId(Long managerId);

    /**
     * 查询可用格口数大于0的柜子列表
     *
     * @param pageable 分页参数
     * @return 柜子分页列表
     */
    Page<Cabinet> findByAvailableCellsGreaterThan(Integer availableCells, Pageable pageable);

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

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

    /**
     * 统计指定负责人的柜子数量
     *
     * @param managerId 负责人ID
     * @return 柜子数量
     */
    long countByManagerId(Long managerId);
}

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

package com.campus.express.repository;

import com.campus.express.model.DeliveryArea;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import java.util.List;
import java.util.Optional;

/**
 * 配送区域数据访问接口
 */
@Repository
public interface DeliveryAreaRepository extends JpaRepository<DeliveryArea, Long> {
    
    /**
     * 根据区域代码查询配送区域
     *
     * @param areaCode 区域代码
     * @return 配送区域
     */
    Optional<DeliveryArea> findByAreaCode(String areaCode);
    
    /**
     * 根据区域名称模糊查询配送区域分页列表
     *
     * @param name 区域名称关键字
     * @param pageable 分页参数
     * @return 配送区域分页列表
     */
    Page<DeliveryArea> findByNameContaining(String name, Pageable pageable);
    
    /**
     * 根据状态查询配送区域分页列表
     *
     * @param status 状态
     * @param pageable 分页参数
     * @return 配送区域分页列表
     */
    Page<DeliveryArea> findByStatus(Integer status, Pageable pageable);
    
    /**
     * 根据区域级别查询配送区域列表
     *
     * @param level 区域级别
     * @return 配送区域列表
     */
    List<DeliveryArea> findByLevel(Integer level);
    
    /**
     * 根据父区域ID查询子区域列表
     *
     * @param parentId 父区域ID
     * @return 子区域列表
     */
    List<DeliveryArea> findByParentId(Long parentId);
    
    /**
     * 根据父区域ID和状态查询子区域列表
     *
     * @param parentId 父区域ID
     * @param status 状态
     * @return 子区域列表
     */
    List<DeliveryArea> findByParentIdAndStatus(Long parentId, Integer status);
    
    /**
     * 统计指定状态的配送区域数量
     *
     * @param status 状态
     * @return 配送区域数量
     */
    long countByStatus(Integer status);
    
    /**
     * 统计指定级别的配送区域数量
     *
     * @param level 区域级别
     * @return 配送区域数量
     */
    long countByLevel(Integer level);
    
    /**
     * 统计指定父区域的子区域数量
     *
     * @param parentId 父区域ID
     * @return 子区域数量
     */
    long countByParentId(Long parentId);
}

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

package com.campus.express.repository;

import com.campus.express.model.Delivery;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

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

/**
 * 配送任务数据访问接口
 */
@Repository
public interface DeliveryRepository extends JpaRepository<Delivery, Long> {
    
    /**
     * 根据快递ID查询配送任务
     *
     * @param expressId 快递ID
     * @return 配送任务
     */
    Optional<Delivery> findByExpressId(Long expressId);
    
    /**
     * 根据配送员ID分页查询配送任务列表
     *
     * @param courierId 配送员ID
     * @param pageable 分页参数
     * @return 配送任务分页列表
     */
    Page<Delivery> findByCourierId(Long courierId, Pageable pageable);
    
    /**
     * 根据状态分页查询配送任务列表
     *
     * @param status 状态
     * @param pageable 分页参数
     * @return 配送任务分页列表
     */
    Page<Delivery> findByStatus(Integer status, Pageable pageable);
    
    /**
     * 根据配送区域分页查询配送任务列表
     *
     * @param area 配送区域
     * @param pageable 分页参数
     * @return 配送任务分页列表
     */
    Page<Delivery> findByDeliveryAreaContaining(String area, Pageable pageable);
    
    /**
     * 根据创建时间范围分页查询配送任务列表
     *
     * @param startTime 开始时间
     * @param endTime 结束时间
     * @param pageable 分页参数
     * @return 配送任务分页列表
     */
    Page<Delivery> findByCreatedTimeBetween(LocalDateTime startTime, LocalDateTime endTime, Pageable pageable);
    
    /**
     * 根据计划配送时间范围分页查询配送任务列表
     *
     * @param startTime 开始时间
     * @param endTime 结束时间
     * @param pageable 分页参数
     * @return 配送任务分页列表
     */
    Page<Delivery> findByPlannedDeliveryTimeBetween(LocalDateTime startTime, LocalDateTime endTime, Pageable pageable);
    
    /**
     * 统计配送员的配送任务数量
     *
     * @param courierId 配送员ID
     * @return 配送任务数量
     */
    long countByCourierId(Long courierId);
    
    /**
     * 统计指定状态的配送任务数量
     *
     * @param status 状态
     * @return 配送任务数量
     */
    long countByStatus(Integer status);
    
    /**
     * 统计指定区域的配送任务数量
     *
     * @param area 配送区域
     * @return 配送任务数量
     */
    long countByDeliveryAreaContaining(String area);
    
    /**
     * 统计指定时间范围内的配送任务数量
     *
     * @param startTime 开始时间
     * @param endTime 结束时间
     * @return 配送任务数量
     */
    long countByCreatedTimeBetween(LocalDateTime startTime, LocalDateTime endTime);
}

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

package com.campus.express.repository;

import com.campus.express.model.DeliveryRoute;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import java.util.List;
import java.util.Optional;

/**
 * 配送路线数据访问接口
 */
@Repository
public interface DeliveryRouteRepository extends JpaRepository<DeliveryRoute, Long> {
    
    /**
     * 根据路线编号查询配送路线
     *
     * @param routeNumber 路线编号
     * @return 配送路线
     */
    Optional<DeliveryRoute> findByRouteNumber(String routeNumber);
    
    /**
     * 根据名称模糊查询配送路线分页列表
     *
     * @param name 名称关键字
     * @param pageable 分页参数
     * @return 配送路线分页列表
     */
    Page<DeliveryRoute> findByNameContaining(String name, Pageable pageable);
    
    /**
     * 根据状态查询配送路线分页列表
     *
     * @param status 状态
     * @param pageable 分页参数
     * @return 配送路线分页列表
     */
    Page<DeliveryRoute> findByStatus(Integer status, Pageable pageable);
    
    /**
     * 根据区域查询配送路线列表
     *
     * @param area 区域
     * @return 配送路线列表
     */
    List<DeliveryRoute> findByArea(String area);
    
    /**
     * 根据配送员ID查询配送路线列表
     *
     * @param courierId 配送员ID
     * @return 配送路线列表
     */
    List<DeliveryRoute> findByCourierId(Long courierId);
    
    /**
     * 根据区域和状态查询配送路线列表
     *
     * @param area 区域
     * @param status 状态
     * @return 配送路线列表
     */
    List<DeliveryRoute> findByAreaAndStatus(String area, Integer status);
    
    /**
     * 统计指定状态的配送路线数量
     *
     * @param status 状态
     * @return 配送路线数量
     */
    long countByStatus(Integer status);
    
    /**
     * 统计指定区域的配送路线数量
     *
     * @param area 区域
     * @return 配送路线数量
     */
    long countByArea(String area);
    
    /**
     * 统计指定配送员的配送路线数量
     *
     * @param courierId 配送员ID
     * @return 配送路线数量
     */
    long countByCourierId(Long courierId);
}

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

package com.campus.express.repository;

import com.campus.express.model.Express;
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.JpaSpecificationExecutor;
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 ExpressRepository extends JpaRepository<Express, Long>, JpaSpecificationExecutor<Express> {

    /**
     * 根据快递单号查询快递
     *
     * @param trackingNumber 快递单号
     * @return 快递信息
     */
    Optional<Express> findByTrackingNumber(String trackingNumber);

    /**
     * 根据用户ID查询快递列表
     *
     * @param userId 用户ID
     * @param pageable 分页参数
     * @return 快递分页列表
     */
    Page<Express> findByUserId(Long userId, Pageable pageable);

    /**
     * 根据快递员ID查询快递列表
     *
     * @param courierId 快递员ID
     * @param pageable 分页参数
     * @return 快递分页列表
     */
    Page<Express> findByCourierId(Long courierId, Pageable pageable);

    /**
     * 根据柜子ID查询快递列表
     *
     * @param cabinetId 柜子ID
     * @param pageable 分页参数
     * @return 快递分页列表
     */
    Page<Express> findByCabinetId(Long cabinetId, Pageable pageable);

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

    /**
     * 根据收件人姓名或电话模糊查询快递列表
     *
     * @param keyword 关键字
     * @param pageable 分页参数
     * @return 快递分页列表
     */
    @Query("SELECT e FROM Express e WHERE e.recipientName LIKE %:keyword% OR e.recipientPhone LIKE %:keyword%")
    Page<Express> findByRecipientNameOrPhoneContaining(@Param("keyword") String keyword, Pageable pageable);

    /**
     * 根据快递公司查询快递列表
     *
     * @param company 快递公司
     * @param pageable 分页参数
     * @return 快递分页列表
     */
    Page<Express> findByCompany(String company, Pageable pageable);

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

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

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

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

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

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

    /**
     * 查询未分配快递员的快递列表
     *
     * @param pageable 分页参数
     * @return 快递分页列表
     */
    Page<Express> findByCourierIdIsNull(Pageable pageable);

    /**
     * 查询未分配柜子的快递列表
     *
     * @param pageable 分页参数
     * @return 快递分页列表
     */
    Page<Express> findByCabinetIdIsNull(Pageable pageable);
}

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

package com.campus.express.repository;

import com.campus.express.model.ExpressTracking;
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.JpaSpecificationExecutor;
import org.springframework.stereotype.Repository;

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

/**
 * 快递跟踪记录数据访问接口
 */
@Repository
public interface ExpressTrackingRepository extends JpaRepository<ExpressTracking, Long>, JpaSpecificationExecutor<ExpressTracking> {

    /**
     * 根据快递ID查询跟踪记录列表
     *
     * @param expressId 快递ID
     * @return 跟踪记录列表
     */
    List<ExpressTracking> findByExpressIdOrderByCreatedTimeDesc(Long expressId);

    /**
     * 根据快递单号查询跟踪记录列表
     *
     * @param trackingNumber 快递单号
     * @return 跟踪记录列表
     */
    List<ExpressTracking> findByTrackingNumberOrderByCreatedTimeDesc(String trackingNumber);

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

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

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

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

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

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

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

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

天天进步2015

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

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

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

打赏作者

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

抵扣说明:

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

余额充值