Mall后端基础部分

Mall后端基础部分

1.JSR303数据校验

OSS存储

<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alicloud-oss</artifactId>
</dependency>
1.给Bean添加校验注解import javax.validation.constraints,并定义自己的message提示
	@Email
	@NotNull
	@NotEmpty
	@NotBlank(message = "品牌名必须提交")
	@Future
	@Url//必须是一个Url地址
	@Pattern(regexp = "/^[a-zA-Z]$/",message = "检索首字母必须是正则表达式")
	@Min(value = 0)//最小为【0】
	
2.开启校验注解@Valid
	效果:校验错误会有默认的响应
3.给校验的Bean后面紧跟一个BindingResult,就可以获得到校验的结果,进行自定义封装
	    public R save(@Valid @RequestBody BrandEntity brand, BindingResult result){
	    
4.分组校验(完成多场的复杂校验)
	1)@NotBlank(message = "品牌名必须提交",groups = {AddGroup.class,UpdateGroup.class})
		给校验注解标注什么情况需要进行校验
	2)@Validated({AddGroup.class})
		默认没有指定分组的校验注解@NotBlank,在分组校验情况@Validated({AddGroup.class})下不生效,只有在@Validated不分组的情况下生效

5.自定义校验
	1)编写自定义校验注解
	2)编写自定义校验器
	3)关联自定义校验器和自定义校验注解

添加校验器依赖

<dependency>
    <groupId>javax.validation</groupId>
    <artifactId>validation-api</artifactId>
    <version>1.1.0.Final</version>
</dependency>
 @RequestMapping("/save")
//    @RequiresPermissions("product:brand:save")
    public R save(@Valid @RequestBody BrandEntity brand, BindingResult result){
        if (result.hasErrors()){
            Map<String,String> map = new HashMap<>();
            //获取校验的错误结果
            List<FieldError> fieldErrors = result.getFieldErrors();
            for (FieldError fieldError : fieldErrors) {
                //获取到错误提示
                String message = fieldError.getDefaultMessage();
                //获取错误属性名
                String field = fieldError.getField();
                map.put(field,message);

            }

            return R.error(400,"提交的数据不合法").put("data",map);
        }

		brandService.save(brand);

        return R.ok();
    }

2.统一异常处理

定义统一异常处理

@ControllerAdvice

@Slf4j//答应日志log
//@ResponseBody//以Json发送出去,加ResponseBody
//@ControllerAdvice(basePackages = "com.atguigu.gulimall.product.controller")
@RestControllerAdvice(basePackages = "com.atguigu.gulimall.product.controller")
public class GulimallExceptionControllerAdvice {

//    @ResponseBody//以Json发送出去,加ResponseBody
    //能处理什么异常,数据校验异常
    @ExceptionHandler(value =MethodArgumentNotValidException.class)
    public R handleVaildException(MethodArgumentNotValidException e){
        log.error("数据校验出现问题{},异常类型:{}",e.getMessage(),e.getClass());
        BindingResult bindingResult = e.getBindingResult();
        Map<String,String> errorMap = new HashMap<>();
        List<FieldError> fieldErrors = bindingResult.getFieldErrors();
        for (FieldError fieldError : fieldErrors) {
            String field = fieldError.getField();
            String message = fieldError.getDefaultMessage();
            errorMap.put(field,message);
        }

        return R.error(BizCodeEnume.VAILD_EXCEPTION.getCode(),BizCodeEnume.VAILD_EXCEPTION.getMsg()).put("data", errorMap);
    }

    //统一异常处理
    @ExceptionHandler(value = Throwable.class)
    public R handlerException(Throwable throwable){
        return R.error(BizCodeEnume.UNKNOW_EXCEPTION.getCode(),BizCodeEnume.UNKNOW_EXCEPTION.getMsg());
    }
}

JSR303分组校验

设置分组{AddGroup,UpdateGroup}

自定义校验

@Documented
@Constraint(validatedBy = { ListValueConstraintValidator.class })//确定校验器,可以指定多个校验器实现不同类型的校验
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
@Retention(RUNTIME)
public @interface ListValue {
    String message() default "{com.atguigu.common.valid.ListValue.message}";

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

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

    int[] vals() default { };
}

自定义校验器,实现ConstraintValidator<,>接口

public class ListValueConstraintValidator implements ConstraintValidator<ListValue,Integer> {

    private Set<Integer> set = new HashSet<>();
    //初始化方法
    @Override
    public void initialize(ListValue constraintAnnotation) {

        int[] vals = constraintAnnotation.vals();
        for (int val : vals) {
            set.add(val);
        }

    }

    //判断是否校验成功

    /**
     *
     * @param value 需要校验的值
     * @param context
     * @return
     */
    @Override
    public boolean isValid(Integer value, ConstraintValidatorContext context) {

        return set.contains(value);
    }
}

编写自定义校验信息ValidationMessages.properties

com.atguigu.common.valid.ListValue.message=\u5FC5\u987B\u63D0\u4EA4\u6307\u5B9A\u7684\u503C

3.SPU&&SKU规格参数,销售属性(PK)

