7.商品服務-品牌管理

1.新增品牌管理菜單-上級菜單選擇商品管理

 2.拿到代碼生成器生成的兩個文件放到以下位置

 

 npm run dev運行起來前端

進入品牌系統發現已經有基本的頁面了vue

 解決控制檯報錯問題java

 

更改權限,讓新增和刪除按鈕一直顯示spring

 

 

 

 

 給出前端brand.vue的代碼npm

<template>
  <div class="mod-config">
    <el-form :inline="true" :model="dataForm" @keyup.enter.native="getDataList()">
      <el-form-item>
        <el-input v-model="dataForm.key" placeholder="參數名" clearable></el-input>
      </el-form-item>
      <el-form-item>
        <el-button @click="getDataList()">查詢</el-button>
        <el-button
          v-if="isAuth('product:brand:save')"
          type="primary"
          @click="addOrUpdateHandle()"
        >新增
        </el-button>
        <el-button
          v-if="isAuth('product:brand:delete')"
          type="danger"
          @click="deleteHandle()"
          :disabled="dataListSelections.length <= 0"
        >批量刪除
        </el-button>
      </el-form-item>
    </el-form>
    <el-table
      :data="dataList"
      border
      v-loading="dataListLoading"
      @selection-change="selectionChangeHandle"
      style="width: 100%;"
    >
      <el-table-column type="selection" header-align="center" align="center" width="50"></el-table-column>
      <el-table-column prop="brandId" header-align="center" align="center" label="品牌id"></el-table-column>
      <el-table-column prop="name" header-align="center" align="center" label="品牌名"></el-table-column>
      <el-table-column prop="logo" header-align="center" align="center" label="品牌logo地址">
        <template slot-scope="scope">
          <!-- <el-image
              style="width: 100px; height: 80px"
              :src="scope.row.logo"
          fit="fill"></el-image>-->
          <img :src="scope.row.logo" style="width: 100px; height: 80px"/>
        </template>
      </el-table-column>
      <el-table-column prop="descript" header-align="center" align="center" label="介紹"></el-table-column>
      <el-table-column prop="showStatus" header-align="center" align="center" label="顯示狀態">
        <template slot-scope="scope">
          <el-switch
            v-model="scope.row.showStatus"
            active-color="#13ce66"
            inactive-color="#ff4949"
            :active-value="1"
            :inactive-value="0"
            @change="updateBrandStatus(scope.row)"
          ></el-switch>
        </template>
      </el-table-column>
      <el-table-column prop="firstLetter" header-align="center" align="center" label="檢索首字母"></el-table-column>
      <el-table-column prop="sort" header-align="center" align="center" label="排序"></el-table-column>
      <el-table-column fixed="right" header-align="center" align="center" width="250" label="操做">
        <template slot-scope="scope">
          <el-button type="text" size="small" @click="updateCatelogHandle(scope.row.brandId)">關聯分類</el-button>
          <el-button type="text" size="small" @click="addOrUpdateHandle(scope.row.brandId)">修改</el-button>
          <el-button type="text" size="small" @click="deleteHandle(scope.row.brandId)">刪除</el-button>
        </template>
      </el-table-column>
    </el-table>
    <el-pagination
      @size-change="sizeChangeHandle"
      @current-change="currentChangeHandle"
      :current-page="pageIndex"
      :page-sizes="[10, 20, 50, 100]"
      :page-size="pageSize"
      :total="totalPage"
      layout="total, sizes, prev, pager, next, jumper"
    ></el-pagination>
    <!-- 彈窗, 新增 / 修改 -->
    <add-or-update v-if="addOrUpdateVisible" ref="addOrUpdate" @refreshDataList="getDataList"></add-or-update>

    <el-dialog title="關聯分類" :visible.sync="cateRelationDialogVisible" width="30%">
      <el-popover placement="right-end" v-model="popCatelogSelectVisible">
        <category-cascader :catelogPath.sync="catelogPath"></category-cascader>
        <div style="text-align: right; margin: 0">
          <el-button size="mini" type="text" @click="popCatelogSelectVisible = false">取消</el-button>
          <el-button type="primary" size="mini" @click="addCatelogSelect">肯定</el-button>
        </div>
        <el-button slot="reference">新增關聯</el-button>
      </el-popover>
      <el-table :data="cateRelationTableData" style="width: 100%">
        <el-table-column prop="id" label="#"></el-table-column>
        <el-table-column prop="brandName" label="品牌名"></el-table-column>
        <el-table-column prop="catelogName" label="分類名"></el-table-column>
        <el-table-column fixed="right" header-align="center" align="center" label="操做">
          <template slot-scope="scope">
            <el-button
              type="text"
              size="small"
              @click="deleteCateRelationHandle(scope.row.id,scope.row.brandId)"
            >移除
            </el-button>
          </template>
        </el-table-column>
      </el-table>
      <span slot="footer" class="dialog-footer">
        <el-button @click="cateRelationDialogVisible = false">取 消</el-button>
        <el-button type="primary" @click="cateRelationDialogVisible = false">確 定</el-button>
      </span>
    </el-dialog>
  </div>
