程序员的资源宝库

网站首页 > gitee 正文

2.商品服务

sanyeah 2024-04-13 16:11:28 gitee 3 ℃ 0 评论

一、分类维护-通过Java8 Stream API 获取商品三级分类数据

数据库

实体类

@Data
@TableName("pms_category")
public class CategoryEntity implements Serializable {
    private static final long serialVersionUID = 1L;
    /**
     * 分类id
     */
    @TableId
    private Long catId;
    /**
     * 分类名称
     */
    private String name;
    /**
     * 父分类id
     */
    private Long parentCid;
    /**
     * 层级
     */
    private Integer catLevel;
    /**
     * 是否显示[0-不显示,1显示]
     */
    @TableLogic(value = "1", delval = "0")
    private Integer showStatus;
    /**
     * 排序
     */
    private Integer sort;
    /**
     * 图标地址
     */
    private String icon;
    /**
     * 计量单位
     */
    private String productUnit;
    /**
     * 商品数量
     */
    private Integer productCount;

    @JsonInclude(JsonInclude.Include.NON_EMPTY)
    @TableField(exist = false)
    private List<CategoryEntity> children;
}

实现

要实现树形显示:

 (1).查询出所有分类

 (2).组装成父子的树形结构

  @Autowired
    private CategoryBrandRelationDao categoryBrandRelationDao;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Autowired
    private RedissonClient redissonClient;

@Override
    public List<CategoryEntity> listWithTree() {

        //1、查询出所有分类
        List<CategoryEntity> entities = super.baseMapper.selectList(null);

        //2、组装成父子的树形结构

        //2.1)、找到所有一级分类
        List<CategoryEntity> levelMenus = entities.stream()
                .filter(e -> e.getParentCid() == 0)
            //2.2)、将子级设置到父级中
                .peek((menu) -> menu.setChildren(getChildrens(menu, entities)))
            //2.3)、将父菜单进行排序
                .sorted((menu, menu2) -> {
                    return (menu.getSort() == null ? 0 : menu.getSort()) - (menu2.getSort() == null ? 0 : menu2.getSort());
                })
            //2.4)、最后组合
                .collect(Collectors.toList());

        return levelMenus;
    }

    //递归查找所有菜单的子菜单
    private List<CategoryEntity> getChildrens(CategoryEntity root, List<CategoryEntity> all) {

        return all.stream().filter(categoryEntity -> {
            return categoryEntity.getParentCid().equals(root.getCatId());
        }).peek(categoryEntity -> {
            //1、找到子菜单(递归)
            categoryEntity.setChildren(getChildrens(categoryEntity, all));
        }).sorted((menu, menu2) -> {
            //2、菜单的排序
            return (menu.getSort() == null ? 0 : menu.getSort()) - (menu2.getSort() == null ? 0 : menu2.getSort());
        }).collect(Collectors.toList());

    }

二、分类维护-前端路由规范/跨域之三级分类

1.1路由设置

这一节演示了如何通过renren-fast-vue新建组件和导航目录菜单

1.1.1 商品系统新增侧边导航目录

1.1.2 商品系统新增分类维护菜单

之后,在分类维护中维护商品的三级分类。

1.1.3 前端脚手架路由规范

当点击侧边栏目录新增的分类维护时,会跳转到 /product-category,对应新增菜单设置的路由 product/category.

页面:对应前端项目 src->views->modules->product->category.vue 页面组件

2.1、API网关配置和跨域配置

这一节会解决一个前端向多个后端服务请求的问题(API网关),此外还有网关服务与前端前后分离的跨域问题。

2.1.1 前端工程配置API网关作为唯一接口

打开 static->config->index.js 配置统一请求地址

// api网关作为接口请求地址,由网关分发到不同服务
window.SITE_CONFIG['baseUrl'] = 'http://localhost:88/api';

2.2 将renren-fast接入网关服务配置

这里是因为配置第一步的网关地址后,renren-fast-vue 本身要请求到 renren-fast 的请求也会转到网关,所以这里要配置网关转发到renren-fast的接口。

2.2.1将renren-fast注册为nacos服务

  1. 引入 common依赖,
<dependency>
  <groupId>com.zsy</groupId>
  <artifactId>mall-common</artifactId>
</dependency>
  1. 配置nacos地址,
spring:  
  application:
    name: renren-fast
  cloud:
    nacos:
      discovery:
        server-addr: 192.168.163.131:8848
  1. 主启动类增加开启注解。
@EnableDiscoveryClient
@SpringBootApplication
public class RenrenApplication {
	public static void main(String[] args) {
		SpringApplication.run(RenrenApplication.class, args);
	}
}

2.2.2 网关增加路由断言转发不同服务

mall-gateway application.yaml

前端 /api/** 请求会被转发到renren-fast服务 /renren-fast/**

前端 /api/product/** 请求会被转发到mall-product服务 /product/**

spring:
  application:
    name: mall-gateway
    cloud:
    nacos:
      discovery:
        server-addr: 192.168.163.131:8848
    gateway:
      routes:
      	- id: product_route
          uri: lb://mall-product
          predicates:
            - Path=/api/product/**
          filters:
            - RewritePath=/api/(?<segment>.*),/$\{segment}
      
        - id: admin_route
          uri: lb://renren-fast
          predicates:
            - Path=/api/**
          filters:
            - RewritePath=/api/(?<segment>.*),/renren-fast/$\{segment}

2.3 网关服务配置跨域

package com.zsy.gateway.config;

/**
 * @author: zhangshuaiyin
 * @date: 2021/3/7 12:07
 */
