《Spring Boot+策略模式:企业级度假订单Excel导入系统的架构演进与技术实现》

前言

在数字化时代背景下,订单管理系统的高效性与灵活性成为企业竞争力的核心要素。本文档详细剖析了一个基于 策略模式 的度假订单导入系统,通过分层架构设计实现了多源异构数据的标准化处理。系统以 Spring Boot 为核心框架,结合 MyBatis Plus、Excel 工具库和 Swagger 文档化工具,构建了一个支持动态扩展、批量处理、事务安全的订单管理解决方案。

本文将从数据模型定义、策略模式实现、核心业务流程三个维度展开,深入解读系统如何通过 灵活的时间解析高效的事务管理可扩展的架构设计,实现从 Excel 文件到数据库记录的全链路处理。无论是架构师、开发人员还是技术管理者,均可通过本文快速掌握系统设计精髓。

阅读指引

为高效理解本系统,建议按以下路径阅读:

一、架构概览
  1. 数据层(DAO)
    • 核心模型:RouteOrderInfoDO(线路订单数据对象)
    • 核心能力:字段映射、状态枚举关联、多租户支持
    • 路径:数据层 > RouteOrderInfoDO
  1. 服务层(Service)
    • 策略模式:VacationOrderImportStrategy(策略接口)与 TempOrderImportStrategy(具体策略)
    • 工厂模式:VacationProductCategoryFactory(策略路由工厂)
    • 路径:服务层 > 策略 服务层 > 酒店品类工厂
  1. 控制层(Controller)
    • 核心接口:RouteOrderExcelController(Excel 导入/导出)
    • 功能清单:订单导出、多策略导入、模板下载
    • 路径:控制层 > RouteOrderExcelController
二、核心流程
  1. 订单导入流程
    • 阶段分解:Excel 解析 → 数据转换 → 批量持久化
    • 关键代码:TempOrderImportStrategy.importExcel()
    • 路径:数据处理流程
  1. 时间处理机制
    • 双阶段解析:格式标准化 → 智能降级解析
    • 设计亮点:parseDateTime() 方法的多格式兼容性
    • 路径:时间处理
三、扩展指南
  • 新增订单来源:扩展策略接口与工厂路由
  • 性能优化:批量 SQL 优化与异步处理建议
  • 安全增强:文件校验与防 SQL 注入方案

核心总结

1. 架构亮点
维度 设计亮点
分层架构 清晰的 Controller-Service-DAO 分层,通过策略模式实现业务逻辑解耦
扩展性 策略接口标准化 + 工厂路由动态分发,新增订单来源只需实现新策略类,符合开闭原则
性能 批量 SQL 操作(insertBatch/updateBatch)减少数据库交互,事务注解保障原子性
健壮性 双重时间解析机制(支持 3+ 时间格式)、异常分类处理(IO/格式/数据库异常)
2. 关键技术栈
diff 复制代码
- 核心框架:Spring Boot + MyBatis Plus  
- 策略模式:策略接口 + 工厂路由  
- Excel 处理:Alibaba EasyExcel + 自定义 DictConvert 转换器  
- 事务管理:Spring @Transactional 注解  
- 文档化:Swagger3 + @Schema 注解  
3. 性能数据(示例)
指标 数值(单文件)
千级订单处理耗时 ≤2s(含IO与DB操作)
内存占用峰值 ≤50MB(万级订单)
批量插入效率 5000条/秒
4. 扩展建议
  • 校验增强 :添加 @Validated 参数校验与自定义校验注解
  • 监控集成:通过 Micrometer 实现导入成功率、耗时等指标采集
  • 异步化改造 :使用 @Async 注解实现大文件异步导入,提升接口响应速度

通过本文的系统解读,读者可快速掌握基于策略模式的订单导入架构设计方法论,并基于现有代码扩展出符合业务需求的定制化功能。

数据层

RouteOrderInfoDO

scala 复制代码
package cn.iocoder.central.module.vacation.dal.dataobject.order;


import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;
        import java.math.BigDecimal;
import java.time.LocalDateTime;

/**
 * 线路订单信息 DO
 *
 * @author 陕文旅
 */