</template>

<script>
import AddOrUpdate from "./brand-add-or-update";
// import CategoryCascader from "../common/category-cascader";
export default {
  data() {
    return {
      dataForm: {
        key: ""
      },
      brandId: 0,
      catelogPath: [],
      dataList: [],
      cateRelationTableData: [],
      pageIndex: 1,
      pageSize: 10,
      totalPage: 0,
      dataListLoading: false,
      dataListSelections: [],
      addOrUpdateVisible: false,
      cateRelationDialogVisible: false,
      popCatelogSelectVisible: false
    };
  },
  components: {
    AddOrUpdate,
    // CategoryCascader
  },
  activated() {
    this.getDataList();
  },
  methods: {
    addCatelogSelect() {
      //{"brandId":1,"catelogId":2}
      this.popCatelogSelectVisible = false;
      this.$http({
        url: this.$http.adornUrl("/product/categorybrandrelation/save"),
        method: "post",
        data: this.$http.adornData({
          brandId: this.brandId,
          catelogId: this.catelogPath[this.catelogPath.length - 1]
        }, false)
      }).then(({data}) => {
        this.getCateRelation();
      });
    },
    deleteCateRelationHandle(id, brandId) {
      this.$http({
        url: this.$http.adornUrl("/product/categorybrandrelation/delete"),
        method: "post",
        data: this.$http.adornData([id], false)
      }).then(({data}) => {
        this.getCateRelation();
      });
    },
    updateCatelogHandle(brandId) {
      this.cateRelationDialogVisible = true;
      this.brandId = brandId;
      this.getCateRelation();
    },
    getCateRelation() {
      this.$http({
        url: this.$http.adornUrl("/product/categorybrandrelation/catelog/list"),
        method: "get",
        params: this.$http.adornParams({
          brandId: this.brandId
        })
      }).then(({data}) => {
        this.cateRelationTableData = data.data;
      });
    },
    // 獲取數據列表
    getDataList() {
      this.dataListLoading = true;
      this.$http({
        url: this.$http.adornUrl("/product/brand/list"),
        method: "get",
        params: this.$http.adornParams({
          page: this.pageIndex,
          limit: this.pageSize,
          key: this.dataForm.key
        })
      }).then(({data}) => {
        if (data && data.code === 0) {
          this.dataList = data.page.list;
          this.totalPage = data.page.totalCount;
        } else {
          this.dataList = [];
          this.totalPage = 0;
        }
        this.dataListLoading = false;
      });
    },
    updateBrandStatus(data) {
      console.log("最新信息", data);
      let {brandId, showStatus} = data;
      //發送請求修改狀態
      this.$http({
        url: this.$http.adornUrl("/product/brand/update/status"),
        method: "post",
        data: this.$http.adornData({brandId, showStatus}, false)
      }).then(({data}) => {
        this.$message({
          type: "success",
          message: "狀態更新成功"
        });
      });
    },
    // 每頁數
    sizeChangeHandle(val) {
      this.pageSize = val;
      this.pageIndex = 1;
      this.getDataList();
    },
    // 當前頁
    currentChangeHandle(val) {
      this.pageIndex = val;
      this.getDataList();
    },
    // 多選
    selectionChangeHandle(val) {
      this.dataListSelections = val;
    },
    // 新增 / 修改
    addOrUpdateHandle(id) {
      this.addOrUpdateVisible = true;
      this.$nextTick(() => {
        this.$refs.addOrUpdate.init(id);
      });
    },
    // 刪除
    deleteHandle(id) {
      var ids = id
        ? [id]
        : this.dataListSelections.map(item => {
          return item.brandId;
        });
      this.$confirm(
        `肯定對[id=${ids.join(",")}]進行[${id ? "刪除" : "批量刪除"}]操做?`,
        "提示",
        {
          confirmButtonText: "肯定",
          cancelButtonText: "取消",
          type: "warning"
        }
      ).then(() => {
        this.$http({
          url: this.$http.adornUrl("/product/brand/delete"),
          method: "post",
          data: this.$http.adornData(ids, false)
        }).then(({data}) => {
          if (data && data.code === 0) {
            this.$message({
              message: "操做成功",
              type: "success",
              duration: 1500,
              onClose: () => {
                this.getDataList();
              }
            });
          } else {
            this.$message.error(data.msg);
          }
        });
      });
    }
  }
};
</script>
<style scoped>
</style>

