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

相关推荐
!chen3 分钟前
【Spring Boot】自定义starter
java·数据库·spring boot
hrrrrb37 分钟前
【Spring Boot】Spring Boot 中常见的加密方案
java·spring boot·后端
程序定小飞1 小时前
基于springboot的在线商城系统设计与开发
java·数据库·vue.js·spring boot·后端
小妖怪的夏天2 小时前
react native android设置邮箱,进行邮件发送
android·spring boot·react native
考虑考虑4 小时前
Jpa中的枚举类型
spring boot·后端·spring
用户874034852515 小时前
家政小程序源码实战:快速部署+多端适配,打造高效家政服务生态
spring boot
小杨的全栈之路5 小时前
从 SSLHandshakeException 到成功调用:RestTemplate 攻克自签 HTTPS 全记录
spring boot
还是鼠鼠6 小时前
《黑马商城》Elasticsearch基础-详细介绍【简单易懂注释版】
java·spring boot·spring·elasticsearch·搜索引擎·spring cloud·全文检索
麦兜*7 小时前
Redis 7.2 新特性实战:Client-Side Caching(客户端缓存)如何大幅降低延迟?
数据库·spring boot·redis·spring·spring cloud·缓存·tomcat
熊小猿7 小时前
Spring Boot 的 7 大核心优势
java·spring boot·后端