当前位置:   article > 正文

谷粒商城-个人笔记(基础篇二)

谷粒商城

目录

 五、功能模块实现

1、 商品系统

1.1、分类维护

1)、递归树形结构获取数据

2)、删除数据

3)、 菜单拖动

4)、拖动菜单时需要修改顺序和级别

1.2、品牌管理

1)、添加“显示状态按钮”

2)、添加上传

3)、上传其他方式

4)、JSR303校验

5)、新增品牌与分类关联关系

6)、获取品牌关联的分类

1.3 、平台属性(商品SPU和SKU管理)

1.3.1、属性分组

1)、获取分类属性分组

2)、获取属性分组详情

3)、查询分组关联属性和删除关联

4)、删除属性与分组的关联关系

5)、获取属性分组没有关联的其他属性

6)、添加属性与分组关联关系

1.3.2、 规格参数

1)、保存属性【规格参数,销售属性】

1.3.3、销售属性

1)、 获取分类销售属性

1.4、商品维护

1.4.1、spu管理

01)、获取spu规格

02)、修改商品规格

1.4.2、发布商品

1)、获取所有会员等级

2)、获取分类关联的品牌

3)、获取分类下所有分组&关联属性

4)、新增商品

5)、spu检索

6)、sku检索

 五、功能模块实现

接口文档:谷粒商城:01、获取所有分类及子分类


 1、 商品系统

1.1、分类维护

1)、递归树形结构获取数据

分类维护接口文档:获取所有分类及子分类

GET:   product/category/list/tree

接口描述

获取所有分类以及子分类,并返回json树形结构

响应参数

参数名参数类型描述
codeint0-成功,其他业务码-失败
msgstring提示消息
dataarray返回的所有菜单

响应示例

{
    "code": 0,
    "msg": "success",
    "data": [{
        "catId": 1,
        "name": "图书、音像、电子书刊",
        "parentCid": 0,
        "catLevel": 1,
        "showStatus": 1,
        "sort": 0,
        "icon": null,
        "productUnit": null,
        "productCount": 0,
        "children": []
    }]
}

在注册中心中“product”命名空间中,创建“gulimall-product.yml”配置文件:

将“application.yml”内容拷贝到该配置文件

  1. #端口
  2. server:
  3. port: 10001
  4. spring:
  5. datasource:
  6. url: jdbc:mysql://192.168.119.127:3306/gulimall_pms?useUnicode=true&characterEncoding=UTF-8
  7. driver-class-name: com.mysql.cj.jdbc.Driver
  8. username: root
  9. password: root
  10. cloud:
  11. nacos:
  12. discovery:
  13. server-addr: 127.0.0.1:8848
  14. mybatis-plus:
  15. mapper-locations: classpath:/mapper/**/*.xml
  16. global-config: #配置每个实体类的主键自增长
  17. db-config:
  18. id-type: auto
  19. logic-delete-value: 1
  20. logic-not-delete-value: 0
  21. logging:
  22. level:
  23. com.atguigu.gulimall: debug

 在本地创建“bootstrap.properties”文件,指明配置中心的位置和使用到的配置文件:

  1. spring.application.name=gulimall-product
  2. spring.cloud.nacos.config.server-addr=127.0.0.1:8848
  3. spring.cloud.nacos.config.namespace=2128d268-4782-4ec8-88ee-ac5254696ea8
  4. spring.cloud.nacos.config.extension-configs[0].data-id=gulimall-product.yml
  5. spring.cloud.nacos.config.extension-configs[0].group=DEFAULT_GROUP
  6. spring.cloud.nacos.config.extension-configs[0].refresh=true

然后启动gulimall-product,查看到该服务已经出现在了nacos的注册中心中了

修改“com.atguigu.gulimall.product.entity.CategoryEntity”类,代码如下:

  1. @Data
  2. @TableName("pms_category")
  3. public class CategoryEntity implements Serializable {
  4. private static final long serialVersionUID = 1L;
  5. /**
  6. * 分类id
  7. */
  8. @TableId
  9. private Long catId;
  10. /**
  11. * 分类名称
  12. */
  13. private String name;
  14. /**
  15. * 父分类id
  16. */
  17. private Long parentCid;
  18. /**
  19. * 层级
  20. */
  21. private Integer catLevel;
  22. /**
  23. * 是否显示[0-不显示,1显示]
  24. */
  25. @TableLogic(value = "1",delval = "0")
  26. private Integer showStatus;
  27. /**
  28. * 排序
  29. */
  30. private Integer sort;
  31. /**
  32. * 图标地址
  33. */
  34. private String icon;
  35. /**
  36. * 计量单位
  37. */
  38. private String productUnit;
  39. /**
  40. * 商品数量
  41. */
  42. private Integer productCount;
  43. /**
  44. * 子分类
  45. */
  46. @JsonInclude(JsonInclude.Include.NON_EMPTY) //属性为NULL 不序列化
  47. @TableField(exist = false) //表示当前属性不是数据库的字段,但在项目中必须使用,这样在新增等使用bean的时候,mybatis-plus就会忽略这个,不会报错
  48. private List<CategoryEntity> children;
  49. }

修改“com.atguigu.gulimall.product.controller.CategoryController”类,添加如下代码:

  1. /**
  2. * 查出所有分类以及子分类,以树形结构组装起来
  3. */
  4. @RequestMapping("/list/tree")
  5. public R list(){
  6. List<CategoryEntity> list = categoryService.listWithTree();
  7. return R.ok().put("data", list);
  8. }

修改‘com.atguigu.gulimall.product.service.CategoryService’类,代码如下 

List<CategoryEntity> listWithTree();

如何区别是哪种分类级别?

答:可以通过分类的parent_cid来进行判断,如果是一级分类,其值为0.

 修改‘com.atguigu.gulimall.product.service.impl.CategoryServiceImpl’类,代码如下 

  1. @Override
  2. public List<CategoryEntity> listWithTree() {
  3. //1、查出所有分类
  4. List<CategoryEntity> entities = baseMapper.selectList(null);
  5. //2、组装成父子的树形结构
  6. List<CategoryEntity> level1Menus = entities.stream().filter(categoryEntity -> categoryEntity.getParentCid() == 0)
  7. .map((menu) -> {
  8. menu.setChildren(getChildrens(menu, entities));
  9. return menu;
  10. }).sorted(Comparator.comparingInt(menu -> (menu.getSort() == null ? 0 : menu.getSort())))
  11. .collect(Collectors.toList());
  12. //2.1、找到所有的一级分类
  13. return level1Menus;
  14. }
  15. //递归查找所有菜单的子菜单
  16. private List<CategoryEntity> getChildrens(CategoryEntity root, List<CategoryEntity> all) {
  17. List<CategoryEntity> children = all.stream()
  18. .filter(categoryEntity -> categoryEntity.getParentCid() == root.getCatId())
  19. .map(categoryEntity -> {
  20. //1、找到子菜单
  21. categoryEntity.setChildren(getChildrens(categoryEntity, all));
  22. return categoryEntity;
  23. // 菜单的排序
  24. }).sorted(Comparator.comparingInt(menu -> (menu.getSort() == null ? 0 : menu.getSort())))
  25. .collect(Collectors.toList());
  26. return children;
  27. }

测试:http://localhost:10000/product/category/list/tree