3.OSS雲端準備

準備詳見商城項目bootstrap

 

 

 服務端簽名後直傳--》安全,不要通過本身的服務器也就是前端直傳,服務器只提供相應的簽名數據api

4.建立子模塊mall-third-party 整合阿里雲OSS等第三方模塊

 添加pom依賴跨域

 <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alicloud-oss</artifactId>
            <version>2.2.0.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>com.wuyimin.gulimall</groupId>
            <artifactId>gulimall-common</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>

添加註冊中心配置中心的文件,新增命名空間third-party.增長配置oss.yml,專門用來處理oss的內容安全

 

 配置第三方模塊服務器

application.yml

bootstrap.properties

 

 測試上傳

@SpringBootTest
class GulimallThirdPartyApplicationTests {
    @Autowired
    OSSClient ossClient;

    @Test
    void contextLoads() throws FileNotFoundException {
        // 上傳文件流。
        InputStream inputStream = new FileInputStream("C:\\Users\\56548\\Desktop\\1.jpg");
        // 上傳
        ossClient.putObject("gulimall-wuyimin", "1.jpg", inputStream);

        // 關閉OSSClient。
        ossClient.shutdown();
        System.out.println("上傳成功.");
    }
}

5.oss服務端直傳

/**
 * @author wuyimin
 * @create 2021-08-06 14:34
 * @description 簽名信息
 */
@RestController
public class OssController {
    @Autowired
    OSS ossClient;

    @Value("${spring.cloud.alicloud.oss.endpoint}")
    private String endpoint;

    private String bucket="gulimall-wuyimin";
    @Value("${spring.cloud.alicloud.access-key}")
    private String accessId;
    @RequestMapping("/oss/policy")
    public R policy(){
        // https://gulimall-hello.oss-cn-beijing.aliyuncs.com/hahaha.jpg  host的格式爲 bucketname.endpoint
        String host = "https://" + bucket + "." + endpoint;
        // callbackUrl爲 上傳回調服務器的URL,請將下面的IP和Port配置爲您本身的真實信息。
        // String callbackUrl = "http://88.88.88.88:8888";
        String format = new SimpleDateFormat("yyyy-MM-dd").format(new Date());
        // 用戶上傳文件時指定的前綴。
        String dir = format + "/";
        Map<String, String> respMap = null;
        try {
            long expireTime = 30;
            long expireEndTime = System.currentTimeMillis() + expireTime * 1000;
            Date expiration = new Date(expireEndTime);
            PolicyConditions policyConds = new PolicyConditions();
            policyConds.addConditionItem(PolicyConditions.COND_CONTENT_LENGTH_RANGE, 0, 1048576000);
            policyConds.addConditionItem(MatchMode.StartWith, PolicyConditions.COND_KEY, dir);

            String postPolicy = ossClient.generatePostPolicy(expiration, policyConds);
            byte[] binaryData = postPolicy.getBytes(StandardCharsets.UTF_8);
            String encodedPolicy = BinaryUtil.toBase64String(binaryData);
            String postSignature = ossClient.calculatePostSignature(postPolicy);

            respMap = new LinkedHashMap<String, String>();
            respMap.put("accessid", accessId);
            respMap.put("policy", encodedPolicy);
            respMap.put("signature", postSignature);
            respMap.put("dir", dir);
            respMap.put("host", host);
            respMap.put("expire", String.valueOf(expireEndTime / 1000));
            // respMap.put("expire", formatISO8601Date(expiration));

        } catch (Exception e) {
            // Assert.fail(e.getMessage());
            System.out.println(e.getMessage());
        }

        return R.ok().put("data", respMap);
    }
}