@Configuration
public class MallCorsConfiguration {
    @Bean
    public CorsWebFilter corsWebFilter() {
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();

        CorsConfiguration corsConfiguration = new CorsConfiguration();

        // 配置跨域
        corsConfiguration.addAllowedHeader("*");
        corsConfiguration.addAllowedMethod("*");
        corsConfiguration.addAllowedOrigin("*");
        corsConfiguration.setAllowCredentials(true);

        source.registerCorsConfiguration("/**", corsConfiguration);
        return new CorsWebFilter(source);
    }
}

@梅子酒 如果springboot版本高于2.4的话  corsConfiguration.addAllowedOrigin("*");要替换成corsConfiguration.addAllowedOriginPattern("*");

2.3.1 renren-fast跨域配置删除

删除 src/main/java/io/renren/config/CorsConfig.java 中的配置内容

这里会导致请求头添加重复,导致跨域失败

Access to XMLHttpRequest at 'http://localhost:88/api/sys/login' from origin 'http://localhost:8001' has been blocked by CORS policy: The 'Access-Control-Allow-Origin' header contains multiple values 'http://localhost:8001, http://localhost:8001', but only one is allowed.

三、云存储-创建子模块作为第三方整合模块

创建子模块mall-third-party 整合阿里云OSS等第三方模块

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.zsy</groupId>
        <artifactId>guli-mall</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>
    <artifactId>mall-third-party</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>mall-third-party</name>
    <description>第三方服务整合</description>
    <dependencies>
      <dependency>
        <groupId>com.zsy</groupId>
        <artifactId>mall-common</artifactId>
      </dependency>
      <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alicloud-oss</artifactId>
        <version>2.2.0.RELEASE</version>
      </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

application.yaml

spring:
  cloud:
    nacos:
      discovery:
        server-addr: 192.168.163.131:8848
    alicloud:
      access-key: LTAI4FwvfjSycd1APnuG9bjj
      secret-key: O6xaxyiWfSIitcOkSuK27ju4hXT5Hl
      oss:
        endpoint: oss-cn-beijing.aliyuncs.com
        bucket: gulimall-hello

  application:
    name: mall-third-party

server:
  port: 30000

bootstrap.yaml

spring:
  application:
    name: mall-third-party
  cloud:
    nacos:
      config:
        server-addr: 192.168.163.131:8848
        namespace: 90a6e497-bfb6-4784-8dd6-244c08b0708c
        file-extension: yaml
        extension-configs:
          - data-id: oss.yaml
            group: DEFAULT_GROUP
            refresh: true

服务端签名接口

OssController.java

package com.zsy.third.party.controller;

/**
 * @author: zhangshuaiyin
 * @date: 2021/3/7 19:29
 */
@RestController
public class OssController {

    @Autowired
    OSS ossClient;

    @Value("${spring.cloud.alicloud.oss.endpoint}")
    private String endpoint;
    @Value("${spring.cloud.alicloud.oss.bucket}")
    private String bucket;
    @Value("${spring.cloud.alicloud.access-key}")
    private String accessId;