下面是得到的部分JSON数据 

 [
  {
    "catId": 1,
    "name": "图书、音像、电子书刊",
    "parentCid": 0,
    "catLevel": 1,
    "showStatus": 1,
    "sort": 0,
    "icon": null,
    "productUnit": null,
    "productCount": 0,
    "childCategoryEntity": [
      {
        "catId": 22,
        "name": "电子书刊",
        "parentCid": 1,
        "catLevel": 2,
        "showStatus": 1,
        "sort": 0,
        "icon": null,
        "productUnit": null,
        "productCount": 0,
        "childCategoryEntity": [
          {
            "catId": 165,
            "name": "电子书",
            "parentCid": 22,
            "catLevel": 3,
            "showStatus": 1,
            "sort": 0,
            "icon": null,
            "productUnit": null,
            "productCount": 0,
            "childCategoryEntity": []
          },
          {
            "catId": 166,
            "name": "网络原创",
            "parentCid": 22,
            "catLevel": 3,
            "showStatus": 1,
            "sort": 0,
            "icon": null,
            "productUnit": null,
            "productCount": 0,
            "childCategoryEntity": []
          },
          {
            "catId": 167,
            "name": "数字杂志",
            "parentCid": 22,
            "catLevel": 3,
            "showStatus": 1,
            "sort": 0,
            "icon": null,
            "productUnit": null,
            "productCount": 0,
            "childCategoryEntity": []
          },
          {
            "catId": 168,
            "name": "多媒体图书",
            "parentCid": 22,
            "catLevel": 3,
            "showStatus": 1,
            "sort": 0,
            "icon": null,
            "productUnit": null,
            "productCount": 0,
            "childCategoryEntity": []
          }
        ]
      },
      {
        "catId": 23,
        "name": "音像",
        "parentCid": 1,
        "catLevel": 2,
        "showStatus": 1,
        "sort": 0,
        "icon": null,
        "productUnit": null,
        "productCount": 0,
        "childCategoryEntity": [
          {
            "catId": 169,
            "name": "音乐",
            "parentCid": 23,
            "catLevel": 3,
            "showStatus": 1,
            "sort": 0,
            "icon": null,
            "productUnit": null,
            "productCount": 0,
            "childCategoryEntity": []
          },
          {
            "catId": 170,
            "name": "影视",
            "parentCid": 23,
            "catLevel": 3,
            "showStatus": 1,
            "sort": 0,
            "icon": null,
            "productUnit": null,
            "productCount": 0,
            "childCategoryEntity": []
          },
          {
            "catId": 171,
            "name": "教育音像",
            "parentCid": 23,
            "catLevel": 3,
            "showStatus": 1,
            "sort": 0,
            "icon": null,
            "productUnit": null,
            "productCount": 0,
            "childCategoryEntity": []
          }
        ]
      },
      {

启动后端项目renren-fast

启动前端项目renren-fast-vue:

npm run dev

访问: http://localhost:8001/#/login

创建一级菜单:

创建完成后,在后台的管理系统中会创建一条记录:

然后创建子菜单:

创建renren-fast-vue\src\views\modules\product目录,之所以是这样来创建,是因为product/category,对应于product-category

在该目录下,新建“category.vue”文件:

  1. <!-- -->
  2. <template>
  3. <el-tree :data="menus" :props="defaultProps" @node-click="handleNodeClick"></el-tree>
  4. </template>
  5. <script>
  6. //这里可以导入其他文件(比如:组件,工具js,第三方插件js,json文件,图片文件等等)
  7. //例如:import 《组件名称》 from '《组件路径》';
  8. export default {
  9. //import引入的组件需要注入到对象中才能使用
  10. components: {},
  11. //监听属性 类似于data概念
  12. computed: {},
  13. //监控data中的数据变化
  14. watch: {},
  15. data() {
  16. return {
  17. menus: [],
  18. defaultProps: {
  19. children: "childrens",
  20. label: "name"
  21. },
  22. }
  23. },
  24. methods: {
  25. handleNodeClick(data) {
  26. console.log(data);
  27. },
  28. getMenus() {
  29. this.dataListLoading = true;
  30. this.$http({
  31. url: this.$http.adornUrl("/product/category/list/tree"),
  32. method: "get"
  33. }).then(({ data }) => {
  34. console.log("获取到数据", data);
  35. this.menus=data;
  36. });
  37. }
  38. },
  39. //生命周期 - 创建完成(可以访问当前this实例)
  40. created() {
  41. this.getMenus();
  42. },
  43. //生命周期 - 挂载完成(可以访问DOM元素)
  44. mounted() {},
  45. beforeCreate() {}, //生命周期 - 创建之前
  46. beforeMount() {}, //生命周期 - 挂载之前
  47. beforeUpdate() {}, //生命周期 - 更新之前
  48. updated() {}, //生命周期 - 更新之后
  49. beforeDestroy() {}, //生命周期 - 销毁之前
  50. destroyed() {}, //生命周期 - 销毁完成
  51. activated() {} //如果页面有keep-alive缓存功能,这个函数会触发
  52. };
  53. </script>
  54. <style scoped>

刷新页面出现404异常,查看请求发现,请求的是“http://localhost:8080/renren-fast/product/category/list/tree”

这个请求是不正确的,正确的请求是:http://localhost:10000/product/category/list/tree,

修正这个问题:

替换“static\config\index.js”文件中的“window.SITE_CONFIG['baseUrl']”

替换前:

window.SITE_CONFIG['baseUrl'] = 'http://localhost:8080/renren-fast';

替换后:

 window.SITE_CONFIG['baseUrl'] = 'http://localhost:88/api';

http://localhost:88,这个地址是我们网关微服务的接口。

这里我们需要通过网关来完成路径的映射,因此将renren-fast注册到nacos注册中心中,并添加配置中心

  1. application:
  2. name: renren-fast
  3. cloud:
  4. nacos:
  5. discovery:
  6. server-addr: 127.0.0.1:8848
  7. config:
  8. name: renren-fast
  9. server-addr: 127.0.0.1.8848
  10. namespace: ee409c3f-3206-4a3b-ba65-7376922a886d

配置网关路由,前台的所有请求都是经由“http://localhost:88/api”来转发的,在“gulimall-gateway”中添加路由规则:

  1. - id: admin_route
  2. uri: lb://renren-fast
  3. predicates:
  4. - Path=/api/**

但是这样做也引入了另外的一个问题,再次访问:http://localhost:8001/#/login,发现验证码不再显示:

分析原因:

  1. 现在的验证码请求路径为,http://localhost:88/api/captcha.jpg?uuid=69c79f02-d15b-478a-8465-a07fd09001e6
  2. 原始的验证码请求路径:http://localhost:8001/renren-fast/captcha.jpg?uuid=69c79f02-d15b-478a-8465-a07fd09001e6

在admin_route的路由规则下,在访问路径中包含了“api”,因此它会将它转发到renren-fast,网关在转发的时候,会使用网关的前缀信息,为了能够正常的取得验证码,我们需要对请求路径进行重写

关于请求路径重写:

6.16. The RewritePath GatewayFilter Factory

The RewritePath GatewayFilter factory takes a path regexp parameter and a replacement parameter. This uses Java regular expressions for a flexible way to rewrite the request path. The following listing configures a RewritePath GatewayFilter:

Example 41. application.yml

  1. spring:
  2. cloud:
  3. gateway:
  4. routes:
  5. - id: rewritepath_route
  6. uri: https://example.org
  7. predicates:
  8. - Path=/foo/**
  9. filters:
  10. - RewritePath=/red(?<segment>/?.*), $\{segment}

For a request path of /red/blue, this sets the path to /blue before making the downstream request. Note that the $ should be replaced with $\ because of the YAML specification.

修改“admin_route”路由规则:

  1. - id: admin_route
  2. uri: lb://renren-fast
  3. predicates:
  4. - Path=/api/**
  5. filters:
  6. - RewritePath=/api/(?<segment>/?.*), /renren-fast/$\{segment}

再次访问: http://localhost:8001/#/login 发现报503

 分析原因:找不到有效的服务,也就是负载均衡不到对应的服务。由于springcloud2020弃用了Ribbon,因此Alibaba在2021版本nacos中删除了Ribbon的jar包,因此无法通过lb路由到指定微服务,出现了503情况。

解决办法:加入这个依赖

  1. <dependency>
  2. <groupId>org.springframework.cloud</groupId>
  3. <artifactId>spring-cloud-starter-loadbalancer</artifactId>
  4. </dependency>

再次访问:http://localhost:8001/#/login,验证码能够正常的加载了。

但是很不幸新的问题又产生了,访问被拒绝了

image-20200425192722821

问题描述:已拦截跨源请求:同源策略禁止读取位于 http://localhost:88/api/sys/login 的远程资源。(原因:CORS 头缺少 'Access-Control-Allow-Origin')。

问题分析:这是一种跨域问题。访问的域名和端口和原来的请求不同,请求就会被限制

image-20200425192902637

跨域流程:

image-20200425193136641

image-20200425193523849

image-20200425193614185

解决方法:在网关中定义“GulimallCorsConfiguration”类,该类用来做过滤,允许所有的请求跨域。

  1. @Configuration
  2. public class GulimallCorsConfiguration {
  3. @Bean
  4. public CorsWebFilter corsWebFilter(){
  5. UrlBasedCorsConfigurationSource source=new UrlBasedCorsConfigurationSource();
  6. CorsConfiguration corsConfiguration = new CorsConfiguration();
  7. corsConfiguration.addAllowedHeader("*");
  8. corsConfiguration.addAllowedMethod("*");
  9. corsConfiguration.addAllowedOrigin("*");
  10. corsConfiguration.setAllowCredentials(true);
  11. source.registerCorsConfiguration("/**",corsConfiguration);
  12. return new CorsWebFilter(source);
  13. }
  14. }

再次访问:http://localhost:8001/#/login

image-20200425195437299

http://localhost:8001/renren已拦截跨源请求:同源策略禁止读取位于 http://localhost:88/api/sys/login 的远程资源。(原因:不允许有多个 ‘Access-Control-Allow-Origin’ CORS 头)n-fast/captcha.jpg?uuid=69c79f02-d15b-478a-8465-a07fd09001e6

出现了多个请求,并且也存在多个跨源请求。

为了解决这个问题,需要修改renren-fast项目,注释掉“io.renren.config.CorsConfig”类。然后再次进行访问。

在显示分类信息的时候,出现了404异常,请求的http://localhost:88/api/product/category/list/tree不存在

image-20200425213240724

这是因为网关上所做的路径映射不正确,映射后的路径为http://localhost:8001/renren-fast/product/category/list/tree

但是只有通过http://localhost:10000/product/category/list/tree路径才能够正常访问,所以会报404异常。

解决方法就是定义一个product路由规则,进行路径重写:

  1. - id: product_route
  2. uri: lb://gulimall-product
  3. predicates:
  4. - Path=/api/product/**
  5. filters:
  6. - RewritePath=/api/(?<segment>/?.*),/$\{segment}

在路由规则的顺序上,将精确的路由规则放置到模糊的路由规则的前面,否则的话,精确的路由规则将不会被匹配到,类似于异常体系中try catch子句中异常的处理顺序。

2)、删除数据

添加delete和append标识,并且增加复选框

  1. <el-tree
  2. :data="menus"
  3. show-checkbox //显示复选框
  4. :props="defaultProps"
  5. :expand-on-click-node="false" //设置节点点击时不展开
  6. node-key="catId"
  7. >
  8. <span class="custom-tree-node" slot-scope="{ node, data }">
  9. <span>{{ node.label }}</span>
  10. <span>
  11. <el-button v-if="node.level <= 2" type="text" size="mini" @click="() => append(data)">Append</el-button>
  12. <el-button
  13. v-if="node.childNodes.length == 0"
  14. type="text"
  15. size="mini"
  16. @click="() => remove(node, data)"
  17. >Delete</el-button>
  18. </span>
  19. </span>
  20. </el-tree>

测试删除数据,打开postman输入“ http://localhost:88/api/product/category/delete ”,请求方式设置为POST,为了比对效果,可以在删除之前查询数据库的pms_category表:

image-20200426112814069

由于delete请求接收的是一个数组,所以这里使用JSON方式,传入了一个数组:

image-20200426113003531

再次查询数据库能够看到cat_id为1000的数据已经被删除了。

修改“com.atguigu.gulimall.product.controller.CategoryController”类,添加如下代码:

  1. /**
  2. * 删除
  3. * @RequestBody:获取请求体,必须发送post请求
  4. * springMVC自动将请求体的数据(json),转为对应的对象
  5. */
  6. @RequestMapping("/delete")
  7. public R delete(@RequestBody Long[] catIds){
  8. //检查当前删除的菜单,是否被别的地方引用
  9. //categoryService.removeByIds(Arrays.asList(catIds));
  10. categoryService.removeMenusByIds(Arrays.asList(catIds));
  11. return R.ok();
  12. }

com.atguigu.gulimall.product.service.impl.CategoryServiceImpl

  1. @Override
  2. public void removeMenusByIds(List<Long> asList) {
  3. //TODO 检查当前删除的菜单,是否被别的地方引用
  4. //逻辑删除
  5. baseMapper.deleteBatchIds(asList);
  6. }

然而多数时候,我们并不希望删除数据,而是标记它被删除了,这就是逻辑删除;

可以设置show_status为0,标记它已经被删除。

mybatis-plus的逻辑删除:image-20200426115420393

配置全局的逻辑删除规则,在“src/main/resources/application.yml”文件中添加如下内容:

  1. mybatis-plus:
  2. global-config:
  3. db-config:
  4. id-type: auto
  5. logic-delete-value: 1
  6. logic-not-delete-value: 0

修改“com.atguigu.gulimall.product.entity.CategoryEntity”类,添加上@TableLogic,表明使用逻辑删除:

  1. /**
  2. * 是否显示[0-不显示,1显示]
  3. */
  4. @TableLogic(value = "1",delval = "0")
  5. private Integer showStatus;

然后在Postman中测试一下是否能够满足需要。另外在“src/main/resources/application.yml”文件中,设置日志级别,打印出SQL语句:

  1. logging:
  2. level:
  3. com.bigdata.gulimall.product: debug

打印的日志:

  1. ==> Preparing: UPDATE pms_category SET show_status=0 WHERE cat_id IN ( ? ) AND show_status=1
  2. ==> Parameters: 1431(Long)
  3. <== Updates: 1
  4. get changedGroupKeys:[]

3)、 菜单拖动

同一个菜单内拖动正常
拖动到父菜单的前面或后面正常
拖动到父菜单同级的另外一个菜单中正常

关注的焦点在于,拖动到目标节点中,使得目标节点的catlevel+deep小于3即可。拖动到目标节点前后的条件是,使得

4)、拖动菜单时需要修改顺序和级别

需要考虑两种类型节点的catLevel

一种关系是:如果是同一个节点下的子节点的前后移动,则不需要修改其catLevel

如果是拖动到另外一个节点内或父节点中,则要考虑修改其catLevel

如果拖动到与父节点平级的节点关系中,则要将该拖动的节点的catLevel,设置为兄弟节点Level,

先考虑parentCid还是先考虑catLevel?

两种关系在耦合

另外还有一种是前后拖动的情况

哪个范围最大?

肯定是拖动类型关系最大,

如果是前后拖动,则拖动后需要看待拖动节点的层级和设置待拖动节点的parentId,

​ 如果待拖动节点和目标节点的层级相同,则认为是同级拖动,只需要修改节点的先后顺序即可;

​ 否则认为是跨级拖动,则需要修改层级和重新设置parentID

​ 如果以拖动类型来分,并不合适,比较合适的是跨级拖动和同级拖动

如何判断是跨级拖动还是同级拖动,根据拖动的层级来看,如果是同一级的拖动,只需要修改先后顺序即可,但是这样也会存在一个问题,就是当拖动到另外一个分组下的同级目录中,显然也需要修改parentID,究竟什么样的模型最好呢?

另外也可以判断在跨级移动时,跨级后的parentID是否相同,如果不相同,则认为是在不同目录下的跨级移动需要修改parentID。

顺序、catLevel和parentID

同级移动:

(1)首先判断待移动节点和目标节点的catLevel是否相同,

(2)相同则认为是同级移动,

​ 如果此时移动后目标节点的parentID和待移动节点的相同,但是移动类型是前后移动,只需要调整顺序即可,此时移动类型是inner,则需要修改catLevel和parentId和顺序

​ 如果此时移动后目标节点的parentID和待移动节点的不相同,但是移动类型是前后移动,则需要调整顺序和parentId,此时移动类型是inner,则需要修改catLevel和parentId和顺序

通过这两步的操作能看到一些共性,如果抽取移动类型作为大的分类,则在这种分类下,

如果是前后移动,则分为下面几种情况:

​ 同级别下的前后移动:界定标准为catLevel相同,但是又可以分为parentID相同和parentID不同,parent相同时,只需要修改顺序即可;parentID不同时,需要修改parentID和顺序

​ 不同级别下的前后移动:界定标准为catLevel不同,此时无论如何都要修改parentID,顺序和catLevel

如果是inner类型移动,则分为一下的几种情况。

​ 此时不论是同级inner,还是跨级innner,都需要修改parentID,顺序和catLevel

哪种情况需要更新子节点呢?

那就要看要拖拽的节点是否含有子节点,如果有子节点,则需要更新子节点的catLevel,不需要更新它之间的顺序和parentId,只需要更新catLevel即可。这种更新子节点的Level应该归类,目前的目标是只要有子节点就更新它的catLevel,

(2)如果待移动节点和目标节点的catLevel不同,则认为是跨级移动。如果是移动到父节点中,则需要设置catLevel,parentID和顺序。此时需要分两种情况来考虑,如果是移动到父节点中,则需要设置catLevel,parentID和顺序,如果是移动到兄弟节点中,则需要设置

包含移动到父节点同级目录,兄弟节点中。

设置菜单拖动开关

 <el-switch v-model="draggable" active-text="开启拖拽" inactive-text="关闭拖拽"></el-switch>

但是现在存在的一个问题是每次拖拽的时候,都会发送请求,更新数据库这样频繁的与数据库交互,现在想要实现一个拖拽过程中不更新数据库,拖拽完成后,统一提交拖拽后的数据。

现在还存在一个问题,如果是将一个菜单连续的拖拽,最终还放到了原来的位置,但updateNode中却出现了很多节点更新信息,这样显然也是一个问题。

批量删除

  <el-button type="danger" plain size="small" @click="batchDelete">批量删除</el-button>
  1. //批量删除
  2. batchDelete() {
  3. let checkNodes = this.$refs.menuTree.getCheckedNodes();
  4. // console.log("被选中的节点:",checkNodes);
  5. let catIds = [];
  6. for (let i = 0; i < checkNodes.length; i++) {
  7. catIds.push(checkNodes[i].catId);
  8. }
  9. this.$confirm(`确定要删除?`, "提示", {
  10. confirmButtonText: "确定",
  11. cancelButtonText: "取消",
  12. type: "warning"
  13. })
  14. .then(() => {
  15. this.$http({
  16. url: this.$http.adornUrl("/product/category/delete"),
  17. method: "post",
  18. data: this.$http.adornData(catIds, false)
  19. }).then(({ data }) => {
  20. this.$message({
  21. message: "菜单批量删除成功",
  22. type: "success"
  23. });
  24. //重新刷新页面
  25. this.getMeus();
  26. });
  27. })
  28. .catch(() => {
  29. //取消删除
  30. });
  31. },

1.2、品牌管理

image-20200428164054517

2)将“”逆向工程得到的resources\src\views\modules\product文件拷贝到gulimall/renren-fast-vue/src/views/modules/product目录下,也就是下面的两个文件

brand.vue brand-add-or-update.vue

但是显示的页面没有新增和删除功能,这是因为权限控制的原因,

image-20200428170325515

  1. <el-button v-if="isAuth('product:brand:save')" type="primary" @click="addOrUpdateHandle()">新增</el-button>
  2. <el-button v-if="isAuth('product:brand:delete')" type="danger" @click="deleteHandle()" :disabled="dataListSelections.length <= 0">批量删除</el-button>

查看“isAuth”的定义位置:

image-20200428170437592

它是在“index.js”中定义,现在将它设置为返回值为true,即可显示添加和删除功能。

再次刷新页面能够看到,按钮已经出现了:

1)、添加“显示状态按钮”

brand.vue

  1. <template slot-scope="scope">
  2. <el-switch
  3. v-model="scope.row.showStatus"
  4. active-color="#13ce66"
  5. inactive-color="#ff4949"
  6. @change="updateBrandStatus(scope.row)"
  7. :active-value = "1"
  8. :inactive-value = "0"
  9. ></el-switch>
  10. </template>

brand-add-or-update.vue

  1. <el-form-item label="显示状态" prop="showStatus">
  2. <el-switch v-model="dataForm.showStatus" active-color="#13ce66" inactive-color="#ff4949"></el-switch>
  3. </el-form-item>
  1. //更新开关的状态
  2. updateBrandStatus(data) {
  3. console.log("最新状态", data);
  4. let {brandId,showStatus} = data;
  5. this.$http({
  6. url: this.$http.adornUrl("/product/brand/update"),
  7. method: "post",
  8. data: this.$http.adornData({brandId,showStatus}, false)
  9. }).then(({ data }) => {
  10. this.$message({
  11. message: "状态更新成功",
  12. type: "success"
  13. });
  14. });
  15. },

2)、添加上传

SpringCloud Alibaba-OSS

对象存储服务(Object Storage Service, OSS)是一种海量、安全、低成本、高可靠的云存储服务,适合存放任意类型的文件。容量和处理能力弹性扩展,多种存储类型供选择,全面优化存储成本。 

和传统的单体应用不同,这里我们选择将数据上传到分布式文件服务器上。

这里我们选择将图片放置到阿里云上,使用对象存储。

阿里云上使使用对象存储方式:

image-20200428182755992

创建Bucket

image-20200428183041570

上传文件:

image-20200428183213694

上传成功后,取得图片的URL

image-20200428183644020

这种方式是手动上传图片,实际上我们可以在程序中设置自动上传图片到阿里云对象存储。

上传模型:

image-20200428184029655

查看阿里云关于文件上传的帮助: https://help.aliyun.com/document_detail/32009.html?spm=a2c4g.11186623.6.768.549d59aaWuZMGJ

(1)、添加依赖包

在Maven项目中加入依赖项(推荐方式)

在 Maven 工程中使用 OSS Java SDK,只需在 pom.xml 中加入相应依赖即可。以 3.8.0 版本为例,在 内加入如下内容:

  1. <dependency>
  2. <groupId>com.aliyun.oss</groupId>
  3. <artifactId>aliyun-sdk-oss</artifactId>
  4. <version>3.8.0</version>
  5. </dependency>

2)、上传文件流

以下代码用于上传文件流:

  1. // Endpoint以杭州为例,其它Region请按实际情况填写。
  2. String endpoint = "http://oss-cn-hangzhou.aliyuncs.com";
  3. // 云账号AccessKey有所有API访问权限,建议遵循阿里云安全最佳实践,创建并使用RAM子账号进行API访问或日常运维,请登录 https://ram.console.aliyun.com 创建。
  4. String accessKeyId = "<yourAccessKeyId>";
  5. String accessKeySecret = "<yourAccessKeySecret>";
  6. // 创建OSSClient实例。
  7. OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
  8. // 上传文件流。
  9. InputStream inputStream = new FileInputStream("<yourlocalFile>");
  10. ossClient.putObject("<yourBucketName>", "<yourObjectName>", inputStream);
  11. // 关闭OSSClient。
  12. ossClient.shutdown();

endpoint的取值:

image-20200428190553350

accessKeyId和accessKeySecret需要创建一个RAM账号:

image-20200428190532924

创建用户完毕后,会得到一个“AccessKey ID”和“AccessKeySecret”,然后复制这两个值到代码的“AccessKey ID”和“AccessKeySecret”。

另外还需要添加访问控制权限:

image-20200428191518591

  1. @Test
  2. public void testUpload() throws FileNotFoundException {
  3. // Endpoint以杭州为例,其它Region请按实际情况填写。
  4. String endpoint = "oss-cn-shanghai.aliyuncs.com";
  5. // 云账号AccessKey有所有API访问权限,建议遵循阿里云安全最佳实践,创建并使用RAM子账号进行API访问或日常运维,请登录 https://ram.console.aliyun.com 创建。
  6. String accessKeyId = "自己申请的子用户"; //自己知道就好,我的暴露安全性被攻击了
  7. String accessKeySecret = "子用户的密码";
  8. // 创建OSSClient实例。
  9. OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
  10. // 上传文件流。
  11. InputStream inputStream = new FileInputStream("C:\\Users\\Administrator\\Pictures\\timg.jpg");
  12. ossClient.putObject("gulimall-images", "time.jpg", inputStream);
  13. // 关闭OSSClient。
  14. ossClient.shutdown();
  15. System.out.println("上传成功.");
  16. }

更为简单的使用方式,是使用SpringCloud Alibaba

https://github.com/alibaba/aliyun-spring-boot/tree/master/aliyun-spring-boot-samples/aliyun-oss-spring-boot-sample

image-20200428195507730

详细使用方法,见: https://help.aliyun.com/knowledge_detail/108650.html

(1)添加依赖

  1. <dependency>
  2. <groupId>com.alibaba.cloud</groupId>
  3. <artifactId>spring-cloud-starter-alicloud-oss</artifactId>
  4. <version>2.2.0.RELEASE</version>
  5. </dependency>

(2)创建“AccessKey ID”和“AccessKeySecret”

(3)配置key,secret和endpoint相关信息

  1. access-key: 自己申请的子用户
  2. secret-key: 自己子用户的密码
  3. oss:
  4. endpoint: oss-cn-shanghai.aliyuncs.com

(4)注入OSSClient并进行文件上传下载等操作

image-20200428224840535

但是这样来做还是比较麻烦,如果以后的上传任务都交给gulimall-product来完成,显然耦合度高。最好单独新建一个Module来完成文件上传任务。

3)、上传其他方式

1)新建gulimall-third-party
2)添加依赖,将原来gulimall-common中的“spring-cloud-starter-alicloud-oss”依赖移动到该项目中

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
  4. <modelVersion>4.0.0</modelVersion>
  5. <parent>
  6. <groupId>org.springframework.boot</groupId>
  7. <artifactId>spring-boot-starter-parent</artifactId>
  8. <version>2.3.4.RELEASE</version>
  9. <relativePath/> <!-- lookup parent from repository -->
  10. </parent>
  11. <groupId>com.atguigu.gulimall</groupId>
  12. <artifactId>gulimall-thrid-party</artifactId>
  13. <version>0.0.1-SNAPSHOT</version>
  14. <name>gulimall-thrid-party</name>
  15. <description>第三方服务</description>
  16. <properties>
  17. <java.version>1.8</java.version>
  18. <spring-cloud.version>Hoxton.SR8</spring-cloud.version>
  19. </properties>
  20. <dependencies>
  21. <!--阿里云文件上传-->
  22. <dependency>
  23. <groupId>com.alibaba.cloud</groupId>
  24. <artifactId>spring-cloud-starter-alicloud-oss</artifactId>
  25. </dependency>
  26. <dependency>
  27. <groupId>com.auguigu.gulimall</groupId>
  28. <artifactId>gulimall-commom</artifactId>
  29. <version>0.0.1-SNAPSHOT</version>
  30. <exclusions>
  31. <exclusion>
  32. <groupId>com.baomidou</groupId>
  33. <artifactId>mybatis-plus-boot-starter</artifactId>
  34. </exclusion>
  35. </exclusions>
  36. </dependency>
  37. <dependency>
  38. <groupId>org.springframework.boot</groupId>
  39. <artifactId>spring-boot-starter-web</artifactId>
  40. </dependency>
  41. <dependency>
  42. <groupId>org.springframework.cloud</groupId>
  43. <artifactId>spring-cloud-starter-openfeign</artifactId>
  44. </dependency>
  45. <dependency>
  46. <groupId>org.springframework.boot</groupId>
  47. <artifactId>spring-boot-starter-test</artifactId>
  48. <scope>test</scope>
  49. <exclusions>
  50. <exclusion>
  51. <groupId>org.junit.vintage</groupId>
  52. <artifactId>junit-vintage-engine</artifactId>
  53. </exclusion>
  54. </exclusions>
  55. </dependency>
  56. </dependencies>
  57. <dependencyManagement>
  58. <dependencies>
  59. <dependency>
  60. <groupId>org.springframework.cloud</groupId>
  61. <artifactId>spring-cloud-dependencies</artifactId>
  62. <version>${spring-cloud.version}</version>
  63. <type>pom</type>
  64. <scope>import</scope>
  65. </dependency>
  66. <dependency>
  67. <groupId>com.alibaba.cloud</groupId>
  68. <artifactId>spring-cloud-alibaba-dependencies</artifactId>
  69. <version>2.2.0.RELEASE</version>
  70. <type>pom</type>
  71. <scope>import</scope>
  72. </dependency>
  73. </dependencies>
  74. </dependencyManagement>
  75. <build>
  76. <plugins>
  77. <plugin>
  78. <groupId>org.springframework.boot</groupId>
  79. <artifactId>spring-boot-maven-plugin</artifactId>
  80. </plugin>
  81. </plugins>
  82. </build>
  83. </project>

3)在主启动类中开启服务的注册和发现

@EnableDiscoveryClient

4)在nacos中注册

(1)创建命名空间“ gulimall-third-party ”

image-20200429075831984

2)在“ gulimall-third-party”命名空间中,创建“ gulimall-third-party.yml”文件

  1. spring:
  2. cloud:
  3. alicloud:
  4. access-key: 自己申请的子用户
  5. secret-key: 子用户的密码
  6. oss:
  7. endpoint: oss-cn-shanghai.aliyuncs.com

5)编写配置文件

application.yml

  1. server:
  2. port: 30000
  3. spring:
  4. application:
  5. name: gulimall-third-party
  6. cloud:
  7. nacos:
  8. discovery:
  9. server-addr: 192.168.137.14:8848
  10. logging:
  11. level:
  12. com.bigdata.gulimall.product: debug

bootstrap.properties

  1. spring.application.name=gulimall-thrid-party
  2. spring.cloud.nacos.config.server-addr=127.0.0.1:8848
  3. spring.cloud.nacos.config.namespace=555d020c-9b7e-4e8e-b625-65a3c4b6ff47
  4. spring.cloud.nacos.config.extension-configs[0].data-id=oss.yml
  5. spring.cloud.nacos.config.extension-configs[0].group=DEFAULT_GROUP
  6. spring.cloud.nacos.config.extension-configs[0].refresh=true

6) 编写测试类

  1. package com.bigdata.gulimall.thirdparty;
  2. import com.aliyun.oss.OSS;
  3. import com.aliyun.oss.OSSClient;
  4. import com.aliyun.oss.OSSClientBuilder;
  5. import org.junit.jupiter.api.Test;
  6. import org.springframework.beans.factory.annotation.Autowired;
  7. import org.springframework.boot.test.context.SpringBootTest;
  8. import java.io.FileInputStream;
  9. import java.io.FileNotFoundException;
  10. import java.io.InputStream;
  11. @SpringBootTest
  12. class GulimallThirdPartyApplicationTests {
  13. @Autowired
  14. OSSClient ossClient;
  15. @Test
  16. public void testUpload() throws FileNotFoundException {
  17. // Endpoint以杭州为例,其它Region请按实际情况填写。
  18. String endpoint = "oss-cn-shanghai.aliyuncs.com";
  19. // 云账号AccessKey有所有API访问权限,建议遵循阿里云安全最佳实践,创建并使用RAM子账号进行API访问或日常运维,请登录 https://ram.console.aliyun.com 创建。
  20. String accessKeyId = "自己申请的子用户";
  21. String accessKeySecret = "子用户的密码";
  22. // 创建OSSClient实例。
  23. OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
  24. //上传文件流。
  25. InputStream inputStream = new FileInputStream("C:\\Users\\Administrator\\Pictures\\timg.jpg");
  26. ossClient.putObject("gulimall-images", "time3.jpg", inputStream);
  27. // 关闭OSSClient。
  28. ossClient.shutdown();
  29. System.out.println("上传成功.");
  30. }
  31. }

服务端签名后直传 - 对象存储 OSS - 阿里云

背景

采用JavaScript客户端直接签名(参见JavaScript客户端签名直传)时,AccessKeyID和AcessKeySecret会暴露在前端页面,因此存在严重的安全隐患。因此,OSS提供了服务端签名后直传的方案。

原理介绍

img

服务端签名后直传的原理如下:

  1. 用户发送上传Policy请求到应用服务器。
  2. 应用服务器返回上传Policy和签名给用户。
  3. 用户直接上传数据到OSS。

编写“com.atguigu.gulimall.thridparty.controller.OSSController”类:

  1. package com.atguigu.gulimall.thridparty.controller;
  2. import com.aliyun.oss.OSS;
  3. import com.aliyun.oss.common.utils.BinaryUtil;
  4. import com.aliyun.oss.model.MatchMode;
  5. import com.aliyun.oss.model.PolicyConditions;
  6. import com.atguigu.common.utils.R;
  7. import org.springframework.beans.factory.annotation.Autowired;
  8. import org.springframework.beans.factory.annotation.Value;
  9. import org.springframework.web.bind.annotation.RequestMapping;
  10. import org.springframework.web.bind.annotation.RestController;
  11. import java.text.SimpleDateFormat;
  12. import java.util.Date;
  13. import java.util.LinkedHashMap;
  14. import java.util.Map;
  15. /**
  16. * @author WangTianShun
  17. * @date 2020/10/10 16:06
  18. */
  19. @RestController
  20. public class OSSController {
  21. @Autowired
  22. OSS ossClient;
  23. @Value("${spring.cloud.alicloud.oss.endpoint}")
  24. private String endpoint;
  25. @Value("${spring.cloud.alicloud.oss.bucket}")
  26. private String bucket;
  27. @Value("${spring.cloud.alicloud.access-key}")
  28. private String accessId;
  29. @RequestMapping("/oss/policy")
  30. public R policy(){
  31. String host = "https://" + bucket + "." + endpoint; // host的格式为 bucketname.endpoint
  32. // callbackUrl为 上传回调服务器的URL,请将下面的IP和Port配置为您自己的真实信息。
  33. //String callbackUrl = "http://88.88.88.88:8888";
  34. String format = new SimpleDateFormat("yyyy-MM-dd").format(new Date());
  35. String dir = format+"/"; // 用户上传文件时指定的前缀。
  36. Map<String, String> respMap = null;
  37. try {
  38. long expireTime = 30;
  39. long expireEndTime = System.currentTimeMillis() + expireTime * 1000;
  40. Date expiration = new Date(expireEndTime);
  41. // PostObject请求最大可支持的文件大小为5 GB,即CONTENT_LENGTH_RANGE为5*1024*1024*1024。
  42. PolicyConditions policyConds = new PolicyConditions();
  43. policyConds.addConditionItem(PolicyConditions.COND_CONTENT_LENGTH_RANGE, 0, 1048576000);
  44. policyConds.addConditionItem(MatchMode.StartWith, PolicyConditions.COND_KEY, dir);
  45. String postPolicy = ossClient.generatePostPolicy(expiration, policyConds);
  46. byte[] binaryData = postPolicy.getBytes("utf-8");
  47. String encodedPolicy = BinaryUtil.toBase64String(binaryData);
  48. String postSignature = ossClient.calculatePostSignature(postPolicy);
  49. respMap = new LinkedHashMap<String, String>();
  50. respMap.put("accessid", accessId);
  51. respMap.put("policy", encodedPolicy);
  52. respMap.put("signature", postSignature);
  53. respMap.put("dir", dir);
  54. respMap.put("host", host);
  55. respMap.put("expire", String.valueOf(expireEndTime / 1000));
  56. // respMap.put("expire", formatISO8601Date(expiration));
  57. } catch (Exception e) {
  58. // Assert.fail(e.getMessage());
  59. System.out.println(e.getMessage());
  60. } finally {
  61. ossClient.shutdown();
  62. }
  63. return R.ok().put("data",respMap);
  64. }
  65. }

测试: http://localhost:30000/oss/policy

{"accessid":"LTAI4G4W1RA4JXz2QhoDwHhi","policy":"eyJleHBpcmF0aW9uIjoiMjAyMC0wNC0yOVQwMjo1ODowNy41NzhaIiwiY29uZGl0aW9ucyI6W1siY29udGVudC1sZW5ndGgtcmFuZ2UiLDAsMTA0ODU3NjAwMF0sWyJzdGFydHMtd2l0aCIsIiRrZXkiLCIyMDIwLTA0LTI5LyJdXX0=","signature":"s42iRxtxGFmHyG40StM3d9vOfFk=","dir":"2020-04-29/","host":"https://gulimall-images.oss-cn-shanghai.aliyuncs.com","expire":"1588129087"}

以后在上传文件时的访问路径为“ http://localhost:88/api/thirdparty/oss/policy”,