SPU:标准化产品单元,商品的最小单元,描述了一个产品的特性基本属性
SKU:销售属性

eg:
	ipX是SPU,MI 10是SPU
	ipX 64G+128 是SKU
	
区别:
	属性是三级分类组织起来的
	规格中有些是可以检索的
	规格参数也是基本属性
	属性的分组也是三级分类
	属性名确定,但是值是每一个商品不同来决定的![](https://img2020.cnblogs.com/blog/1585396/202104/1585396-20210401012404817-1380645494.png)


spu_info-->product_attr_value (SPU)||(SKU)-->sku_info(一个SPU存多个SKU),sku_images

分页查询所有的三级分类对应属性(attr)

使用pageHelper
创建pageutils构造分页参数类,totalCount,totalPage,total
StringUtils.isEmpty(key);<==>key==null and "".equls(key);

查询目录属性值三级分类

@Override
public PageUtils queryPage(Map<String, Object> params, Long catelogId) {
    //没有完整三级分类,
    if (catelogId==0){
        //分页对象
        IPage<AttrGroupEntity> page = this.page(new Query<AttrGroupEntity>().getPage(params),
                new QueryWrapper<AttrGroupEntity>());
        return new PageUtils(page);
    }else {
        //select * from pms_attr_group where catelog_id = ? and(attr_group_id=key or attr_group_name like %key)
        //多字段模糊匹配
        String key = (String) params.get("key");
        QueryWrapper<AttrGroupEntity> wrapper = new QueryWrapper<AttrGroupEntity>().eq("catelog_id", catelogId);
        if (!StringUtils.isEmpty(key)){
            wrapper.and((obj)->{
                obj.eq("attr_group_id", key).or().like("attr_group_name", key);
            });
        }
        //构造模糊查询返回数据,
        Query<AttrGroupEntity> query = new Query<>();
        IPage<AttrGroupEntity> queryPage = query.getPage(params);

        IPage<AttrGroupEntity> page = this.page(queryPage, wrapper);
        return new PageUtils(page);
    }

}

参数查询类

public class Query<T> {

    public IPage<T> getPage(Map<String, Object> params) {
        return this.getPage(params, null, false);
    }

    public IPage<T> getPage(Map<String, Object> params, String defaultOrderField, boolean isAsc) {
        //分页参数
        long curPage = 1;
        long limit = 10;

        if(params.get(Constant.PAGE) != null){
            curPage = Long.parseLong((String)params.get(Constant.PAGE));
        }
        if(params.get(Constant.LIMIT) != null){
            limit = Long.parseLong((String)params.get(Constant.LIMIT));
        }

        //分页对象
        Page<T> page = new Page<>(curPage, limit);

        //分页参数
        params.put(Constant.PAGE, page);

        //排序字段
        //防止SQL注入(因为sidx、order是通过拼接SQL实现排序的,会有SQL注入风险)
        String orderField = SQLFilter.sqlInject((String)params.get(Constant.ORDER_FIELD));
        String order = (String)params.get(Constant.ORDER);


        //前端字段排序
        if(StringUtils.isNotEmpty(orderField) && StringUtils.isNotEmpty(order)){
            if(Constant.ASC.equalsIgnoreCase(order)) {
                return  page.addOrder(OrderItem.asc(orderField));
            }else {
                return page.addOrder(OrderItem.desc(orderField));
            }
        }

        //没有排序字段,则不排序
        if(StringUtils.isBlank(defaultOrderField)){
            return page;
        }

        //默认排序
        if(isAsc) {
            page.addOrder(OrderItem.asc(defaultOrderField));
        }else {
            page.addOrder(OrderItem.desc(defaultOrderField));
        }

        return page;
    }
}

该注解是多级分类,为空时不显示

@JsonInclude(JsonInclude.Include.NON_EMPTY)

级联显示回显(递归)

使用递归查询级联显示,先确定最底层id,然后一步一步查找父节点,最后父节点到达最顶点,即entry.getParentCid=0时,父节点为顶点,

tips:

​ 集合转换数组需要传入数组长度,list.toArray(new 数组类型(list.size());

//[2,25,225]层级关系
@Override
public Long[] findCatelogPath(Long catelogId) {
    List<Long> paths = new ArrayList<>();
    List<Long> parentPath = findParentPath(catelogId, paths);
    //逆序转换
    Collections.reverse(parentPath);
    //列表转数组,list.toArray();
    //cannot be cast to [Ljava.lang.Long,不能将object数组转化成Long
    //<T> T[] toArray(T[] a)要传入数组长度
    return  parentPath.toArray(new Long[parentPath.size()]);
}

//逆序生成的
private List<Long> findParentPath(Long catelogId,List<Long> paths){
    //1.收集当前节点id,catelogId
    paths.add(catelogId);//225
    //查询父节点id
    CategoryEntity entity = this.getById(catelogId);
    if (entity.getParentCid()!=0){
        findParentPath(entity.getParentCid(),paths);
    }
    return paths;

}

MyBatisPlus分页插件

@Configuration//表明是配置
@EnableTransactionManagement//开启能使用事务注解
@MapperScan("com.atguigu.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);
        // 开启 count 的 join 优化,只针对部分 left join
        paginationInterceptor.setCountSqlParser(new JsqlParserCountOptimize(true));
        return paginationInterceptor;
    }
}

品牌和分类(一个品牌对应多个商品分类,一个分类对应多个品牌)多对多关系,创建中间表

category_brand_relation

保存了哪个品牌关联了哪个分类

思路:接口不同,编写方法不同
	增加 品牌_分类关联--品牌表和分类表保存数据
	修改品牌数据(name)--同步关联表中的数据(编写service)--同步其他关联表中的数据
	级联更新category--首先更新自己category分类表--然后跟新关联表

品牌分类关联

CategoryBrandRelationController

/**
     * 获取当前品牌的所有分类列表
     */
//    @RequestMapping(value = "/catelog/list",method = RequestMethod.GET)
    @GetMapping("/catelog/list")
    public R catelogList(@RequestParam(value = "brandId") Long brandId){
        List<CategoryBrandRelationEntity> data = categoryBrandRelationService.list(
                new QueryWrapper<CategoryBrandRelationEntity>()
                        .eq("brand_id",brandId));

        return R.ok().put("data", data);
    }
@Override
    public void saveDetail(CategoryBrandRelationEntity categoryBrandRelation) {
        //品牌名字和分类名字
        Long brandId = categoryBrandRelation.getBrandId();
        Long catelogId = categoryBrandRelation.getCatelogId();

        //查询详细名称,品牌表(小米,苹果)和分类表(手机,配件)
        //1.查询品牌名称
        BrandEntity brandEntity = brandDao.selectById(brandId);
        CategoryEntity categoryEntity = categoryDao.selectById(catelogId);

        categoryBrandRelation.setBrandName(brandEntity.getName());
        categoryBrandRelation.setCatelogName(categoryEntity.getName());

        //保存
//        this.baseMapper.updateById(categoryBrandRelation);
        this.save(categoryBrandRelation);
    }

编写测试属性分组

模糊查询的查找,首先判断是否是id,然后模糊查询

VO编写

value object,值对象,

接收页面传递过来的数据,封装对象

将业务处理完成的对象,封装成页面要用的数据

PO:
	持久对象:
		对应数据库中某个数据表中的一条记录,多个记录可以用PO的集合。PO中不包含任何数据库操作
		
DO:
	领域对象
		就是从现实世界中抽象出来的有形或无形的业务实体
TO:
	数据传输对象:
		不同应用程序之间传输的对象,微服务
DTO

商品属性保存

	@Transactional
    @Override
    public void saveAttr(AttrVo attr) {
        AttrEntity attrEntity = new AttrEntity();
        //封装保存数据,一个一个封装很麻烦,
        // 用BeanUtils工具包中的copyProperties   spring中的
//        attrEntity.setAttrName(attr.getAttrName());
        //前提条件是vo和po属性名是一一对应的
        BeanUtils.copyProperties(attr,attrEntity);
        //保存基本数据
        this.save(attrEntity);
        //保存关联关系
        //封装保存数据
        AttrAttrgroupRelationEntity relationEntity = new AttrAttrgroupRelationEntity();
        relationEntity.setAttrGroupId(attr.getAttrGroupId());
        relationEntity.setAttrId(attrEntity.getAttrId());

        attrAttrgroupRelationDao.insert(relationEntity);
    }

获取规格参数列表

@Override
public PageUtils queryBaseAttr(Map<String, Object> params, Long catelogId) {
    QueryWrapper<AttrEntity> queryWrapper = new QueryWrapper<>();

    if (catelogId!=0){
        queryWrapper.eq("catelog_id",catelogId);
    }
    //检索条件
    String key = (String) params.get("key");
    if (!StringUtils.isEmpty(key)){
        queryWrapper.and((wapper)->{
            wapper.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);
    //得到page获取到的记录
    List<AttrEntity> records = page.getRecords();
    //
    List<AttrRespVo> respVos = records.stream().map((attrEntity) -> {
        AttrRespVo attrRespVo = new AttrRespVo();
        BeanUtils.copyProperties(attrEntity, attrRespVo);

        //设置分组和分组的名字
        //可能是空的
        AttrAttrgroupRelationEntity attrId = attrAttrgroupRelationDao.selectOne(new QueryWrapper<AttrAttrgroupRelationEntity>()
                .eq("attr_id", attrEntity.getAttrId()));
        if (attrId != null) {
            Long attrGroupId = attrId.getAttrGroupId();
            AttrGroupEntity attrGroupEntity = attrGroupDao.selectById(attrGroupId);
            attrRespVo.setGroupName(attrGroupEntity.getAttrGroupName());
        }
        CategoryEntity categoryEntity = categoryDao.selectById(attrEntity.getCatelogId());
        if (categoryEntity != null) {
            attrRespVo.setCatelogName(categoryEntity.getName());
        }
        return attrRespVo;
    }).collect(Collectors.toList());//转化为list集合

    pageUtils.setList(respVos);
    return pageUtils;
}

规格修改

查询

//查询属性信息
public AttrRespVo getAttrInfo(Long attrId) {
    AttrRespVo respVo = new AttrRespVo();
    AttrEntity attrEntity = this.getById(attrId);
    BeanUtils.copyProperties(attrEntity,respVo);

    //去关联表中查询,
    // 设置分组信息
    AttrAttrgroupRelationEntity attrgroupRelationEntity = attrAttrgroupRelationDao.selectOne(new QueryWrapper<AttrAttrgroupRelationEntity>()
            .eq("attr_id", attrId));
    //分组id设置上
    if (attrgroupRelationEntity!=null) {
        respVo.setAttrGroupId(attrgroupRelationEntity.getAttrGroupId());
        AttrGroupEntity attrGroupEntity = attrGroupDao.selectById(attrgroupRelationEntity.getAttrGroupId());
        if (attrGroupEntity!=null) {
            respVo.setGroupName(attrGroupEntity.getAttrGroupName());
        }
    }
    //设置分类信息
    Long catelogId = attrEntity.getCatelogId();
    Long[] catelogPath = categoryService.findCatelogPath(catelogId);
    if (catelogPath!=null){
        respVo.setCatelogPath(catelogPath);
        CategoryEntity categoryEntity = categoryDao.selectById(catelogId);
        if (categoryEntity!=null) {
            respVo.setCatelogName(categoryEntity.getName());
        }
    }
    return respVo;
}

修改

判断是新增还是修改,

@Override
@Transactional
//vo-entity-db
public void updateAttr(AttrRespVo attr) {
    AttrEntity attrEntity = new AttrEntity();
    BeanUtils.copyProperties(attr,attrEntity);
    this.updateById(attrEntity);//先保存到attr数据库

    //修改关联表
    //1.修改分组关联
    AttrAttrgroupRelationEntity attrgroupRelationEntity = new AttrAttrgroupRelationEntity();
    attrgroupRelationEntity.setAttrGroupId(attr.getAttrGroupId());
    attrgroupRelationEntity.setAttrId(attr.getAttrId());

    Integer count = attrAttrgroupRelationDao.selectCount(new QueryWrapper<AttrAttrgroupRelationEntity>().eq("attr_id", attr.getAttrId()));
    if (count>0){
        //修改
        //更新前,跟新后
        attrAttrgroupRelationDao.update(attrgroupRelationEntity, new UpdateWrapper<AttrAttrgroupRelationEntity>()
                .eq("attr_id", attr.getAttrId()));
    }else {
        //新增
        attrAttrgroupRelationDao.insert(attrgroupRelationEntity);
    }
}

tips:mybatis-plusCRUD接口

// 根据 ID 查询
T selectById(Serializable id);
// 根据 entity 条件,查询一条记录
T selectOne(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);

// 查询(根据ID 批量查询)
List<T> selectBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);
// 根据 entity 条件,查询全部记录
List<T> selectList(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
// 查询(根据 columnMap 条件)
List<T> selectByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);
// 根据 Wrapper 条件,查询全部记录
List<Map<String, Object>> selectMaps(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
// 根据 Wrapper 条件,查询全部记录。注意: 只返回第一个字段的值
List<Object> selectObjs(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);

// 根据 entity 条件,查询全部记录(并翻页)
IPage<T> selectPage(IPage<T> page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
// 根据 Wrapper 条件,查询全部记录(并翻页)
IPage<Map<String, Object>> selectMapsPage(IPage<T> page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
// 根据 Wrapper 条件,查询总记录数
Integer selectCount(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);

销售属性

获取分类销售属性
/**
 *获取基础信息
 * 一个当两个用
 *
 */
@GetMapping("/{attrType}/list/{catelogId}")
public R baseAttrList(@RequestParam Map<String,Object> params,
                      @PathVariable("catelogId")Long catelogId,
                      @PathVariable("attrType")String attrType){
    PageUtils page = attrService.queryBaseAttr(params,catelogId,attrType);

    return R.ok().put("page",page);
}
查询分组和属性关联&&删除关联
 /**
     * 根据分组id查找关联的所有基本属性
     *
     * @param attrgroupId
     * @return
     */
    @Override
    public List<AttrEntity> getRelationAttr(Long attrgroupId) {
        List<AttrAttrgroupRelationEntity> entities = attrAttrgroupRelationDao.selectList(new QueryWrapper<AttrAttrgroupRelationEntity>()
                .eq("attr_group_id", attrgroupId));

        List<Long> attrIds = new ArrayList<>();
        for (AttrAttrgroupRelationEntity entity : entities) {
            Long attrId = entity.getAttrId();
            attrIds.add(attrId);
        }

        List<AttrEntity> attrEntities= baseMapper.selectBatchIds(attrIds);

//        //获得所有的属性id
//        List<Long> attrIds = entities.stream().map((attr) -> {
//            return attr.getAttrId();
//        }).collect(Collectors.toList());
//        Collection<AttrEntity> attrEntities = this.listByIds(attrIds);
        return (List<AttrEntity>) attrEntities;
    }
<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>
<!--    遍历的集合,collection="entities"
        item="item",每一个元素称为item
        separator=" OR ":记得加前后空格

-->
查询分组未关联属性
/**
 * 当前分组没有关联的所有属性
 */
@Override
public PageUtils getNoRelationAttr(Map<String, Object> params, Long attrgroupId) {
    //1.当前分组只能关联自己所属的分类里面的所有属性
    AttrGroupEntity attrGroupEntity = attrGroupDao.selectById(attrgroupId);
    Long catelogId = attrGroupEntity.getCatelogId();

    //2.当前分组只能关联别的分组没有引用的属性
    //1)当前分类下的其他分组,其他分类下的分组,多个值
    List<AttrGroupEntity> groupEntities = attrGroupDao.selectList(new QueryWrapper<AttrGroupEntity>()
            .eq("catelog_id", catelogId)
            .ne("attr_group_id",attrgroupId));
    //attrGroupId的集合,实体类通过map映射出所有元素值,
    List<Long> collect = groupEntities.stream().map((item) -> {
        return item.getAttrGroupId();
    }).collect(Collectors.toList());


    //2)当前分组关联的属性
    List<AttrAttrgroupRelationEntity> groupId = attrAttrgroupRelationDao.selectList(new QueryWrapper<AttrAttrgroupRelationEntity>()
            .in("attr_group_id", collect));
    List<Long> attrIds = groupId.stream().map((item) -> {
        return item.getAttrId();
    }).collect(Collectors.toList());


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

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

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

    //获取分页对象,(page,wrapper)
    IPage<AttrEntity> page = this.page(new Query<AttrEntity>().getPage(params), queryWrapper);

    return new PageUtils(page);
}
新增分组与关联属性
@Override
/**
 * 批量添加
 * 使用Stream将对象映射赋值给实体类,再转化为列表,然后通过baseMapper的批量保存方法实现批量保存
 */
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);

}

新增商品

调试会员等级相关接口[product-member]

设置网关

  cloud:
    gateway:
      routes:
        - id: member_route
          uri: lb://gulimall-member
          predicates:
            - Path=/api/member/**
          filters:
            - RewritePath=/api/(?<segment>.*),/$\{segment}

获取分类关联的品牌

@GetMapping("/brands/list")
public R relationBrandsList(@RequestParam(value = "catId",required = true)Long catId){
    List<BrandEntity> vos = categoryBrandRelationService.getBrandsByCatId(catId);

    List<BrandVo> collect = vos.stream().map((item) -> {
        BrandVo brandVo = new BrandVo();
        brandVo.setBrandId(item.getBrandId());
        brandVo.setBrandName(item.getName());
        return brandVo;
    }).collect(Collectors.toList());

    return R.ok().put("data",collect);
}
    @Override
    /**
     * 查询关联表里的品牌信息
     */
    public List<BrandEntity> getBrandsByCatId(Long catId) {
        //查询集合,关联表id集合
        List<CategoryBrandRelationEntity> catelogId = categoryBrandRelationDao.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;
    }
商品新增业务流程分析

spu_info----spu_info_desc----spu_images--spu_comment

设置数据库隔离级别为:read uncommitted(读未提交),数据库可以读到未提交的数据

read commited(读已提交);数据库提交才能读到

@TableName("pms_spu_info_desc")
/**
 * 商品id
 * 不是自增的,需要修改
 */
@TableId(type = IdType.INPUT)
private Long spuId;
/**
 * 新增商品
 *
 * @param vo
 */
@Override
@Transactional
public void saveSpuInfo(SpuSaveVo vo) {
    //1.保存spu的基本信息;spu_info
    SpuInfoEntity spuInfoEntity = new SpuInfoEntity();
    BeanUtils.copyProperties(vo, spuInfoEntity);
    //添加没有的值,时间
    spuInfoEntity.setCreateTime(new Date());
    spuInfoEntity.setUpdateTime(new Date());
    //保存基本信息
    this.saveBaseSpuInfo(spuInfoEntity);

    //2.保存spu的描述图片;spu_info_desc
    List<String> decript = vo.getDecript();
    SpuInfoDescEntity spuInfoDescEntity = new SpuInfoDescEntity();
    spuInfoDescEntity.setSpuId(spuInfoEntity.getId());
    //使用join拼接list集合转化为String  ,xxx.com,xxx.com
    spuInfoDescEntity.setDecript(String.join(",", decript));
    spuInfoDescService.saveSpuInfoDesc(spuInfoDescEntity);

    //3.保存spu的图片集合;spu_images
    List<String> images = vo.getImages();
    //图片很多,需要批量保存;spuId+images(一对多)
    spuImagesService.saveImages(spuInfoEntity.getId(), images);

    //4.保存spu的规格参数product_attr_value
    List<BaseAttrs> baseAttrs = vo.getBaseAttrs();
    List<ProductAttrValueEntity> collect = baseAttrs.stream().map((item) -> {
        ProductAttrValueEntity productAttrValueEntity = new ProductAttrValueEntity();
        productAttrValueEntity.setAttrId(item.getAttrId());

        //查询名称
        AttrEntity byId = attrService.getById(item.getAttrId());
        productAttrValueEntity.setAttrName(byId.getAttrName());
        productAttrValueEntity.setAttrValue(item.getAttrValues());
        productAttrValueEntity.setQuickShow(item.getShowDesc());
        productAttrValueEntity.setSpuId(spuInfoEntity.getId());

        return productAttrValueEntity;
    }).collect(Collectors.toList());
    //批量保存,销售属性
    attrValueService.saveProductAttr(collect);


    //保存spu的积分信息,sms库。spu_bounds
    Bounds bounds = vo.getBounds();
    SpuBoundsTo spuBoundsTo = new SpuBoundsTo();
    BeanUtils.copyProperties(bounds,spuBoundsTo);
    spuBoundsTo.setSpuId(spuInfoEntity.getId());
    R r = couponFeignService.saveSpuBounds(spuBoundsTo);
    if (r.getCode() !=0){
        log.error("远程保存spu信息失败");
    }


    //5.保存当前spu对应的所有sku信息;
    List<Skus> skus = vo.getSkus();//多个
    if (skus != null && skus.size() > 0) {
        //每一个sku信息遍历
        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);
            //5.1sku的基本信息,sku_info
            skuInfoService.saveSkuInfo(skuInfoEntity);

            Long skuId = skuInfoEntity.getSkuId();

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

            //5.2.sku的图片信息,sku——images
            //批量保存图片,使用默认方法
            skuImagesService.saveBatch(imagesEntities);
            //TODO没有图片路径无需保存


            //5.3.sku的销售属性值;sku_sale_attr_value
            //获取请求值,销售属性
            List<Attr> attr = item.getAttr();
            //收集成对应的集合
            List<SkuSaleAttrValueEntity> skuSaleAttrValueEntities = attr.stream().map((a) -> {
                SkuSaleAttrValueEntity skuSaleAttrValueEntity = new SkuSaleAttrValueEntity();
                BeanUtils.copyProperties(a, skuSaleAttrValueService);
                //补全差的值
                skuSaleAttrValueEntity.setSkuId(skuInfoEntity.getSkuId());
                return skuSaleAttrValueEntity;
            }).collect(Collectors.toList());
            skuSaleAttrValueService.saveBatch(skuSaleAttrValueEntities);

            //5.4sku的优惠满减信息;sms库,sku_ladder/sku_full_reduction/member_price
            SkuReductionTo skuReductionTo = new SkuReductionTo();
            BeanUtils.copyProperties(item,skuReductionTo);
            skuReductionTo.setSkuId(skuInfoEntity.getSkuId());
            if (skuReductionTo.getFullCount()>0 || skuReductionTo.getFullPrice().compareTo(new BigDecimal("0")) == 1){
                R r1 = couponFeignService.saveSkuReduction(skuReductionTo);
                if (r1.getCode()!=0){
                    log.error("远程调用sku优惠信息失败");
                }
            }



        });
    }
}
服务之间的调用
@FeignClient("gulimall-coupon")//声明一下调用哪个远程服务
给启动类调用@EnableFeignClients(basePackages = "com.atguigu.gulimall.product.feign")//扫描feign
@FeignClient("gulimall-coupon")//声明一下调用哪个远程服务
public interface CouponFeignService {