@TableName("order_route_order_info")
@KeySequence("order_route_order_info_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class RouteOrderInfoDO extends BaseDO {

    /**
     * 主键
     */
    @TableId
    private Long id;

    /**
     * 渠道订单号
     */
    private String systemCode;


    /**
     * 订单状态
     *
     * 枚举 {@link TODO order_module_order_status 对应的类}
     */
    private Integer orderStatus;

    /**
     * 订单总金额
     */
    private BigDecimal totalAmount;

    /**
     * 支付方式
     *
     * 枚举 {@link TODO order_module_payment_method 对应的类}
     */
    private Integer paymentMethod;

    /**
     * 支付状态
     *
     * 枚举 {@link TODO order_module_payment_status 对应的类}
     */
    private Integer paymentStatus;

    /**
     * 路线ID
     */
    private Long routeId;

    /**
     * 路线名称
     */
    private String routeName;

    /**
     * 游玩人数
     */
    private Integer playPersonNumber;

    /**
     * 下单时间
     */
    private LocalDateTime buyTime;

    /**
     * 游玩开始时间
     */
    private LocalDateTime playStartTime;

    /**
     * 游玩结束时间
     */
    private LocalDateTime playEndTime;

    /**
     * 取票人/收货人/联系人
     */
    private String orderUserName;

    /**
     * 手机号
     */
    private String orderUserPhone;

    /**
     * 创建者
     */
    private String creator;

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

    /**
     * 更新者
     */
    private String updater;

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

    /**
     * 是否删除
     */
    private Boolean deleted;

    /**
     * 租户编号
     */
    private Long tenantId;
    /*
     * 来源渠道
     * */
    private String origin;
}

服务层

策略

TempOrderImportStrategy

java 复制代码
package cn.iocoder.central.module.vacation.service.order.excel.strategy;


import cn.iocoder.central.module.vacation.controller.admin.order.excel.vo.RouteImportRespVo;
import cn.iocoder.central.module.vacation.dal.dataobject.order.RouteOrderInfoDO;
import cn.iocoder.central.module.vacation.dal.mysql.order.RouteOrderInfoMapper;
import cn.iocoder.central.module.vacation.service.order.excel.vo.TempVacationOrderVo;
import cn.iocoder.yudao.framework.common.util.date.DateTimeFormatterUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeParseException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

@Component
@Slf4j
public class TempOrderImportStrategy implements VacationOrderImportStrategy {

    @Resource
    private RouteOrderInfoMapper routeOrderInfoMapper;


    @Override
    @Transactional(rollbackFor = Exception.class)
    public RouteImportRespVo importExcel(MultipartFile file, String origin) throws IOException {
        // 1. 读取Excel文件内容
        List<TempVacationOrderVo> orderList = ExcelUtils.read(file, TempVacationOrderVo.class);

        // 2. 初始化数据容器
        List<RouteOrderInfoDO> createRouteOrderList = new ArrayList<>();  // 待新增订单列表
        List<RouteOrderInfoDO> updateRouteOrderList = new ArrayList<>();  // 待更新订单列表
        List<String> addOrderCodes = new ArrayList<>();  // 新增订单编号集合
        List<String> updateOrderCodes = new ArrayList<>();  // 更新订单编号集合

        // 3. 处理每条订单数据
        for (TempVacationOrderVo order : orderList) {
            // 3.1 转换VO为DO对象
            RouteOrderInfoDO RouteOrderInfoDO = BeanUtils.toBean(order, RouteOrderInfoDO.class);
            RouteOrderInfoDO.setOrigin(origin);  // 设置订单来源

            // 3.2 格式化时间字段
            RouteOrderInfoDO.setBuyTime(parseDateTime(DateTimeFormatterUtils.formatDateTime(order.getBuyTime())));
            RouteOrderInfoDO.setPlayStartTime(parseDateTime(DateTimeFormatterUtils.formatDateTime(order.getPlayStartTime())));
            RouteOrderInfoDO.setPlayEndTime(parseDateTime(DateTimeFormatterUtils.formatDateTime(order.getPlayEndTime())));

            // 3.3 检查订单是否已存在
            Long tableId = RouteOrderInfoMapper.selectIdBySystemCodeAndOrigin(
                    RouteOrderInfoDO.getSystemCode(),
                    RouteOrderInfoDO.getOrigin()
            );

            if (tableId != null) {
                // 3.4 已存在订单 - 准备更新
                RouteOrderInfoDO.setId(tableId);
                updateRouteOrderList.add(RouteOrderInfoDO);
                updateOrderCodes.add(RouteOrderInfoDO.getSystemCode());
            } else {
                // 3.5 新订单 - 准备新增
                createRouteOrderList.add(RouteOrderInfoDO);
                addOrderCodes.add(RouteOrderInfoDO.getSystemCode());
            }
        }

        // 4. 批量数据库操作
        if (!createRouteOrderList.isEmpty()) {
            routeOrderInfoMapper.insertBatch(createRouteOrderList);// 批量插入新订单
        }
        if (!updateRouteOrderList.isEmpty()) {
            routeOrderInfoMapper.updateBatch(updateRouteOrderList);  // 批量更新已有订单
        }


        RouteImportRespVo respVo = new RouteImportRespVo();
        respVo.setAddOrderCodes(addOrderCodes);  // 设置新增订单编号列表
        respVo.setUpdateOrderCodes(updateOrderCodes);  // 设置更新订单编号列表
        respVo.setFailureOrderCodes(Collections.emptyList());  // 空列表表示没有失败订单

        return respVo;
    }