    /**
     * Oss 获取服务端签名
     * @return
     */
    @RequestMapping("/oss/policy")
    public R policy() {

        // https://gulimall-hello.oss-cn-beijing.aliyuncs.com/hahaha.jpg  host的格式为 bucketname.endpoint
        String host = "https://" + bucket + "." + endpoint;
        // callbackUrl为 上传回调服务器的URL,请将下面的IP和Port配置为您自己的真实信息。
        // String callbackUrl = "http://88.88.88.88:8888";
        String format = new SimpleDateFormat("yyyy-MM-dd").format(new Date());
        // 用户上传文件时指定的前缀。
        String dir = format + "/";

        Map<String, String> respMap = null;
        try {
            long expireTime = 30;
            long expireEndTime = System.currentTimeMillis() + expireTime * 1000;
            Date expiration = new Date(expireEndTime);
            PolicyConditions policyConds = new PolicyConditions();
            policyConds.addConditionItem(PolicyConditions.COND_CONTENT_LENGTH_RANGE, 0, 1048576000);
            policyConds.addConditionItem(MatchMode.StartWith, PolicyConditions.COND_KEY, dir);

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

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

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

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

使用网关服务统一接入

mall-gateway 服务 application.yaml

注意配置规则断言要讲更精确的放在前面

spring:
  application:
    name: mall-gateway
  cloud:
    gateway:
      routes:
        - id: third_party_route
          uri: lb://mall-third-party
          predicates:
            - Path=/api/third-party/**
          filters:
            - RewritePath=/api/third-party/(?<segment>.*),/$\{segment}

四、品牌管理-JSR303数据校验

后端在处理前端传过来的数据时,尽管前端表单已经加了校验逻辑,但是作为严谨考虑,在后端对接口传输的数据做校验也必不可少。

1.开启校验:

实体类上增加校验注解,接口参数前增加@Valid 开启校验

package com.zsy.product.entity;

import javax.validation.constraints.*;
import org.hibernate.validator.constraints.URL;

/**
 * 品牌
 *
 * @author zsy
 * @email 594983498@qq.com
 * @date 2019-10-01 21:08:49
 */
@Data
@TableName("pms_brand")
public class BrandEntity implements Serializable {
    private static final long serialVersionUID = 1L;

    /**
     * 品牌id
     */
    @NotNull(message = "修改必须指定品牌id", groups = {UpdateGroup.class})
    @Null(message = "新增不能指定id", groups = {AddGroup.class})
    @TableId
    private Long brandId;
    /**
     * 品牌名
     */
    @NotBlank(message = "品牌名必须提交", groups = {AddGroup.class, UpdateGroup.class})
    private String name;
    /**
     * 品牌logo地址
     */
    @NotBlank(groups = {AddGroup.class})
    @URL(message = "logo必须是一个合法的url地址", groups = {AddGroup.class, UpdateGroup.class})
    private String logo;
    /**
     * 介绍
     */
    private String descript;
    /**
     * 显示状态[0-不显示;1-显示]
     */
//	@Pattern()
    @NotNull(groups = {AddGroup.class, UpdateStatusGroup.class})
    @ListValue(vals = {0, 1}, groups = {AddGroup.class, UpdateStatusGroup.class})
    private Integer showStatus;
    /**
     * 检索首字母
     */
    @NotEmpty(groups = {AddGroup.class})
    @Pattern(regexp = "^[a-zA-Z]$", message = "检索首字母必须是一个字母", groups = {AddGroup.class, UpdateGroup.class})
    private String firstLetter;
    /**
     * 排序
     */
    @NotNull(groups = {AddGroup.class})
    @Min(value = 0, message = "排序必须大于等于0", groups = {AddGroup.class, UpdateGroup.class})
    private Integer sort;
}
@RequestMapping("/save")
//@RequiresPermissions("product:brand:save")
public R save(@Validated({AddGroup.class}) @RequestBody BrandEntity brand/*,BindingResult result*/) {
    brandService.save(brand);
    return R.ok();
}

JSR303
 1)、给Bean添加校验注解:javax.validation.constraints,并定义自己的message提示
 2)、开启校验功能@Valid
    效果:校验错误以后会有默认的响应;
 3)、给校验的参数bean后紧跟一个BindingResult,就可以获取到校验的结果
 4)、分组校验(多场景的复杂校验)
       1)、    @NotBlank(message = "品牌名必须提交",groups = {AddGroup.class,UpdateGroup.class})
        给校验注解标注什么情况需要进行校验
       2)、@Validated({AddGroup.class})
       3)、默认没有指定分组的校验注解@NotBlank,在分组校验情况@Validated({AddGroup.class})下不生效,只会在@Validated生效;
 5)、自定义校验
    1)、编写一个自定义的校验注解
    2)、编写一个自定义的校验器 ConstraintValidator
    3)、关联自定义的校验器和自定义的校验注解

2.自定义校验器和校验注解

2.1 自定义校验注解

/**
 * 自定义校验注解 声明可以取那些值
 * @author ZSY
 */
@Documented
@Constraint(validatedBy = {ListValueConstraintValidator.class})
@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})
@Retention(RUNTIME)
public @interface ListValue {
    String message() default "{com.zsy.common.valid.ListValue.message}";

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

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

    int[] values() default {};
}

2.2 自定义校验器

package com.zsy.common.valid;

/**
 * @author ZSY
 */
public class ListValueConstraintValidator implements ConstraintValidator<ListValue, Integer> {
    private final Set<Integer> set = new HashSet<>();

    /**
     * 初始化方法
     * 参数:自定义注解的详细信息
     */
    @Override
    public void initialize(ListValue constraintAnnotation) {
        int[] values = constraintAnnotation.values();
        for (int val : values) {
            set.add(val);
        }

    }

    /**
     * 判断是否校验成功
     *
     * @param value   需要校验的值
     * @param context
     * @return
     */
    @Override
    public boolean isValid(Integer value, ConstraintValidatorContext context) {
        return set.contains(value);
    }
}

2.3 创建校验信息提示配置文件

在resource文件下创建:ValidationMessages.properties

com.zsy.common.valid.ListValue.message=必须提交指定的值

五、品牌管理-统一异常处理

统一异常处理类

对于项目中出现的异常,我们通常采用ControllerAdvice的方式进行统一处理,简化代码。

1)、编写异常处理类,使用@ControllerAdvice
2)、使用@ExceptionHandler标注方法可以处理的异常。

package com.zsy.product.exception;

/**
 * 集中处理所有异常
 * @author ZSY
 */
@Slf4j
//@ResponseBody
//@ControllerAdvice(basePackages = "com.zsy.product.controller")
@RestControllerAdvice(basePackages = "com.zsy.product.controller")
public class MallExceptionControllerAdvice {

    @ExceptionHandler(value= MethodArgumentNotValidException.class)
    public R handleValidException(MethodArgumentNotValidException e){
        log.error("数据校验出现问题{},异常类型:{}",e.getMessage(),e.getClass());
        BindingResult bindingResult = e.getBindingResult();

        Map<String,String> errorMap = new HashMap<>();
        bindingResult.getFieldErrors().forEach((fieldError)->{
            errorMap.put(fieldError.getField(),fieldError.getDefaultMessage());
        });
        return R.error(BizCodeEnume.VAILD_EXCEPTION.getCode(),BizCodeEnume.VAILD_EXCEPTION.getMsg()).put("data",errorMap);
    }

    @ExceptionHandler(value = Throwable.class)
    public R handleException(Throwable throwable){

        log.error("错误:",throwable);
        return R.error(BizCodeEnume.UNKNOW_EXCEPTION.getCode(),BizCodeEnume.UNKNOW_EXCEPTION.getMsg());
    }
}

异常代码枚举

package com.zsy.common.exception;