在“gulimall-gateway”中配置路由规则:

  1.         - id: third_party_route
  2.           uri: lb://gulimall-gateway
  3.           predicates:
  4.             - Path=/api/thirdparty/**
  5.           filters:
  6.             - RewritePath=/api/thirdparty/(?<segment>/?.*),/$\{segment}

测试是否能够正常跳转: http://localhost:88/api/thirdparty/oss/policy

image-20200429111408164

上传组件

放置项目提供的upload文件夹到components目录下,一个是单文件上传,另外一个是多文件上传

  1. PS D:\Project\gulimall\renren-fast-vue\src\components\upload> ls
  2.     目录: D:\Project\gulimall\renren-fast-vue\src\components\upload
  3. Mode                LastWriteTime         Length Name
  4. ----                -------------         ------ ----
  5. -a----  2020/4/29 星期三     12:0           3122 multiUpload.vue
  6.                                 2
  7. -a----  2019/11/11 星期一     21:            343 policy.js
  8.                                20
  9. -a----  2020/4/29 星期三     12:0           3053 singleUpload.vue
  10.                                 1
  11. PS D:\Project\gulimall\renren-fast-vue\src\components\upload>

修改这两个文件的配置后

开始执行上传,但是在上传过程中,出现了如下的问题:

image-20200429124629150

  1. Access to XMLHttpRequest at 'http://gulimall-images.oss-cn-shanghai.aliyuncs.com/' from origin 'http://localhost:8001' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.

这又是一个跨域的问题,解决方法就是在阿里云上开启跨域访问:

image-20200429124940091

再次执行文件上传。

4)、JSR303校验

步骤1:使用校验注解

在Java中提供了一系列的校验方式,它这些校验方式在“javax.validation.constraints”包中,提供了如@Email,@NotNull等注解。

在非空处理方式上提供了@NotNull,@Blank和@

(1)@NotNull

The annotated element must not be null. Accepts any type.
注解元素禁止为null,能够接收任何类型

(2)@NotEmpty

the annotated element must not be null nor empty.

该注解修饰的字段不能为null或""

Supported types are:

支持以下几种类型

CharSequence (length of character sequence is evaluated)

字符序列(字符序列长度的计算)

Collection (collection size is evaluated)
集合长度的计算

Map (map size is evaluated)
map长度的计算

Array (array length is evaluated)
数组长度的计算

(3)@NotBlank

The annotated element must not be null and must contain at least one non-whitespace character. Accepts CharSequence.
该注解不能为null,并且至少包含一个非空白字符。接收字符序列。

步骤2:在请求方法中,使用校验注解@Valid,开启校验,

  1. @RequestMapping("/save")
  2. public R save(@Valid @RequestBody BrandEntity brand){
  3. brandService.save(brand);
  4. return R.ok();
  5. }

测试: http://localhost:88/api/product/brand/save

在postman种发送上面的请求

  1. {
  2. "timestamp": "2020-04-29T09:20:46.383+0000",
  3. "status": 400,
  4. "error": "Bad Request",
  5. "errors": [
  6. {
  7. "codes": [
  8. "NotBlank.brandEntity.name",
  9. "NotBlank.name",
  10. "NotBlank.java.lang.String",
  11. "NotBlank"
  12. ],
  13. "arguments": [
  14. {
  15. "codes": [
  16. "brandEntity.name",
  17. "name"
  18. ],
  19. "arguments": null,
  20. "defaultMessage": "name",
  21. "code": "name"
  22. }
  23. ],
  24. "defaultMessage": "不能为空",
  25. "objectName": "brandEntity",
  26. "field": "name",
  27. "rejectedValue": "",
  28. "bindingFailure": false,
  29. "code": "NotBlank"
  30. }
  31. ],
  32. "message": "Validation failed for object='brandEntity'. Error count: 1",
  33. "path": "/product/brand/save"
  34. }

能够看到"defaultMessage": “不能为空”,这些错误消息定义在“hibernate-validator”的“\org\hibernate\validator\ValidationMessages_zh_CN.properties”文件中。在该文件中定义了很多的错误规则:

  1. javax.validation.constraints.AssertFalse.message = 只能为false
  2. javax.validation.constraints.AssertTrue.message = 只能为true
  3. javax.validation.constraints.DecimalMax.message = 必须小于或等于{value}
  4. javax.validation.constraints.DecimalMin.message = 必须大于或等于{value}
  5. javax.validation.constraints.Digits.message = 数字的值超出了允许范围(只允许在{integer}位整数和{fraction}位小数范围内)
  6. javax.validation.constraints.Email.message = 不是一个合法的电子邮件地址
  7. javax.validation.constraints.Future.message = 需要是一个将来的时间
  8. javax.validation.constraints.FutureOrPresent.message = 需要是一个将来或现在的时间
  9. javax.validation.constraints.Max.message = 最大不能超过{value}
  10. javax.validation.constraints.Min.message = 最小不能小于{value}
  11. javax.validation.constraints.Negative.message = 必须是负数
  12. javax.validation.constraints.NegativeOrZero.message = 必须是负数或零
  13. javax.validation.constraints.NotBlank.message = 不能为空
  14. javax.validation.constraints.NotEmpty.message = 不能为空
  15. javax.validation.constraints.NotNull.message = 不能为null
  16. javax.validation.constraints.Null.message = 必须为null
  17. javax.validation.constraints.Past.message = 需要是一个过去的时间
  18. javax.validation.constraints.PastOrPresent.message = 需要是一个过去或现在的时间
  19. javax.validation.constraints.Pattern.message = 需要匹配正则表达式"{regexp}"
  20. javax.validation.constraints.Positive.message = 必须是正数
  21. javax.validation.constraints.PositiveOrZero.message = 必须是正数或零
  22. javax.validation.constraints.Size.message = 个数必须在{min}和{max}之间
  23. org.hibernate.validator.constraints.CreditCardNumber.message = 不合法的信用卡号码
  24. org.hibernate.validator.constraints.Currency.message = 不合法的货币 (必须是{value}其中之一)
  25. org.hibernate.validator.constraints.EAN.message = 不合法的{type}条形码
  26. org.hibernate.validator.constraints.Email.message = 不是一个合法的电子邮件地址
  27. org.hibernate.validator.constraints.Length.message = 长度需要在{min}和{max}之间
  28. org.hibernate.validator.constraints.CodePointLength.message = 长度需要在{min}和{max}之间
  29. org.hibernate.validator.constraints.LuhnCheck.message = ${validatedValue}的校验码不合法, Luhn模10校验和不匹配
  30. org.hibernate.validator.constraints.Mod10Check.message = ${validatedValue}的校验码不合法, 模10校验和不匹配
  31. org.hibernate.validator.constraints.Mod11Check.message = ${validatedValue}的校验码不合法, 模11校验和不匹配
  32. org.hibernate.validator.constraints.ModCheck.message = ${validatedValue}的校验码不合法, ${modType}校验和不匹配
  33. org.hibernate.validator.constraints.NotBlank.message = 不能为空
  34. org.hibernate.validator.constraints.NotEmpty.message = 不能为空
  35. org.hibernate.validator.constraints.ParametersScriptAssert.message = 执行脚本表达式"{script}"没有返回期望结果
  36. org.hibernate.validator.constraints.Range.message = 需要在{min}和{max}之间
  37. org.hibernate.validator.constraints.SafeHtml.message = 可能有不安全的HTML内容
  38. org.hibernate.validator.constraints.ScriptAssert.message = 执行脚本表达式"{script}"没有返回期望结果
  39. org.hibernate.validator.constraints.URL.message = 需要是一个合法的URL
  40. org.hibernate.validator.constraints.time.DurationMax.message = 必须小于${inclusive == true ? '或等于' : ''}${days == 0 ? '' : days += '天'}${hours == 0 ? '' : hours += '小时'}${minutes == 0 ? '' : minutes += '分钟'}${seconds == 0 ? '' : seconds += '秒'}${millis == 0 ? '' : millis += '毫秒'}${nanos == 0 ? '' : nanos += '纳秒'}
  41. org.hibernate.validator.constraints.time.DurationMin.message = 必须大于${inclusive == true ? '或等于' : ''}${days == 0 ? '' : days += '天'}${hours == 0 ? '' : hours += '小时'}${minutes == 0 ? '' : minutes += '分钟'}${seconds == 0 ? '' : seconds += '秒'}${millis == 0 ? '' : millis += '毫秒'}${nanos == 0 ? '' : nanos += '纳秒'}

想要自定义错误消息,可以覆盖默认的错误提示信息,如@NotBlank的默认message是

  1. public @interface NotBlank {
  2. String message() default "{javax.validation.constraints.NotBlank.message}";

可以在添加注解的时候,修改message:

  1. @NotBlank(message = "品牌名必须非空")
  2. private String name;

当再次发送请求时,得到的错误提示信息:

  1. {
  2. "timestamp": "2020-04-29T09:36:04.125+0000",
  3. "status": 400,
  4. "error": "Bad Request",
  5. "errors": [
  6. {
  7. "codes": [
  8. "NotBlank.brandEntity.name",
  9. "NotBlank.name",
  10. "NotBlank.java.lang.String",
  11. "NotBlank"
  12. ],
  13. "arguments": [
  14. {
  15. "codes": [
  16. "brandEntity.name",
  17. "name"
  18. ],
  19. "arguments": null,
  20. "defaultMessage": "name",
  21. "code": "name"
  22. }
  23. ],
  24. "defaultMessage": "品牌名必须非空",
  25. "objectName": "brandEntity",
  26. "field": "name",
  27. "rejectedValue": "",
  28. "bindingFailure": false,
  29. "code": "NotBlank"
  30. }
  31. ],
  32. "message": "Validation failed for object='brandEntity'. Error count: 1",
  33. "path": "/product/brand/save"
  34. }

但是这种返回的错误结果并不符合我们的业务需要。

步骤3:给校验的Bean后,紧跟一个BindResult,就可以获取到校验的结果。拿到校验的结果,就可以自定义的封装。

  1. @RequestMapping("/save")
  2. public R save(@Valid @RequestBody BrandEntity brand, BindingResult result){
  3. if( result.hasErrors()){
  4. Map<String,String> map=new HashMap<>();
  5. //1.获取错误的校验结果
  6. result.getFieldErrors().forEach((item)->{
  7. //获取发生错误时的message
  8. String message = item.getDefaultMessage();
  9. //获取发生错误的字段
  10. String field = item.getField();
  11. map.put(field,message);
  12. });
  13. return R.error(400,"提交的数据不合法").put("data",map);
  14. }else {
  15. }
  16. brandService.save(brand);
  17. return R.ok();
  18. }

这种是针对于该请求设置了一个内容校验,如果针对于每个请求都单独进行配置,显然不是太合适,实际上可以统一的对于异常进行处理。

步骤4:统一异常处理

可以使用SpringMvc所提供的@ControllerAdvice,通过“basePackages”能够说明处理哪些路径下的异常。

(1)抽取一个异常处理类

  1. package com.atguigu.gulimall.product.exception;
  2. import com.atguigu.common.exception.BizCodeEnume;
  3. import com.atguigu.common.utils.R;
  4. import lombok.extern.slf4j.Slf4j;
  5. import org.springframework.validation.BindingResult;
  6. import org.springframework.web.bind.MethodArgumentNotValidException;
  7. import org.springframework.web.bind.annotation.ControllerAdvice;
  8. import org.springframework.web.bind.annotation.ExceptionHandler;
  9. import org.springframework.web.bind.annotation.ResponseBody;
  10. import org.springframework.web.bind.annotation.RestControllerAdvice;
  11. import org.springframework.web.servlet.ModelAndView;
  12. import java.util.HashMap;
  13. import java.util.Map;
  14. /**
  15. * 集中处理所有异常
  16. */
  17. @Slf4j
  18. //@ResponseBody
  19. //@ControllerAdvice(basePackages = "com.atguigu.gulimall.product.controller")
  20. @RestControllerAdvice(basePackages = "com.atguigu.gulimall.product.controller")
  21. public class GulimallExceptionControllerAdvice {
  22. @ExceptionHandler(value= MethodArgumentNotValidException.class)
  23. public R handleVaildException(MethodArgumentNotValidException e){
  24. log.error("数据校验出现问题{},异常类型:{}",e.getMessage(),e.getClass());
  25. BindingResult bindingResult = e.getBindingResult();
  26. Map<String,String> errorMap = new HashMap<>();
  27. bindingResult.getFieldErrors().forEach((fieldError)->{
  28. errorMap.put(fieldError.getField(),fieldError.getDefaultMessage());
  29. });
  30. return R.error(BizCodeEnume.VAILD_EXCEPTION.getCode(),BizCodeEnume.VAILD_EXCEPTION.getMsg()).put("data",errorMap);
  31. }
  32. @ExceptionHandler(value = Throwable.class)
  33. public R handleException(Throwable throwable){
  34. log.error("错误:",throwable);
  35. return R.error(BizCodeEnume.UNKNOW_EXCEPTION.getCode(),BizCodeEnume.UNKNOW_EXCEPTION.getMsg());
  36. }
  37. }

(2)测试: http://localhost:88/api/product/brand/save

image-20200429183334783

(3)默认异常处理

  1. @ExceptionHandler(value = Throwable.class)
  2. public R handleException(Throwable throwable){
  3. log.error("未知异常{},异常类型{}",throwable.getMessage(),throwable.getClass());
  4. return R.error(BizCodeEnum.UNKNOW_EXEPTION.getCode(),BizCodeEnum.UNKNOW_EXEPTION.getMsg());
  5. }

(4)错误状态码

上面代码中,针对于错误状态码,是我们进行随意定义的,然而正规开发过程中,错误状态码有着严格的定义规则,如该在项目中我们的错误状态码定义

image-20200429183748249

为了定义这些错误状态码,我们可以单独定义一个常量类,用来存储这些错误状态码

  1. package com.bigdata.common.exception;
  2. /***
  3. * 错误码和错误信息定义类
  4. * 1. 错误码定义规则为5为数字
  5. * 2. 前两位表示业务场景,最后三位表示错误码。例如:100001。10:通用 001:系统未知异常
  6. * 3. 维护错误码后需要维护错误描述,将他们定义为枚举形式
  7. * 错误码列表:
  8. * 10: 通用
  9. * 001:参数格式校验
  10. * 11: 商品
  11. * 12: 订单
  12. * 13: 购物车
  13. * 14: 物流
  14. */
  15. public enum BizCodeEnum {
  16. UNKNOW_EXEPTION(10000,"系统未知异常"),
  17. VALID_EXCEPTION( 10001,"参数格式校验失败");
  18. private int code;
  19. private String msg;
  20. BizCodeEnum(int code, String msg) {
  21. this.code = code;
  22. this.msg = msg;
  23. }
  24. public int getCode() {
  25. return code;
  26. }
  27. public String getMsg() {
  28. return msg;
  29. }
  30. }

(5)测试: http://localhost:88/api/product/brand/save

image-20200429191830967

分组校验功能(完成多场景的复杂校验)

1、给校验注解,标注上groups,指定什么情况下才需要进行校验

如:指定在更新和添加的时候,都需要进行校验

  1. @NotEmpty
  2. @NotBlank(message = "品牌名必须非空",groups = {UpdateGroup.class,AddGroup.class})
  3. private String name;

在这种情况下,没有指定分组的校验注解,默认是不起作用的。想要起作用就必须要加groups。

2、业务方法参数上使用@Validated注解

@Validated的value方法:

Specify one or more validation groups to apply to the validation step kicked off by this annotation.
指定一个或多个验证组以应用于此注释启动的验证步骤。

JSR-303 defines validation groups as custom annotations which an application declares for the sole purpose of using
them as type-safe group arguments, as implemented in SpringValidatorAdapter.

JSR-303 将验证组定义为自定义注释,应用程序声明的唯一目的是将它们用作类型安全组参数,如 SpringValidatorAdapter 中实现的那样。

Other SmartValidator implementations may support class arguments in other ways as well.

其他SmartValidator 实现也可以以其他方式支持类参数。

3、默认情况下,在分组校验情况下,没有指定分组的校验注解,将不会生效,它只会在分组的情况下生效。

自定义校验功能

1、编写一个自定义的校验注解

  1. @Documented
  2. @Constraint(validatedBy = { ListValueConstraintValidator.class})
  3. @Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
  4. @Retention(RUNTIME)
  5. public @interface ListValue {
  6. String message() default "{com.bigdata.common.valid.ListValue.message}";
  7. Class<?>[] groups() default { };
  8. Class<? extends Payload>[] payload() default { };
  9. int[] value() default {};
  10. }

2、编写一个自定义的校验器

  1. public class ListValueConstraintValidator implements ConstraintValidator<ListValue,Integer> {
  2. private Set<Integer> set=new HashSet<>();
  3. @Override
  4. public void initialize(ListValue constraintAnnotation) {
  5. int[] value = constraintAnnotation.value();
  6. for (int i : value) {
  7. set.add(i);
  8. }
  9. }
  10. @Override
  11. public boolean isValid(Integer value, ConstraintValidatorContext context) {
  12. return set.contains(value);
  13. }
  14. }

3、关联自定义的校验器和自定义的校验注解

@Constraint(validatedBy = { ListValueConstraintValidator.class})

4、使用实例

  1. /**
  2. * 显示状态[0-不显示;1-显示]
  3. */
  4. @ListValue(value = {0,1},groups ={AddGroup.class})
  5. private Integer showStatus;

5)、新增品牌与分类关联关系

POST   product/categorybrandrelation/save

请求参数

{"brandId":1,"catelogId":2}

响应数据

{
    "msg": "success",
    "code": 0
}

修改 “com.atguigu.gulimall.product.controller.CategoryBrandRelationController”类,代码如下:

  1. /**
  2. * 保存
  3. */
  4. @RequestMapping("/save")
  5. public R save(@RequestBody CategoryBrandRelationEntity categoryBrandRelation){
  6. categoryBrandRelationService.saveDetail(categoryBrandRelation);
  7. return R.ok();
  8. }

 修改“com.atguigu.gulimall.product.service.CategoryBrandRelationService”接口,代码如下:

void saveDetail(CategoryBrandRelationEntity categoryBrandRelation);

 修改“com.atguigu.gulimall.product.service.impl.CategoryBrandRelationServiceImpl”类,代码如下:

  1. @Override
  2. public void saveDetail(CategoryBrandRelationEntity categoryBrandRelation) {
  3. Long brandId = categoryBrandRelation.getBrandId();
  4. Long catelogId = categoryBrandRelation.getCatelogId();
  5. //1、查询详细名字
  6. BrandEntity brandEntity = brandDao.selectById(brandId);
  7. CategoryEntity categoryEntity = categoryDao.selectById(catelogId);
  8. categoryBrandRelation.setBrandName(brandEntity.getName());
  9. categoryBrandRelation.setCatelogName(categoryEntity.getName());
  10. this.save(categoryBrandRelation);
  11. }
