商品模块接口开发API定义

前面的章节我们花了大力气整好了项目开发的基础框架,并完成了商品分类模块的开发。相信小伙伴了已经能胜任独自开发一个新的业务模块了。本小节开始小卷将带小伙伴一起来开发商品模块的接口,首先我们来定义web层 API,开干!

前面我们已经学习了通过swagger生成器的方式来生成controller的API接口,而不用自己再手动编码定义。看下商品模块相关的openApi3的文档定义吧。

商品保存接口

商品在进行后台的新增和更新操作时,我们将实现一些组件的复用,这里我们的服用方案是DTO对象的复用,而API的定义以及controller方法和后台service逻辑都是分开的。因为如果API实现为共用,使用validation框架进行参数校验时无法使用分组校验,且后台service逻辑混杂在一起并不好维护。因此我们仅采用DTO的共用,API接口分开,且可以实现参数的分组校验。

API定义

看下这块在openApi3文档的定义:

yaml 复制代码
openapi: 3.0.1
...
paths:
  ...
  /mall-api/admin/products:
    post:
      tags:
        - productAdmin
      summary: 商品新增
      description: 新增一条商品记录
      operationId: addProduct
      x-group: Add
      requestBody:
        content:
          multipart/form-data:
            schema:
              $ref: '#/components/schemas/ProductSave'
      responses:
        200:
          description: 新增成功
    put:
      tags:
        - productAdmin
      summary: 商品更新
      description: 更新一条商品记录
      operationId: updateProduct
      x-group: Update
      requestBody:
        content:
          multipart/form-data:
            schema:
              $ref: '#/components/schemas/ProductSave'
      responses:
        200:
          description: 更新成功
components:
  schemas:
    ProductSave:
      description: 商品保存DTO类
      type: object
      x-groups:
        - Add
        - Update
      properties:
        id:
          description: 商品id
          type: integer
          format: int64
          x-validation: "@NotNull(message = MSG_PRODUCT_ID_REQUIRED, groups = Update.class)"
        categoryId:
          description: 三级分类id
          type: integer
          format: int64
          x-validation: "@NotNull(message = MSG_CATEGORY_ID_REQUIRED, groups = Add.class)"
        name:
          description: 商品名称
          type: string
          x-validation: "@NotBlank(message = MSG_PRODUCT_NAME_REQUIRED, groups = Add.class)"
        imgFile:
          description: 图片文件
          type: multipartFile
        detail:
          description: 商品详情
          type: string
          x-validation: "@Size(min = 50, max = 500, message = MSG_PRODUCT_DETAIL_RANGE)"
        price:
          description: 价格
          type: integer
          x-validation: "@NotNull(message = MSG_PRODUCT_PRICE_REQUIRED, groups = Add.class) @Min(value = 1, message = MSG_PRODUCT_PRICE_MIN)"
        stock:
          description: 库存
          type: integer
          x-validation: "@NotNull(message = MSG_PRODUCT_STOCK_REQUIRED, groups = Add.class) @Max(value = 10000, message = MSG_PRODUCT_STOCK_MAX)"

配置说明

这里针对新增商品和更新商品的两个请求API我们采用了标准的restful api的规范,对资源的不同操作映射到不同的请求方法。

注意这里请求内容的类型为multipart/form-data,也就是说,前端是以formData的形式来提交的,可以同时将业务字段和文件类型的字段一并提交,而不用先异步上传文件再提交表单,我们杜绝这种做法。后台用一个ProductSaveDTO来接收前端提交的可以包含文件域的formData格式的数据,这样就要求swagger生成器能帮我们生成MultipartFile类型的字段,而默认这是不支持的,我们需要修改swagger生成器的源码。

扩展swagger生成器

修改swagger-codegen-generators-1.0.39源码工程,增加新的文件上传的类型和映射:

然后我们通过扩展openApi文档定义的方式,增加对分组校验的支持,我们在DTO的schema定义中增加了一个扩展的属性x-groups,用来定义校验分组的列表,比如这里我们对商品进行新增和更新涉及这两个分组,对于公共的字段校验不用指定分组,而为特定于一个API的字段校验指定分组,比如更新时商品id不能为空,看下x-validation中指定的分组,分组的class来自于x-groups中的定义。不要忘了在API定义中我们同样要用扩展属性x-group来应用哪个组别的校验。这些都是我们扩展的定义,而swagger生成器默认是不支持的,还是要我们修改源码。修改swagger-codegen-generators-1.0.39的源码:

在处理模型的扩展属性时,我们加一个布尔类型的x-has-group的扩展变量。这样我们只要在项目中的pojo.mustache模板的最后加上这样的内容即可渲染出用于分组的类内部接口定义:

而要在api文件中生成的表单或者json body对应的DTO上应用校验分组注解定义,我们需要继续修改生成器源码:

针对API参数的代码生成逻辑,我们基于API操作对象codegenOperation中是否能获取x-group扩展属性的判断,为参数生成模型设置扩展属性x-has-validate-group和新加的一个validateGroup字段,前者用于判断是否对DTO参数启用分组校验,后者指定组名。还需要修改CodegenParamter类源码:

源码改完后在本地install后在项目中更新依赖,最后调整项目中form参数和body参数对应的mustache模板文件:

注意,form参数模板要在isFormObj判断内调整,因为我们只关心接收类型为form的DTO对象,为其应用分组校验。

注意

swagger分组校验的在线文档并不会区分分组,都会被应用,这是一个比较坑的地方。

删除商品(批量)

这里我们要支持传入多个要删除的商品id数组:

yaml 复制代码
  /mall-api/admin/products:
    ...
    delete:
      tags:
        - productAdmin
      summary: 商品删除
      description: 按照一个或多个id删除商品
      operationId: delete
      parameters:
        - name: ids
          in: query
          schema:
            type: array
            items:
              type: integer
              format: int64
          x-validation: "@NotEmpty(message = MSG_PRODUCT_IDS_REQUIRED)"
      responses:
        200:
          description: 删除成功

生成的参数类型为List<Long>,这里我们将生成@NotEmpty注解对其进行校验,前端的提交形式:

http 复制代码
### 商品删除
DELETE http://localhost:8080/mall-api/admin/products
Content-Type: application/x-www-form-urlencoded

ids=1&ids=2&ids=3

商品批量上下架

对于该接口,我们前端会传多个商品id以及上架或下架的操作类型字段oprType,为了校验传入的操作类型值为我们后台期望的范围,这里我们将增加一个枚举值范围校验的自定义注解,注解可以参考以前的MyPattern来定义,其区别的地方:

java 复制代码
package com.xiaojuan.boot.common.validation.annotation;

import ...

@Constraint(validatedBy = InValidator.class)
...
public @interface In {
    int[] value();
    ...
}

校验器实现逻辑很简单:

java 复制代码
package com.xiaojuan.boot.common.validation.validator;

import ...

public class InValidator implements ConstraintValidator<In, Integer> {

    private int[] arr;

    @Override
    public void initialize(In inAnnotation) {
        arr = inAnnotation.value();
    }

    @Override
    public boolean isValid(Integer value, ConstraintValidatorContext context) {
        if (value == null) return true;
        return ArrayUtils.contains(arr, value);
    }
}

现在我们来定义API:

yaml 复制代码
  /mall-api/admin/product/updateSellStatus:
    post:
      tags:
        - productAdmin
      summary: 商品上下架
      description: 批量对商品上架或下架处理
      operationId: batchUpdateSellStatus
      parameters:
        - name: ids
          in: query
          schema:
            type: array
            items:
              type: integer
              format: int64
          x-validation: "@NotEmpty(message = MSG_PRODUCT_IDS_REQUIRED)"
        - name: oprType
          in: query
          schema:
            type: integer
          x-validation: "@NotNull(message = MSG_PRODUCT_UPDATE_SELL_STATUS_OPR_TYPE_REQUIRED) @In(value = {0, 1}, message = MSG_PRODUCT_UPDATE_SELL_STATUS_OPR_TYPE_ILLEGAL)"
      responses:
        200:
          description: 操作成功

这里我们对oprType字段采用了双重校验。通过自定义注解@In来校验操作类型必须要在指定的一组值中。如果传入为空,@In注解的校验实现逻辑中将直接返回true,即校验通过,这样只会应用非空检查。

重新执行generateAPI任务后启动服务,进行测试:

后台商品分页检索

该API只开放给后台管理员进行商品查询的,因此我们希望检索功能做的更细化些:

  1. 支持按照商品名称的模糊搜索
  2. 支持传入任意一个层级的分类检索
  3. 支持按上下架状态检索
  4. 按照更新时间倒序排列

API定义:

yaml 复制代码
openapi: 3.0.1
...
paths:
  ...
  /mall-api/admin/products:
    get:
      tags:
        - productAdmin
      summary: 商品检索
      description: 按照条件查询商品分页列表
      operationId: findProducts
      requestBody:
        content:
          application/x-www-form-urlencoded:
            schema:
              $ref: '#/components/schemas/ProductBackQuery'
      x-pageResult: true
      responses:
        200:
          description: 查询成功
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProductBackInfo'
    ...
components:
  schemas:
    ...
    ProductBackQuery:
      description: 商品后台查询DTO
      type: object
      allOf:
        - $ref: '#/components/schemas/PageQuery'
      properties:
        keyword:
          description: 商品名称关键字
          type: string
        categoryId:
          description: 分类id
          type: integer
          format: int64
        status:
          description: 上下架状态
          type: integer
    ProductBackInfo:
      description: 商品后台信息DTO
      type: object
      properties:
        id:
          description: 商品id
          type: integer
          format: int64
        categoryIds:
          description: 分类id列表
          type: array
          items:
            type: integer
            format: int64
        name:
          description: 商品名称
          type: string
        imgUrl:
          description: 商品图片url
          type: string
        detail:
          description: 商品详情
          type: string
        price:
          description: 商品价格(单位:分)
          type: integer
        stock:
          description: 库存
          type: integer
        status:
          description: 上下架状态
          type: integer
        createTime:
          description: 创建时间
          type: string
          format: date
        updateTime:
          description: 更新时间
          type: string
          format: date
    ...