/***
 * 错误码和错误信息定义类
 * 1. 错误码定义规则为5为数字
 * 2. 前两位表示业务场景,最后三位表示错误码。例如:100001。10:通用 001:系统未知异常
 * 3. 维护错误码后需要维护错误描述,将他们定义为枚举形式
 * 错误码列表:
 *  10: 通用
 *      001:参数格式校验
 *  11: 商品
 *  12: 订单
 *  13: 购物车
 *  14: 物流
 * @author ZSY
 */
public enum BizCodeEnum {
    /**
     * 系统未知异常
     */
    UNKNOWN_EXCEPTION(10000, "系统未知异常"),
    /**
     * 参数校验错误
     */
    VALID_EXCEPTION(10001, "参数格式校验失败");
    private final int code;
    private final String msg;
    BizCodeEnum(int code, String msg) {
        this.code = code;
        this.msg = msg;
    }
    public int getCode() {
        return code;
    }
    public String getMsg() {
        return msg;
    }
}

六、基础概念-SPU 和 SKU(数据表关系)SPU

1、SPU

Standard Product Unit 标准化产品单元:是商品信息聚合的最小单位,是一组可复用、易检索的标准化信息的集合,该集合描述了一个产品的特性。

SPU: Standard Product Unit(标准产品单位)

  • SPU 是商品信息聚合的最小单位,是一组可复用、易检索的标准化信息的集合,该集合描述了一个产品的特性。
  • 通俗点讲,属性值、特性相同的商品就可以称为一个 SPU。
  • 例如:iPhone 12就是一个 SPU,与商家,与颜色、款式、套餐都无关。

2、SKU

Stock Keeping Unit 库存量单位:即库存进出计量的基本单元,可以是以件、盒等为单位。SKU这是对于大型连锁超市 DC (配送中心)物流管理的一个必要的方法。现在已经被引申为产品统一编号的简称,每种产品均对应有唯一的SKU号。

SKU: Stock Keeping Unit(库存量单位)

  • SKU 即库存进出计量的单位, 可以是以件、盒、托盘等为单位。
  • SKU 是物理上不可分割的最小存货单元。在使用时要根据不同业态,不同管理模式来处理。
  • 在服装、鞋类商品中使用最多最普遍。
  • 例如: iPhone 12 的颜色(深空灰等),存储容量(64GB 256GB)。

3、举例区分:

iPhoneX 是 SPU、MI8 是 SPU

iPhoneX 64G 黑曜石是 SKU

MI8 8+64G 黑色是 SKU

4、基础属性【规格参数】与销售属性

每个分类下的商品共享规格参数与销售属性。知识有些商品不一定要用这个分类下全部的属性。

  • 属性是以三级分类组织起来的;
  • 规格参数中有些是可以提供检索的;
  • 规格参数也是基本属性,他们具有自己的分组;
  • 属性的分组也是以三级分类组织起来的;
  • 属性名是确定的,但是值是每一个商品不同决定的;

商品的基础属性是SPU的特性,商品的销售属性是SKU的特性。

七、获取分类属性分组

@Override
    public PageUtils queryPage(Map<String, Object> params, Long catelogId) {
        //1.获取key
        String key = (String) params.get("key");

        //2.构造QueryWrapper
        QueryWrapper<AttrGroupEntity> wrapper = new QueryWrapper<AttrGroupEntity>();
        //3.判断
        if (!StringUtils.isEmpty(key)) {
            //4. //select * from pms_attr_group where catelog_id=? and (attr_group_id=key or attr_group_name like %key%)
            wrapper.and((obj) -> {
                obj.eq("attr_group_id",key).or().like("attr_group_name",key);
            });
        }

        //5.如果传过来的三级分类id为0,就查询所有数据
        if (catelogId == 0) {
            IPage<AttrGroupEntity> page = this.page(
                    new Query<AttrGroupEntity>().getPage(params),
                    wrapper
            );
            return new PageUtils(page);
        } else {
            wrapper.eq("catelog_id",catelogId);
            IPage<AttrGroupEntity> page = this.page(new Query<AttrGroupEntity>().getPage(params),wrapper);
            return new PageUtils(page);
        }
    }

 八、返回完整路径

  //[2,29,20]
    @Override
    public Long[] findCatelogPath(Long catelogId) {
        List<Long> paths = new ArrayList<>();
        //递归查询是否还有父节点
        List<Long> parentPath = findParentPath(catelogId, paths);
        //进行一个逆序排列
        Collections.reverse(parentPath);
        return (Long[]) parentPath.toArray(new Long[parentPath.size()]);
    }

    private List<Long> findParentPath(Long catelogId, List<Long> paths) {

        //1、收集当前节点id
        paths.add(catelogId);

        //根据当前分类id查询信息
        CategoryEntity byId = this.getById(catelogId);
        //如果当前不是父分类
        if (byId.getParentCid() != 0) {
            findParentPath(byId.getParentCid(), paths);
        }

        return paths;
    }

 九、品牌分类关联与级联更新

1、引入分页插件

@Configuration
@EnableTransactionManagement        //开启使用
@MapperScan("com.xunqi.gulimall.product.dao")
public class MyBatisConfig {

    //引入分页插件
    @Bean
    public PaginationInterceptor paginationInterceptor() {
        PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
        // 设置请求的页面大于最大页后操作, true调回到首页,false 继续请求  默认false
        paginationInterceptor.setOverflow(true);
        // 设置最大单页限制数量,默认 500 条,-1 不受限制
        paginationInterceptor.setLimit(1000);
        return paginationInterceptor;
    }

}