    //注意完整路径

    /**
     * 1.CouponFeignService.saveSpuBounds(@RequestBody SpuBoundsTo spuBoundsTo);
     *      1.@RequestBody将这个数据转化为json
     *      2.找到gulimall-coupon服务,给/coupon/spubounds/save发送请求。
     *          将上一步转的对象转为json放在请求体位置,发送请求;
     *      3.对方服务收到请求。实际上收到的是请求体里的json数据
     *          将请求体的json转化为实体类
     *      4.只要json数据是兼容的。双方无需使用同一个to
     */
    @PostMapping("/coupon/spubounds/save")
    R saveSpuBounds(@RequestBody SpuBoundsTo spuBoundsTo);

    @PostMapping("/coupon/skufullreduction/saveinfo")
    R saveSkuReduction(SkuReductionTo skuReductionTo);
}
SPU检索
@Override
public PageUtils queryPageByCondition(Map<String, Object> params) {
    QueryWrapper<SpuInfoEntity> wrapper = new QueryWrapper<>();
    String key = (String) params.get("key");
    //动态判断
    //status = 1 and (id = 1 or name = xx)
    if (StringUtils.isEmpty(key)){
        //箭头函数代表单独的括号括起来
        wrapper.and((w)->{
                w.eq("id",key).or().like("spu_name",key);
        });
    }

    String status = (String) params.get("status");
    if (StringUtils.isEmpty(status)){
        wrapper.eq("publish_status",status);
    }
    String brandId = (String) params.get("brandId");
    //动态判断
    if (StringUtils.isEmpty(brandId)){
        wrapper.eq("brand_id",brandId);
    }
    String catelogId = (String) params.get("catelogId");
    //动态判断
    if (StringUtils.isEmpty(catelogId)){
        wrapper.eq("catelog_id",catelogId);
    }
    IPage<SpuInfoEntity> page = this.page(
            new Query<SpuInfoEntity>().getPage(params),
            wrapper
    );

    return new PageUtils(page);

}
 
 #格式化时间戳\