    /**
     * 自动解析日期或日期时间字符串为LocalDateTime
     * 支持以下格式:
     * - 日期格式:yyyy-MM-dd
     * - 日期时间格式:yyyy-MM-dd'T'HH:mm:ss 或 yyyy-MM-dd HH:mm:ss
     *
     * @param timeString 输入的时间字符串
     * @return 转换后的LocalDateTime
     * @throws DateTimeParseException 如果时间字符串格式不正确
     */
    public static LocalDateTime parseDateTime(String timeString) throws DateTimeParseException {
        try {
            // 尝试直接解析为LocalDateTime(适用于包含时间的格式)
            return LocalDateTime.parse(timeString);
        } catch (DateTimeParseException e1) {
            try {
                // 尝试解析为LocalDate(适用于只有日期的格式)
                LocalDate date = LocalDate.parse(timeString);
                // 将日期与默认时间00:00:00组合
                return date.atTime(LocalTime.MIDNIGHT);
            } catch (DateTimeParseException e2) {
                // 如果两种格式都不匹配,抛出异常
                throw new DateTimeParseException("无法解析时间字符串:" + timeString, timeString, 0);
            }
        }
    }

}

数据处理流程

我来详细讲解 <mcsymbol name="TempOrderImportStrategy" filename="TempOrderImportStrategy.java" path="c:\project\centralservice\central-module-vacation\central-module-vacation-biz\src\main\java\cn\iocoder\central\module\vacation\service\order\excel\strategy\TempOrderImportStrategy.java" startline="1" type="class"></mcsymbol> 的数据处理流程:

1. 整体流程

这是一个实现 VacationOrderImportStrategy 接口的订单导入策略类,专门处理临时订单的Excel导入,主要流程分为4个阶段:

  1. 读取Excel数据
  2. 初始化数据容器
  3. 逐条处理订单数据
  4. 批量数据库操作
2. 详细数据流转过程
2.1 读取Excel阶段
ini 复制代码
List<TempVacationOrderVo> orderList = ExcelUtils.read(file, TempVacationOrderVo.class);
  • 输入:MultipartFile 类型的Excel文件
  • 输出:List<TempVacationOrderVo> 订单VO列表
  • 技术:使用 ExcelUtils.read() 将Excel自动映射为Java对象列表
2.2 数据容器初始化
ini 复制代码
List<RouteOrderInfoDO> createRouteOrderList = new ArrayList<>();  // 待新增
List<RouteOrderInfoDO> updateRouteOrderList = new ArrayList<>();  // 待更新
List<String> addOrderCodes = new ArrayList<>();  // 新增订单号
List<String> updateOrderCodes = new ArrayList<>();  // 更新订单号
  • 创建4个集合分别存储:
    • 需要新增的DO对象
    • 需要更新的DO对象
    • 新增订单的编号(用于返回结果)
    • 更新订单的编号(用于返回结果)
2.3 单条数据处理流程

对每条订单数据执行以下操作:

  1. VO转DO转换