2、模糊查询

  @Override
    public PageUtils queryPage(Map<String, Object> params) {

        //1、获取key
        String key = (String) params.get("key");
        QueryWrapper<BrandEntity> queryWrapper = new QueryWrapper<>();
        //如果传过来的数据不是空的,就进行多参数查询
        if (!StringUtils.isEmpty(key)) {
            queryWrapper.eq("brand_id",key).or().like("name",key);
        }

        IPage<BrandEntity> page = this.page(
                new Query<BrandEntity>().getPage(params),
                queryWrapper
        );

        return new PageUtils(page);
    }

3、关联分类

@Override
public void saveDetail(CategoryBrandRelationEntity categoryBrandRelation) {
    Long brandId = categoryBrandRelation.getBrandId();
    Long catelogId = categoryBrandRelation.getCatelogId();

    //1、查询品牌详细信息
    BrandEntity brandEntity = brandDao.selectById(brandId);
    //2、查询分类详细信息
    CategoryEntity categoryEntity = categoryDao.selectById(catelogId);

    //将信息保存到categoryBrandRelation中
    categoryBrandRelation.setBrandName(brandEntity.getName());
    categoryBrandRelation.setCatelogName(categoryEntity.getName());

    // 保存到数据库中
    this.baseMapper.insert(categoryBrandRelation);
}

4、级联更新

@Transactional(rollbackFor = Exception.class)
@Override
public void updateDetail(BrandEntity brand) {
    //保证冗余字段的数据一致
    baseMapper.updateById(brand);

    if (!StringUtils.isEmpty(brand.getName())) {
        //同步更新其他关联表中的数据
        categoryBrandRelationService.updateBrand(brand.getBrandId(),brand.getName());

        //TODO 更新其他关联
    }
}

 十、规格测试新增

@Transactional(rollbackFor = Exception.class)
    @Override
    public void saveAttr(AttrVo attr) {
        AttrEntity attrEntity = new AttrEntity();
        BeanUtils.copyProperties(attr,attrEntity);
        //1、保存基本数据
        this.save(attrEntity);

        //2、保存关联关系
        //判断类型,如果是基本属性就设置分组id
        if (attr.getAttrType() == ProductConstant.AttrEnum.ATTR_TYPE_BASE.getCode() && attr.getAttrGroupId() != null) {
            AttrAttrgroupRelationEntity relationEntity = new AttrAttrgroupRelationEntity();
            relationEntity.setAttrGroupId(attr.getAttrGroupId());
            relationEntity.setAttrId(attrEntity.getAttrId());
            relationDao.insert(relationEntity);
        }

    }

 十一、查询规格参数列表

@Override
public PageUtils queryBaseAttrPage(Map<String, Object> params, Long catelogId, String attrType) {

    QueryWrapper<AttrEntity> queryWrapper = new QueryWrapper<AttrEntity>()
            .eq("attr_type","base".equalsIgnoreCase(attrType) ?
                    ProductConstant.AttrEnum.ATTR_TYPE_BASE.getCode() : ProductConstant.AttrEnum.ATTR_TYPE_SALE.getCode());

    //根据catelogId查询信息
    if (catelogId != 0) {
        queryWrapper.eq("catelog_id",catelogId);
    }

    String key = (String) params.get("key");
    if (!StringUtils.isEmpty(key)) {
        //attr_id attr_name
        queryWrapper.and((wrapper) -> {
           wrapper.eq("attr_id",key).or().like("attr_name",key);
        });
    }

    IPage<AttrEntity> page = this.page(
            new Query<AttrEntity>().getPage(params),
            queryWrapper
    );

    PageUtils pageUtils = new PageUtils(page);
    List<AttrEntity> records = page.getRecords();

    List<AttrRespVo> respVos = records.stream().map((attrEntity) -> {
        AttrRespVo attrRespVo = new AttrRespVo();
        BeanUtils.copyProperties(attrEntity, attrRespVo);

        //设置分类和分组的名字
        if ("base".equalsIgnoreCase(attrType)) {
            AttrAttrgroupRelationEntity relationEntity =
                    relationDao.selectOne(new QueryWrapper<AttrAttrgroupRelationEntity>().eq("attr_id",attrEntity.getAttrId()));
            if (relationEntity != null && relationEntity.getAttrGroupId() != null) {
                AttrGroupEntity attrGroupEntity = attrGroupDao.selectById(relationEntity.getAttrGroupId());
                attrRespVo.setGroupName(attrGroupEntity.getAttrGroupName());
            }

        }

        CategoryEntity categoryEntity = categoryDao.selectById(attrEntity.getCatelogId());
        if (categoryEntity != null) {
            attrRespVo.setCatelogName(categoryEntity.getName());

        }
        return attrRespVo;
    }).collect(Collectors.toList());

    pageUtils.setList(respVos);
    return pageUtils;

}

 十二、回显规格