<el-button type="text" size="small" @click="updateCatelogHandle(scope.row.brandId)">关联分类</el-button>
  1. updateCatelogHandle(brandId) {
  2. this.cateRelationDialogVisible = true;
  3. this.brandId = brandId;
  4. this.getCateRelation();
  5. },

6)、获取品牌关联的分类

GET    /product/categorybrandrelation/catelog/list

请求参数

参数名参数类型描述
brandIdlong品牌id

响应数据

{
    "msg": "success",
    "code": 0,
    "data": [{
        "catelogId": 0,
        "catelogName": "string",
    }]
}

 修改 “com.atguigu.gulimall.product.controller.CategoryBrandRelationController”类,代码如下:

  1. /**
  2.  * 获取品牌关联的所有分类列表
  3.  */
  4. @RequestMapping(value = "/catelog/list", method = RequestMethod.GET)
  5. public R catelogList(@RequestParam("brandId") Long brandId){
  6.     List<CategoryBrandRelationEntity> data = categoryBrandRelationService.list(
  7.             new QueryWrapper<CategoryBrandRelationEntity>().eq("brand_id",brandId));
  8.     return R.ok().put("data", data);
  9. }

1.3 、平台属性(商品SPU和SKU管理)

1.3.1、属性分组

重新执行“sys_menus.sql”

1)、获取分类属性分组

接口文档:

GET   /product/attrgroup/list/{catelogId}

请求参数

{
   page: 1,//当前页码
   limit: 10,//每页记录数
   sidx: 'id',//排序字段
   order: 'asc/desc',//排序方式
   key: '华为'//检索关键字
}

分页数据

响应数据

{
    "msg": "success",
    "code": 0,
    "page": {
        "totalCount": 0,
        "pageSize": 10,
        "totalPage": 0,
        "currPage": 1,
        "list": [{
            "attrGroupId": 0, //分组id
            "attrGroupName": "string", //分组名
            "catelogId": 0, //所属分类
            "descript": "string", //描述
            "icon": "string", //图标
            "sort": 0 //排序
            "catelogPath": [2,45,225] //分类完整路径
        }]
    }
}

现在想要实现点击菜单的左边,能够实现在右边展示数据

image-20200430215649355

父子组件传递数据:

1)子组件给父组件传递数据,事件机制;

在category中绑定node-click事件,

  <el-tree :data="menus" :props="defaultProps" node-key="catId" ref="menuTree" @node-click="nodeClick"	></el-tree>

2)子组件给父组件发送一个事件,携带上数据;

  1. nodeClick(data,Node,component){
  2. console.log("子组件",data,Node,component);
  3. this.$emit("tree-node-click",data,Node,component);
  4. },

this.$emit(事件名,“携带的数据”);

3)父组件中的获取发送的事件

<category @tree-node-click="treeNodeClick"></category>
  1. //获取发送的事件数据
  2. treeNodeClick(data,Node,component){
  3. console.log("attgroup感知到的category的节点被点击",data,Node,component);
  4. console.log("刚才被点击的菜单ID",data.catId);
  5. },

修改:“com.atguigu.gulimall.product.controller.AttrGroupController”类,代码如下:

  1. /**
  2. * 列表
  3. */
  4. @RequestMapping("/list/{catelogId}")
  5. public R list(@RequestParam Map<String, Object> params, @PathVariable("catelogId") Long catelogId){
  6. // PageUtils page = attrGroupService.queryPage(params);
  7. PageUtils page = attrGroupService.queryPage(params,catelogId);
  8. return R.ok().put("page", page);
  9. }

修改:“com.atguigu.gulimall.product.service.AttrGroupService”类,代码如下:

PageUtils queryPage(Map<String, Object> params, Long catelogId);

修改:“com.atguigu.gulimall.product.service.impl.AttrGroupServiceImpl”类,代码如下:

  1. @Override
  2. public PageUtils queryPage(Map<String, Object> params, Long catelogId) {
  3. String key = (String) params.get("key");
  4. //select * from pms_attr_group where catelog_id = ? and (attr_group_id = key or attr_group_name like %key%)
  5. QueryWrapper<AttrGroupEntity> wrapper = new QueryWrapper<AttrGroupEntity>();
  6. if (!StringUtils.isEmpty(key)){
  7. wrapper.and((obj)->{
  8. obj.eq("attr_group_id",key).or().like("attr_group_name",key);
  9. });
  10. }
  11. if (catelogId == 0){
  12. IPage<AttrGroupEntity> page = this.page(new Query<AttrGroupEntity>().getPage(params), wrapper);
  13. return new PageUtils(page);
  14. }else{
  15. // String key = (String) params.get("key");
  16. // //select * from pms_attr_group where catelog_id = ? and (attr_group_id = key or attr_group_name like %key%)
  17. // QueryWrapper<AttrGroupEntity> wrapper = new QueryWrapper<AttrGroupEntity>().eq("catelog_id",catelogId);
  18. // if (!StringUtils.isEmpty(key)){
  19. // wrapper.and((obj)->{
  20. // obj.eq("attr_group_id",key).or().like("attr_group_name",key);
  21. // });
  22. // }
  23. wrapper.eq("catelog_id",catelogId);
  24. IPage<AttrGroupEntity> page = this.page(new Query<AttrGroupEntity>().getPage(params), wrapper);
  25. return new PageUtils(page);
  26. }
  27. }

2)、获取属性分组详情

GET   /product/attrgroup/info/{attrGroupId}

响应数据

{
    "code": 0,
    "msg": "success",
    "attrGroup": {
        "attrGroupId": 1,
        "attrGroupName": "主体",
        "sort": 0,
        "descript": null,
        "icon": null,
        "catelogId": 225,
        "catelogPath": [
            2,
            34,
            225
        ] //完整分类路径
    }
}

修改:“com.atguigu.gulimall.product.controller.AttrGroupController”类,代码如下:

  1. /**
  2. * 信息
  3. */
  4. @RequestMapping("/info/{attrId}")
  5. //@RequiresPermissions("product:attr:info")
  6. public R info(@PathVariable("attrId") Long attrId){
  7. //AttrEntity attr = attrService.getById(attrId);
  8. AttrResponseVo respVo = attrService.getAttrInfo(attrId);
  9. return R.ok().put("attr", respVo);
  10. }

修改:“com.atguigu.gulimall.product.service.AttrService”类,代码如下:

PageUtils queryPage(Map<String, Object> params, Long catelogId);

修改:“com.atguigu.gulimall.product.service.impl.AttrServiceImpl”类,代码如下:

  1. @Override
  2. public AttrResponseVo getAttrInfo(Long attrId) {
  3. AttrResponseVo responseVo = new AttrResponseVo();
  4. AttrEntity attrEntity = this.getById(attrId);
  5. BeanUtils.copyProperties(attrEntity,responseVo);
  6. //1、设置分组信息
  7. AttrAttrgroupRelationEntity relationEntity = attrAttrgroupRelationDao.selectOne(new QueryWrapper<AttrAttrgroupRelationEntity>().eq("attr_id", attrId));
  8. if (relationEntity != null){
  9. responseVo.setAttrGroupId(relationEntity.getAttrGroupId());
  10. AttrGroupEntity attrGroupEntity = attrGroupDao.selectById(relationEntity.getAttrGroupId());
  11. if (attrGroupEntity != null){
  12. responseVo.setGroupName(attrGroupEntity.getAttrGroupName());
  13. }
  14. }
  15. //2、设置分类信息
  16. Long catelogId = attrEntity.getCatelogId();
  17. Long[] catelogPath = categoryService.findCatelogPath(catelogId);
  18. responseVo.setCatelogPath(catelogPath);
  19. CategoryEntity categoryEntity = categoryDao.selectById(catelogId);
  20. if (categoryEntity != null){
  21. responseVo.setCatelogName(categoryEntity.getName());
  22. }
  23. return responseVo;
  24. }

3)、查询分组关联属性和删除关联

获取属性分组的关联的所有属性

API:10、获取属性分组的关联的所有属性 - 谷粒商城

GET:/product/attrgroup/{attrgroupId}/attr/relation

接口描述

获取当前属性分组所关联的属性

请求参数

响应数据

{
  "msg": "success",
  "code": 0,
  "data": [
    {
      "attrId": 4,
      "attrName": "aad",
      "searchType": 1,
      "valueType": 1,
      "icon": "qq",
      "valueSelect": "v;q;w",
      "attrType": 1,
      "enable": 1,
      "catelogId": 225,
      "showDesc": 1
    }
  ]
}

1588766303205

如何查找:既然给出了attr_group_id,那么到中间表中查询出来所关联的attr_id,然后得到最终的所有属性即可。

可能出现null值的问题

修改“com.atguigu.gulimall.product.controller.AttrGroupController”类,代码如下:

  1. @GetMapping("{attrgroupId}/attr/relation")
  2. public R attrRelation(@PathVariable("attrgroupId") Long attrgroupId){
  3. List<AttrEntity> attrEntities = attrService.getRelationAttr(attrgroupId);
  4. return R.ok().put("data",attrEntities);
  5. }

修改“com.atguigu.gulimall.product.service.impl.AttrServiceImpl”类,代码如下:

  1. /**
  2. * 根据分组id查找所有关联的基本属性
  3. * @param attrgroupId
  4. * @return
  5. */
  6. @Override
  7. public List<AttrEntity> getRelationAttr(Long attrgroupId) {
  8. List<AttrAttrgroupRelationEntity> entities = attrAttrgroupRelationDao.selectList(new QueryWrapper<AttrAttrgroupRelationEntity>().eq("attr_group_id", attrgroupId));
  9. List<Long> addrIds = entities.stream().map((attr) -> {
  10. return attr.getAttrId();
  11. }).collect(Collectors.toList());
  12. if (attrIds == null || attrIds.size() == 0){
  13. return null;
  14. }
  15. List<AttrEntity> attrEntities = this.listByIds(addrIds);
  16. return attrEntities;
  17. }

4)、删除属性与分组的关联关系

POST  /product/attrgroup/attr/relation/delete

请求参数

[{"attrId":1,"attrGroupId":2}]

响应数据

 {
    "msg": "success",
        "code": 0
}

 修改“com.atguigu.gulimall.product.controller.AttrGroupController”类,代码如下:

  1. // /product/attrgroup/attr/relation/delete
  2. @PostMapping("/attr/relation/delete")
  3. public R deleteRelation(@RequestBody AttrGroupRelationVo[] vos){
  4. attrService.deletRelation(vos);
  5. return R.ok();
  6. }

修改“com.atguigu.gulimall.product.service.AttrService”类,代码如下:

    void deletRelation(AttrGroupRelationVo[] vos);

修改“com.atguigu.gulimall.product.service.impl.AttrServiceImpl”类,代码如下:

  1. @Override
  2. public void deletRelation(AttrGroupRelationVo[] vos) {
  3. //attrAttrgroupRelationDao.delete(new QueryWrapper<AttrAttrgroupRelationEntity>().eq("attr_id",1).eq("attr_group_id",1));
  4. List<AttrAttrgroupRelationEntity> entities = Arrays.asList(vos).stream().map((item) -> {
  5. AttrAttrgroupRelationEntity entity = new AttrAttrgroupRelationEntity();
  6. BeanUtils.copyProperties(item, entity);
  7. return entity;
  8. }).collect(Collectors.toList());
  9. attrAttrgroupRelationDao.deleteBatchRelation(entities);
  10. }

 修改“com.atguigu.gulimall.product.dao.AttrAttrgroupRelationDao”类,代码如下:

void deleteBatchRelation(@Param("entities") List<AttrAttrgroupRelationEntity> entities);

 注意如果遍历的是一个实体类里的某一个参数,分隔符 separator=" or "

  1. <delete id="deleteBatchRelation">
  2. delete from pms_attr_attrgroup_relation where
  3. <foreach collection="entities" item="item" separator=" or ">
  4. (attr_id=#{item.attrId} and attr_group_id = #{item.attrGroupId})
  5. </foreach>
  6. </delete>

5)、获取属性分组没有关联的其他属性

GET      /product/attrgroup/{attrgroupId}/noattr/relation

接口描述

获取属性分组里面还没有关联的本分类里面的其他基本属性,方便添加新的关联

请求参数

{
   page: 1,//当前页码
   limit: 10,//每页记录数
   sidx: 'id',//排序字段
   order: 'asc/desc',//排序方式
   key: '华为'//检索关键字
}

分页数据

响应数据

{
    "msg": "success",
    "code": 0,
    "page": {
        "totalCount": 3,
        "pageSize": 10,
        "totalPage": 1,
        "currPage": 1,
        "list": [{
            "attrId": 1,
            "attrName": "aaa",
            "searchType": 1,
            "valueType": 1,
            "icon": "aa",
            "valueSelect": "aa;ddd;sss;aaa2",
            "attrType": 1,
            "enable": 1,
            "catelogId": 225,
            "showDesc": 1
        }]
    }
}

6)、添加属性与分组关联关系

POST    /product/attrgroup/attr/relation

请求参数

[{
    "attrGroupId": 0, //分组id
    "attrId": 0, //属性id
}]

响应数据

{
    "msg": "success", 
    "code": 0

修改“com.atguigu.gulimall.product.controller.AttrGroupController”类,代码如下:

  1. // /product/attrgroup/attr/relation
  2. @PostMapping("/attr/relation")
  3. public R addRelation(@RequestBody List<AttrGroupRelationVo> vos){
  4. relationService.saveBatch(vos);
  5. return R.ok();
  6. }

修改“com.atguigu.gulimall.product.service.AttrAttrgroupRelationService”类,代码如下:

 void saveBatch(List<AttrGroupRelationVo> vos);

修改“com.atguigu.gulimall.product.service.impl.AttrAttrgroupRelationServiceImpl”类,代码如下:

  1. @Override
  2. public void saveBatch(List<AttrGroupRelationVo> vos) {
  3. List<AttrAttrgroupRelationEntity> collect = vos.stream().map(item -> {
  4. AttrAttrgroupRelationEntity relationEntity = new AttrAttrgroupRelationEntity();
  5. BeanUtils.copyProperties(item, relationEntity);
  6. return relationEntity;
  7. }).collect(Collectors.toList());
  8. this.saveBatch(collect);
  9. }

1.3.2、 规格参数

1)、保存属性【规格参数,销售属性】

POST    /product/attr/save

请求参数

{
  "attrGroupId": 0, //属性分组id
  "attrName": "string",//属性名
  "attrType": 0, //属性类型
  "catelogId": 0, //分类id
  "enable": 0, //是否可用 
  "icon": "string", //图标
  "searchType": 0, //是否检索
  "showDesc": 0, //快速展示
  "valueSelect": "string", //可选值列表
  "valueType": 0 //可选值模式
}

分页数据

响应数据

{
    "msg": "success",
    "code": 0
}

规格参数新增时,请求的URL:Request URL:

http://localhost:88/api/product/attr/base/list/0?t=1588731762158&page=1&limit=10&key=

当有新增字段时,我们往往会在entity实体类中新建一个字段,并标注数据库中不存在该字段,然而这种方式并不规范

1588732021702

比较规范的做法是,新建一个vo文件夹,将每种不同的对象,按照它的功能进行了划分。在java中,涉及到了这几种类型

1588732152646

Request URL: http://localhost:88/api/product/attr/save,现在的情况是,它在保存的时候,只是保存了attr,并没有保存attrgroup,为了解决这个问题,我们新建了一个vo/AttrVo,在原AttrEntity基础上增加了attrGroupId字段,使得保存新增数据的时候,也保存了它们之间的关系。

