欢迎光临
我们一直在努力

【苍穹外卖|Day01】项目初识:从多模块结构到 OpenAPI 接口文档踩坑

从今天开始,博主准备更新苍穹外卖项目相关的学习笔记。

这篇是 Day01,目标不是一上来就写复杂业务,而是先解决三个基础问题:

  • 一个后端项目从需求到上线大概会经历哪些环节。
  • 苍穹外卖这个项目的多模块结构应该怎么看。
  • 当前项目使用 Spring Boot 4.0.6 时,接口文档为什么不能继续照搬旧版 Swagger 2 / Knife4j 思路。
  • 本文内容主要来自三部分:我的课程笔记、当前项目代码,以及 springdoc 官方文档。因为接口文档生成和框架版本强相关,所以这部分会以当前项目依赖为准,不把旧教程里的配置直接当成结论。

    一、先看软件开发流程

    Day01 笔记里先讲了软件开发流程。我一开始觉得这部分偏理论,但放到项目里看,其实它对应的是后面每一类文件为什么会出现。

    阶段常见产物和后端开发的关系
    需求分析 需求规格说明书、产品原型 确定系统要做哪些功能
    设计 UI 设计、数据库设计、接口设计 决定页面、表结构、接口路径和参数
    编码 项目代码、单元测试 按照设计实现 Controller、Service、Mapper
    测试 测试用例、测试报告 验证接口、业务规则和边界条件
    上线运维 环境安装、配置、部署 让项目在真实环境稳定运行

    1.1 角色分工

    一个完整项目里通常会有这些角色:

    • 项目经理:负责整体进度和任务分配。
    • 产品经理:整理需求,输出需求文档和产品原型。
    • UI 设计师:根据原型设计界面效果。
    • 架构师:负责整体架构和技术选型。
    • 开发工程师:完成业务编码、接口实现和必要的测试。
    • 测试工程师:编写测试用例,输出测试报告。
    • 运维工程师:负责环境搭建、部署和上线维护。

    这部分先不用背得很死。对我来说,更重要的是形成一个意识:后端代码不是凭空写出来的,它背后一定有需求、接口、数据库和测试这些约束。

    1.2 软件环境

    项目环境一般分为三类:

    环境说明
    开发环境 development 开发人员本地或团队内部调试使用
    测试环境 testing 测试人员验证功能使用
    生产环境 production 正式对外提供服务的环境

    当前 Day01 主要是在开发环境里整理项目结构和接口文档。后面如果涉及生产环境配置,就不能直接照搬本地配置,尤其是数据库密码、JWT 密钥、OSS AccessKey 这类敏感信息。

    二、苍穹外卖项目整体结构

    苍穹外卖项目主要包含两个端:

    • 管理端:给商家或后台人员使用,例如员工管理、分类管理、菜品管理、订单管理。
    • 用户端:给用户使用,例如浏览菜品、下单、支付、查看订单。

    前后端联调时会用到 Nginx。这里先把它理解成前端静态资源和后端接口之间的一层代理。它的常见作用包括:

  • 提高访问速度:静态资源可以由 Nginx 处理。
  • 进行负载均衡:后面多个服务实例时,可以由 Nginx 转发请求。
  • 保护后端服务:后端服务不一定直接暴露给外部访问。
  • 三、Maven 多模块:先知道每个模块放什么

    当前项目是一个 Maven 父工程,下面聚合了三个子模块。

    项目根目录的 pom.xml 里可以看到模块声明:

    <!– pom.xml –>
    <modules>
    <module>sky-common</module>
    <module>sky-pojo</module>
    <module>sky-server</module>
    </modules>

    几个模块的职责如下:

    模块作用
    sky-take-out Maven 父工程,统一管理依赖版本,聚合子模块
    sky-common 公共模块,存放工具类、常量类、异常类、统一返回结果等
    sky-pojo 模型模块,存放 Entity、DTO、VO 等
    sky-server 后端服务模块,存放配置文件、Controller、Service、Mapper 等

    这套结构的好处是职责比较清楚。

    比如登录接口中,前端请求参数对应 sky-pojo 里的 EmployeeLoginDTO,登录成功的返回数据对应 EmployeeLoginVO,JWT 工具类在 sky-common,真正处理请求的 Controller 和 Service 在 sky-server。

    也就是说,读项目时不要只看 Controller。一个接口能跑起来,背后通常是多个模块一起配合。

    四、当前项目版本:为什么旧教程不能直接套

    我的项目导入后做过版本适配,目前能从 pom.xml 中确认这些版本:

    <!– pom.xml –>
    <parent>
    <artifactId>spring-boot-starter-parent</artifactId>
    <groupId>org.springframework.boot</groupId>
    <version>4.0.6</version>
    <relativePath/>
    </parent>

    <properties>
    <java.version>25</java.version>
    <mybatis.spring>4.0.1</mybatis.spring>
    <springdoc>3.0.3</springdoc>
    <swagger>2.2.47</swagger>
    </properties>

    这里最关键的是 Spring Boot 4.0.6 和 Java 25。

    版本一旦比较新,很多旧资料里的依赖就不能直接复制。比如接口文档这块,旧项目里常见的是 Swagger 2 或旧版 Knife4j,但当前项目实际接入的是 springdoc:

    <!– sky-server/pom.xml –>
    <dependency>
    <groupId>org.springdoc</groupId>
    <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
    </dependency>

    所以这篇的接口文档部分,不写成“Knife4j 配置教程”,而是写成“当前项目使用 springdoc/OpenAPI 3 生成接口文档”。

    五、接口文档踩坑:从 Swagger 2 切到 OpenAPI 3

    这一块是 Day01 最容易卡住的地方。

    一开始看到“Swagger 可以生成接口文档,Knife4j 集成 Swagger”时,很容易沿着旧教程去找 @Api、@ApiOperation、@ApiModelProperty 这些注解。但当前项目已经不是这套写法。

    根据 springdoc 官方文档,从 SpringFox / Swagger 2 迁移到 springdoc 时,需要移除旧的 SpringFox 和 Swagger 2 依赖,改用 springdoc-openapi-starter-webmvc-ui,并替换成 Swagger 3 注解。常见替换关系如下:

    Swagger 2 旧注解OpenAPI 3 新注解作用
    @Api @Tag 标注 Controller 分组
    @ApiOperation @Operation 标注接口方法说明
    @ApiParam @Parameter 标注请求参数
    @ApiModel @Schema 标注模型类
    @ApiModelProperty @Schema 标注模型字段
    @ApiResponse(code = …) @ApiResponse(responseCode = …) 标注响应码

    这里的重点不是记表格,而是先判断自己项目到底用了哪套依赖。依赖路线确认错了,后面注解、配置、静态资源路径都会跟着错。

    5.1 Controller 上的接口分组

    当前项目在 EmployeeController 上使用 @Tag 给接口分组:

    // sky-server/src/main/java/com/sky/controller/admin/EmployeeController.java
    @RestController
    @RequestMapping("/admin/employee")
    @Slf4j
    @Tag(name = "员工管理相关接口")
    public class EmployeeController {
    }

    @Tag 的作用是让这一组接口在接口文档中归到同一个分类下。管理端接口多起来以后,如果没有分组,接口文档会比较乱。

    5.2 方法上的接口说明

    登录接口上使用 @Operation 描述接口作用:

    // sky-server/src/main/java/com/sky/controller/admin/EmployeeController.java
    @PostMapping("/login")
    @Operation(summary = "员工登录接口")
    public Result<EmployeeLoginVO> login(@RequestBody EmployeeLoginDTO employeeLoginDTO) {
    // 登录逻辑
    }

    这里还有一个值得注意的点:@RequestBody 表示请求体中的 JSON 会绑定到 EmployeeLoginDTO。所以前端传参、DTO 字段、接口文档展示的模型,其实是串在一起的。

    5.3 DTO/VO 上的字段说明

    当前项目里,登录请求参数用 EmployeeLoginDTO 表示:

    // sky-pojo/src/main/java/com/sky/dto/EmployeeLoginDTO.java
    @Data
    @Schema(description = "员工登录时传递的数据模型")
    public class EmployeeLoginDTO implements Serializable {

    @Schema(description = "用户名")
    private String username;

    @Schema(description = "密码")
    private String password;
    }

    登录成功后的返回对象用 EmployeeLoginVO 表示:

    // sky-pojo/src/main/java/com/sky/vo/EmployeeLoginVO.java
    @Data
    @Builder
    @NoArgsConstructor
    @AllArgsConstructor
    @Schema(description = "员工登录返回的数据格式")
    public class EmployeeLoginVO implements Serializable {

    @Schema(description = "主键值")
    private Long id;

    @Schema(description = "用户名")
    private String userName;

    @Schema(description = "姓名")
    private String name;

    @Schema(description = "jwt令牌")
    private String token;
    }

    DTO 和 VO 分开以后,接口文档也会更清楚:请求时需要什么字段,响应时返回什么字段,读者和前端都能直接看出来。

    六、springdoc 配置和验证方式

    项目里通过 OpenAPI Bean 设置了接口文档的标题、版本和描述:

    // sky-server/src/main/java/com/sky/config/WebMvcConfiguration.java
    @Bean
    public OpenAPI customOpenAPI() {
    return new OpenAPI()
    .info(new Info()
    .title("苍穹外卖项目接口文档")
    .version("2.0")
    .description("苍穹外卖项目接口文档"));
    }

    同时配置了 Swagger UI 相关静态资源映射:

    // sky-server/src/main/java/com/sky/config/WebMvcConfiguration.java
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
    log.info("开始设置静态资源映射…");
    registry.addResourceHandler("/swagger-ui/**")
    .addResourceLocations("classpath:/META-INF/resources/webjars/swagger-ui/");
    registry.addResourceHandler("/webjars/**")
    .addResourceLocations("classpath:/META-INF/resources/webjars/");
    }

    当前可以通过下面的地址查看接口文档:

    http://localhost:8080/swagger-ui/index.html#/

    如果访问不到,我会优先按这个顺序排查:

  • pom.xml 中是否存在 springdoc-openapi-starter-webmvc-ui。
  • 是否还残留旧版 SpringFox / Swagger 2 依赖。
  • Controller、DTO、VO 的注解包是否来自 io.swagger.v3.oas.annotations。
  • 静态资源路径是否配置正确。
  • 拦截器是否误拦截了接口文档路径。
  • 第 5 点也要看当前代码。项目中注册了管理端 JWT 拦截器:

    // sky-server/src/main/java/com/sky/config/WebMvcConfiguration.java
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
    log.info("开始注册自定义拦截器…");
    registry.addInterceptor(jwtTokenAdminInterceptor)
    .addPathPatterns("/admin/**")
    .excludePathPatterns("/admin/employee/login");
    }

    这个拦截器目前拦截的是 /admin/**,接口文档地址是 /swagger-ui/**,所以正常情况下不会被它拦住。后面如果自己扩大拦截范围,就要记得给接口文档和静态资源留出放行规则。

    七、基于 Apifox 实现自我测试

    课程资料里会提供接口定义,可以先在 YApi 中创建管理端接口项目和用户端接口项目,再把对应 JSON 文件导入进去。

    我自己使用 Apifox 做接口自测时,可以通过:

    项目设置 -> 导入设置 -> 选择 YApi -> 导入接口

    这样做的意义是把“接口约定”和“自己写的后端代码”对起来。

    比如员工登录接口,后端代码里的请求路径是:

    // sky-server/src/main/java/com/sky/controller/admin/EmployeeController.java
    @PostMapping("/login")
    public Result<EmployeeLoginVO> login(@RequestBody EmployeeLoginDTO employeeLoginDTO) {
    // 登录逻辑
    }

    类上还有统一路径:

    @RequestMapping("/admin/employee")

    所以完整登录接口路径就是:

    POST /admin/employee/login

    自测时要重点看这些内容是否一致:

  • Apifox 里的请求方法是不是 POST。
  • 接口路径是不是 /admin/employee/login。
  • 请求体字段是否和 EmployeeLoginDTO 对应。
  • 返回结果是否符合统一返回结构 Result<EmployeeLoginVO>。
  • 这里不需要一开始就追求把所有接口测完。Day01 阶段先把导入、查看、发起一次请求这条链路跑通,后面每写一个模块再补对应接口测试。

    八、登录安全:MD5 和 JWT 先看清链路

    笔记里提到一个安全点:密码如果明文存放在数据库中,安全性很低,所以要进行加密后存储或比对。

    当前员工登录逻辑中,Service 层会先根据用户名查询员工,再对用户输入的密码做 MD5 处理后比较:

    // sky-server/src/main/java/com/sky/service/impl/EmployeeServiceImpl.java
    public Employee login(EmployeeLoginDTO employeeLoginDTO) {
    String username = employeeLoginDTO.getUsername();
    String password = employeeLoginDTO.getPassword();

    Employee employee = employeeMapper.getByUsername(username);

    if (employee == null) {
    throw new AccountNotFoundException(MessageConstant.ACCOUNT_NOT_FOUND);
    }

    password = DigestUtils.md5DigestAsHex(password.getBytes());
    if (!password.equals(employee.getPassword())) {
    throw new PasswordErrorException(MessageConstant.PASSWORD_ERROR);
    }

    if (employee.getStatus() == StatusConstant.DISABLE) {
    throw new AccountLockedException(MessageConstant.ACCOUNT_LOCKED);
    }

    return employee;
    }

    这段代码的判断顺序很清楚:

  • 查不到员工,抛出账号不存在异常。
  • 密码比对失败,抛出密码错误异常。
  • 账号被禁用,抛出账号锁定异常。
  • 都通过后,返回员工对象。
  • 登录成功后,Controller 层会生成 JWT:

    // sky-server/src/main/java/com/sky/controller/admin/EmployeeController.java
    Map<String, Object> claims = new HashMap<>();
    claims.put(JwtClaimsConstant.EMP_ID, employee.getId());

    String token = JwtUtil.createJWT(
    jwtProperties.getAdminSecretKey(),
    jwtProperties.getAdminTtl(),
    claims);

    这里要注意两点:

    • 写博客时不要暴露真实 JWT 密钥、数据库密码、OSS AccessKey。
    • MD5 在课程项目里方便理解加密比对流程,但生产系统里通常还要考虑更安全的密码存储方案,例如加盐哈希或专门的密码编码器。

    这也是专业度容易扣分的地方。不能只说“用了 MD5 所以安全”,更准确的说法是:当前项目通过 MD5 避免明文比对,但这只是学习项目里的实现方式,生产环境还需要更严格的安全设计。

    九、TODO 的作用:标记后续要补的坑

    笔记里还提到了 TODO。它的作用不是随便写一行注释,而是标记“这里后面还要回来处理”。

    比如某段逻辑暂时能跑通,但还有优化空间,就可以先用 TODO 标出来。这样后面在 IDE 里统一查看时,不容易忘掉。

    不过 TODO 也不能滥用。如果一个问题已经修完,最好同步清理或改成准确注释。否则时间久了,别人看到 TODO 会误以为这个逻辑还没完成。

    十、Day01 总结

    Day01 的核心不是写多少业务代码,而是把项目的入口摸清楚。

    这一篇整理下来,我觉得有几个结论比较重要:

  • 软件开发流程决定了代码不是孤立存在的。后端接口、DTO、数据库字段都和需求、原型、接口设计有关。
  • 苍穹外卖是多模块项目。sky-common、sky-pojo、sky-server 分别承担公共能力、模型对象和后端服务职责。
  • 接口文档要以当前项目版本为准。当前项目使用 Spring Boot 4.0.6 和 springdoc,不适合直接套旧 Swagger 2 注解。
  • 自测要落到具体路径和对象上。比如登录接口要能对应到 POST /admin/employee/login、EmployeeLoginDTO、EmployeeLoginVO 和统一返回结果。
  • 安全内容要写边界。MD5 和 JWT 能帮助理解认证链路,但真实生产环境还要关注密钥保护、日志脱敏和更安全的密码存储。
  • 参考资料:

    • springdoc 官方文档:https://springdoc.org/
    • springdoc 迁移说明:https://springdoc.org/migrating-from-springfox.html
    赞(0)
    未经允许不得转载:171主机测评 » 【苍穹外卖|Day01】项目初识:从多模块结构到 OpenAPI 接口文档踩坑
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址