@Override
public AttrRespVo getAttrInfo(Long attrId) {

    //查询详细信息
    AttrEntity attrEntity = this.getById(attrId);

    //查询分组信息
    AttrRespVo respVo = new AttrRespVo();
    BeanUtils.copyProperties(attrEntity,respVo);

    //判断是否是基本类型
    if (attrEntity.getAttrType() == ProductConstant.AttrEnum.ATTR_TYPE_BASE.getCode()) {
        //1、设置分组信息
        AttrAttrgroupRelationEntity relationEntity = relationDao.selectOne
                (new QueryWrapper<AttrAttrgroupRelationEntity>().eq("attr_id", attrId));
        if (relationEntity != null) {
            respVo.setAttrGroupId(relationEntity.getAttrGroupId());
            //获取分组名称
            AttrGroupEntity attrGroupEntity = attrGroupDao.selectById(relationEntity.getAttrGroupId());
            if (attrGroupEntity != null) {
                respVo.setGroupName(attrGroupEntity.getAttrGroupName());
            }
        }
    }

    //2、设置分类信息
    Long catelogId = attrEntity.getCatelogId();
    Long[] catelogPath = categoryService.findCatelogPath(catelogId);

    respVo.setCatelogPath(catelogPath);
    CategoryEntity categoryEntity = categoryDao.selectById(catelogId);
    if (categoryEntity != null) {
        respVo.setCatelogName(categoryEntity.getName());
    }

    return respVo;
}

 十三、修改规格

@Transactional(rollbackFor = Exception.class)
@Override
public void updateAttrById(AttrVo attr) {

    AttrEntity attrEntity = new AttrEntity();
    BeanUtils.copyProperties(attr,attrEntity);

    this.updateById(attrEntity);

    if (attrEntity.getAttrType() == ProductConstant.AttrEnum.ATTR_TYPE_BASE.getCode()) {
        //1、修改分组关联
        AttrAttrgroupRelationEntity relationEntity = new AttrAttrgroupRelationEntity();
        relationEntity.setAttrGroupId(attr.getAttrGroupId());
        relationEntity.setAttrId(attr.getAttrId());

        Integer count = relationDao.selectCount(new QueryWrapper<AttrAttrgroupRelationEntity>()
                .eq("attr_id", attr.getAttrId()));

        if (count > 0) {
            relationDao.update(relationEntity,
                    new UpdateWrapper<AttrAttrgroupRelationEntity>().eq("attr_id",attr.getAttrId()));
        } else {
            relationDao.insert(relationEntity);
        }
    }

}

 十四、查询、删除分组关联属性

1.查询

/**
 * 根据分组id找到关联的所有属性
 * @param attrgroupId
 * @return
 */
@Override
public List<AttrEntity> getRelationAttr(Long attrgroupId) {

    List<AttrAttrgroupRelationEntity> entities = relationDao.selectList
            (new QueryWrapper<AttrAttrgroupRelationEntity>().eq("attr_group_id", attrgroupId));

    List<Long> attrIds = entities.stream().map(AttrAttrgroupRelationEntity::getAttrId).collect(Collectors.toList());

    //根据attrIds查找所有的属性信息
    //Collection<AttrEntity> attrEntities = this.listByIds(attrIds);

    //如果attrIds为空就直接返回一个null值出去
    if (attrIds.size() == 0) {
        return null;
    }

    return this.baseMapper.selectBatchIds(attrIds);
}

 2.删除

@Override
public void deleteRelation(AttrGroupRelationVo[] vos) {
    //relationDao.delete(new QueryWrapper<>().eq("attr_id",1L).eq("attr_group_id",1L));

    List<AttrAttrgroupRelationEntity> entities = Arrays.stream(vos).map((item) -> {
        AttrAttrgroupRelationEntity relationEntity = new AttrAttrgroupRelationEntity();
        BeanUtils.copyProperties(item, relationEntity);
        return relationEntity;
    }).collect(Collectors.toList());

    relationDao.deleteBatchRelation(entities);
}

 3.deleteBatchRelation(entities);

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