ini 复制代码
RouteOrderInfoDO RouteOrderInfoDO = BeanUtils.toBean(order, RouteOrderInfoDO.class);
RouteOrderInfoDO.setOrigin(origin);  // 设置来源
  • 使用 BeanUtils.toBean() 进行对象属性拷贝
  • 手动设置订单来源字段
  1. 时间字段处理
vbscript 复制代码
RouteOrderInfoDO.setBuyTime(parseDateTime(...));
RouteOrderInfoDO.setPlayStartTime(parseDateTime(...)); 
RouteOrderInfoDO.setPlayEndTime(parseDateTime(...));
  • 调用 parseDateTime() 方法统一处理各种时间格式
  • 支持两种时间格式:
    • 完整日期时间格式:yyyy-MM-dd'T'HH:mm:ssyyyy-MM-dd HH:mm:ss
    • 简单日期格式:yyyy-MM-dd (会自动补全时间为00:00:00)
  1. 订单存在性检查
ini 复制代码
Long tableId = RouteOrderInfoMapper.selectIdBySystemCodeAndOrigin(
        RouteOrderInfoDO.getSystemCode(),
        RouteOrderInfoDO.getOrigin()
);
  • 根据系统编号和来源查询是否已存在相同订单
  • 返回已存在订单的ID或null
  1. 数据分类存储
csharp 复制代码
if (tableId != null) {
    // 更新逻辑
    RouteOrderInfoDO.setId(tableId);
    updateRouteOrderList.add(RouteOrderInfoDO);
    updateOrderCodes.add(RouteOrderInfoDO.getSystemCode());
} else {
    // 新增逻辑
    createRouteOrderList.add(RouteOrderInfoDO);
    addOrderCodes.add(RouteOrderInfoDO.getSystemCode());
}
2.4 批量数据库操作
scss 复制代码
if (!createRouteOrderList.isEmpty()) {
    routeOrderInfoMapper.insertBatch(createRouteOrderList);
}
if (!updateRouteOrderList.isEmpty()) {
    routeOrderInfoMapper.updateBatch(updateRouteOrderList); 
}
  • 使用批量操作提高性能
  • 分别处理新增和更新操作
2.5 返回结果构造
ini 复制代码
RouteImportRespVo respVo = new RouteImportRespVo();
respVo.setAddOrderCodes(addOrderCodes);
respVo.setUpdateOrderCodes(updateOrderCodes); 
respVo.setFailureOrderCodes(Collections.emptyList());
  • 构造包含处理结果的响应对象
  • 当前实现没有失败订单(空列表)
3. 关键设计点
  1. 事务管理@Transactional 确保整个导入过程原子性
  2. 批量处理:使用批量SQL提高性能
  3. 灵活的时间解析parseDateTime() 方法支持多种时间格式
  4. 结果追踪:记录所有处理过的订单编号
  5. 策略模式:实现标准接口,便于扩展其他导入方式
4. 数据流转图
scss 复制代码
Excel文件 
  → List<TempVacationOrderVo> 
    → List<RouteOrderInfoDO> (分新增/更新)
      → 批量SQL操作
        → RouteImportRespVo (结果统计)

这个实现很好地平衡了性能、可靠性和可维护性,是典型的批处理数据导入解决方案。

!!时间处理

我来详细讲解 <mcsymbol name="TempOrderImportStrategy" filename="TempOrderImportStrategy.java" path="c:\project\centralservice\central-module-vacation\central-module-vacation-biz\src\main\java\cn\iocoder\central\module\vacation\service\order\excel\strategy\TempOrderImportStrategy.java" startline="52" type="function"></mcsymbol> 中的时间处理逻辑:

1. 时间处理流程分解

这三行代码处理了三个时间字段(购买时间、游玩开始时间、游玩结束时间),处理流程完全一致,分为两个关键步骤:

vbscript 复制代码
RouteOrderInfoDO.setBuyTime(parseDateTime(DateTimeFormatterUtils.formatDateTime(order.getBuyTime())));
  1. 格式化阶段DateTimeFormatterUtils.formatDateTime()
    • 将原始时间对象格式化为标准字符串
    • 确保时间格式统一化处理
  1. 解析阶段parseDateTime()
    • 将格式化后的字符串解析为 LocalDateTime 对象
    • 支持多种时间格式的灵活解析
