商品模块接口开发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接口的实现上,大家加油!

相关推荐
掐指一算乀缺钱13 分钟前
SpringBoot 数据库表结构文档生成
java·数据库·spring boot·后端·spring
飞翔的佩奇42 分钟前
xxl-job适配sqlite本地数据库及mysql数据库。可根据配置指定使用哪种数据库。
数据库·spring boot·mysql·sqlite·xxl-job·任务调度
luoluoal3 小时前
java项目之基于Spring Boot智能无人仓库管理源码(springboot+vue)
java·vue.js·spring boot
ChinaRainbowSea3 小时前
十三,Spring Boot 中注入 Servlet,Filter,Listener
java·spring boot·spring·servlet·web
2的n次方_3 小时前
掌握Spring Boot数据库集成:用JPA和Hibernate构建高效数据交互与版本控制
数据库·spring boot·hibernate
青灯文案13 小时前
SpringBoot 项目统一 API 响应结果封装示例
java·spring boot·后端
二十雨辰3 小时前
[苍穹外卖]-12Apache POI入门与实战
java·spring boot·mybatis
用生命在耍帅ㅤ6 小时前
java spring boot 动态添加 cron(表达式)任务、动态添加停止单个cron任务
java·开发语言·spring boot
程序员-珍6 小时前
SpringBoot v2.6.13 整合 swagger
java·spring boot·后端
徐*红6 小时前
springboot使用minio(8.5.11)
java·spring boot·spring