spring:
  jackson:
    date-format: yyyy-MM-dd HH:mm:ss
sku检索
@Override
public PageUtils queryPageBycondition(Map<String, Object> params) {
    QueryWrapper<SkuInfoEntity> wrapper = new QueryWrapper<>();
    String key = (String) params.get("key");
    if (!StringUtils.isEmpty(key)){
        wrapper.and((w)->{
            w.eq("id",key).or().eq("sku_name",key);
        });
    }
    String catelogId = (String) params.get("catelogId");
    if (!StringUtils.isEmpty(catelogId) && !"0".equalsIgnoreCase(catelogId)){
        wrapper.eq("catelog_id", catelogId);
    }
    String brandId = (String) params.get("brandId");
    if (!StringUtils.isEmpty(brandId)&& !"0".equalsIgnoreCase(brandId)){
        wrapper.eq("brand_id", brandId);
    }
    String min = (String) params.get("min");
    if (!StringUtils.isEmpty(min)){
        wrapper.ge("price",min);
    }
    
    //注意max不能等于0
    String max = (String) params.get("max");
    if (!StringUtils.isEmpty(max)){
        try {
            BigDecimal bigDecimal = new BigDecimal(max);
            if (bigDecimal.compareTo(new BigDecimal(0)) == 1){
                wrapper.le("price",max);
            }

        }catch (Exception e){
            
        }

    }


    IPage<SkuInfoEntity> page = this.page(
            new Query<SkuInfoEntity>().getPage(params),
            wrapper
    );

    return new PageUtils(page);
}