2. 关键技术点解析
2.1 格式化阶段

DateTimeFormatterUtils.formatDateTime() 的作用:

  • 输入:可能是各种格式的时间对象(Date/LocalDateTime/String等)
  • 输出:统一格式的字符串(如 yyyy-MM-dd HH:mm:ss
  • 确保后续解析阶段输入格式的一致性
2.2 解析阶段

parseDateTime() 方法的智能解析逻辑:

  1. 优先尝试解析完整时间格式
    • 支持 ISO 格式:yyyy-MM-dd'T'HH:mm:ss
    • 支持常规格式:yyyy-MM-dd HH:mm:ss
  1. 降级解析日期格式
    • 当完整时间解析失败时,尝试解析简单日期:yyyy-MM-dd
    • 自动补全时间为 00:00:00
  1. 异常处理
    • 双重异常捕获确保解析可靠性
    • 最终会抛出包含详细错误信息的 DateTimeParseException
3. 设计优势
  1. 格式兼容性
    • 可以处理来自不同数据源的多样化时间格式
    • Excel、数据库、API等不同来源的时间数据都能适配
  1. 数据一致性
    • 最终统一存储为 LocalDateTime 类型
    • 确保业务逻辑处理时时间格式统一
  1. 健壮性设计
    • 通过格式化+解析的两阶段处理增加可靠性
    • 清晰的错误提示便于问题排查
4. 处理流程图解
scss 复制代码
原始时间对象(order.getBuyTime())
  → DateTimeFormatterUtils.formatDateTime() 
    → 标准化时间字符串 
      → parseDateTime() 
        → LocalDateTime对象
          → 设置到RouteOrderInfoDO
5. 典型应用场景

这种处理方式特别适合:

  • 从Excel导入的时间数据(可能有各种格式)
  • 需要与数据库时间字段对接的场景
  • 需要严格时间计算的业务逻辑(如游玩时长计算)

这种设计既保证了灵活性,又确保了最终数据的一致性,是时间处理的最佳实践。

VacationOrderImportStrategy

java 复制代码
package cn.iocoder.central.module.vacation.service.order.excel.strategy;


import cn.iocoder.central.module.vacation.controller.admin.order.excel.vo.RouteImportRespVo;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;

public interface VacationOrderImportStrategy {

    /**
     * 导入Excel
     *
     * @param file
     * @param origin
     * @return
     */
    RouteImportRespVo importExcel(MultipartFile file, String origin) throws IOException;

}

我来详细讲解 <mcsymbol name="VacationOrderImportStrategy" filename="VacationOrderImportStrategy.java" path="c:\project\centralservice\central-module-vacation\central-module-vacation-biz\src\main\java\cn\iocoder\central\module\vacation\service\order\excel\strategy\VacationOrderImportStrategy.java" startline="1" type="interface"></mcsymbol> 这个策略接口的核心作用:

核心定位

这是订单导入功能的标准策略接口,采用策略模式设计,主要作用包括:

  1. 统一导入规范
    • 定义所有订单导入策略必须实现的importExcel方法
    • 标准化输入参数和返回值格式
  1. 多源订单支持
    • 通过origin参数区分不同来源的订单
    • 允许为不同来源实现定制化导入逻辑
  1. 解耦设计
    • 将导入逻辑与业务调用方解耦
    • 新增导入渠道只需实现新策略,无需修改现有代码

方法详解

arduino 复制代码
RouteImportRespVo importExcel(MultipartFile file, String origin) throws IOException;
  • 参数说明
    • file:需要导入的Excel文件
    • origin:订单来源标识(如"shanwenlv")
  • 返回值
    • 统一返回RouteImportRespVo结果对象
    • 包含成功/失败订单统计信息
  • 异常处理
    • 声明抛出IOException处理文件读取异常
    • 实现类可添加更多特定异常

设计优势

  1. 扩展性
    • 轻松支持新增订单来源
    • 已有策略互不影响
  1. 可维护性
    • 每种导入策略独立实现
    • 便于单独测试和修改
  1. 业务隔离
    • 不同来源的导入逻辑完全隔离
    • 避免代码耦合

典型实现

如之前看到的TempOrderImportStrategy就是其具体实现,处理特定来源的订单导入。

这个接口是订单导入功能的核心抽象,为系统提供了灵活、可扩展的导入架构。

service-vo

kotlin 复制代码
package cn.iocoder.central.module.vacation.service.order.excel.vo;

import cn.iocoder.central.module.vacation.enums.DictTypeConstants;
import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
import cn.iocoder.yudao.framework.excel.core.convert.DictConvert;
import com.alibaba.excel.annotation.ExcelProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;

import java.math.BigDecimal;

@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = false) // 设置 chain = false,避免用户导入有问题
public class TempVacationOrderVo {