<delete id="deleteBatchRelation">
        DELETE FROM pms_attr_attrgroup_relation WHERE
        <foreach collection="entities" item="item" separator=" OR ">
            (attr_id = #{item.attrId} AND attr_group_id = #{item.attrGroupId})
        </foreach>
    </delete>

十五、获取当前分组没有被关联的所有属性

/**
 * 获取当前分组没有被关联的所有属性
 * @param params
 * @param attrgroupId
 * @return
 */
@Override
public PageUtils getNoRelationAttr(Map<String, Object> params, Long attrgroupId) {

    //1、当前分组只能关联自己所属的分类里面的所有属性
    AttrGroupEntity attrGroupEntity = attrGroupDao.selectById(attrgroupId);
    //获取当前分类的id
    Long catelogId = attrGroupEntity.getCatelogId();

    //2、当前分组只能关联别的分组没有引用的属性
    //2.1)、当前分类下的其它分组
    List<AttrGroupEntity> groupEntities = attrGroupDao.selectList(new QueryWrapper<AttrGroupEntity>()
            .eq("catelog_id", catelogId));

    //获取到所有的attrGroupId
    List<Long> collect = groupEntities.stream().map((item) -> {
        return item.getAttrGroupId();
    }).collect(Collectors.toList());


    //2.2)、这些分组关联的属性
    List<AttrAttrgroupRelationEntity> groupId = relationDao.selectList
            (new QueryWrapper<AttrAttrgroupRelationEntity>().in("attr_group_id", collect));

    List<Long> attrIds = groupId.stream().map(AttrAttrgroupRelationEntity::getAttrId).collect(Collectors.toList());

    //2.3)、从当前分类的所有属性移除这些属性
    QueryWrapper<AttrEntity> queryWrapper = new QueryWrapper<AttrEntity>()
            .eq("catelog_id", catelogId).eq("attr_type",ProductConstant.AttrEnum.ATTR_TYPE_BASE.getCode());

    if (attrIds.size() > 0) {
        queryWrapper.notIn("attr_id", attrIds);
    }

    //判断是否有参数进行模糊查询
    String key = (String) params.get("key");
    if (!StringUtils.isEmpty(key)) {
        queryWrapper.and((w) -> {
            w.eq("attr_id",key).or().like("attr_name",key);
        });
    }
    IPage<AttrEntity> page = this.page(new Query<AttrEntity>().getPage(params), queryWrapper);

    PageUtils pageUtils = new PageUtils(page);


    return pageUtils;
}

 十六、批量添加属性与分组关联关系

/**
     * 批量添加属性与分组关联关系
     * @param vos
     */
    @Override
    public void saveBatch(List<AttrGroupRelationVo> vos) {
        List<AttrAttrgroupRelationEntity> collect = vos.stream().map((item) -> {
            AttrAttrgroupRelationEntity relationEntity = new AttrAttrgroupRelationEntity();
            BeanUtils.copyProperties(item, relationEntity);
            return relationEntity;
        }).collect(Collectors.toList());

        this.saveBatch(collect);
    }

 十七、获取分类关联的品牌

@Override
public List<BrandEntity> getBrandsByCatId(Long catId) {

    List<CategoryBrandRelationEntity> catelogId = relationDao.selectList(new QueryWrapper<CategoryBrandRelationEntity>().eq("catelog_id", catId));

    List<BrandEntity> collect = catelogId.stream().map(item -> {
        Long brandId = item.getBrandId();
        //查询品牌的详情
        BrandEntity byId = brandService.getById(brandId);
        return byId;
    }).collect(Collectors.toList());

    return collect;
}

 十八、根据分类id查询出所有的分组以及这些组里面的属性

/**
     * 根据分类id查询出所有的分组以及这些组里面的属性
     * @param catelogId
     * @return
     */
    @Override
    public List<AttrGroupWithAttrsVo> getAttrGroupWithAttrsByCatelogId(Long catelogId) {

        //1、查询分组信息
        List<AttrGroupEntity> attrGroupEntities = this.list(new QueryWrapper<AttrGroupEntity>().eq("catelog_id", catelogId));

        //2、查询所有属性
        List<AttrGroupWithAttrsVo> collect = attrGroupEntities.stream().map(group -> {
            AttrGroupWithAttrsVo attrGroupWithAttrsVo = new AttrGroupWithAttrsVo();
            BeanUtils.copyProperties(group,attrGroupWithAttrsVo);

            List<AttrEntity> attrs = attrService.getRelationAttr(attrGroupWithAttrsVo.getAttrGroupId());
            attrGroupWithAttrsVo.setAttrs(attrs);

            return attrGroupWithAttrsVo;
        }).collect(Collectors.toList());

        return collect;
    }
 /**
     * 根据分组id找到关联的所有属性
     * @param attrgroupId
     * @return
     */
    @Override
    public List<AttrEntity> getRelationAttr(Long attrgroupId) {

        List<AttrAttrgroupRelationEntity> entities = relationDao.selectList
                (new QueryWrapper<AttrAttrgroupRelationEntity>().eq("attr_group_id", attrgroupId));

        List<Long> attrIds = entities.stream().map(AttrAttrgroupRelationEntity::getAttrId).collect(Collectors.toList());

        //根据attrIds查找所有的属性信息
        //Collection<AttrEntity> attrEntities = this.listByIds(attrIds);

        //如果attrIds为空就直接返回一个null值出去
        if (attrIds.size() == 0) {
            return null;
        }

        return this.baseMapper.selectBatchIds(attrIds);
    }

 十九、新增商品

   /**
     *  //TODO:高级部分完善后续
     * @param vo 新增商品
     */
    @GlobalTransactional(rollbackFor = Exception.class)
    //@Transactional(rollbackFor = Exception.class)
    @Override
    public void savesupInfo(SpuSaveVo vo) {

        //1、保存spu基本信息:pms_spu_info
        SpuInfoEntity spuInfoEntity = new SpuInfoEntity();
        BeanUtils.copyProperties(vo,spuInfoEntity);
        spuInfoEntity.setCreateTime(new Date());
        spuInfoEntity.setUpdateTime(new Date());
        this.saveBaseSpuInfo(spuInfoEntity);

        //2、保存spu的描述图片:pms_spu_info_desc
        List<String> decript = vo.getDecript();
        SpuInfoDescEntity spuInfoDescEntity = new SpuInfoDescEntity();
        spuInfoDescEntity.setSpuId(spuInfoEntity.getId());
        spuInfoDescEntity.setDecript(String.join(",",decript));
        spuInfoDescService.saveSpuInfoDesc(spuInfoDescEntity);

        //3、保存spu的图片集:pms_spu_images
        List<String> images = vo.getImages();
        spuImagesService.saveImages(spuInfoEntity.getId(),images);

        //4、保存spu的规格参数:pms_product_attr_value
        List<BaseAttrs> baseAttrs = vo.getBaseAttrs();
        List<ProductAttrValueEntity> collect = baseAttrs.stream().map(attr -> {
            ProductAttrValueEntity valueEntity = new ProductAttrValueEntity();
            valueEntity.setAttrId(attr.getAttrId());

            //查询attr属性名
            AttrEntity byId = attrService.getById(attr.getAttrId());

            valueEntity.setAttrName(byId.getAttrName());
            valueEntity.setAttrValue(attr.getAttrValues());
            valueEntity.setQuickShow(attr.getShowDesc());
            valueEntity.setSpuId(spuInfoEntity.getId());
            return valueEntity;
        }).collect(Collectors.toList());
        productAttrValueService.saveProductAttr(collect);


        //5、保存spu的积分信息:gulimall_sms--->sms_spu_bounds
        Bounds bounds = vo.getBounds();
        SpuBoundTo spuBoundTo = new SpuBoundTo();
        BeanUtils.copyProperties(bounds,spuBoundTo);
        spuBoundTo.setSpuId(spuInfoEntity.getId());
        R r = couponFeignService.saveSpuBounds(spuBoundTo);

        if (r.getCode() != 0) {
            log.error("远程保存spu积分信息失败");
        }

        //5、保存当前spu对应的所有sku信息:pms_sku_info
        //5、1)、sku的基本信息:pms_sku_info
        List<Skus> skus = vo.getSkus();
        if(skus!=null && skus.size()>0){
            skus.forEach(item->{
                String defaultImg = "";
                for (Images image : item.getImages()) {
                    if(image.getDefaultImg() == 1){
                        defaultImg = image.getImgUrl();
                    }
                }

                SkuInfoEntity skuInfoEntity = new SkuInfoEntity();
                BeanUtils.copyProperties(item,skuInfoEntity);
                skuInfoEntity.setBrandId(spuInfoEntity.getBrandId());
                skuInfoEntity.setCatalogId(spuInfoEntity.getCatalogId());
                skuInfoEntity.setSaleCount(0L);
                skuInfoEntity.setSpuId(spuInfoEntity.getId());
                skuInfoEntity.setSkuDefaultImg(defaultImg);
                skuInfoService.saveSkuInfo(skuInfoEntity);

                Long skuId = skuInfoEntity.getSkuId();

                List<SkuImagesEntity> imagesEntities = item.getImages().stream().map(img -> {
                    SkuImagesEntity skuImagesEntity = new SkuImagesEntity();
                    skuImagesEntity.setSkuId(skuId);
                    skuImagesEntity.setImgUrl(img.getImgUrl());
                    skuImagesEntity.setDefaultImg(img.getDefaultImg());
                    return skuImagesEntity;
                }).filter(entity -> {
                    //返回true就是需要,false就是剔除
                    return !StringUtils.isEmpty(entity.getImgUrl());
                }).collect(Collectors.toList());

                //5、2)、sku的图片信息:pms_sku_images
                skuImagesService.saveBatch(imagesEntities);

                //5、3)、sku的销售属性:pms_sku_sale_attr_value
                List<Attr> attr = item.getAttr();
                List<SkuSaleAttrValueEntity> skuSaleAttrValueEntities = attr.stream().map(a -> {
                    SkuSaleAttrValueEntity skuSaleAttrValueEntity = new SkuSaleAttrValueEntity();
                    BeanUtils.copyProperties(a, skuSaleAttrValueEntity);
                    skuSaleAttrValueEntity.setSkuId(skuId);
                    return skuSaleAttrValueEntity;
                }).collect(Collectors.toList());

                skuSaleAttrValueService.saveBatch(skuSaleAttrValueEntities);

                //5、4)、sku的优惠、满减等信息:gulimall_sms--->sms_sku_ladder、sms_sku_full_reduction、sms_member_price
                SkuReductionTo skuReductionTo = new SkuReductionTo();
                BeanUtils.copyProperties(item,skuReductionTo);
                skuReductionTo.setSkuId(skuId);
                if (skuReductionTo.getFullCount() > 0 || skuReductionTo.getFullPrice().compareTo(BigDecimal.ZERO) > 0) {
                    R r1 = couponFeignService.saveSkuReduction(skuReductionTo);
                    if (r1.getCode() != 0) {
                        log.error("远程保存sku积分信息失败");
                    }
                }
            });
        }

    }
/*保存图片信息*/
  @Override
    public void saveImages(Long id, List<String> images) {

        if (images == null || images.size() == 0) {

        } else {
            List<SpuImagesEntity> collect = images.stream().map(img -> {
                SpuImagesEntity spuImagesEntity = new SpuImagesEntity();
                spuImagesEntity.setSpuId(id);
                spuImagesEntity.setImgUrl(img);
                return spuImagesEntity;
            }).collect(Collectors.toList());

            this.saveBatch(collect);
        }

    }

 二十、SPU、SKU检索

@Override
public PageUtils queryPageByCondtion(Map<String, Object> params) {

    QueryWrapper<SpuInfoEntity> queryWrapper = new QueryWrapper<>();

    String key = (String) params.get("key");
    if (!StringUtils.isEmpty(key)) {
        queryWrapper.and((wrapper) -> wrapper.eq("id",key).or().like("spu_name",key));
    }

    String status = (String) params.get("status");
    if (!StringUtils.isEmpty(status)) {
        queryWrapper.eq("publish_status",status);
    }

    String brandId = (String) params.get("brandId");
    if (!StringUtils.isEmpty(brandId) && !"0".equalsIgnoreCase(brandId)) {
        queryWrapper.eq("brand_id",brandId);
    }

    String catelogId = (String) params.get("catelogId");
    if (!StringUtils.isEmpty(catelogId) && !"0".equalsIgnoreCase(catelogId)) {
        queryWrapper.eq("catalog_id",catelogId);
    }

    IPage<SpuInfoEntity> page = this.page(new Query<SpuInfoEntity>().getPage(params), queryWrapper);

    return new PageUtils(page);
}

 

Tags:

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表