測試簽名返回數據

配置網關

spring:
  cloud:
    gateway:
      routes:
        #商品的路由
        #localhost:88/api/product/category/list/tree--->localhost:10000/product....
        #優先級比下面的哪一個路由要高因此要放在上面,否則會被截斷
        - id: product_route
          uri: lb://gulimall-product
          predicates:
            - Path=/api/product/**
          filters:
            - RewritePath=/api/(?<segment>.*),/$\{segment}
#第三方路由配置
        - id: third_party_route
          uri: lb://gulimall-third-party
          predicates:
            - Path=/api/thirdparty/**
          filters:
            - RewritePath=/api/thirdparty/(?<segment>.*),/$\{segment}

        - id: admin_route
          #lb表示負載均衡
          uri: lb://renren-fast
          #規定前端項目必須帶有一個api前綴
          #原來驗證碼的uri ...localhost:88/api/captcha.jpg
          #應該改爲的uri ...localhost:88/renrenfast/captcha.jpg
          predicates:
            - Path=/api/**
          filters:
            - RewritePath=/api/(?<segment>.*),/renren-fast/$\{segment}

6.前端

增長文件夾

 

 須要把這個地址改掉,單文件和多文件都要改

 

<template>
  <el-dialog
    :title="!dataForm.id ? '新增' : '修改'"
    :close-on-click-modal="false"
    :visible.sync="visible"
  >
    <el-form
      :model="dataForm"
      :rules="dataRule"
      ref="dataForm"
      @keyup.enter.native="dataFormSubmit()"
      label-width="140px"
    >
      <el-form-item label="品牌名" prop="name">
        <el-input v-model="dataForm.name" placeholder="品牌名"></el-input>
      </el-form-item>
      <el-form-item label="品牌logo地址" prop="logo">
        <!-- <el-input v-model="dataForm.logo" placeholder="品牌logo地址"></el-input> -->
        <single-upload v-model="dataForm.logo"></single-upload>
      </el-form-item>
      <el-form-item label="介紹" prop="descript">
        <el-input v-model="dataForm.descript" placeholder="介紹"></el-input>
      </el-form-item>
      <el-form-item label="顯示狀態" prop="showStatus">
        <el-switch
          v-model="dataForm.showStatus"
          active-color="#13ce66"
          inactive-color="#ff4949"
          :active-value="1"
          :inactive-value="0"
        ></el-switch>
      </el-form-item>
      <el-form-item label="檢索首字母" prop="firstLetter">
        <el-input v-model="dataForm.firstLetter" placeholder="檢索首字母"></el-input>
      </el-form-item>
      <el-form-item label="排序" prop="sort">
        <el-input v-model.number="dataForm.sort" placeholder="排序"></el-input>
      </el-form-item>
    </el-form>
    <span slot="footer" class="dialog-footer">
      <el-button @click="visible = false">取消</el-button>
      <el-button type="primary" @click="dataFormSubmit()">肯定</el-button>
    </span>
  </el-dialog>
</template>

<script>
import SingleUpload from '@/components/upload/singleUpload';

export default {
  components: {SingleUpload},
  data() {
    return {
      visible: false,
      dataForm: {
        brandId: 0,
        name: "",
        logo: "",
        descript: "",
        showStatus: 1,
        firstLetter: "",
        sort: 0
      },
      dataRule: {
        name: [{required: true, message: "品牌名不能爲空", trigger: "blur"}],
        logo: [
          {required: true, message: "品牌logo地址不能爲空", trigger: "blur"}
        ],
        descript: [
          {required: true, message: "介紹不能爲空", trigger: "blur"}
        ],
        showStatus: [
          {
            required: true,
            message: "顯示狀態[0-不顯示;1-顯示]不能爲空",
            trigger: "blur"
          }
        ],
        firstLetter: [
          {
            validator: (rule, value, callback) => {
              if (value == "") {
                callback(new Error("首字母必須填寫"));
              } else if (!/^[a-zA-Z]$/.test(value)) {
                callback(new Error("首字母必須a-z或者A-Z之間"));
              } else {
                callback();
              }
            },
            trigger: "blur"
          }
        ],
        sort: [
          {
            validator: (rule, value, callback) => {
              if (value == "") {
                callback(new Error("排序字段必須填寫"));
              } else if (!Number.isInteger(value) || value < 0) {
                callback(new Error("排序必須是一個大於等於0的整數"));
              } else {
                callback();
              }
            },
            trigger: "blur"
          }
        ]
      }
    };
  },
  methods: {
    init(id) {
      this.dataForm.brandId = id || 0;
      this.visible = true;
      this.$nextTick(() => {
        this.$refs["dataForm"].resetFields();
        if (this.dataForm.brandId) {
          this.$http({
            url: this.$http.adornUrl(
              `/product/brand/info/${this.dataForm.brandId}`
            ),
            method: "get",
            params: this.$http.adornParams()
          }).then(({data}) => {
            if (data && data.code === 0) {
              this.dataForm.name = data.brand.name;
              this.dataForm.logo = data.brand.logo;
              this.dataForm.descript = data.brand.descript;
              this.dataForm.showStatus = data.brand.showStatus;
              this.dataForm.firstLetter = data.brand.firstLetter;
              this.dataForm.sort = data.brand.sort;
            }
          });
        }
      });
    },
    // 表單提交
    dataFormSubmit() {
      this.$refs["dataForm"].validate(valid => {
        if (valid) {
          this.$http({
            url: this.$http.adornUrl(
              `/product/brand/${!this.dataForm.brandId ? "save" : "update"}`
            ),
            method: "post",
            data: this.$http.adornData({
              brandId: this.dataForm.brandId || undefined,
              name: this.dataForm.name,
              logo: this.dataForm.logo,
              descript: this.dataForm.descript,
              showStatus: this.dataForm.showStatus,
              firstLetter: this.dataForm.firstLetter,
              sort: this.dataForm.sort
            })
          }).then(({data}) => {
            if (data && data.code === 0) {
              this.$message({
                message: "操做成功",
                type: "success",
                duration: 1500,
                onClose: () => {
                  this.visible = false;
                  this.$emit("refreshDataList");
                }
              });
            } else {
              this.$message.error(data.msg);
            }
          });
        }
      });
    }
  }
};
</script>

7.修改oss跨域

 

 

 

點擊上傳文件後

 

 能夠看到oss裏也有了

 8.JSR303校驗註解

product加入依賴

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>

添加notBlank註解

添加@Valid註解

 

/**
     * 保存
     */
    @RequestMapping("/save")
    //@RequiresPermissions("product:brand:save")
    public R save(@Valid @RequestBody BrandEntity brand, BindingResult result){//告訴springMVC這個字段須要校驗,後面緊跟一個參數能夠獲取錯誤信息
        if(result.hasErrors()){
            Map<String ,String> map=new HashMap<>();
            result.getFieldErrors().forEach((item)->{
                String defaultMessage = item.getDefaultMessage();//獲取信息
                String field=item.getField();//獲取錯誤的名字
                map.put(field,defaultMessage);
            });
            return R.error(400,"提交的數據不合法").put("data",map);
        }else{
            brandService.save(brand);
            return R.ok();
        }
    }