    //    导入渠道订单号
    @ExcelProperty("订单编号")
    private String systemCode;

    @ExcelProperty(value = "订单状态", converter = DictConvert.class)
    @DictFormat(DictTypeConstants.ORDER_MODULE_ORDER_STATUS)
    private String orderStatus;

    @ExcelProperty("产品名称")
    private String routeName;

    @ExcelProperty("下单时间")
    private String buyTime;

    @ExcelProperty("游玩人数")
    private Integer playPersonNumber;

    @ExcelProperty("开始时间")
    private String playStartTime;

    @ExcelProperty("结束时间")
    private String playEndTime;

    @ExcelProperty("金额")
    private BigDecimal totalAmount;


    @ExcelProperty(value = "支付方式", converter = DictConvert.class)
    @DictFormat(DictTypeConstants.ORDER_MODULE_PAYMENT_METHOD)
    private String paymentMethod;

    @ExcelProperty(value = "支付状态", converter = DictConvert.class)
    @DictFormat(DictTypeConstants.ORDER_MODULE_PAYMENT_STATUS)
    private String paymentStatus;

    @ExcelProperty("联系人")
    private String orderUserName;

    @ExcelProperty("联系方式")
    private String orderUserPhone;
}

我来详细讲解 TempVacationOrderVo 这个类的作用和设计特点:

核心定位

这是一个Excel导入数据映射的VO(Value Object)类,专门用于临时度假订单的Excel导入场景,主要作用包括:

  1. Excel数据映射
    • 定义了Excel列名与Java字段的映射关系
    • 通过注解实现Excel列到对象属性的自动转换
  1. 数据格式转换
    • 处理字典类型数据的转换(如订单状态、支付方式等)
    • 规范时间、金额等特殊字段的格式

关键设计特点

  1. Excel映射配置
    • 使用@ExcelProperty注解配置Excel列名
    • 支持自定义转换器(如字典转换)
  1. 字典数据处理
    • 通过@DictFormat关联字典常量
    • 使用DictConvert实现字典值与显示文本的转换
  1. 数据类型设计
    • 金额使用BigDecimal保证精度
    • 时间字段使用String保持原始格式
  1. Lombok简化
    • 自动生成getter/setter等方法
    • 禁用链式调用避免导入问题

业务价值

  1. 标准化导入
    • 统一Excel导入的数据结构
    • 确保不同来源订单的导入一致性
  1. 扩展性
    • 新增字段只需添加属性
    • 不影响现有导入逻辑
  1. 数据校验
    • 通过注解实现基础校验
    • 为后续业务处理提供干净数据

这个VO类在订单导入流程中起到了承上启下的关键作用,既对接了前端Excel文件的结构,又为后端业务处理提供了标准化的数据对象。

酒店品类工厂

java 复制代码
package cn.iocoder.central.module.vacation.service.order.excel;


import cn.iocoder.central.module.vacation.service.order.excel.strategy.TempOrderImportStrategy;
import cn.iocoder.central.module.vacation.service.order.excel.strategy.VacationOrderImportStrategy;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Component;

/**
 * 酒店品类工厂工厂
 */
@Component
public class VacationProductCategoryFactory {

    private static final String TEMP_TYPE = "shanwenlv";

    @Resource
    private TempOrderImportStrategy tempOrderImportStrategy;

    public VacationOrderImportStrategy getImportStrategy(String origin) {
        return switch (origin) {
            case TEMP_TYPE -> tempOrderImportStrategy;
            default -> null;
        };
    }
}

我来详细讲解 VacationProductCategoryFactory 这个工厂类的作用和设计特点:

核心定位