通过" BeanUtils.copyProperties(attr,attrEntity);"能够实现在两个Bean之间拷贝数据,但是两个Bean的字段要相同

修改“com.atguigu.gulimall.product.controller.AttrController”类,代码如下:

  1. /**
  2. * 保存
  3. */
  4. @RequestMapping("/save")
  5. public R save(@RequestBody AttrVo attr){
  6. attrService.saveAttr(attr);
  7. return R.ok();
  8. }

修改“com.atguigu.gulimall.product.service.AttrService”类,代码如下:

void saveAttr(AttrVo attr);

修改“com.atguigu.gulimall.product.service.impl.AttrServiceImpl”类,代码如下:

  1. @Transactional
  2. @Override
  3. public void saveAttr(AttrVo attr) {
  4. AttrEntity attrEntity = new AttrEntity();
  5. BeanUtils.copyProperties(attr,attrEntity);
  6. //1、保存基本数据
  7. this.save(attrEntity);
  8. //2、保存关联关系
  9. AttrAttrgroupRelationEntity relationEntity = new AttrAttrgroupRelationEntity();
  10. relationEntity.setAttrGroupId(attr.getAttrGroupId());
  11. relationEntity.setAttrId(attrEntity.getAttrId());
  12. attrAttrgroupRelationDao.insert(relationEntity);
  13. }

问题:现在有两个查询,一个是查询部分,另外一个是查询全部,但是又必须这样来做吗?还是有必要的,但是可以在后台进行设计,两种查询是根据catId是否为零进行区分的。

1.3.3、销售属性

1)、 获取分类销售属性

GET     /product/attr/sale/list/{catelogId}

请求参数 

{
   page: 1,//当前页码
   limit: 10,//每页记录数
   sidx: 'id',//排序字段
   order: 'asc/desc',//排序方式
   key: '华为'//检索关键字
}

分页数据

响应数据

{
    "msg": "success",
    "code": 0,
    "page": {
        "totalCount": 0,
        "pageSize": 10,
        "totalPage": 0,
        "currPage": 1,
        "list": [{
            "attrId": 0, //属性id
            "attrName": "string", //属性名
            "attrType": 0, //属性类型,0-销售属性,1-基本属性
            "catelogName": "手机/数码/手机", //所属分类名字
            "groupName": "主体", //所属分组名字
            "enable": 0, //是否启用
            "icon": "string", //图标
            "searchType": 0,//是否需要检索[0-不需要,1-需要]
            "showDesc": 0,//是否展示在介绍上;0-否 1-是
            "valueSelect": "string",//可选值列表[用逗号分隔]
            "valueType": 0//值类型[0-为单个值,1-可以选择多个值]
        }]
    }
}

修改“com.atguigu.gulimall.product.controller.AttrController”类,代码如下:

  1. public class AttrController {
  2. @Autowired
  3. private AttrService attrService;
  4. // /product/attr/sale/list/{catelogId}
  5. // /product/attr/base/list/{catelogId}
  6. @GetMapping("/{attrType}/list/{catelogId}")
  7. public R baseAttrList(@RequestParam Map<String,Object> params,
  8. @PathVariable("catelogId") Long catelogId,
  9. @PathVariable("attrType") String type){
  10. PageUtils page = attrService.queryBaseAttrPage(params,catelogId,type);
  11. return R.ok().put("page",page);
  12. }

修改“com.atguigu.gulimall.product.service.impl.AttrServiceImpl”类,代码如下:

  1. @Override
  2. public PageUtils queryBaseAttrPage(Map<String, Object> params, Long catelogId, String type) {
  3. QueryWrapper<AttrEntity> queryWrapper = new QueryWrapper<AttrEntity>().eq("attr_type",
  4. "base".equalsIgnoreCase(type)?ProductConstant.AttrEnum.ATTR_TYPE_BASE.getCode():ProductConstant.AttrEnum.ATTR_TYPE_SALE.getCode());
  5. if (catelogId != 0){
  6. queryWrapper.eq("catelog_id",catelogId);
  7. }
  8. String key = (String) params.get("key");
  9. if (!StringUtils.isEmpty(key)){
  10. //attr_id attr_name
  11. queryWrapper.and((wrapper)->{
  12. wrapper.eq("attr_id",key).or().like("attr_name",key);
  13. });
  14. }
  15. IPage<AttrEntity> page = this.page(
  16. new Query<AttrEntity>().getPage(params),
  17. queryWrapper
  18. );
  19. PageUtils pageUtils = new PageUtils(page);
  20. List<AttrEntity> records = page.getRecords();
  21. List<AttrResponseVo> responseVos = records.stream().map(attrEntity -> {
  22. AttrResponseVo responseVo = new AttrResponseVo();
  23. BeanUtils.copyProperties(attrEntity, responseVo);
  24. //1、设置分类和分组的名字(只有基本属性显示)
  25. if ("base".equalsIgnoreCase(type)){
  26. AttrAttrgroupRelationEntity attrId = attrAttrgroupRelationDao.selectOne(new QueryWrapper<AttrAttrgroupRelationEntity>().eq("attr_id", attrEntity.getAttrId()));
  27. if (attrId != null) {
  28. AttrGroupEntity attrGroupEntity = attrGroupDao.selectById(attrId.getAttrGroupId());
  29. responseVo.setGroupName(attrGroupEntity.getAttrGroupName());
  30. }
  31. }
  32. CategoryEntity categoryEntity = categoryDao.selectById(attrEntity.getCatelogId());
  33. if (categoryEntity != null) {
  34. responseVo.setCatelogName(categoryEntity.getName());
  35. }
  36. return responseVo;
  37. }).collect(Collectors.toList());
  38. pageUtils.setList(responseVos);
  39. return pageUtils;
  40. }

gulimall-common添加常量的类“com.atguigu.common.constant.ProductConstant”

  1. package com.atguigu.common.constant;
  2. /**
  3. * @author WangTianShun
  4. * @date 2020/10/14 15:11
  5. */
  6. public class ProductConstant {
  7. public enum AttrEnum{
  8. ATTR_TYPE_BASE(1,"基本属性"),
  9. ATTR_TYPE_SALE(0,"销售属性");
  10. AttrEnum(int code,String msg){
  11. this.code = code;
  12. this.msg = msg;
  13. }
  14. private int code;
  15. private String msg;
  16. public int getCode() {
  17. return code;
  18. }
  19. public String getMsg() {
  20. return msg;
  21. }
  22. }
  23. }

因为基本属性和销售属性共用的一张表,所以保存和修改的方法也是共用的,只根据attr_type来判断,所以保存和修改的方法也要修改销售(关于属性和分组的关联,销售属性模块不需要添加到数据库)

修改“com.atguigu.gulimall.product.service.impl.AttrServiceImpl”类,代码如下:

保存 

  1. @Transactional
  2. @Override
  3. public void saveAttr(AttrVo attr) {
  4. AttrEntity attrEntity = new AttrEntity();
  5. BeanUtils.copyProperties(attr,attrEntity);
  6. //1、保存基本数据
  7. this.save(attrEntity);
  8. //2、保存关联关系(只有基本信息才保存关联关系)
  9. if (attr.getAttrType() == ProductConstant.AttrEnum.ATTR_TYPE_BASE.getCode()){
  10. AttrAttrgroupRelationEntity relationEntity = new AttrAttrgroupRelationEntity();
  11. relationEntity.setAttrGroupId(attr.getAttrGroupId());
  12. relationEntity.setAttrId(attrEntity.getAttrId());
  13. attrAttrgroupRelationDao.insert(relationEntity);
  14. }
  15. }

查看详情

  1. @Override
  2. public AttrResponseVo getAttrInfo(Long attrId) {
  3. AttrResponseVo responseVo = new AttrResponseVo();
  4. AttrEntity attrEntity = this.getById(attrId);
  5. BeanUtils.copyProperties(attrEntity,responseVo);
  6. //基本类型才查询分组
  7. if (attrEntity.getAttrType() == ProductConstant.AttrEnum.ATTR_TYPE_BASE.getCode()){
  8. //1、设置分组信息
  9. AttrAttrgroupRelationEntity relationEntity = attrAttrgroupRelationDao.selectOne(new QueryWrapper<AttrAttrgroupRelationEntity>().eq("attr_id", attrId));
  10. if (relationEntity != null){
  11. responseVo.setAttrGroupId(relationEntity.getAttrGroupId());
  12. AttrGroupEntity attrGroupEntity = attrGroupDao.selectById(relationEntity.getAttrGroupId());
  13. if (attrGroupEntity != null){
  14. responseVo.setGroupName(attrGroupEntity.getAttrGroupName());
  15. }
  16. }
  17. }
  18. //2、设置分类信息
  19. Long catelogId = attrEntity.getCatelogId();
  20. Long[] catelogPath = categoryService.findCatelogPath(catelogId);
  21. responseVo.setCatelogPath(catelogPath);
  22. CategoryEntity categoryEntity = categoryDao.selectById(catelogId);
  23. if (categoryEntity != null){
  24. responseVo.setCatelogName(categoryEntity.getName());
  25. }
  26. return responseVo;
  27. }

修改

  1. @Transactional
  2. @Override
  3. public void updateAttr(AttrVo attr) {
  4. AttrEntity attrEntity = new AttrEntity();
  5. BeanUtils.copyProperties(attr,attrEntity);
  6. this.updateById(attrEntity);
  7. //基本类型才修改分组
  8. if (attrEntity.getAttrType() == ProductConstant.AttrEnum.ATTR_TYPE_BASE.getCode()){
  9. //1、修改分组关联
  10. AttrAttrgroupRelationEntity relationEntity = new AttrAttrgroupRelationEntity();
  11. relationEntity.setAttrGroupId(attr.getAttrGroupId());
  12. relationEntity.setAttrId(attr.getAttrId());
  13. Integer count = attrAttrgroupRelationDao.selectCount(new QueryWrapper<AttrAttrgroupRelationEntity>().eq("attr_id", attrEntity.getAttrId()));
  14. //有数据说明是修改,没数据是新增
  15. if (count>0){
  16. attrAttrgroupRelationDao.update(relationEntity,new UpdateWrapper<AttrAttrgroupRelationEntity>().eq("attr_id",attrEntity.getAttrId()));
  17. }else {
  18. attrAttrgroupRelationDao.insert(relationEntity);
  19. }
  20. }
  21. }

1.4、商品维护

1.4.1、spu管理

01)、获取spu规格

GET     /product/attr/base/listforspu/{spuId}

响应数据

{
    "msg": "success",
    "code": 0,
    "data": [{
    "id": 43,
    "spuId": 11,
    "attrId": 7,
    "attrName": "入网型号",
    "attrValue": "LIO-AL00",
    "attrSort": null,
    "quickShow": 1
    }]

修改“com.atguigu.gulimall.product.controller.AttrController”类,代码如下:

  1. @GetMapping("/base/listforspu/{spuId}")
  2. public R baseAttrListForSpu(@PathVariable("spuId") Long spuId){
  3. List<ProductAttrValueEntity> entities = productAttrValueService.baseAttrListForSpu(spuId);
  4. return R.ok().put("data",entities);
  5. }

修改“com.atguigu.gulimall.product.service.ProductAttrValueService”类,代码如下:

List<ProductAttrValueEntity> baseAttrListForSpu(Long spuId);

修改“com.atguigu.gulimall.product.service.impl.ProductAttrValueServiceImpl”类,代码如下:

  1. @Override
  2. public List<ProductAttrValueEntity> baseAttrListForSpu(Long spuId) {
  3. List<ProductAttrValueEntity> entities = this.baseMapper.selectList(new QueryWrapper<ProductAttrValueEntity>().eq("spu_id", spuId));
  4. return entities;
  5. }

02)、修改商品规格

POST    /product/attr/update/{spuId}

请求参数

[{
        "attrId": 7,
                "attrName": "入网型号",
                "attrValue": "LIO-AL00",
                "quickShow": 1
    }, {
        "attrId": 14,
                "attrName": "机身材质工艺",
                "attrValue": "玻璃",
                "quickShow": 0
    }, {
        "attrId": 16,
                "attrName": "CPU型号",
                "attrValue": "HUAWEI Kirin 980",
                "quickShow": 1
}]

响应数据

{
    "msg": "success",
    "code": 0
}

修改“com.atguigu.gulimall.product.controller.AttrController”类,代码如下:

  1. @PostMapping("/update/{spuId}")
  2. public R updateSpuAttr(@PathVariable("spuId") Long spuId,
  3. @RequestBody List<ProductAttrValueEntity> entities){
  4. productAttrValueService.updateSpuAttr(spuId,entities);
  5. return R.ok();
  6. }

修改“com.atguigu.gulimall.product.controller.AttrController”类,代码如下:

void updateSpuAttr(Long spuId, List<ProductAttrValueEntity> entities);

修改“com.atguigu.gulimall.product.controller.AttrController”类,代码如下:

  1. @Transactional
  2. @Override
  3. public void updateSpuAttr(Long spuId, List<ProductAttrValueEntity> entities) {
  4. //1、删除spu之前对应的所有属性
  5. this.baseMapper.delete(new QueryWrapper<ProductAttrValueEntity>().eq("spu_id",spuId));
  6. List<ProductAttrValueEntity> collect = entities.stream().map(item -> {
  7. item.setSpuId(spuId);
  8. return item;
  9. }).collect(Collectors.toList());
  10. this.saveBatch(collect);
  11. }

1.4.2、发布商品

商品管理需要会员等级,先把资料前端文件夹里的modules里的文件导入vsCode里重新运行,添加几个会员

1)、获取所有会员等级

POST     /member/memberlevel/list

请求参数

{
   page: 1,//当前页码
   limit: 10,//每页记录数
   sidx: 'id',//排序字段
   order: 'asc/desc',//排序方式
   key: '华为'//检索关键字
}

分页数据

响应数据

{
    "msg": "success",
    "code": 0,
    "page": {
        "totalCount": 0,
        "pageSize": 10,
        "totalPage": 0,
        "currPage": 1,
        "list": [{
            "id": 1,
            "name": "aaa",
            "growthPoint": null,
            "defaultStatus": null,
            "freeFreightPoint": null,
            "commentGrowthPoint": null,
            "priviledgeFreeFreight": null,
            "priviledgeMemberPrice": null,
            "priviledgeBirthday": null,
            "note": null
        }]
    }
}

修改“com.atguigu.gulimall.member.controller.MemberLevelController”类,代码如下:

  1. @RequestMapping("/list")
  2. public R list(@RequestParam Map<String, Object> params){
  3. PageUtils page = memberLevelService.queryPage(params);
  4. return R.ok().put("page", page);
  5. }

修改“com.atguigu.gulimall.member.service.MemberLevelService”类,代码如下: 

 PageUtils queryPage(Map<String, Object> params);

 修改“com.atguigu.gulimall.member.service.impl.MemberLevelServiceImpl”类,代码如下: 

  1. @Override
  2. public PageUtils queryPage(Map<String, Object> params) {
  3. IPage<MemberLevelEntity> page = this.page(
  4. new Query<MemberLevelEntity>().getPage(params),
  5. new QueryWrapper<MemberLevelEntity>()
  6. );
  7. return new PageUtils(page);
  8. }

2)、获取分类关联的品牌

GET       /product/categorybrandrelation/brands/list

请求参数

参数名

参数类型描述
catIdlong分类id

响应数据

{
    "msg": "success",
    "code": 0,
    "data": [{
        "brandId": 0,
        "brandName": "string",
    }]
}