用postman發送一個空串,這時候就會報400錯誤

 

 給出完整的校驗Bean

@Data
@TableName("pms_brand")
public class BrandEntity implements Serializable {
    private static final long serialVersionUID = 1L;

    /**
     * 品牌id
     */
    @TableId
    private Long brandId;
    /**
     * 品牌名
     */
    @NotBlank(message = "品牌名不能爲空")//非空,空串也不行
    private String name;
    /**
     * 品牌logo地址
     */
    @NotEmpty
    @URL(message = "logo必須是url地址")//必須是一個url地址
    private String logo;
    /**
     * 介紹
     */
    private String descript;
    /**
     * 顯示狀態[0-不顯示;1-顯示]
     */
    private Integer showStatus;
    /**
     * 檢索首字母
     */
    @NotEmpty
    @Pattern(regexp = "/^[a-zA-z]$/",message = "檢索首字母必須是一個字母")//自定義的規則
    private String firstLetter;
    /**
     * 排序
     */
    @NotNull//integer不能使用notEmpty
    @Min(value = 0,message = "排序必須大於等於0")
    private Integer sort;

}

9.集中異常處理

 咱們以前的方法處理異常很是麻煩,如今來統一處理異常

首先把bindingresult那一段代碼刪掉,不去感知異常,異常就會拋出去