这是一个基于策略模式的工厂类,专门用于管理和提供不同来源的订单导入策略实现,主要作用包括:

  1. 策略集中管理
    • 统一管理所有订单导入策略实现
    • 作为策略模式的入口点
  1. 策略动态分发
    • 根据订单来源标识动态返回对应策略
    • 隔离调用方与具体策略实现的耦合

关键设计特点

  1. 简单工厂模式
    • 通过静态常量定义支持的来源类型
    • 使用switch-case返回对应策略
  1. 松耦合设计
    • 依赖注入具体策略实现
    • 新增策略只需扩展工厂方法
  1. 可扩展性
    • 预留了default分支便于扩展
    • 新增来源只需添加case分支

业务价值

  1. 统一入口
    • 为订单导入提供标准化的策略获取方式
    • 简化调用方的使用复杂度
  1. 灵活扩展
    • 支持动态新增订单来源类型
    • 不影响现有导入流程
  1. 维护便利
    • 策略变更只需修改工厂类
    • 不影响业务调用代码

这个工厂类在订单导入系统中起到了策略路由的关键作用,是策略模式的核心实现组件。

控制层

RouteOrderExcelController

less 复制代码
package cn.iocoder.central.module.vacation.controller.admin.order.excel;

import cn.hutool.core.util.ObjectUtil;
import cn.iocoder.central.module.vacation.controller.admin.order.excel.vo.RouteImportRespVo;
import cn.iocoder.central.module.vacation.controller.admin.order.excel.vo.RouteImportVo;
import cn.iocoder.central.module.vacation.controller.admin.order.vo.RouteOrderInfoPageReqVO;
import cn.iocoder.central.module.vacation.controller.admin.order.vo.RouteOrderInfoRespVO;
import cn.iocoder.central.module.vacation.dal.dataobject.order.RouteOrderInfoDO;
import cn.iocoder.central.module.vacation.service.order.RouteOrderInfoService;
import cn.iocoder.central.module.vacation.service.order.excel.VacationProductCategoryFactory;
import cn.iocoder.central.module.vacation.service.order.excel.strategy.TempOrderImportStrategy;
import cn.iocoder.central.module.vacation.service.order.excel.strategy.VacationOrderImportStrategy;
import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Parameters;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.Valid;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.util.List;

import static cn.iocoder.central.module.vacation.enums.ErrorCodeConstants.*;
import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.error;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;

@Tag(name = "管理后台 - 度假订单 Excel")
@RestController
@RequestMapping("/vacation/order/excel")
@Validated
public class RouteOrderExcelController {

    @Resource
    private RouteOrderInfoService routeOrderInfoService;

    @Resource
    private VacationProductCategoryFactory vacationProductCategoryFactory;


    @GetMapping("/export-excel")
    @Operation(summary = "导出度假订单信息 Excel")
//    @PreAuthorize("@ss.hasPermission('order:child-route-order-info:export')")
    @ApiAccessLog(operateType = EXPORT)
    public void exportChildRouteOrderInfoExcel(@Valid RouteOrderInfoPageReqVO pageReqVO,
                                               HttpServletResponse response) throws IOException {
        pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
        List<RouteOrderInfoRespVO> list = routeOrderInfoService.getRouteOrderInfoPage(pageReqVO).getList();
        // 导出 Excel
        ExcelUtils.write(response, "路线订单信息.xls", "数据", RouteOrderInfoRespVO.class,
                BeanUtils.toBean(list, RouteOrderInfoRespVO.class));

    }

    @PostMapping("/import")
    @Operation(summary = "导入度假订单信息")
    @Parameters({
            @Parameter(name = "file", description = "Excel 文件", required = true),
            @Parameter(name = "origin", description = "来源", example = "true")
    })
//    @PreAuthorize("@ss.hasPermission('order:child-route-order-info:import')")
    public CommonResult<RouteImportRespVo> importExcel(@RequestParam("file") MultipartFile file,
                                                       @RequestParam("origin") String origin) throws Exception {
        VacationOrderImportStrategy importStrategy = vacationProductCategoryFactory.getImportStrategy(origin);
        if (ObjectUtil.isEmpty(importStrategy)) {
            return error(VACATION_NOT_FOUND_STRATEGY);
        }
        try {
            RouteImportRespVo orderImportRespVo = importStrategy.importExcel(file, origin);
            return success(orderImportRespVo);
        } catch (IOException e) {
            return error(VACATION_IMPORT_ERROR);
        }

    }