修改“com.atguigu.gulimall.product.controller.CategoryBrandRelationController”类,代码如下: 

  1. /**
  2. * 获取三级分类下的所有品牌
  3. * /product/categorybrandrelation/brands/list
  4. *
  5. * 1、Controller: 处理请求,接收和校验数据
  6. * 2、service接收controller数据,进行业务处理
  7. * 3、Controller接收Service处理完的数据,封装页面指定的vo
  8. */
  9. @GetMapping("brands/list")
  10. public R relationBrandList(@RequestParam(value = "catId", required = true) Long catId){
  11. List<BrandEntity> list = categoryBrandRelationService.getBrandsByCatId(catId);
  12. List<BrandVo> collect = list.stream().map(item -> {
  13. BrandVo brandVo = new BrandVo();
  14. brandVo.setBrandId(item.getBrandId());
  15. brandVo.setBrandName(item.getName());
  16. return brandVo;
  17. }).collect(Collectors.toList());
  18. return R.ok().put("data",collect);
  19. }

修改“com.atguigu.gulimall.product.service.CategoryBrandRelationService”类,代码如下:

List<BrandEntity> getBrandsByCatId(Long catId);

修改“com.atguigu.gulimall.product.service.impl.CategoryBrandRelationServiceImpl”类,代码如下:

  1. @Override
  2. public List<BrandEntity> getBrandsByCatId(Long catId) {
  3. List<CategoryBrandRelationEntity> catelogId = categoryBrandRelationDao.selectList(new QueryWrapper<CategoryBrandRelationEntity>().eq("catelog_id", catId));
  4. List<BrandEntity> collect = catelogId.stream().map(item -> {
  5. Long brandId = item.getBrandId();
  6. BrandEntity byId = brandService.getById(brandId);
  7. return byId;
  8. }).collect(Collectors.toList());
  9. return collect;
  10. }

3)、获取分类下所有分组&关联属性

GET     /product/attrgroup/{catelogId}/withattr

响应数据

{
    "msg": "success",
    "code": 0,
    "data": [{
        "attrGroupId": 1,
        "attrGroupName": "主体",
        "sort": 0,
        "descript": "主体",
        "icon": "dd",
        "catelogId": 225,
        "attrs": [{
            "attrId": 7,
            "attrName": "入网型号",
            "searchType": 1,
            "valueType": 0,
            "icon": "xxx",
            "valueSelect": "aaa;bb",
            "attrType": 1,
            "enable": 1,
            "catelogId": 225,
            "showDesc": 1,
            "attrGroupId": null
            }, {
            "attrId": 8,
            "attrName": "上市年份",
            "searchType": 0,
            "valueType": 0,
            "icon": "xxx",
            "valueSelect": "2018;2019",
            "attrType": 1,
            "enable": 1,
            "catelogId": 225,
            "showDesc": 0,
            "attrGroupId": null
            }]
        },
        {
        "attrGroupId": 2,
        "attrGroupName": "基本信息",
        "sort": 0,
        "descript": "基本信息",
        "icon": "xx",
        "catelogId": 225,
        "attrs": [{
            "attrId": 11,
            "attrName": "机身颜色",
            "searchType": 0,
            "valueType": 0,
            "icon": "xxx",
            "valueSelect": "黑色;白色",
            "attrType": 1,
            "enable": 1,
            "catelogId": 225,
            "showDesc": 1,
            "attrGroupId": null
        }]
    }]
}

修改“com.atguigu.gulimall.product.controller.AttrGroupController”类,代码如下:

  1. @GetMapping("/{catelogId}/withattr")
  2. public R getAttrGroupWithAttrs(@PathVariable("catelogId") Long catelogId){
  3. //1、查出当前分类下的所有分组
  4. //2、查出每个分组下的所有属性
  5. List<AttrGroupWithAttrsVo> list = attrGroupService.getAttrGroupWithAttrsByCatelogId(catelogId);
  6. return R.ok().put("data",list);
  7. }

修改“com.atguigu.gulimall.product.service.AttrGroupService”类,代码如下:

    List<AttrGroupWithAttrsVo> getAttrGroupWithAttrsByCatelogId(Long catelogId);

修改“com.atguigu.gulimall.product.service.impl.AttrGroupServiceImpl”类,代码如下:

  1. /**
  2. * 根据分类id查出所有的分组以及这些组里面的属性
  3. * @param catelogId
  4. * @return
  5. */
  6. @Override
  7. public List<AttrGroupWithAttrsVo> getAttrGroupWithAttrsByCatelogId(Long catelogId) {
  8. //1、查询分组信息
  9. List<AttrGroupEntity> attrGroupEntities = this.list(new QueryWrapper<AttrGroupEntity>().eq("catelog_id", catelogId));
  10. //2、查询所有属性
  11. List<AttrGroupWithAttrsVo> collect = attrGroupEntities.stream().map(item -> {
  12. AttrGroupWithAttrsVo attrsVo = new AttrGroupWithAttrsVo();
  13. BeanUtils.copyProperties(item, attrsVo);
  14. List<AttrEntity> attrs = attrService.getRelationAttr(attrsVo.getAttrGroupId());
  15. attrsVo.setAttrs(attrs);
  16. return attrsVo;
  17. }).collect(Collectors.toList());
  18. return collect;
  19. }

4)、新增商品

POST   /product/spuinfo/save

请求参数

{
    "spuName": "Apple XR",
    "spuDescription": "Apple XR",
    "catalogId": 225,
    "brandId": 12,
    "weight": 0.048,
    "publishStatus": 0,
    "decript": ["https://gulimall-hello.oss-cn-beijing.aliyuncs.com/2019-11-22//66d30b3f-e02f-48b1-8574-e18fdf454a32_f205d9c99a2b4b01.jpg"],
    "images": ["https://gulimall-hello.oss-cn-beijing.aliyuncs.com/2019-11-22//dcfcaec3-06d8-459b-8759-dbefc247845e_5b5e74d0978360a1.jpg", "https://gulimall-hello.oss-cn-beijing.aliyuncs.com/2019-11-22//5b15e90a-a161-44ff-8e1c-9e2e09929803_749d8efdff062fb0.jpg"],
    "bounds": {
        "buyBounds": 500,
        "growBounds": 6000
    },
    "baseAttrs": [{
        "attrId": 7,
        "attrValues": "aaa;bb",
        "showDesc": 1
    }, {
        "attrId": 8,
        "attrValues": "2019",
        "showDesc": 0
    }],
    "skus": [{
        "attr": [{
            "attrId": 9,
            "attrName": "颜色",
            "attrValue": "黑色"
        }, {
            "attrId": 10,
            "attrName": "内存",
            "attrValue": "6GB"
        }],
        "skuName": "Apple XR 黑色 6GB",
        "price": "1999",
        "skuTitle": "Apple XR 黑色 6GB",
        "skuSubtitle": "Apple XR 黑色 6GB",
        "images": [{
            "imgUrl": "https://gulimall-hello.oss-cn-beijing.aliyuncs.com/2019-11-22//dcfcaec3-06d8-459b-8759-dbefc247845e_5b5e74d0978360a1.jpg",
            "defaultImg": 1
            }, {
            "imgUrl": "https://gulimall-hello.oss-cn-beijing.aliyuncs.com/2019-11-22//5b15e90a-a161-44ff-8e1c-9e2e09929803_749d8efdff062fb0.jpg",
            "defaultImg": 0
        }],
        "descar": ["黑色", "6GB"],
        "fullCount": 5,
        "discount": 0.98,
        "countStatus": 1,
        "fullPrice": 1000,
        "reducePrice": 10,
        "priceStatus": 0,
        "memberPrice": [{
            "id": 1,
            "name": "aaa",
            "price": 1998.99
        }]
        }, {
        "attr": [{
            "attrId": 9,
            "attrName": "颜色",
            "attrValue": "黑色"
        }, {
            "attrId": 10,
            "attrName": "内存",
            "attrValue": "12GB"
        }],
        "skuName": "Apple XR 黑色 12GB",
        "price": "2999",
        "skuTitle": "Apple XR 黑色 12GB",
        "skuSubtitle": "Apple XR 黑色 6GB",
        "images": [{
            "imgUrl": "",
            "defaultImg": 0
        }, {
            "imgUrl": "",
            "defaultImg": 0
        }],
        "descar": ["黑色", "12GB"],
        "fullCount": 0,
        "discount": 0,
        "countStatus": 0,
        "fullPrice": 0,
        "reducePrice": 0,
        "priceStatus": 0,
        "memberPrice": [{
            "id": 1,
            "name": "aaa",
            "price": 1998.99
        }]
    }, {
        "attr": [{
            "attrId": 9,
            "attrName": "颜色",
            "attrValue": "白色"
        }, {
            "attrId": 10,
            "attrName": "内存",
            "attrValue": "6GB"
        }],
        "skuName": "Apple XR 白色 6GB",
        "price": "1998",
        "skuTitle": "Apple XR 白色 6GB",
        "skuSubtitle": "Apple XR 黑色 6GB",
        "images": [{
            "imgUrl": "",
            "defaultImg": 0
        }, {
            "imgUrl": "",
            "defaultImg": 0
        }],
        "descar": ["白色", "6GB"],
        "fullCount": 0,
        "discount": 0,
        "countStatus": 0,
        "fullPrice": 0,
        "reducePrice": 0,
        "priceStatus": 0,
        "memberPrice": [{
            "id": 1,
            "name": "aaa",
            "price": 1998.99
        }]
    }, {
        "attr": [{
            "attrId": 9,
            "attrName": "颜色",
            "attrValue": "白色"
        }, {
            "attrId": 10,
            "attrName": "内存",
            "attrValue": "12GB"
        }],
        "skuName": "Apple XR 白色 12GB",
        "price": "2998",
        "skuTitle": "Apple XR 白色 12GB",
        "skuSubtitle": "Apple XR 黑色 6GB",
        "images": [{
            "imgUrl": "",
            "defaultImg": 0
        }, {
            "imgUrl": "",
            "defaultImg": 0
        }],
        "descar": ["白色", "12GB"],
        "fullCount": 0,
        "discount": 0,
        "countStatus": 0,
        "fullPrice": 0,
        "reducePrice": 0,
        "priceStatus": 0,
        "memberPrice": [{
            "id": 1,
            "name": "aaa",
            "price": 1998.99
        }]
    }]
}

分页数据

响应数据

{
    "msg": "success",
    "code": 0
}

修改“com.atguigu.gulimall.product.controller.SpuInfoController”类,代码如下:

  1. /**
  2. * 保存
  3. */
  4. @RequestMapping("/save")
  5. public R save(@RequestBody SpuSaveVo vo){
  6. //spuInfoService.save(spuInfo);
  7. spuInfoService.save(vo);
  8. return R.ok();
  9. }

修改“com.atguigu.gulimall.product.service.SpuInfoService”类,代码如下:

  1. void save(SpuSaveVo vo);
  2. void saveBaseSpuInfo(SpuInfoEntity infoEntity);

修改“com.atguigu.gulimall.product.service.impl.SpuInfoServiceImpl”类,代码如下:

  1. /**
  2. * //TODO 高级部分完善
  3. * @param vo
  4. */
  5. @Transactional
  6. @Override
  7. public void save(SpuSaveVo vo) {
  8. //1、保存spu基本信息 pms_spu_info
  9. SpuInfoEntity infoEntity = new SpuInfoEntity();
  10. BeanUtils.copyProperties(vo,infoEntity);
  11. infoEntity.setCreateTime(new Date());
  12. infoEntity.setUpdateTime(new Date());
  13. this.saveBaseSpuInfo(infoEntity);
  14. //2、保存spu的描述图片 pms_spu_info_desc
  15. List<String> decript = vo.getDecript();
  16. SpuInfoDescEntity descEntity = new SpuInfoDescEntity();
  17. descEntity.setSpuId(infoEntity.getId());
  18. //将所有图片描述拼接起来,用逗号隔开
  19. descEntity.setDecript(String.join(",",decript));
  20. spuInfoDescService.saveSpuInfoDesc(descEntity);
  21. //3、保存spu的图片集 pms_spu_images
  22. List<String> images = vo.getImages();
  23. spuImagesService.saveImages(infoEntity.getId(),images);
  24. //4、保存spu的规格参数 pms_product_attr_value
  25. List<BaseAttrs> baseAttrs = vo.getBaseAttrs();
  26. List<ProductAttrValueEntity> collect = baseAttrs.stream().map(attr -> {
  27. ProductAttrValueEntity valueEntity = new ProductAttrValueEntity();
  28. valueEntity.setAttrId(attr.getAttrId());
  29. AttrEntity byId = attrService.getById(attr.getAttrId());
  30. valueEntity.setAttrName(byId.getAttrName());
  31. valueEntity.setAttrValue(attr.getAttrValues());
  32. valueEntity.setQuickShow(attr.getShowDesc());
  33. valueEntity.setSpuId(infoEntity.getId());
  34. return valueEntity;
  35. }).collect(Collectors.toList());
  36. productAttrValueService.saveProductAttr(collect);
  37. //5、保存spu的积分信息 gulimall_sms -> sms_spu_bounds
  38. Bounds bounds = vo.getBounds();
  39. SpuBoundTo spuBoundTo = new SpuBoundTo();
  40. BeanUtils.copyProperties(bounds,spuBoundTo);
  41. spuBoundTo.setSpuId(infoEntity.getId());
  42. R r = couponFeignService.saveSpuBounds(spuBoundTo);
  43. if (r.getCode() != 0){
  44. log.error("远程保存spu积分信息失败");
  45. }
  46. //6、保存当前spu对应的所有sku信息
  47. //6.1)、sku的基本信息 pms_sku_info
  48. List<Skus> skus = vo.getSkus();
  49. if (skus != null && skus.size() >0){
  50. skus.forEach(item->{
  51. String defaultImg = "";
  52. for (Images image : item.getImages()) {
  53. if (image.getDefaultImg() == 1){
  54. defaultImg = image.getImgUrl();
  55. }
  56. }
  57. SkuInfoEntity skuInfoEntity = new SkuInfoEntity();
  58. BeanUtils.copyProperties(item,skuInfoEntity);
  59. skuInfoEntity.setBrandId(infoEntity.getBrandId());
  60. skuInfoEntity.setCatalogId(infoEntity.getCatalogId());
  61. skuInfoEntity.setSaleCount(0L);
  62. skuInfoEntity.setSpuId(infoEntity.getId());
  63. skuInfoEntity.setSkuDefaultImg(defaultImg);
  64. skuInfoService.saveSkuInfo(skuInfoEntity);
  65. //6.2)、sku的图片信息 pms_sku_images
  66. Long skuId = skuInfoEntity.getSkuId();
  67. List<SkuImagesEntity> imagesEntities = item.getImages().stream().map(img -> {
  68. SkuImagesEntity imagesEntity = new SkuImagesEntity();
  69. imagesEntity.setSkuId(skuId);
  70. imagesEntity.setImgUrl(img.getImgUrl());
  71. imagesEntity.setDefaultImg(img.getDefaultImg());
  72. return imagesEntity;
  73. }).filter(entity->{
  74. //返回true就是需要,返回false就是剔除
  75. return !StringUtils.isEmpty(entity.getImgUrl());
  76. }).collect(Collectors.toList());
  77. // TODO 没有图片;路径的无需保存
  78. skuImagesService.saveBatch(imagesEntities);
  79. //6.3)、sku的销售属性信息 pms_sku_sale_attr_value
  80. List<Attr> attr = item.getAttr();
  81. List<SkuSaleAttrValueEntity> skuSaleAttrValueEntities = attr.stream().map(a -> {
  82. SkuSaleAttrValueEntity saleAttrValueEntity = new SkuSaleAttrValueEntity();
  83. BeanUtils.copyProperties(a, saleAttrValueEntity);
  84. saleAttrValueEntity.setSkuId(skuId);
  85. return saleAttrValueEntity;
  86. }).collect(Collectors.toList());
  87. skuSaleAttrValueService.saveBatch(skuSaleAttrValueEntities);
  88. //6.4)、sku的优惠、满减等信息 gulimall_sms -> sms_sku_ladder/sms_sku_full_reduction/sms_member_price
  89. SkuReductionTo skuReductionTo = new SkuReductionTo();
  90. BeanUtils.copyProperties(item,skuReductionTo);
  91. skuReductionTo.setSkuId(skuId);
  92. if (skuReductionTo.getFullCount() > 0 || skuReductionTo.getFullPrice().compareTo(new BigDecimal("0")) == 1){
  93. R r1 = couponFeignService.saveSkuReduction(skuReductionTo);
  94. if (r1.getCode() != 0){
  95. log.error("远程保存sku优惠信息失败");
  96. }
  97. }
  98. });
  99. }
  100. }