@Slf4j//記錄日誌
//restController+ControllerAdvice
@RestControllerAdvice(basePackages = "com.wuyimin.gulimall.product.controller")
public class GulimallExceptionControllerAdvice {
    @ExceptionHandler(value = MethodArgumentNotValidException.class)
    public R handleValidException(MethodArgumentNotValidException e){
        log.error("數據校驗出現問題:{},異常類型:{}",e.getMessage(),e.getClass());
        BindingResult bindingResult = e.getBindingResult();//以前的BindingResult屬性
        Map<String,String> map=new HashMap<>();
        bindingResult.getFieldErrors().forEach(item->{
            map.put(item.getField(),item.getDefaultMessage());
        });
        return R.error(400,"數據校驗出現問題").put("data",map);
    }
}

 

 爲了規範枚舉一下各類錯誤狀態碼放在common模塊裏

/**
 * @author wuyimin
 * @create 2021-08-06 16:44
 * @description 錯誤狀態碼枚舉
 */
    /***
     * 錯誤碼和錯誤信息定義類
     * 1. 錯誤碼定義規則爲5爲數字
     * 2. 前兩位表示業務場景,最後三位表示錯誤碼。例如:100001。10:通用 001:系統未知異常
     * 3. 維護錯誤碼後須要維護錯誤描述,將他們定義爲枚舉形式
     * 錯誤碼列表:
     *  10: 通用
     *      001:參數格式校驗
     *  11: 商品
     *  12: 訂單
     *  13: 購物車
     *  14: 物流
     * @author ZSY
     */
    public enum BizCodeEnum {
        /**
         * 系統未知異常
         */
        UNKNOWN_EXCEPTION(10000, "系統未知異常"),
        /**
         * 參數校驗錯誤
         */
        VALID_EXCEPTION(10001, "參數格式校驗失敗");
        private final int code;
        private final String msg;
        BizCodeEnum(int code, String msg) {
            this.code = code;
            this.msg = msg;
        }
        public int getCode() {
            return code;
        }
        public String getMsg() {
            return msg;
        }
    }

更改後的代碼

@Slf4j//記錄日誌
//restController+ControllerAdvice
@RestControllerAdvice(basePackages = "com.wuyimin.gulimall.product.controller")
public class GulimallExceptionControllerAdvice {
    @ExceptionHandler(value = MethodArgumentNotValidException.class)
    public R handleValidException(MethodArgumentNotValidException e){
        log.error("數據校驗出現問題:{},異常類型:{}",e.getMessage(),e.getClass());
        BindingResult bindingResult = e.getBindingResult();//以前的BindingResult屬性
        Map<String,String> map=new HashMap<>();
        bindingResult.getFieldErrors().forEach(item->{
            map.put(item.getField(),item.getDefaultMessage());
        });
        return R.error(BizCodeEnum.VALID_EXCEPTION.getCode(),BizCodeEnum.VALID_EXCEPTION.getMsg()).put("data",map);
    }

    //其餘任何異常都默認返回error
    @ExceptionHandler(value=Throwable.class)
    public R handleException(Throwable throwable){
        return R.error(BizCodeEnum.UNKNOWN_EXCEPTION.getCode(),BizCodeEnum.UNKNOWN_EXCEPTION.getMsg());
    }
}
/**
 * @author wuyimin
 * @create 2021-08-06 16:44
 * @description 錯誤狀態碼枚舉
*/
    /***
     * 錯誤碼和錯誤信息定義類
* 1. 錯誤碼定義規則爲5爲數字
* 2. 前兩位表示業務場景,最後三位表示錯誤碼。例如:100001。10:通用 001:系統未知異常
* 3. 維護錯誤碼後須要維護錯誤描述,將他們定義爲枚舉形式
* 錯誤碼列表:
*  10: 通用
*      001:參數格式校驗
*  11: 商品
*  12: 訂單
*  13: 購物車
*  14: 物流
* @author ZSY
     */