仓库管理服务

@EnableTransactionManagement
@EnableDiscoveryClient
@MapperScan("com.atguigu.gulimall.ware.dao")
@SpringBootApplication
public class GulimallWareApplication {

   public static void main(String[] args) {
      SpringApplication.run(GulimallWareApplication.class, args);
   }

}
合并采购单
@Transactional
@Override
public void mergePurchase(MergeVo mergeVo) {
    Long purchaseId = mergeVo.getPurchaseId();
    //有id合并,无id新建采购单
    if (purchaseId == null){
        PurchaseEntity purchaseEntity = new PurchaseEntity();
        purchaseEntity.setStatus(0);
        purchaseEntity.setCreateTime(new Date());
        purchaseEntity.setUpdateTime(new Date());
        this.save(purchaseEntity);
        //新建的采购单,赋值
        purchaseId=purchaseEntity.getId();
    }
    //id的集合
    List<Long> items = mergeVo.getItems();
    //合并采购单
    Long finalPurchaseId = purchaseId;
    List<PurchaseDetailEntity> collect = items.stream().map((i) -> {
        PurchaseDetailEntity purchaseDetailEntity = new PurchaseDetailEntity();
        purchaseDetailEntity.setId(i);
        purchaseDetailEntity.setPurchaseId(finalPurchaseId);
        purchaseDetailEntity.setStatus(WareConstant.PurchaseDetailStatusEnum.ASSIGNED.getCode());

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

    //合并完成
    detailService.updateBatchById(collect);

    //修改采购单的时间
    PurchaseEntity purchaseEntity = new PurchaseEntity();
    purchaseEntity.setId(purchaseId);
    purchaseEntity.setUpdateTime(new Date());
    this.updateById(purchaseEntity);

}
领取采购单
@Override
public void received(List<Long> ids) {
    //1.确认当前采购单是新建或者已分配状态
    List<PurchaseEntity> collect = ids.stream().map((id) -> {
        PurchaseEntity byId = this.getById(id);
        return byId;

    }).filter(item -> {
        if (item.getStatus() == WareConstant.PurchaseStatusEnum.ASSIGNED.getCode() || item.getStatus() == WareConstant.PurchaseStatusEnum.CREATED.getCode()) {
            return true;
        }
        return false;
    }).map(item->{
        item.setStatus(WareConstant.PurchaseStatusEnum.RECEIVE.getCode());
        return item;
    }).collect(Collectors.toList());
    //2。改变采购单的状态
    this.updateBatchById(collect);

    //3.改变采购项的状态
    collect.forEach(item->{
        //查询采购项byId
        List<PurchaseDetailEntity> entities = detailService.listDetailByPurchaseId(item.getId());
        List<PurchaseDetailEntity> detailEntities = entities.stream().map(i -> {
            PurchaseDetailEntity purchaseDetailEntity = new PurchaseDetailEntity();
            purchaseDetailEntity.setId(i.getId());
            purchaseDetailEntity.setStatus(WareConstant.PurchaseDetailStatusEnum.BUYING.getCode());

            return purchaseDetailEntity;
        }).collect(Collectors.toList());
        detailService.updateBatchById(detailEntities);
    });
完成采购

已经采购数量,实际采购数量

@Override
/**
 * 完成采购
 */
@Transactional
public void done(PurchaseDoneVo doneVo) {
    //改变采购项的状态
    Boolean flag = true;
    List<PurchaseItemDoneVo> items = doneVo.getItems();

    List<PurchaseDetailEntity> updates = new ArrayList<>();
    for (PurchaseItemDoneVo item : items) {
        //未采购成功
        PurchaseDetailEntity purchaseDetailEntity = new PurchaseDetailEntity();

        if (item.getStatus() == WareConstant.PurchaseDetailStatusEnum.HASERROR.getCode()){
            flag = false;
            purchaseDetailEntity.setStatus(item.getStatus());
        }else {
            //采购成功
            purchaseDetailEntity.setStatus(WareConstant.PurchaseDetailStatusEnum.FINISH.getCode());

            //将采购成功的商品入库
            //添加库存数据
            PurchaseDetailEntity byId = detailService.getById(item.getItemId());

            wareSkuService.addStock(byId.getSkuId(),byId.getWareId(),byId.getSkuNum());
        }

        purchaseDetailEntity.setId(item.getItemId());
        updates.add(purchaseDetailEntity);

    }
    detailService.updateBatchById(updates);

    //采购单状态改变
    Long id = doneVo.getId();
    PurchaseEntity purchaseEntity = new PurchaseEntity();
    purchaseEntity.setId(id);
    purchaseEntity.setStatus(flag?WareConstant.PurchaseStatusEnum.FINISH.getCode():WareConstant.PurchaseStatusEnum.HASERROR.getCode());
    purchaseEntity.setUpdateTime(new Date());

    this.updateById(purchaseEntity);
}

方法addStock

@Override
public void addStock(Long skuId, Long wareId, Integer skuNum) {
    //判断,如果没有这个库存记录,新增操作,如果有库存记录,则查询
    //查询库存
    List<WareSkuEntity> wareSkuEntities = this.baseMapper.selectList(new QueryWrapper<WareSkuEntity>().eq("sku_id", skuId).eq("ware_id", wareId));
    if (wareSkuEntities.size()==0 || wareSkuEntities == null){
        WareSkuEntity wareSkuEntity = new WareSkuEntity();
        wareSkuEntity.setSkuId(skuId);
        wareSkuEntity.setWareId(wareId);
        wareSkuEntity.setStock(skuNum);

        this.baseMapper.insert(wareSkuEntity);
    } else {
        //更新
        this.baseMapper.addStock(skuId,wareId,skuNum);
    }

}

wareSkuDao.xml

<update id="addStock">
    update `wms_ware_sku` set stock=stock+#{skuNum} where sku_id=#{skuId} and ware_id=#{wareId}
</update>
调用其他微服务

方案一:通过网关,给网关发送请求,url需要带api

方案二:直接调用其他微服务

@FeignClient("gulimall-product")
public interface  ProductFeignService {

    /**
     * 网关配置方案
     * 1.让所有请求过网关:
     * 1.@FeignClient("gulimall-gateway"):给gulimall-gateway所在的机器发送请求;
     * 2./api/product/skuinfo/info/{skuid};
     * 2.直接让后台服务处理
     * 1.@FeignClient("gulimall-product")
     * 2./product/skuinfo/info/{skuid};
     */
    @RequestMapping("/product/skuinfo/info/{skuId}")
    public abstract R info(@PathVariable("skuId") Long skuId);

}
新增其他微服务调用,获取skuInfo
@Override
public void addStock(Long skuId, Long wareId, Integer skuNum) {
    //判断,如果没有这个库存记录,新增操作,如果有库存记录,则查询
    //查询库存
    List<WareSkuEntity> wareSkuEntities = this.baseMapper.selectList(new QueryWrapper<WareSkuEntity>().eq("sku_id", skuId).eq("ware_id", wareId));
    if (wareSkuEntities.size()==0 || wareSkuEntities == null){
        WareSkuEntity wareSkuEntity = new WareSkuEntity();
        wareSkuEntity.setSkuId(skuId);
        wareSkuEntity.setWareId(wareId);
        wareSkuEntity.setStock(0);

        //如果失败,整个事务无需回滚
        //1.自己catch异常,没有抛出去,所以不需要回滚
        //TODO 异常处理
        try {
            R info = productFeignService.info(skuId);
            Map<String,Object> data = (Map<String, Object>) info.get("skuInfo");
            if (info.getCode() == 0){
                wareSkuEntity.setSkuName((String) data.get("skuName"));
            }
        }catch (Exception e){

        }
        this.baseMapper.insert(wareSkuEntity);
    } else {
        //更新
        this.baseMapper.addStock(skuId,wareId,skuNum);
    }

}

Spu规格维护

@Override
/**
 * 批量保存,先删除旧数据,再保留新数据
 */
@Transactional
public void updateSpuAttr(Long spuId, List<ProductAttrValueEntity> entities) {
    //删除spuId之前对应的所有属性
    this.baseMapper.delete(new QueryWrapper<ProductAttrValueEntity>().eq("spu_id",spuId));

    //插入批量数据
    List<ProductAttrValueEntity> collect = entities.stream().map(item -> {
        item.setSpuId(spuId);
        return item;
    }).collect(Collectors.toList());

    this.saveBatch(collect);
    
}

基础部分总结

posted @ 2021-04-01 01:22  yindong  阅读(244)  评论(0)    收藏  举报