前台商品检索

这里我们给前端用户提供:

  1. 按商品名称模糊检索
  2. 按三级分类检索
  3. 排序方式:默认排序、价格递增、上架日期倒序
  4. 分页展示

API定义:

yaml 复制代码
openapi: 3.0.1
...
paths:
  ...
  /mall-api/portal/products:
    get:
      tags:
        - productPortal
      summary: 前台检索商品列表
      description: 前台检索商品列表
      operationId: findProducts
      requestBody:
        content:
          application/x-www-form-urlencoded:
            schema:
              $ref: '#/components/schemas/ProductQuery'
      x-pageResult: true
      responses:
        200:
          description: 查询成功
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProductInfo'
  ...
components:
  schemas:
    ...
    ProductQuery:
      description: 商品前台查询DTO
      type: object
      allOf:
        - $ref: '#/components/schemas/PageQuery'
      properties:
        keyword:
          description: 商品名称关键字
          type: string
        categoryId:
          description: 三级分类id
          type: integer
          format: int64
        sortType:
          description: 排序类型:1-价格升序 2-上架日期倒序
          type: integer
    ...
    ProductInfo:
      description: 商品前台信息DTO
      type: object
      properties:
        id:
          description: 商品id
          type: integer
          format: int64
        name:
          description: 商品名称
          type: string
        imgUrl:
          description: 商品图片url
          type: string
        price:
          description: 商品价格(单位:分)
          type: integer
    ...

前台商品详情

yaml 复制代码
openapi: 3.0.1
...
paths:
  ...
  /mall-api/portal/product-detail/{id}:
    get:
      tags:
        - productPortal
      summary: 查看商品详情
      description: 按照id获取商品详情
      operationId: getProduct
      parameters:
        - name: id
          in: path
          description: 商品id
          schema:
            type: integer
            format: int64
      responses:
        200:
          description: 查询成功
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProductDetail'
  ...
components:
  schemas:
    ...
    ProductDetail:
      description: 商品前台详情DTO
      type: object
      properties:
        id:
          description: 商品id
          type: integer
          format: int64
        name:
          description: 商品名称
          type: string
        categoryNames:
          description: 多层级分类名称列表
          type: array
          items:
            type: integer
            format: int64
        imgUrl:
          description: 商品图片url
          type: string
        detail:
          description: 商品详情
          type: string
        price:
          description: 商品价格(单位:分)
          type: integer
        stock:
          description: 库存
          type: integer
    ...

按大类查最新6款商品

在商品门户的首页我们希望查找每个分类下的最新上架的6款商品,定义如下api:

yaml 复制代码
  /mall-api/portal/product-latest/{categoryId}:
    get:
      tags:
        - productPortal
      summary: 查找最新商品
      description: 按照大类查找最新上架6款商品
      operationId: getLatestProducts
      parameters:
        - name: categoryId
          in: path
          description: 大类id
          schema:
            type: integer
            format: int64
      responses:
        200:
          description: 查询成功
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/ProductInfo'

以上就是本节的主要内容,我们对商品模块的API接口通过openApi3的形式进行了定义,并借助swagger生成器帮我们生成了相应的Java代码,然后我们开发相应的Controller组件来实现这些接口即可。下一小节,我们将把主要精力放在API接口的实现上,大家加油!

相关推荐
JH30738 小时前
SpringBoot 优雅处理金额格式化:拦截器+自定义注解方案
java·spring boot·spring
qq_124987075311 小时前
基于SSM的动物保护系统的设计与实现(源码+论文+部署+安装)
java·数据库·spring boot·毕业设计·ssm·计算机毕业设计
Coder_Boy_11 小时前
基于SpringAI的在线考试系统-考试系统开发流程案例
java·数据库·人工智能·spring boot·后端
2301_8187320612 小时前
前端调用控制层接口,进不去,报错415,类型不匹配
java·spring boot·spring·tomcat·intellij-idea
汤姆yu15 小时前
基于springboot的尿毒症健康管理系统
java·spring boot·后端
暮色妖娆丶15 小时前
Spring 源码分析 单例 Bean 的创建过程
spring boot·后端·spring
biyezuopinvip16 小时前
基于Spring Boot的企业网盘的设计与实现(任务书)
java·spring boot·后端·vue·ssm·任务书·企业网盘的设计与实现
JavaGuide17 小时前
一款悄然崛起的国产规则引擎,让业务编排效率提升 10 倍!
java·spring boot
figo10tf17 小时前
Spring Boot项目集成Redisson 原始依赖与 Spring Boot Starter 的流程
java·spring boot·后端
zhangyi_viva17 小时前
Spring Boot(七):Swagger 接口文档
java·spring boot·后端