public enum BizCodeEnum {
        /**
         * 系統未知異常
*/
UNKNOWN_EXCEPTION(10000, "系統未知異常"),
/**
         * 參數校驗錯誤
*/
VALID_EXCEPTION(10001, "參數格式校驗失敗");
        private final int code;
        private final String msg;
BizCodeEnum(int code, String msg) {
            this.code = code;
            this.msg = msg;
}
        public int getCode() {
            return code;
}
        public String getMsg() {
            return msg;
}
    }

測試校驗

 10.分組校驗+自定義校驗註解

entity和controller

 

 

/**
 * 品牌
 * 
 * @author wuyimin
 * @email 565482647@qq.com
 * @date 2021-08-03 11:43:58
 */
@Data
@TableName("pms_brand")
public class BrandEntity implements Serializable {
    private static final long serialVersionUID = 1L;

    /**
     * 品牌id
     */
    @NotNull(message = "修改必須指定品牌id", groups = {UpdateGroup.class})
    @Null(message = "新增不能指定id", groups = {AddGroup.class})
    @TableId
    private Long brandId;
    /**
     * 品牌名
     */
    @NotBlank(message = "品牌名必須提交", groups = {AddGroup.class, UpdateGroup.class})
    private String name;
    /**
     * 品牌logo地址
     */
    @NotBlank(groups = {AddGroup.class})
    @URL(message = "logo必須是一個合法的url地址", groups = {AddGroup.class, UpdateGroup.class})
    private String logo;
    /**
     * 介紹
     */
    private String descript;
    /**
     * 顯示狀態[0-不顯示;1-顯示]
     */
//  @Pattern()
    @NotNull(groups = {AddGroup.class, UpdateStatusGroup.class})
    @ListValue(vals = {0, 1}, groups = {AddGroup.class, UpdateStatusGroup.class})
    private Integer showStatus;
    /**
     * 檢索首字母
     */
    @NotEmpty(groups = {AddGroup.class})
    @Pattern(regexp = "^[a-zA-Z]$", message = "檢索首字母必須是一個字母", groups = {AddGroup.class, UpdateGroup.class})
    private String firstLetter;
    /**
     * 排序
     */
    @NotNull(groups = {AddGroup.class})
    @Min(value = 0, message = "排序必須大於等於0", groups = {AddGroup.class, UpdateGroup.class})
    private Integer sort;

}
    /**
     * 修改
     */
    @RequestMapping("/update")
    //@RequiresPermissions("product:brand:update")
    public R update(@RequestBody BrandEntity brand){
        brandService.updateById(brand);

        return R.ok();
    }

    /**
     * 修改狀態
     */
    @RequestMapping("/update/status")
    //@RequiresPermissions("product:brand:update")
    public R updateStatus(@Validated(UpdateStatusGroup.class)@RequestBody BrandEntity brand){
        brandService.updateById(brand);
        return R.ok();
    }

自定義校驗:

common引入依賴

<!-- https://mvnrepository.com/artifact/javax.validation/validation-api -->
<dependency>
    <groupId>javax.validation</groupId>
    <artifactId>validation-api</artifactId>
    <version>2.0.1.Final</version>
</dependency>

自定義註解

add update updateStatus是三個空接口用於給校驗註解分類

 

 ListValue自定義註解

@Documented
@Constraint(validatedBy = {ListValueConstraintValidator.class})//關聯自定義的校驗器
@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})
@Retention(RUNTIME)
public @interface ListValue {
    String message() default "{com.wuyimin.common.valid.ListValue.message}";//提示信息從ValidationMessages.Properties裏拿到,也能夠直接定義

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};

    int[] vals() default {};
}

校驗器 ListValueConstraintValidator

public class ListValueConstraintValidator implements ConstraintValidator<ListValue,Integer> {
    private Set<Integer> set=new HashSet<>();
    //初始化方法 把val值拿到存入集合
    @Override
    public void initialize(ListValue constraintAnnotation) {
        int[] vals=constraintAnnotation.vals();
        for (int val:vals){
            set.add(val);
        }
    }
    //判斷是否校驗成功
    /**
     *
     * @param integer 須要校驗的值
     * @param constraintValidatorContext
     * @return
     */
    @Override
    public boolean isValid(Integer integer, ConstraintValidatorContext constraintValidatorContext) {
        return set.contains(integer);//包含就返回true,不包含就返回false
    }
}

測試

 

 

 

 

相關文章
相關標籤/搜索