//1、保存spu基本信息 pms_spu_info  

修改“com.atguigu.gulimall.product.service.impl.SpuInfoServiceImpl”类,代码如下: 

  1. @Override
  2. public void saveBaseSpuInfo(SpuInfoEntity infoEntity) {
  3. this.baseMapper.insert(infoEntity);
  4. }

//2、保存spu的描述图片  pms_spu_info_desc 

修改“com.atguigu.gulimall.product.service.SpuInfoDescService”类,代码如下: 

void saveSpuInfoDesc(SpuInfoDescEntity descEntity);

修改“com.atguigu.gulimall.product.service.impl.SpuInfoDescServiceImpl”类,代码如下:

  1. @Override
  2. public void saveSpuInfoDesc(SpuInfoDescEntity descEntity) {
  3. this.baseMapper.insert(descEntity);
  4. }

//3、保存spu的图片集    pms_spu_images 
修改“com.atguigu.gulimall.product.service.SpuImagesService”类,代码如下:

void saveImages(Long id, List<String> images);

修改“com.atguigu.gulimall.product.service.impl.SpuImagesServiceImpl”类,代码如下:

  1. @Override
  2. public void saveImages(Long id, List<String> images) {
  3. if (images == null || images.size() == 0){
  4. }else{
  5. List<SpuImagesEntity> collect = images.stream().map(img -> {
  6. SpuImagesEntity entity = new SpuImagesEntity();
  7. entity.setSpuId(id);
  8. entity.setImgUrl(img);
  9. return entity;
  10. }).collect(Collectors.toList());
  11. this.saveBatch(collect);
  12. }
  13. }

//4、保存spu的规格参数  pms_product_attr_value 

修改“com.atguigu.gulimall.product.service.ProductAttrValueService”类,代码如下:

void saveProductAttr(List<ProductAttrValueEntity> collect);

修改“com.atguigu.gulimall.product.service.impl.ProductAttrValueServiceImpl”类,代码如下:

  1. @Override
  2. public void saveProductAttr(List<ProductAttrValueEntity> collect) {
  3. this.saveBatch(collect);
  4. }

//5、保存spu的积分信息 gulimall_sms --> sms_spu_bounds
注意:

重点:需要远程调用第三方服务
1、创建openFeign配置(前提第三方服务已经注册和配置到注册中心了)

2、在主程序类中加上@EnableFeignClients(basePackages = "com.atguigu.gulimall.product.feign")

  1. @EnableFeignClients(basePackages = "com.atguigu.gulimall.product.feign")
  2. @EnableDiscoveryClient
  3. @MapperScan("com.atguigu.gulimall.product.dao")
  4. @SpringBootApplication
  5. public class GulimallProductApplication {
  6. public static void main(String[] args) {
  7. SpringApplication.run(GulimallProductApplication.class, args);
  8. }
  9. }

3、在gulimall-common添加服务与服务之间调用的to类“com.atguigu.common.to.SpuBoundTo”类,代码如下:

  1. @Data
  2. public class SpuBoundTo {
  3. private Long SpuId;
  4. private BigDecimal buyBounds;
  5. private BigDecimal growBounds;
  6. }

修改“com.atguigu.gulimall.product.feign.CouponFeignService”类,代码如下:

  1. package com.atguigu.gulimall.product.feign;
  2. import com.atguigu.common.to.SpuBoundTo;
  3. import com.atguigu.common.utils.R;
  4. import org.springframework.cloud.openfeign.FeignClient;
  5. import org.springframework.web.bind.annotation.PostMapping;
  6. import org.springframework.web.bind.annotation.RequestBody;
  7. import org.springframework.web.bind.annotation.ResponseBody;
  8. /**
  9. * @author WangTianShun
  10. * @date 2020/10/16 8:54
  11. */
  12. @FeignClient("gulimall-coupon")
  13. public interface CouponFeignService {
  14. /**
  15. * 1、couponFeignService.saveSpuBounds(spuBoundTo)
  16. * 1)、@RequestBody将这个对象转为json
  17. * 2)、找到gulimall-coupon服务,给/coupon/spubounds/save发送请求。将上一步转的json放在请求体的位置发送请求
  18. * 3)、对方服务收到请求请求体有json数据。
  19. * (@RequestBody SpuBoundsEntity spuBoundTo)将请求体里的json转为SpuBoundsEntity
  20. * 只要json数据模型是兼容的。对方服务无需使用同一个to
  21. * @param spuBoundTo
  22. * @return
  23. */
  24. @PostMapping("/coupon/spubounds/save")
  25. R saveSpuBounds(@RequestBody SpuBoundTo spuBoundTo);
  26. }

4、第三方服务接口

  1. /**
  2. * 保存
  3. */
  4. @PostMapping("/save")
  5. public R save(@RequestBody SpuBoundsEntity spuBounds){
  6. spuBoundsService.save(spuBounds);
  7. return R.ok();
  8. }

//6、保存当前spu对应的所有sku信息
//6.1)、sku的基本信息  pms_sku_info

修改“com.atguigu.gulimall.product.service.SkuInfoServicel”类,代码如下:

 void saveSkuInfo(SkuInfoEntity skuInfoEntity);

修改“com.atguigu.gulimall.product.service.impl.SkuInfoServiceImpl”类,代码如下:

  1. @Override
  2. public void saveSkuInfo(SkuInfoEntity skuInfoEntity) {
  3. this.baseMapper.insert(skuInfoEntity);
  4. }
//6.4)、sku的优惠、满减等信息   gulimall_sms -> sms_sku_ladder/sms_sku_full_reduction/sms_member_price

 在gulimall-common添加服务与服务之间调用的to类“com.atguigu.common.to.MemberPrice”,“com.atguigu.common.to.SkuReductionTo”类,代码如下:

  1. /**
  2. * Copyright 2019 bejson.com
  3. */
  4. package com.atguigu.common.to;
  5. import lombok.Data;
  6. import java.math.BigDecimal;
  7. /**
  8. * Auto-generated: 2019-11-26 10:50:34
  9. *
  10. * @author bejson.com (i@bejson.com)
  11. * @website http://www.bejson.com/java2pojo/
  12. */
  13. @Data
  14. public class MemberPrice {
  15. private Long id;
  16. private String name;
  17. private BigDecimal price;
  18. }

  1. package com.atguigu.common.to;
  2. import lombok.Data;
  3. import java.math.BigDecimal;
  4. import java.util.List;
  5. /**
  6. * @author WangTianShun
  7. * @date 2020/10/16 9:53
  8. */
  9. @Data
  10. public class SkuReductionTo {
  11. private Long skuId;
  12. private int fullCount;
  13. private BigDecimal discount;
  14. private int countStatus;
  15. private BigDecimal fullPrice;
  16. private BigDecimal reducePrice;
  17. private int priceStatus;
  18. private List<MemberPrice> memberPrice;
  19. }

 修改“com.atguigu.gulimall.coupon.controller.SkuFullReductionController”类,代码如下:

  1. /**
  2. * 保存
  3. */
  4. @RequestMapping("/saveInfo")
  5. public R saveInfo(@RequestBody SkuReductionTo skuReductionTo){
  6. skuFullReductionService.saveReduction(skuReductionTo);
  7. return R.ok();
  8. }

 修改“com.atguigu.gulimall.coupon.service.SkuFullReductionService”类,代码如下:

 void saveReduction(SkuReductionTo skuReductionTo);

 修改“com.atguigu.gulimall.coupon.service.impl.SkuFullReductionServiceImpl”类,代码如下:

  1. @Override
  2. public void saveReduction(SkuReductionTo reductionTo) {
  3. //1、sku的优惠、满减等信息 gulimall_sms -> sms_sku_ladder/sms_sku_full_reduction/sms_member_price
  4. //sms_sku_ladder
  5. SkuLadderEntity skuLadderEntity = new SkuLadderEntity();
  6. skuLadderEntity.setSkuId(reductionTo.getSkuId());
  7. skuLadderEntity.setFullCount(reductionTo.getFullCount());
  8. skuLadderEntity.setDiscount(reductionTo.getDiscount());
  9. skuLadderEntity.setAddOther(reductionTo.getCountStatus());
  10. if(reductionTo.getFullCount() > 0){
  11. skuLadderService.save(skuLadderEntity);
  12. }
  13. //2、ms_sku_full_reduction
  14. SkuFullReductionEntity reductionEntity = new SkuFullReductionEntity();
  15. BeanUtils.copyProperties(reductionTo,reductionEntity);
  16. if (reductionEntity.getFullPrice().compareTo(new BigDecimal("0")) == 1 ){
  17. this.save(reductionEntity);
  18. }

5)、spu检索

GET      /product/spuinfo/list

请求参数

{
    page: 1,//当前页码
            limit: 10,//每页记录数
        sidx: 'id',//排序字段
        order: 'asc/desc',//排序方式
        key: '华为',//检索关键字
        catelogId: 6,//三级分类id
        brandId: 1,//品牌id 
        status: 0,//商品状态
}

分页数据

响应数据

{
    "msg": "success",
        "code": 0,
        "page": {
    "totalCount": 0,
            "pageSize": 10,
            "totalPage": 0,
            "currPage": 1,
            "list": [{

        "brandId": 0, //品牌id
                "brandName": "品牌名字",
                "catalogId": 0, //分类id
                "catalogName": "分类名字",
                "createTime": "2019-11-13T16:07:32.877Z", //创建时间
                "id": 0, //商品id
                "publishStatus": 0, //发布状态
                "spuDescription": "string", //商品描述
                "spuName": "string", //商品名字
                "updateTime": "2019-11-13T16:07:32.877Z", //更新时间
                "weight": 0 //重量

    }]
}
}

修改“com.atguigu.gulimall.product.controller.SpuInfoController”类,代码如下:

  1. /**
  2. * 列表
  3. */
  4. @RequestMapping("/list")
  5. public R list(@RequestParam Map<String, Object> params){
  6. PageUtils page = spuInfoService.queryPageByCondition(params);
  7. return R.ok().put("page", page);
  8. }

修改“com.atguigu.gulimall.product.service.SpuInfoService”类,代码如下:

PageUtils queryPageByCondition(Map<String, Object> params);

修改“com.atguigu.gulimall.product.service.impl.SpuInfoServiceImpl”类,代码如下:

  1. @Override
  2. public PageUtils queryPageByCondition(Map<String, Object> params) {
  3. QueryWrapper<SpuInfoEntity> wrapper = new QueryWrapper<>();
  4. String key = (String) params.get("key");
  5. if(!StringUtils.isEmpty(key)){
  6. wrapper.and((w)->{w.eq("id",key).or().like("spu_name",key);});
  7. }
  8. // status=1 and (id=1 or spu_name like xxx)
  9. String status = (String) params.get("status");
  10. if(!StringUtils.isEmpty(status)){
  11. wrapper.eq("publish_status",status);
  12. }
  13. String brandId = (String) params.get("brandId");
  14. if(!StringUtils.isEmpty(brandId)&&!"0".equalsIgnoreCase(brandId)){
  15. wrapper.eq("brand_id",brandId);
  16. }
  17. String catelogId = (String) params.get("catelogId");
  18. if(!StringUtils.isEmpty(catelogId)&&!"0".equalsIgnoreCase(catelogId)){
  19. wrapper.eq("catalog_id",catelogId);
  20. }
  21. /**
  22. * status: 2
  23. * key:
  24. * brandId: 9
  25. * catelogId: 225
  26. */
  27. IPage<SpuInfoEntity> page = this.page(
  28. new Query<SpuInfoEntity>().getPage(params),
  29. wrapper
  30. );
  31. return new PageUtils(page);
  32. }

6)、sku检索

GET   /product/skuinfo/list

请求参数

{
        page: 1,//当前页码
        limit: 10,//每页记录数
        sidx: 'id',//排序字段
        order: 'asc/desc',//排序方式
        key: '华为',//检索关键字
        catelogId: 0,
        brandId: 0,
        min: 0,
        max: 0
}

分页数据

响应数据

{
        "msg": "success",
        "code": 0,
        "page": {
        "totalCount": 26,
        "pageSize": 10,
        "totalPage": 3,
        "currPage": 1,
        "list": [{
        "skuId": 1,
        "spuId": 11,
        "skuName": "华为 HUAWEI Mate 30 Pro 星河银 8GB+256GB",
        "skuDesc": null,
        "catalogId": 225,
        "brandId": 9,
        "skuDefaultImg": "https://gulimall-hello.oss-cn-beijing.aliyuncs.com/2019-11-26/60e65a44-f943-4ed5-87c8-8cf90f403018_d511faab82abb34b.jpg",
        "skuTitle": "华为 HUAWEI Mate 30 Pro 星河银 8GB+256GB麒麟990旗舰芯片OLED环幕屏双4000万徕卡电影四摄4G全网通手机",
        "skuSubtitle": "【现货抢购!享白条12期免息!】麒麟990,OLED环幕屏双4000万徕卡电影四摄;Mate30系列享12期免息》",
        "price": 6299.0000,
        "saleCount": 0
        }]
        }
}

修改“com.atguigu.gulimall.product.controller.SkuInfoController”类,代码如下:

  1. @RequestMapping("/list")
  2. public R list(@RequestParam Map<String, Object> params){
  3. PageUtils page = skuInfoService.queryPageByCondition(params);
  4. return R.ok().put("page", page);
  5. }

修改“com.atguigu.gulimall.product.service.SkuInfoService”类,代码如下:

PageUtils queryPageByCondition(Map<String, Object> params);

修改“com.atguigu.gulimall.product.service.impl.SkuInfoServiceImpl”类,代码如下:

  1. @Override
  2. public PageUtils queryPageByCondition(Map<String, Object> params) {
  3. QueryWrapper<SkuInfoEntity> queryWrapper = new QueryWrapper<>();
  4. String key = (String) params.get("key");
  5. if (!StringUtils.isEmpty(key)){
  6. queryWrapper.and((w)->{
  7. w.eq("sku_id",key).or().like("sku_name",key);
  8. });
  9. }
  10. String catelogId = (String) params.get("catelogId");
  11. if (!StringUtils.isEmpty(catelogId) && !"0".equalsIgnoreCase(catelogId)){
  12. queryWrapper.eq("catalog_id",catelogId);
  13. }
  14. String brandId = (String) params.get("brandId");
  15. if (!StringUtils.isEmpty(brandId) && !"0".equalsIgnoreCase(brandId)){
  16. queryWrapper.eq("brand_id",brandId);
  17. }
  18. String min = (String) params.get("min");
  19. if (!StringUtils.isEmpty(min)){
  20. queryWrapper.ge("price",min);
  21. }
  22. String max = (String) params.get("max");
  23. if (!StringUtils.isEmpty(max)){
  24. try{
  25. BigDecimal bigDecimal = new BigDecimal(max);
  26. if (bigDecimal.compareTo(new BigDecimal("0"))==1){
  27. queryWrapper.le("price",max);
  28. }
  29. }catch (Exception e){
  30. }
  31. }
  32. IPage<SkuInfoEntity> page = this.page(
  33. new Query<SkuInfoEntity>().getPage(params),
  34. queryWrapper
  35. );
  36. return new PageUtils(page);
  37. }


 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Cpp五条/article/detail/462400
推荐阅读
相关标签
  

闽ICP备14008679号