    @GetMapping("/get-route-import-template")
    @Operation(summary = "获取度假导入模板")
    public void RouteImportTemplate(HttpServletResponse response) throws IOException {
        ExcelUtils.write(response, "度假导入模板.xlsx", "度假列表", RouteImportVo.class, null);
    }

}

是一个Spring Boot的RESTful控制器,用于处理与度假订单Excel文件相关的操作。具体功能如下:1. 提供导出度假订单信息到Excel文件的接口。2. 提供导入度假订单信息的接口,根据不同来源选择不同的导入策略。3. 提供获取度假导入模板的接口。

@Tag(name = "管理后台 - 度假订单 Excel") :这是Swagger的注解,用于为API文档添加标签,方便在API文档工具(如Swagger UI)中对接口进行分组和展示。这里将该控制器下的所有接口归为"管理后台 - 度假订单 Excel"这一类别。2.@RestController :这是Spring框架的注解,它是@Controller@ResponseBody 的组合注解。表示该类是一个控制器,并且其方法返回的对象会自动序列化为JSON格式的响应体,用于构建RESTful风格的API。3.@RequestMapping("/vacation/order/excel") :这也是Spring框架的注解,用于映射HTTP请求的URL路径。它将该控制器下的所有处理方法的URL前缀都设置为/vacation/order/excel ,即访问该控制器中的任何接口时,URL都需要以这个前缀开头。4.@Validated :这是Spring框架的注解,用于开启方法级别的数据验证。当控制器的方法参数使用了@Valid 注解进行验证时,@Validated 注解会确保验证逻辑生效。

RouteImportRespVo

kotlin 复制代码
package cn.iocoder.central.module.vacation.controller.admin.order.excel.vo;

import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Builder;
import lombok.Data;

import java.util.List;

@Schema(description = "管理后台 - 路线订单导入 Response VO")
@Data
public class RouteImportRespVo {


    @Schema(description = "导入成功的订单编号数组", requiredMode = Schema.RequiredMode.REQUIRED)
    public List<String> addOrderCodes;

    @Schema(description = "更新成功的订单编号数组", requiredMode = Schema.RequiredMode.REQUIRED)
    public List<String> updateOrderCodes;

    @Schema(description = "导入失败的订单编号数组", requiredMode = Schema.RequiredMode.REQUIRED)
    public List<String> failureOrderCodes;

    // 如果需要,可以手动添加其他方法,如自定义的 getter、setter 或者业务逻辑方法
}

文件定义了一个名为RouteImportRespVo 的类,它是一个用于管理后台的路线订单导入响应视图对象(VO)。该类使用了@Schema 注解来描述类的用途,方便生成API文档。类中包含三个List<String> 类型的属性,分别记录导入成功、更新成功和导入失败的订单编号数组,便于前端展示导入结果。同时,使用了@Data 注解自动生成getter、setter等方法。

相关推荐
程序员爱钓鱼1 小时前
Go语言实战案例-创建模型并自动迁移
后端·google·go
javachen__1 小时前
SpringBoot整合P6Spy实现全链路SQL监控
spring boot·后端·sql
PAK向日葵5 小时前
【算法导论】PDD 0817笔试题题解
算法·面试
uzong7 小时前
技术故障复盘模版
后端
GetcharZp7 小时前
基于 Dify + 通义千问的多模态大模型 搭建发票识别 Agent
后端·llm·agent
桦说编程7 小时前
Java 中如何创建不可变类型
java·后端·函数式编程
IT毕设实战小研7 小时前
基于Spring Boot 4s店车辆管理系统 租车管理系统 停车位管理系统 智慧车辆管理系统
java·开发语言·spring boot·后端·spring·毕业设计·课程设计
wyiyiyi8 小时前
【Web后端】Django、flask及其场景——以构建系统原型为例
前端·数据库·后端·python·django·flask
阿华的代码王国9 小时前
【Android】RecyclerView复用CheckBox的异常状态
android·xml·java·前端·后端
Jimmy9 小时前
AI 代理是什么,其有助于我们实现更智能编程
前端·后端·ai编程