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
区别:
属性是三级分类组织起来的
规格中有些是可以检索的
规格参数也是基本属性
属性的分组也是三级分类
属性名确定,但是值是每一个商品不同来决定的
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);
}
基础部分总结


浙公网安备 33010602011771号