谷粒商城笔记合集
九、商品服务&品牌管理⚠️
9.1 前端开发:引入组件
注意:组件中使用到的所有API都是逆向工程生成好的
-
在 管理系统前端项目 系统管理-菜单管理 中商品系统目录下新增:品牌管理菜单
-
将 逆向工程renren-generator 中生成的前端组件拷贝到 管理系统前端项目renren-fast-vue 中
-
保存修改,查看组件效果
9.2 前后端联调:显示状态按钮优化
API
/**
* 修改状态
*/
@RequestMapping("/update/status")
public R updateStatus(@RequestBody BrandEntity brand){
brandService.updateById(brand);
return R.ok();
}
前端
-
src/utils/index.js:注释权限验证
/** * 是否有权限 * @param {*} key */ export function isAuth (key) { // return JSON.parse(sessionStorage.getItem('permissions') || '[]').indexOf(key) !== -1 || false return true }
-
views/modules/product/brand.vue:优化显示状态按钮
<template> <div class="mod-config"> <el-table :data="dataList" border v-loading="dataListLoading" @selection-change="selectionChangeHandle" style="width: 100%;"> <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> </div> </template>
-
views/modules/product/brand-add-or-update.vue:新增对话框优化显示状态按钮
<template> <el-dialog :title="!dataForm.brandId ? '新增' : '修改'" :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="showStatus"> <!-- :active-value="1" :inactive-value="0" @change="updateBrandStatus(scope.row) --> <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> </el-dialog> </template>
-
修改请求URL:/product/brand/update/status
9.3 文件上传分析💡
9.3.1 文件存储方式
9.3.2 云存储上传方式
1)普通上方式
2)服务端签名后上传
9.4 后端开发:第三方服务-OSS⚠️
9.4.1 创建阿里云OSS相关服务
-
创建OSS存储桶
-
创建RAM用户
-
给创建的RAM用户添加OSS管理权限
9.4.2 创建第三方服务模块💡
-
创建 第三方服务模块:bilimall-third-party
-
在 第三方服务 中修改 pom.xml
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <!-- 1、修改 SpringBoot 版本 --> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.8.RELEASE</version> <relativePath/> </parent> <!-- 2、修改描述信息 --> <description>第三方服务</description> <properties> <java.version>17</java.version> <!-- 3、修改 SpringCloud 版本 --> <spring-cloud.version>Greenwich.SR3</spring-cloud.version> </properties> <dependencies> <!-- 4、引入 Spring Cloud Alibaba Oss --> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alicloud-oss</artifactId> </dependency> <!-- 5、引入 公共依赖 --> <dependency> <groupId>cn.lzwei.bilimall</groupId> <artifactId>bilimall-common</artifactId> <version>0.0.1-SNAPSHOT</version> <exclusions> <!-- 6、剔除 MybatisPlus 依赖 --> <exclusion> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> </exclusion> </exclusions> </dependency> </dependencies> <dependencyManagement> <dependencies> <!-- 7、引入 Spring cloud alibaba --> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-alibaba-dependencies</artifactId> <version>2.1.0.RELEASE</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> </project>
-
在 第三方服务 中创建配置文件配置nacos注册中心、oss、服务端口:application.yaml
spring: cloud: nacos: discovery: server-addr: 114.132.162.129:8848 alicloud: access-key: LTAI5t9sqxLYEuj8ShZ4km9b secret-key: UN7Bl1u9wj4mOK4qzni7YeKc3xgvOr oss: endpoint: oss-cn-guangzhou.aliyuncs.com application: name: bilimall-third-party server: port: 30000
-
在 nacos服务 中为 第三方服务 创建命名空间:third-party
-
在 nacos服务third-party命名空间 中添加 OSS配置文件:oss.yaml
spring: cloud: alicloud: access-key: LTAI5t9sqxLYEuj8ShZ4km9b secret-key: UN7Bl1u9wj4mOK4qzni7YeKc3xgvOr oss: endpoint: oss-cn-guangzhou.aliyuncs.com
-
在 第三方服务 创建配置文件配置nacos配置中心:bootstrap.properties
spring.application.name=bilimall-third-party spring.cloud.nacos.config.server-addr=114.132.162.129:8848 spring.cloud.nacos.config.namespace=1185ba57-96ae-4b78-ba83-cc4e5f1ae05a #命名空间中,默认配置文件的分组:服务名.properties spring.cloud.nacos.config.group=dev #命名空间中,其他配置集[0] spring.cloud.nacos.config.ext-config[0].data-id=oss.yaml spring.cloud.nacos.config.ext-config[0].group=dev spring.cloud.nacos.config.ext-config[0].refresh=true
-
在 第三方服务 的主启动类上开启 服务发现与注册 功能
@EnableDiscoveryClient
9.4.3 API:OSS服务端签名💡
-
在 第三方服务 中创建 cn.lzwei.bilimall.thirdparty.controller.OssController
package cn.lzwei.bilimall.thirdparty.controller; @RestController public class OssController { @Resource OSS ossClient; @Value("${spring.cloud.alicloud.access-key}") private String accessId; @Value("${spring.cloud.alicloud.oss.endpoint}") private String endpoint; @Value("${spring.cloud.alicloud.oss.bucket}") private String bucket; @RequestMapping("/oss/policy") public R policy(){ // 设置上传到OSS文件的前缀,可置空此项。置空后,文件将上传至Bucket的根目录下。 String dir = DateTimeFormatter.ofPattern("yyyy-MM-dd").format(LocalDate.now())+"/"; String host= "https://" + bucket + "." + endpoint; 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("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: alicloud: oss: bucket: bilimall-20221226
-
在 网关服务bilimall-gateway 中添加路由规则:注意规则顺序,越具体的匹配路径优先级越高
spring: cloud: gateway: routes: - id: thir_party_route uri: lb://bilimall-third-party predicates: - Path=/api/thirdparty/** filters: - RewritePath=/api/thirdparty/?(?<segment>.*),/$\{segment}
-
启动 第三方服务 ,查看访问效果
9.5 前后端联调:文件上传💡
9.5.1 前端开发:文件上传控件
-
将文件上传组件的文件夹拷贝到 src/components 目录下
-
修改 components/upload/singleUpload.vue:修改上传的请求地址为自己 存储桶域名
<template> <div> <el-upload action="https://bilimall-20221226.oss-cn-guangzhou.aliyuncs.com" </el-upload> </div> </template>
-
修改 components/upload/policy.js:删除请求参数的设置
import http from '@/utils/httpRequest.js' export function policy () { return new Promise((resolve, reject) => { http({ url: http.adornUrl('/thirdparty/oss/policy'), method: 'get' //删除请求参数 }).then(({ data }) => { resolve(data) }) }) }
-
在品牌新增的弹窗组件中引入文件上传组件:views/modules/product/brand-add-or-update.vue
<template> <el-dialog :title="!dataForm.brandId ? '新增' : '修改'" :close-on-click-modal="false" :visible.sync="visible"> <el-form-item label="品牌logo地址" prop="logo"> <!-- <el-input v-model="dataForm.logo" placeholder="品牌logo地址"></el-input> --> <singleUpload v-model="dataForm.logo"></singleUpload> </el-form-item> </el-dialog> </template> <script> import singleUpload from '@/components/upload/singleUpload' export default { components: { singleUpload } } </script>
9.5.2 出现问题:上传阿里云跨域
9.5.3 问题解决:配置阿里云跨域
-
在阿里云控制台中对 存储桶bilimall-20221226 进行跨域设置
-
上传成功
9.6 API&前端开发:新增、表单图片显示💡
API
BrandController
/**
* 保存
*/
@RequestMapping("/save")
public R save(@RequestBody BrandEntity brand){
brandService.save(brand);
return R.ok();
}
前端
-
将 views/modules/product/brand.vue 表单品牌logo地址显示更改为图片控件:引入elementui所有组件,使用<el-image>,效果不佳
-
将 views/modules/product/brand.vue 表单品牌logo地址显示使用原生 <image>
<template> <div class="mod-config"> <el-table :data="dataList" border v-loading="dataListLoading" @selection-change="selectionChangeHandle" style="width: 100%;"> <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="contain"></el-image> --> <img :src="scope.row.logo" style="width: 100px; height: 80px" /> </template> </el-table-column> </el-table> </div> </template>
-
更改 views/modules/product/brand-add-or-update.vue 的表单校验规则:首字母、排序
<template> <el-dialog :title="!dataForm.brandId ? '新增' : '修改'" :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="sort"> <el-input v-model.number="dataForm.sort" placeholder="排序"></el-input> </el-form-item> </el-form> </el-dialog> </template> <script> export default { data () { return { dataRule: { 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' } ] } } } } </script>
9.7 后端开发:数据校验&统一异常处理⚠️
9.7.1 API:添加JSR303校验💡
添加校验,开启校验功能并封装返回信息
-
给 BrandEntity 添加校验规则:添加javax.validation.constraints包下的注解 @NotBlank、@URL、@Pattern、@Min@NotEmpty、@NotNull
@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(message = "品牌logo地址") @URL(message = "品牌logo地址必须是标准的url地址") private String logo; /** * 介绍 */ private String descript; /** * 显示状态[0-不显示;1-显示] */ @NotNull(message = "显示状态必须非空") private Integer showStatus; /** * 检索首字母 */ @NotEmpty(message = "首字母非空") @Pattern(regexp = "^[a-zA-Z]$",message = "首字母必须是a-z或A-Z") private String firstLetter; /** * 排序 */ @NotNull(message = "排序非空") @Min(message = "排序字段必须非空且大于等于0",value = 0) private Integer sort; }
-
在 BrandController 相应API中开启校验功能:添加 @Valid 注解
效验的bean后紧跟一个 BindingResult 获取到效验的结果
/** * 保存 */ @RequestMapping("/save") public R save(@Valid @RequestBody BrandEntity brand, BindingResult result){ if (result.hasErrors()) { Map<String,String> map=new HashMap(); //1.获取所有校验异常的字段,并进行遍历组装 result.getFieldErrors().forEach(item->{ String field = item.getField(); String defaultMessage = item.getDefaultMessage(); map.put(field,defaultMessage); }); //2.返回收集结果 return R.error(400,"数据校验失败").put("data",map); }else { brandService.save(brand); return R.ok(); } }
9.7.2 统一异常处理:获取校验异常类型
2022-12-27 17:59:36.053 ERROR 21812 --- [io-11000-exec-1] .b.p.e.BilimallExceptionControllerAdvice : 数据校验异常信息...异常类型class org.springframework.web.bind.MethodArgumentNotValidException
这里使用到了 SpringMVC 的注解 @ControllerAdvice
-
编写异常处理类使用SpringMvc的@ControllerAdvice
-
使用@ExceptionHandler标记方法可以处理异常
发送请求获取校验异常类型:MethodArgumentNotValidException
-
关闭 BrandController 中的校验异常拦截
/** * 保存 */ @RequestMapping("/save") public R save(@Valid @RequestBody BrandEntity brand/**,BindingResult result*/){ // if (result.hasErrors()) { // Map<String,String> map=new HashMap(); // //1.获取所有校验异常的字段,并进行遍历组装 // result.getFieldErrors().forEach(item->{ // String field = item.getField(); // String defaultMessage = item.getDefaultMessage(); // map.put(field,defaultMessage); // }); // //2.返回收集结果 // return R.error(400,"数据校验失败").put("data",map); // }else { // brandService.save(brand); // return R.ok(); // } brandService.save(brand); return R.ok(); }
-
在 商品服务 中添加统一异常处理类:BilimallExceptionControllerAdvice
/** * 集中处理所有异常 */ @Slf4j @ResponseBody @ControllerAdvice(basePackages = "cn.lzwei.bilimall.product.controller") public class BilimallExceptionControllerAdvice { /** * 数据校验失败 * @param e * @return */ @ExceptionHandler(value = Exception.class) public R handleVaildException(Exception e){ log.debug("数据校验异常信息{},异常类型{}",e.getMessage(),e.getClass());//获取校验异常类型 return R.error(BizCodeEnume.VAILD_EXCEPTION.getCode(), BizCodeEnume.VAILD_EXCEPTION.getMessage()).put("data",e.getMessage()); } }
9.7.3 统一异常处理:优化⚠️
区分数据校验异常、其他异常
统一异常码、异常信息:后端将定义的错误码写入到开发手册,前端出现对于的错误,就可以通过手册查询到对应的异常
-
在 公共服务 中增加异常状态枚举类:cn.lzwei.common.exception.BizCodeEnume
/*** * 错误码和错误信息定义类 * 1. 错误码定义规则为5为数字 * 2. 前两位表示业务场景,最后三位表示错误码。例如:100001。10:通用 001:系统未知异常 * 3. 维护错误码后需要维护错误描述,将他们定义为枚举形式 * 错误码列表: * 10: 通用 * 001:参数格式校验 * 11: 商品 * 12: 订单 * 13: 购物车 * 14: 物流 */ public enum BizCodeEnume { UNKNOW_EXCEPTION(10000,"系统未知异常"), VAILD_EXCEPTION(10001,"参数格式校验失败"); private Integer code; private String message; BizCodeEnume(Integer code,String message){ this.code=code; this.message=message; } public Integer getCode() { return code; } public String getMessage() { return message; } }
-
在 商品服务 统一异常处理类 BilimallExceptionControllerAdvice 中进一步优化:区分数据校验异常、其他异常;统一异常码、异常信息
/** * 集中处理所有异常 */ @Slf4j @RestControllerAdvice(basePackages = "cn.lzwei.bilimall.product.controller") public class BilimallExceptionControllerAdvice { /** * 数据校验失败 * @param e * @return */ @ExceptionHandler(value = MethodArgumentNotValidException.class) public R handleVaildException(MethodArgumentNotValidException e){ log.error("数据校验异常信息{},异常类型{}",e.getMessage(),e.getClass()); Map<String,String> map=new HashMap(); //1.获取所有校验异常的字段,并进行遍历组装 BindingResult bindingResult = e.getBindingResult(); bindingResult.getFieldErrors().forEach(item->{ String field = item.getField(); String defaultMessage = item.getDefaultMessage(); map.put(field,defaultMessage); }); //2.返回收集结果 return R.error(BizCodeEnume.VAILD_EXCEPTION.getCode(), BizCodeEnume.VAILD_EXCEPTION.getMessage()).put("data",map); } /** * 其他异常 */ @ExceptionHandler(value = Throwable.class) public R handleException(Throwable t){ return R.error(BizCodeEnume.UNKNOW_EXCEPTION.getCode(), BizCodeEnume.UNKNOW_EXCEPTION.getMessage()); } }
9.7.4 数据校验:分组校验⚠️
注意💡:
API指定分组@Validated(value = {xxx.class}) :@NotNull(groups = {xxx.class}) 生效,其他不生效
API未指定分组 @Valid:@NotNull(groups = {xxx.class})不生效,其他生效
-
在 公共服务 中新增分组接口:cn.lzwei.common.valid.SaveGroup、UpdateStatusGroup、UpdateGroup
public interface SaveGroup { } public interface UpdateStatusGroup { } public interface UpdateGroup { }
-
在 商品服务 BrandEntity中添加校验分组
@Data @TableName("pms_brand") public class BrandEntity implements Serializable { private static final long serialVersionUID = 1L; /** * 品牌id */ @NotNull(message = "修改时品牌id不能为空",groups = {UpdateGroup.class, UpdateStatusGroup.class}) @Null(message = "新增品牌时id必须为空",groups = {SaveGroup.class}) @TableId private Long brandId; /** * 品牌名 */ @NotBlank(message = "品牌名称至少包含一个非空字符",groups = {UpdateGroup.class,SaveGroup.class}) @Null(message = "修改状态时品牌名必须为空",groups = {UpdateStatusGroup.class}) private String name; /** * 品牌logo地址 */ @NotEmpty(message = "品牌logo地址",groups = {UpdateGroup.class,SaveGroup.class}) @URL(message = "品牌logo地址必须是标准的url地址",groups = {UpdateGroup.class,SaveGroup.class}) @Null(message = "修改状态时品牌logo地址必须为空",groups = {UpdateStatusGroup.class}) private String logo; /** * 介绍 */ @Null(message = "修改状态时介绍必须为空",groups = {UpdateStatusGroup.class}) private String descript; /** * 显示状态[0-不显示;1-显示] */ @NotNull(message = "显示状态必须非空",groups = {UpdateGroup.class,SaveGroup.class, UpdateStatusGroup.class}) private Integer showStatus; /** * 检索首字母 */ @NotEmpty(message = "首字母非空",groups = {UpdateGroup.class,SaveGroup.class}) @Pattern(regexp = "^[a-zA-Z]$",message = "首字母必须是a-z或A-Z",groups = {UpdateGroup.class,SaveGroup.class}) @Null(message = "修改状态时首字母必须为空",groups = {UpdateStatusGroup.class}) private String firstLetter; /** * 排序 */ @NotNull(message = "排序非空",groups = {UpdateGroup.class,SaveGroup.class}) @Min(message = "排序字段必须非空且大于等于0",value = 0,groups = {UpdateGroup.class,SaveGroup.class}) @Null(message = "修改状态时排序必须为空",groups = {UpdateStatusGroup.class}) private Integer sort; }
-
在 商品服务 BrandController中添加注解开启分组校验:@Validated
/** * 保存 */ @RequestMapping("/save") public R save(@Validated(value = {SaveGroup.class}) @RequestBody BrandEntity brand/**,BindingResult result*/){ brandService.save(brand); return R.ok(); } /** * 修改 */ @RequestMapping("/update") public R update(@Validated(value = {UpdateGroup.class}) @RequestBody BrandEntity brand){ brandService.updateById(brand); return R.ok(); } /** * 修改状态 */ @RequestMapping("/update/status") public R updateStatus(@Validated(value = {UpdateStatusGroup.class}) @RequestBody BrandEntity brand){ brandService.updateById(brand); return R.ok(); }
9.7.5 数据校验:自定义校验⚠️
-
在 公共服务 中创建自定义校验注解:cn.lzwei.common.valid.@ListValue
@Constraint(validatedBy = {StatusConstraintValidatorForInteger.class }) //校验器 @Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE }) @Retention(RUNTIME) @Documented public @interface ListValue { String message() default "{cn.lzwei.common.valid.ListValue.message}"; //默认在配置文件中获取消息 Class<?>[] groups() default { }; Class<? extends Payload>[] payload() default { }; int[] value() default { }; //可接受的值 }
-
在 公共服务 中创建配置文件并配置 自定义注解 的默认消息:ValidationMessages.properties
cn.lzwei.common.valid.ListValue.message = 必须使用指定的值
-
在 公共服务 中为自定义校验注解创建校验器:cn.lzwei.common.valid.StatusConstraintValidatorForInteger
/** * 自定义校验注解@ListValue 的Integer校验器 */ public class StatusConstraintValidatorForInteger implements ConstraintValidator<ListValue,Integer> { Set<Integer> set=new HashSet<>(); //初始化:获取可接受的数据集合 @Override public void initialize(ListValue constraintAnnotation) { int[] value = constraintAnnotation.value(); if (value!=null || value.length>0){ for (int i : value) { set.add(i); } } } //判断:值是否可接受 @Override public boolean isValid(Integer value, ConstraintValidatorContext context) { return set.contains(value); } }
-
在 商品服务 的 BrandEntity 的显示状态上使用自定义注解
public class BrandEntity implements Serializable { private static final long serialVersionUID = 1L; ... /** * 品牌id */ @NotNull(message = "修改时品牌id不能为空",groups = {UpdateGroup.class, UpdateStatusGroup.class}) @Null(message = "新增品牌时id必须为空",groups = {SaveGroup.class}) @TableId private Long brandId; /** * 显示状态[0-不显示;1-显示] */ @ListValue(value={1,0},groups = {UpdateStatusGroup.class}) @NotNull(message = "显示状态必须非空",groups = {UpdateGroup.class,SaveGroup.class, UpdateStatusGroup.class}) private Integer showStatus; }
9.8 后端开发:优化 分页&搜索💡
-
分页优化:cn.lzwei.bilimall.product.config.MybatisConfig
/** * 分页查询 */ @MapperScan("cn.lzwei.bilimall.product.dao") @Configuration @EnableTransactionManagement public class MybatisConfig { @Bean public PaginationInterceptor paginationInterceptor(){ PaginationInterceptor paginationInterceptor=new PaginationInterceptor(); //设置请求的页面大于最大页后操作,true回到首页,false继续请求。默认false paginationInterceptor.setOverflow(true); //设置最大单页限制数量,默认500条,-1不受限制 paginationInterceptor.setLimit(1000); return paginationInterceptor; } }
-
搜索优化:cn.lzwei.bilimall.product.service.impl.BrandServiceImpl
@Service("brandService") public class BrandServiceImpl extends ServiceImpl<BrandDao, BrandEntity> implements BrandService { @Override public PageUtils queryPage(Map<String, Object> params) { String key = (String) params.get("key"); QueryWrapper<BrandEntity> queryWrapper=new QueryWrapper(); //添加搜索条件 if(!StringUtils.isNullOrEmpty(key)){ queryWrapper.eq("brand_id",key).or().like("name",key); } IPage<BrandEntity> page= this.page( new Query<BrandEntity>().getPage(params), queryWrapper ); return new PageUtils(page); } }
9.9 API:关联分类 查询&新增💡
API开发
-
CategoryBrandRelationController
@RestController @RequestMapping("product/categorybrandrelation") public class CategoryBrandRelationController { @Autowired private CategoryBrandRelationService categoryBrandRelationService; /** * 获取品牌关联的所有分类列表 */ @GetMapping("/catelog/list") public R cateloglist(@RequestParam(name = "brandId") Long brandId){ List<CategoryBrandRelationEntity> data=categoryBrandRelationService.list( new QueryWrapper<CategoryBrandRelationEntity>().eq("brand_id",brandId) ); return R.ok().put("data", data); } /** * 保存品牌的分类信息:添加冗余字段 分类名+品牌名 */ @RequestMapping("/save") public R save(@RequestBody CategoryBrandRelationEntity categoryBrandRelation){ categoryBrandRelationService.saveDetail(categoryBrandRelation); return R.ok(); } }
-
CategoryBrandRelationService
public interface CategoryBrandRelationService extends IService<CategoryBrandRelationEntity> { PageUtils queryPage(Map<String, Object> params); /** * 保存品牌的分类信息:添加冗余字段 分类名+品牌名 */ void saveDetail(CategoryBrandRelationEntity categoryBrandRelation); }
-
CategoryBrandRelationServiceImpl
@Service("categoryBrandRelationService") public class CategoryBrandRelationServiceImpl extends ServiceImpl<CategoryBrandRelationDao, CategoryBrandRelationEntity> implements CategoryBrandRelationService { @Resource BrandService brandService; @Resource CategoryService categoryService; /** * 保存品牌的分类信息:添加冗余字段 分类名+品牌名 */ @Override public void saveDetail(CategoryBrandRelationEntity categoryBrandRelation) { //1.获取品牌、分类信息 Long brandId = categoryBrandRelation.getBrandId(); Long catelogId = categoryBrandRelation.getCatelogId(); BrandEntity brand = brandService.getById(brandId); CategoryEntity category = categoryService.getById(catelogId); //2.添加冗余字段 分类名+品牌名 categoryBrandRelation.setBrandName(brand.getName()); categoryBrandRelation.setCatelogName(category.getName()); //3.保存关联信息 this.save(categoryBrandRelation); } }
9.10 API:修改优化&品牌分类关系表💡
-
BrandController:修改品牌,还需同步修改品牌分类关系表
@RestController @RequestMapping("product/brand") public class BrandController { @Autowired private BrandService brandService; /** * 品牌修改:并更新在其他表中的冗余字段 */ @RequestMapping("/update") public R update(@Validated(value = {UpdateGroup.class}) @RequestBody BrandEntity brand){ brandService.updateDetail(brand); return R.ok(); } }
-
BrandService:修改品牌,还需同步修改品牌分类关系表
public interface BrandService extends IService<BrandEntity> { /** * 品牌修改:并更新在其他表中的冗余字段 */ void updateDetail(BrandEntity brand); }
-
BrandServiceImpl:修改品牌,还需同步修改品牌分类关系表
@Service("brandService") public class BrandServiceImpl extends ServiceImpl<BrandDao, BrandEntity> implements BrandService { @Resource CategoryBrandRelationService categoryBrandRelationService; /** * 品牌修改:并更新在其他表中的冗余字段 */ @Transactional @Override public void updateDetail(BrandEntity brand) { this.updateById(brand); //1.更新 品牌分类关联表 Long brandId = brand.getBrandId(); String name = brand.getName(); categoryBrandRelationService.updateBrand(brandId,name); //TODO 品牌修改:更新在其他表中的冗余字段 } }
-
CategoryBrandRelationService:修改品牌,还需同步修改品牌分类关系表
public interface CategoryBrandRelationService extends IService<CategoryBrandRelationEntity> { /** * 品牌修改:并更新在其他表中的冗余字段 */ void updateBrand(Long brandId, String name); }
-
CategoryBrandRelationServiceImpl:修改品牌,还需同步修改品牌分类关系表
@Service("categoryBrandRelationService") public class CategoryBrandRelationServiceImpl extends ServiceImpl<CategoryBrandRelationDao, CategoryBrandRelationEntity> implements CategoryBrandRelationService { /** * 品牌修改:并更新在其他表中的冗余字段 */ @Override public void updateBrand(Long brandId, String name) { CategoryBrandRelationEntity categoryBrandRelationEntity = new CategoryBrandRelationEntity(); categoryBrandRelationEntity.setBrandName(name); baseMapper.update( categoryBrandRelationEntity, new UpdateWrapper<CategoryBrandRelationEntity>().eq("brand_id",brandId) ); } }