P15 对将要使用的数据库的表进行介绍
介绍了如何使用PowerDesigner
所有数据库设计的最大特点:所有的表之间关系再复杂不建立外键,在电商系统里面数据量超大,做外键关联是一个非常耗费数据库性能的操作。
在Docker中每一个安装的进项最终启动的这些容器,docker 一启动默认都是不启动的---->使用sudo docker update redis --restart=always。
创建数据库使用utf8mb4,能够兼容utf8和解决一些乱码问题。
P16 使用人人开源搭建后台管理系统
使用搭建好的脚手架工程,稍加改造就能够给我们使用了--->人人开源---->快速访问人人开源。
renren-fast为后台管理系统,renren-fast-vue为后台管理系统的前端工程,renren-generator为代码生成器,配套为一个前后分离的管理系统。
使用方式直接克隆----->git clone 地址。将生成好的项目的.git文件删掉,然后直接粘在自己的项目里使用。
renren-fast 里面有后台管理的数据库,记得创建好,接着修改application.dev配置文件,改数据库配置就行了。
后端项目需要跟前端进行对接,使用VSCode,并且安装好需要的插件(Node.js),将Node.js配置好之后来到VUE的终端,由于项目是第一次运行,需要下载前端依赖的所有组件,使用npm install命令
P17 使用逆向生成器
将代码生成器克隆然后导入项目(IDEA),在application修改配置,要连上哪个数据库都要配置好。将.properties配置文件修改成我们要用的就行了。
配置好数据源,mapper扫描路径,以及主键自增
P24-P26
使用Nacos配置中心动态获取配置:
加依赖nacos-config,创建bootstrap.properties配置文件,配置好注册中心的名字和地址端口号,同理,要在Nacos上创建配置,默认是服务名称加.properties,但是仅完成这操作还不行,要在需要获取配置的类上添加注解@RefreshScope注解才算完成、
P45递归树形结构数据获取
查出所以一级分类(parentId==0),为实体类多添加一个属性(后面会接触Vo)为List<CategoryEntity> children 表示子分类
//查出所有分类以及子分类,以树形结构组装起来
public List<CategoryEntity> listWithTree() {
//1、查出所有的分类数据
List<CategoryEntity> entities = baseMapper.selectList(null);
//2、组装成父子的树形结构
//2.1 找到所有的一级分类,一级分类的特征是ParentCid==0
List<CategoryEntity> entities1 = entities.stream().filter(categoryEntity -> categoryEntity.getParentCid() == 0)
.map((menu)->{
menu.setChildren(getChildrens(menu,entities));
return menu;
}).sorted((menu1,menu2)->{
return (menu1.getSort() ==null ? 0 :menu1.getSort()) - (menu2.getSort() ==null ? 0 :menu2.getSort());
})
.collect(Collectors.toList());
return entities1;
}
private List<CategoryEntity> getChildrens(CategoryEntity root,List<CategoryEntity> all){
//通过递归的方式找出所有的子菜单
List<CategoryEntity> children = all.stream().filter((categoryEntity) -> {
return categoryEntity.getParentCid().equals(root.getCatId());
}).map((categoryEntity) -> {
//1、找到子菜单
categoryEntity.setChildren(getChildrens(categoryEntity, all));
return categoryEntity;
}).sorted((menu1, menu2) -> {
//2、菜单排序
return (menu1.getSort() ==null ? 0 :menu1.getSort()) - (menu2.getSort() ==null ? 0 :menu2.getSort());
}).collect(Collectors.toList());
return children;
}
完成了前端代码的编写之后,报错,为什么?因为访问的路径不是我想要的路径,怎么解决?
在前端代码static-config-index.jsp 中定义了api接口请求地址,要将其改为网关的地址。
登陆界面的时候验证码被拦截了,怎么解决?
将验证码转给人人-fast,将人人fast注册到注册中心,接着轮到网关
- id: admin_route
uri: lb://renren-fast
predicates:
- Path=/api/**
filters:
- RewritePath=/api/(?<segment>.*),/renren-fast/$\{segment}
lb表示负载均衡到某个服务,看清楚是服务。
只要是前端项目,都要带上/api的前缀,上述表示只要有api前缀,都要先路由到我们的renren-fast这个服务中。
接下来又遇到问题,获取验证码的请求地址并不是真正的地址,需要我们再讲/api转换为renren-fast,所以就有上面的filters,讲api直接转换为renren-fast。
遇到问题,登陆失败,请求由于跨域被拦截(CORS),浏览器默认是拒绝跨域请求的,如何解决?
解决跨域:配置当前请求允许跨域,在网关服务里配置过滤器,添加返回值类型为CorsWebFilter
要注意:renren-fast本身也配置了跨域,要注释掉,否则会报错
####什么是跨域?
跨域是指浏览器不能执行其他网站的脚本。它是由浏览器的同源策略造成的,是浏览器对JavaScript施加的安全限制。
同源策略:是指协议、域名、端口都要相同,其中一个不同都会产生跨域。
@Configuration
public class GulimallCorsConfiguration {
@Bean
public CorsWebFilter corsWebFilter(){
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration corsConfiguration = new CorsConfiguration();
//跨域配置
corsConfiguration.addAllowedHeader("*");
corsConfiguration.addAllowedMethod("*");
corsConfiguration.addAllowedOrigin("*");
corsConfiguration.setAllowCredentials(true);
//registerCorsConfiguration注册跨域的配置 /**任意路径都要进行跨域配置
source.registerCorsConfiguration("/**",corsConfiguration);
//参数是跨域的配置信息
return new CorsWebFilter(source);
/*
new CorsWebFilter需要跨域的配置信息,所以需要source,source注册跨域配置需要跨域配置信息,所以有了以上的代码
*/
}
}
接下来通过获取数据库的数据将数据展示为树形三级分类
首先,我现在发送请求发出的地址是发送到renren-fast这里的,因为上面配置了网关的问题,如何解决?再配置一个网关到product服务
- id: product_route
uri: lb://gulimall-product
predicates:
- Path=/api/product/**
filters:
- RewritePath=/api/(?<segment>.*),/$\{segment}
注意:要把精确高的路由放在前面
后端的数据已经准备好了,接下来写前端 ,前端懒得粘了,太长了。
P50的逻辑删除
@RequestBody:获取请求体,必须发送POST请求,POST请求才有请求体。
SPringMVC自动将请求体的数据(json),转为对应的对象。
在java代码中自定义删除节点方法,删除之前需要检查被删除的菜单有没有被引用,现在做不了,将这个需求设置为待办事项,怎么设置?
//TODO 1、检查当前删除的菜单有没有被引用
在IDEA中6:TODO有显示待办事项
开发中常用的是逻辑删除。什么是逻辑删除?
使用某一个字段作为标志位,来表示数据是否被删除。在Mybatis如何使用逻辑删除?在配置文件中添加
mybatis-plus:
mapper-locations: classpath:/mapper/**/*.xml
global-config:
db-config:
id-type: auto
logic-delete-value: 1 代表已删除
logic-not-delete-value: 0 代表没删除
2、 配置组件bean,3.1之后不用,这里直接跳过
3、在实体类上加上逻辑删除注解@TableLogic
/**
* 是否显示[0-不显示,1显示]
*/
@TableLogic(value = "1",delval = "0")
private Integer showStatus;
完成,接下来让IDEA在控制台打印出SQL语句,怎么打印?调整日志级别。
logging:
level:
com.atguigu.gulimall.product: debug
P51删除效果细化---前端部分 P52新增效果完成 P53基本修改功能 P54拖拽效果 P55拖拽数据收集 P56拖拽功能完成 P57设置开关按钮 P58节点删除功能 P59品牌管理功能 P60效果优化与快速显示开关
如果前端需要用到数据库的数据,不能写数据库的字段名,要写的是java实体类相对应的属性。。。
P61品牌logo的文件上传功能
微服务环境下,获取文件,不要把文件直接存储在服务中,统一存储在文件系统,可以选择自己搭建,也可以选择云存储,这里使用的是阿里云的对象存储OSS。
阿里云对象存储-服务端签名后直传:
把账号密码等操作信息存在自己的服务里,浏览器想要给阿里云上传数据,上传之前先找服务器要到一个Policy(上传策略),服务器利用阿里云的账号密码生成一个防伪的签名,签名里面包含访问阿里云这一次的授权令牌以及要上传给阿里云的哪个地址哪个位置等,前端带着签名以及要上传的文件交给阿里云,阿里云可以来验证这防伪签名是否正确,正确就接受。
如何使用对象存储 ?
1、引入oss-starter 依赖 2、配置阿里云子账号的key,endpoint相关信息
3、使用OSSClient,将其注入进来
spring:
cloud:
alicloud:
access-key: LTAI5t9fNvJrFZGQVqBKmxcz
secret-key: E90JiXtwh2tRctydHUjSjAO1AM2IRF
oss:
endpoint: oss-cn-shenzhen.aliyuncs.com
bucket: gulimall-dankai
专门创建第三方服务整合这个功能,详情参考IDEA
P64 前后端连调测试上传
在VSCode中的src/assets/components/upload中,需要用到直接粘贴,需要修改阿里云的Backet域名:action="http://gulimall-dankai.oss-cn-shenzhen.aliyuncs.com" 。single是单文件上传,multiUpload是多文件上传,这两个是组件,使用的时候需要import导入,不懂的再参考P64。
P65表单校验 自定义校验器
p66 服务端进行校验 使用JSR303数据校验
* 3、JSR303
详情参考:javax.validation.Constraint;
* 1)、给Bean添加校验注解:javax.validation.constraints,并定义自己的message提示
* 2)、开启校验功能@Valid
* 效果:校验错误以后会有默认的响应;
* 3)、给校验的bean后紧跟一个BindingResult,就可以获取到校验的结果
当已有的注解不能满足业务需求时,可以添加@Pattern在实体类上,这个注解可以自己写正则表达式
* 4)、分组校验(多场景的复杂校验)
* 1)、 @NotBlank(message = "品牌名必须提交",groups = {AddGroup.class,UpdateGroup.class})
* 给校验注解标注什么情况需要进行校验
* 2)、@Validated({AddGroup.class})
* 3)、默认没有指定分组的校验注解@NotBlank,在分组校验情况@Validated({AddGroup.class})下不生效,只会在@Validated生效;
@NotNull(message = "修改必须有品牌ID",groups = {UpdateGroup.class})
@Null(message ="新增不能指定品牌ID" ,groups = {AddGroup.class})
private Long brandId;
P67统一进行异常处理
新建exception包以及类,在类上添加上注解@RestControllerAdvice
@ExceptionHandler(value= MethodArgumentNotValidException.class)
类中肯定要有方法来处理业务,这个注解就是来告诉springmvc这个方法能处理哪些异常。
不同的异常错误应该有不同的状态码以及信息来提示,这个可以自定义一个枚举类型的类放在common里面
@RestControllerAdvice(basePackages = "com.atguigu.gulimall.product.controller")
public class GulimallExceptionControllerAdvice {
@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<>();
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());
}
}
P68分组校验。
数据校验的更高级功能 ----->给检验注解标注什么情况需要进行检验
假设有这样的一个业务场景,我已经将品牌的ID定义不能为空,这种情况我对品牌进行修改没问题,但是假设我是要新增品牌,本来就不需要带上ID,但是又被限制了不得不带,所以就有了这个分组校验。
具体应该怎么操作?在实体类属性上标的注解添加上groups属性。属性为数组,并且必须是一个接口,在common中定义一个包valid,接口就写里面就行,接口只起到标识作用
配置分组校验还需要一个注解:@Validated
前端处理请求用的是哪个函数,@Validated 注解就加在哪个函数当中,属性就是上面刚定义好的groups属性
/**
* 保存
*/
@RequestMapping("/save")
//@RequiresPermissions("product:brand:save")
public R save(@Validated({AddGroup.class}) @RequestBody BrandEntity brand){
brandService.save(brand);
return R.ok();
}
/**
* 修改
*/
@RequestMapping("/update")
//@RequiresPermissions("product:brand:update")
public R update(@Validated({UpdateGroup.class})@RequestBody BrandEntity brand){
brandService.updateDetail(brand);
return R.ok();
}
注意:没有标志分组的属性,即使添加了检验注解,默认也是不起作用的!!!
默认没有指定分组的校验注解@NotBlank,在分组校验情况@Validated({AddGroup.class})下不生效,只会在@Validated生效;
P69自定义校验注解
自定义校验
1)、编写一个自定义的校验注解
2)、编写一个自定义的校验器 ConstraintValidator
3)、关联自定义的校验器和自定义的校验注解
在common包下创建校验注解如以下:
@Documented
//检验器
@Constraint(validatedBy = {ListValueConstraintValidator.class})//指定校验器
@Target({
ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER
})
@Retention(RetentionPolicy.RUNTIME)
public @interface ListValue {
String message() default "{com.atguigu.common.vaild.ListValue.message}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
//注解上的属性值,是一个int数组,默认为空
//出错后可以自定义错误消息,在配置文件上配置
int[] value() default { };
}
/*
自定义校验器 实现ConstraintValidator 定义两个泛型,第一个自定义注解名,第二个是定义给什么类型的属性使用
*/
public class ListValueConstraintValidator implements ConstraintValidator<ListValue,Integer> {
private Set<Integer> set = new HashSet<Integer>();
//初始化方法
public void initialize(ListValue constraintAnnotation) {
//可以拿到注解属性的值
int[] value = constraintAnnotation.value();
for (int i : value) {
set.add(i);
}
}
//判断是否校验成功
public boolean isValid(Integer integer, ConstraintValidatorContext constraintValidatorContext) {
//在这里可以进行判断属性是否符合我想要的要求
return set.contains(integer);
}
}
@Constraint(validatedBy = { ListValueConstraintValidator.class【可以指定多个不同的校验器,适配不同类型的校验】 })
//结果如下
@ListValue(value ={0,1},groups = {AddGroup.class, UpdateStatusGroup.class})
private Integer showStatus;
具体信息再参考common里面的vaild的LIstValue和ListValueController以及配置文件
使用自定义校验注解要记得添加依赖:
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>2.0.1.Final</version>
</dependency>
P71属性分组,使用父子传递事件
子组件被单机,父组件感知到,并且能够将子组件的东西发送给父组件
详情参考VSCode中的@node-click="nodeclick"
P72获取分类属性分组
/**
* 实现逻辑是这样的,先获取查询条件带来的参数key,然后将查询数据库对应的工具类准备好,也就是
* new QueryWrapper<AttrGroupEntity>();
* 再判断key是不是空的,如果不是,那么如果id等于key,或名字中有key的数据库数据就是被查询出来,也就是将想要的东西已经存到wrapper当中了
* 再判断catlogId是不是空的,如果是,返回查询全部
* 如果不是,那就根据catlogId以及带来的key查询出想要的数据返回给前端处理
*/
@RequestMapping("/list/{catelogId}")
public R list(@RequestParam Map<String, Object> params,@PathVariable("catelogId") Long catelogId){
PageUtils page = attrGroupService.queryPage(params,catelogId);
return R.ok().put("page", page);
}
public PageUtils queryPage(Map<String, Object> params, Long catelogId) {
String key = (String) params.get("key");
//QueryWrapper是已经准备好的工具类,不用自己再去写函数,泛型中的参数是要查哪张表对应的实体类
QueryWrapper<AttrGroupEntity> wrapper = new QueryWrapper<AttrGroupEntity>();
if(!StringUtils.isEmpty(key)){
wrapper.and((obj)->{//and 用并且的关系关联这一组条件
obj.eq("attr_group_id",key).or().like("attr_group_name",key);
});
}
//如果catelogId==0 那么就查询所有
if( catelogId == 0){ //this.page 默认生成的方法,第一个参数是分页信息,第二个参数是查询信息
//new Query Query逆向封装的工具类 可以将前端传来的Map调用get方法获取想要的参数信息
IPage<AttrGroupEntity> page = this.page(new Query<AttrGroupEntity>().getPage(params),
wrapper);
//PageUtils工具类 将page传过来能自动解析出当前页码,每页大小等信息
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);
}
}
P73 属性分组新增功能 --分类的级联选择
详情参考ElementUI的Cascader 级联选择器 组件 / Basic / Layout 布局
通过一系列的操作(cv),能够拿到数据,但是显示不出来,怎么解决?
添加props对象,要动态绑定的。
完成后遇到问题:三级的分类之后还会弹出一个空表选择框,原因是?
在第三级分类下还有一个children,ElementUI以为我们还有数据,所以就再弹出一个窗口。
怎么解决?
在java实体类中的children属性上添加:
@JsonInclude(JsonInclude.Include.NON_EMPTY)
表示只能返回不为空的children,接着还有一个catelogs数组和catelog的问题,详情再参考视频,这里就不说了
最后遇到问题,想要修改刚刚新增提交上去的品牌属性,但是品牌的id却不能回显,怎么解决?看p74
P74级联选择器回显
为什么不回显?
因为返回的catId是一个值,并不是一个数组,所以会回显不出来。
解决办法:
编写方法,通过传过来的三级分类ID,获取三级分类对象,再拿出里面的父ID,
然后再编写一个函数(使用递归),拿出二级分类以及一级分类,返回给AttrGroupController,再返回给前端
@TableField(exist = false) //在数据库中不存在
private Long[] catelogPath;
@RequestMapping("/info/{attrGroupId}")
public R info(@PathVariable("attrGroupId") Long attrGroupId){
AttrGroupEntity attrGroup = attrGroupService.getById(attrGroupId);
//获取ID,然后通过调用三级分类方法传入ID获取完整路径
Long catelogId = attrGroup.getCatelogId();
Long [] catelogPath = categoryService.findCatelogPath(catelogId);
attrGroup.setCatelogPath(catelogPath);
return R.ok().put("attrGroup", attrGroup);
}
@Override
public Long[] findCatelogPath(Long catelogId) {
List<Long> list = new ArrayList<>();
List<Long> parentPath = findParentPath(catelogId, list);
//已经成功拿到了数据,但是数据的顺序不对,这里是三级分类在第一位,我们应该把三级分类放最后,把一级分类(数字小的)放前面
Collections.reverse(parentPath);//逆序转换过来
return parentPath.toArray(new Long[parentPath.size()]);
}
/*
编写函数,通过一个第三级分类的ID查找出两个父分类ID,使用递归的操作
*/
private List<Long> findParentPath(Long catelogId,List<Long> paths){
//将当前的节点ID先收集进集合,这时集合已经有第一个值了
//这个添加的操作写在最前面很值得借鉴,只要递归下面的判断成立了那么它就会再调用本方法,并把第一个参数添加到集合当中
paths.add(catelogId);
CategoryEntity byId = this.getById(catelogId);
//如果不等于0那就说明还有父分类,可以哪出来,怎么拿?递归
if (byId.getParentCid() != 0){
findParentPath(byId.getParentCid(),paths);
}
return paths;
}
P75品牌分类关联和级联更新
完成品牌的分页功能,创建包config,再创建好配置类,直接cv就行了,完成。
@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);
return paginationInterceptor;
}
}
完善品牌的模糊查询功能
开始编写关联分类功能
一个品牌有多个分类,一个分类有多个品牌(小米有电视,手机,家具等,但是电视也有索尼等品牌),所以就是一个多对多的关系,多对多一般会有一张中间表。
更改catelog-brand-relation的Controller的save方法
@RequestMapping("/save")
public R save(@RequestBody CategoryBrandRelationEntity categoryBrandRelation){
categoryBrandRelationService.saveDetail(categoryBrandRelation);
return R.ok();
}
@Override
public void saveDetail(CategoryBrandRelationEntity categoryBrandRelation) {
Long brandId = categoryBrandRelation.getBrandId();
Long catelogId = categoryBrandRelation.getCatelogId();
//查询详细名字
BrandEntity brandEntity = brandDao.selectById(brandId);
CategoryEntity categoryEntity = categoryDao.selectById(catelogId);
//将名字设置进关系表中
categoryBrandRelation.setBrandName(brandEntity.getName());
categoryBrandRelation.setCatelogName(categoryEntity.getName());
//调用自己的Dao进行保存,baseMapper 就是自己的Dao,直接用就行
this.save(categoryBrandRelation);
}
获取品牌关联的所有分类
@GetMapping("/catelog/list") //查询功能,使用Get更快
public R cateloglist(@RequestParam("brandId")Long brandId){
List<CategoryBrandRelationEntity> data = categoryBrandRelationService.list(
new QueryWrapper<CategoryBrandRelationEntity>().eq("brand_id",brandId)
);
return R.ok().put("data",data);
}
有一个问题,假设我的品牌名修改了,但是我关联的界面上得品牌并没有修改,这是一个BUG,怎么解决?
Brand的update修改的时候,不能只改品牌表,那么代码如何操作呢?
@Override
public void updateDetail(BrandEntity brand) {
//保证冗余字段的数据一致 先更新自己品牌表里的数据
this.updateById(brand);
if (!StringUtils.isEmpty(brand.getBrandId())){
categoryBrandRelationService.updateBrand(brand.getBrandId(),brand.getName());
//TODO 更新其他关联
}
}
//将categoryBrandRelation中的brandId和name属性更改
@Override
public void updateBrand(Long brandId, String name) {
CategoryBrandRelationEntity relationEntity = new CategoryBrandRelationEntity();
relationEntity.setBrandId(brandId);
relationEntity.setBrandName(name);
this.update(relationEntity,new UpdateWrapper<CategoryBrandRelationEntity>().eq("brand_id",brandId));
}
分类也是一样需要进行级联更新的。
@RequestMapping("/update")
public R update(@RequestBody CategoryEntity category){
categoryService.updateCascade(category);
return R.ok();
}
public void updateCascade(CategoryEntity category) {
//先更新分类表的数据
this.updateById(category);
//接着调用关系表的函数(自己写的)更新Category相关的数据(ID以及name)
categoryBrandRelationService.updateCategory(category.getCatId(),category.getName());
}
public void updateCategory(Long catId, String name) {
this.baseMapper.updateCategory(catId,name);
}
@Mapper
public interface CategoryBrandRelationDao extends BaseMapper<CategoryBrandRelationEntity> {
void updateCategory(@Param("catId") Long catId, @Param("name") String name);
}
<update id="updateCategory">
UPDATE `pms_category_brand_relation` SET catelog_name = #{name} WHERE catelog_id = #{catId}
</update>
P76 规格参数
商城系统-平台属性-属性分组
之前的关联是分类与品牌进行关联,现在是分组跟属性关联
@RequestMapping("/save")
public R save(@RequestBody AttrVo attr){
attrService.saveAttr(attr);
return R.ok();
}
@Override
public void saveAttr(AttrVo attr) {
//1、保存基本数据 AttrEntity才是实际跟数据库对接的实体类,我们要将AttrVO接受过来的参数Copy给Entity
AttrEntity attrEntity = new AttrEntity();
BeanUtils.copyProperties(attr,attrEntity);
this.save(attrEntity);
if (attr.getAttrType() == ProductConstant.AttrEnum.ATTR_TYPE_BASE.getCode() && attr.getAttrGroupId() != null){
//2、保存关联关系
AttrAttrgroupRelationEntity relationEntity = new AttrAttrgroupRelationEntity();
relationEntity.setAttrGroupId(attr.getAttrGroupId());
relationEntity.setAttrId(attrEntity.getAttrId());
attrAttrgroupRelationDao.insert(relationEntity);
}
}
P77规格参数展示
以下的方法跟之后的方法合并一起
//product/attr/base/list/{catelogId}
@GetMapping("/{attrType}/list/{catelogId}")//获取基本属性
public R baseAttrList(@RequestParam Map<String, Object> params,@PathVariable("catelogId") Long catelogId,
@PathVariable("attrType") String type){
PageUtils page = attrService.baseAttrPage(params,catelogId,type);
return R.ok().put("page",page);
}
public PageUtils baseAttrPage(Map<String, Object> params, Long catelogId, String type) {
//创建检索条件的实体类
QueryWrapper<AttrEntity> queryWrapper = new QueryWrapper<AttrEntity>().eq
("attr_type","base".equalsIgnoreCase(type)?ProductConstant.AttrEnum.ATTR_TYPE_BASE.getCode()
:ProductConstant.AttrEnum.ATTR_TYPE_SALE.getCode());
//添加检索条件
if (catelogId != 0){
queryWrapper.eq("catelog_id",catelogId);
}
//模糊查询
String key = (String) params.get("key");
if (!StringUtils.isEmpty(key)){
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 -> {
//1、将Entity的数据更新到attrRespVo中,接着再来拿到name
AttrRespVo attrRespVo = new AttrRespVo();
BeanUtils.copyProperties(attrEntity, attrRespVo);
if ("base".equalsIgnoreCase(type)){
//设置分类和分组的名字 通过以上的操作已经拿到了AttrEntity实体类,
//通过AttrEntity实体类拿到attr_id作为查询条件
AttrAttrgroupRelationEntity attr_id = attrAttrgroupRelationDao.selectOne
(new QueryWrapper<AttrAttrgroupRelationEntity>().eq("attr_id", attrEntity.getAttrId()));
if (attr_id != null && attr_id.getAttrGroupId()!= null) {
//通过分组ID拿到了分组实体类
AttrGroupEntity attrGroupEntity = attrGroupDao.selectById(attr_id.getAttrGroupId());
//通过实体类拿到了分组名并存入AttrRespVo中
attrRespVo.setGroupName(attrGroupEntity.getAttrGroupName());
}
}
//拿到对应ID三级分类的实体类
CategoryEntity categoryEntity = categoryDao.selectById(attrEntity.getCatelogId());
if (categoryEntity != null) {
//通过实体类获取分类名词并将其注入进AttrRespVo中
attrRespVo.setCatelogName(categoryEntity.getName());
}
return attrRespVo;
}).collect(Collectors.toList());
pageUtils.setList(respVos);
return pageUtils;
}
P78规格修改
@Override//展示分组姓名,分类名称
public AttrRespVo getAttrInfo(Long attrId) {
AttrRespVo respVo = new AttrRespVo();
//通过ID拿到对应属性的实体类
AttrEntity attrEntity = this.getById(attrId);
BeanUtils.copyProperties(attrEntity,respVo);
if (attrEntity.getAttrType()==ProductConstant.AttrEnum.ATTR_TYPE_BASE.getCode()){
//1、设置分组信息,通过属性ID拿到对应关系表一整条数据
AttrAttrgroupRelationEntity attrgroupRelation = attrAttrgroupRelationDao.
selectOne(new QueryWrapper<AttrAttrgroupRelationEntity>().eq("attr_id", attrId));
if(attrgroupRelation!=null){
//将respvo得GroupID设置为AttrID对应的分组ID
respVo.setAttrGroupId(attrgroupRelation.getAttrGroupId());
//通过关系表的GroupId获取整个AttrGroup,再将对应的GroupName设置进去
AttrGroupEntity attrGroupEntity = attrGroupDao.selectById(attrgroupRelation.getAttrGroupId());
if(attrGroupEntity!=null){
respVo.setGroupName(attrGroupEntity.getAttrGroupName());
}
}
}
//2、设置分类信息 拿到分类ID,通过分类ID查到二级和一级分类的ID,将ID赋值给respvo
Long catelogId = attrEntity.getCatelogId();
//通过三级分类ID拿到二级分类一级分类ID
Long[] catelogPath = categoryService.findCatelogPath(catelogId);
respVo.setCatelogPath(catelogPath);
//通过分类ID拿到分类实体,通过分类实体拿到分类名称,将名称赋值给respVo类,返回给亲爱的前端工程师
CategoryEntity categoryEntity = categoryDao.selectById(catelogId);
if(categoryEntity!=null){
respVo.setCatelogName(categoryEntity.getName());
}
return respVo;
}
public void updateAttr(AttrVo attr) {
AttrEntity attrEntity = new AttrEntity();
BeanUtils.copyProperties(attr,attrEntity);
this.updateById(attrEntity);
//如果属性是基本属性,也就是规格参数则进行修改 Type==1就是规格参数
if (attr.getAttrType() == ProductConstant.AttrEnum.ATTR_TYPE_BASE.getCode()){
//1、修改分组关联
AttrAttrgroupRelationEntity relationEntity = new AttrAttrgroupRelationEntity();
//将关联表中的分组ID以及属性ID设置成AttrVo对应的值
relationEntity.setAttrGroupId(attr.getAttrGroupId());
relationEntity.setAttrId(attr.getAttrId());
//查看对应属性ID中在关联表有没有数据,如果有就更新,没有就新增
Integer count = attrAttrgroupRelationDao.selectCount(new QueryWrapper<AttrAttrgroupRelationEntity>()
.eq("attr_id", attr.getAttrId()));
if(count>0){
attrAttrgroupRelationDao.update(relationEntity,new UpdateWrapper<AttrAttrgroupRelationEntity>()
.eq("attr_id",attr.getAttrId()));
}else{
attrAttrgroupRelationDao.insert(relationEntity);
}
}
}
P79销售属性维护
跟P77一样,详情参考
P80 查询分组关联属性/删除关联
展示该分组关联的所有属性
@GetMapping("/{attrgroupId}/attr/relation")
public R attrRelation(@PathVariable("attrgroupId") Long attrgroupId){
List<AttrEntity> entities = attrService.getRelationAttr(attrgroupId);
return R.ok().put("data",entities);
}
@Override//根据分组ID查找关联的所有属性(规格参数)
public List<AttrEntity> getRelationAttr(Long attrgroupId) {
List<AttrAttrgroupRelationEntity> attr_group_id = attrAttrgroupRelationDao.selectList
(new QueryWrapper<AttrAttrgroupRelationEntity>()
.eq("attr_group_id", attrgroupId));
//只需要attrId,所以使用箭头函数将属性ID收集成一个集合
List<Long> attrIds = attr_group_id.stream().map((attr) -> {
return attr.getAttrId();
}).collect(Collectors.toList());
//通过属性ID查找所有的属性信息
if (attrIds == null || attrIds.size() == 0){
return null;
}
Collection<AttrEntity> attrEntities = this.listByIds(attrIds);
return (List<AttrEntity>) attrEntities;
}
删除关联关系
@Override
public void deleteRelation(AttrGroupRelationVo[] vos) {
//attrAttrgroupRelationDao.delete(new QueryWrapper<>(AttrAttrgroupRelationEntity).eq("attr_id",1L)
//.eq("attr_group_id",1L));
//希望只发一次删除请求,完成批量删除,编写自定义方法,自己写SQL
//要给deleteBatchRelation()传入一个关系实体类,所以要将上面的对象处理一下
List<AttrAttrgroupRelationEntity> entities = Arrays.asList(vos).stream().map((item) -> {
AttrAttrgroupRelationEntity relationEntity = new AttrAttrgroupRelationEntity();
BeanUtils.copyProperties(item, relationEntity);
return relationEntity;
}).collect(Collectors.toList());
//将浏览器传过来的参数拷贝给关系表实体类,将这个实体类作为参数传给自定义的删除函数
attrAttrgroupRelationDao.deleteBatchRelation(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>
P81查询分组未关联的所有属性
@GetMapping("/{attrgroupId}/noattr/relation")
public R attrNoRelation(@PathVariable("attrgroupId") Long attrgroupId,
@RequestParam Map<String, Object> params){
//获取属性分组里面还没有关联的本分类里面的其他基本属性,方便添加新的关联
PageUtils page = attrService.getNoRelationAttr(params,attrgroupId);
return R.ok().put("page", page);
}
public PageUtils getNoRelationAttr(Map<String, Object> params, Long attrgroupId) {
//1、当前分组只能关联自己所属的分类的所有属性
AttrGroupEntity attrGroupEntity = attrGroupDao.selectById(attrgroupId);
Long catelogId = attrGroupEntity.getCatelogId();
//2、当前分组只能关联别的分组没有引用的属性
//2.1拿到当前分类下的其他分组,通过分类Id拿到分类的其他分组
List<AttrGroupEntity> groups = attrGroupDao.selectList
(new QueryWrapper<AttrGroupEntity>().eq("catelog_id", catelogId));
//2.2这些分组关联的属性 拿到分类下所有分组Id
List<Long> collect = groups.stream().map(item -> {
return item.getAttrGroupId();
}).collect(Collectors.toList());
//拿到一整条关系表数据,通过关系表拿到属性Id
List<AttrAttrgroupRelationEntity> attrRelation = attrAttrgroupRelationDao.selectList
(new QueryWrapper<AttrAttrgroupRelationEntity>().in("attr_group_id", collect));
List<Long> attrIds = attrRelation.stream().map(item -> {
return item.getAttrId();
}).collect(Collectors.toList());
//2.3从当前分类的所有属性中移除这些属性 查出当前分组可以关联的其他属性
QueryWrapper<AttrEntity> wrapper = new QueryWrapper<AttrEntity>()
.eq("catelog_id", catelogId)
.eq("attr_type",ProductConstant.AttrEnum.ATTR_TYPE_BASE.getCode());
if (attrIds!= null && attrIds.size()>0){
wrapper.notIn("attr_id", attrIds);
}
String key = (String) params.get("key");
if (!StringUtils.isEmpty(key)){
wrapper.and((w)->{
w.eq("attr_id",key).or().like("attr_name",key);
});
}
IPage<AttrEntity> page = this.page(new Query<AttrEntity>().getPage(params), wrapper);
PageUtils pageUtils = new PageUtils(page);
return pageUtils;
}
P82新增分组和属性的关联
@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);
}
P84 获取分类关联的品牌
套路:
1、Contoller:处理请求,接受和校验数据
2、Service接受controller传来的数据,进行业务处理
3、Controller接受Service处理完的数据,封装页面指定的返回(返回结果)
@GetMapping("/brands/list")
public R relationBrandsList(@RequestParam(value = "catId",required = true) Long catId){
List<BrandEntity> brandEntities= categoryBrandRelationService.getBrandsByCatId(catId);
//由于返回的参数只需要品牌的Id和品牌的姓名,所以自己再封装一个Vo
List<BrandVo> collect = brandEntities.stream().map(item -> {
BrandVo brandVo = new BrandVo();
BeanUtils.copyProperties(item, brandVo);
return brandVo;
}).collect(Collectors.toList());
return R.ok().put("data",collect);
}
@Override
public List<BrandEntity> getBrandsByCatId(Long catId) {
//本方法用于获得分类关联的品牌
List<CategoryBrandRelationEntity> catelogId = relationDao.selectList
(new QueryWrapper<CategoryBrandRelationEntity>().eq("catelog_id",catId));
//获取关系表获取品牌Id,通过品牌Id获取品牌实体然后返回
List<BrandEntity> collect = catelogId.stream().map(item -> {
Long brandId = item.getBrandId();
BrandEntity brandEntity = brandService.getById(brandId);
return brandEntity;
}).collect(Collectors.toList());
return collect;
}
P85 获取分类下的所有分组及所有属性
@GetMapping("/{catelogId}/withattr")
public R getAttrGroupWithAttrs(@PathVariable("catelogId") Long catelogId){
// 1、查出当前分类下的所有属性分组
// 2、查出每个属性分组下的所有属性
List<AttrGroupWithAttrsVo> vos = attrGroupService.getAttrGroupWithAttrsByCatelogId(catelogId);
return R.ok().put("data",vos);
}
@Override
public List<AttrGroupWithAttrsVo> getAttrGroupWithAttrsByCatelogId(Long catelogId) {
//获取分类下的所有分组及属性
//根据分类Id查出所有分组
List<AttrGroupEntity> attrGroupEntities = this.list
(new QueryWrapper<AttrGroupEntity>().eq("catelog_id", catelogId));
List<AttrGroupWithAttrsVo> collect = attrGroupEntities.stream().map(group -> {
//查出所有的属性,将属性赋值给AttrGroupWithAttrsVo
AttrGroupWithAttrsVo attrsVo = new AttrGroupWithAttrsVo();
BeanUtils.copyProperties(group, attrsVo);
List<AttrEntity> relationAttr = attrService.getRelationAttr(attrsVo.getAttrGroupId());
attrsVo.setAttrs(relationAttr);
return attrsVo;
}).collect(Collectors.toList());
return collect;
}
P87-P92商品添加
@RequestMapping("/save")
public R save(@RequestBody SpuSaveVo vo){
spuInfoService.saveSpuInfo(vo);
return R.ok();
}
@Override
@Transactional
public void saveSpuInfo(SpuSaveVo vo) {
// 1、保存spu基本信息 pms_spu_info
SpuInfoEntity spuInfoEntity = new SpuInfoEntity();
BeanUtils.copyProperties(vo,spuInfoEntity);
spuInfoEntity.setCreateTime(new Date());
spuInfoEntity.setUpdateTime(new Date());
this.saveBatchSpuInfo(spuInfoEntity);
// 2、保存spu的描述图片 pms_spu_info_desc
List<String> decript = vo.getDecript();
SpuInfoDescEntity descEntity = new SpuInfoDescEntity();
/*
数据库中表Id的字段不是自增的,但是Mybatis默认设置为自增,所有插入的时候会出错
@TableId(type = IdType.INPUT)
private Long spuId;
*/
descEntity.setSpuId(spuInfoEntity.getId());
descEntity.setDecript(String.join(",",decript));
spuInfoDescService.saveSpuInfoDesc(descEntity);
// 3、保存spu的图片集 pms_sku_images
List<String> images = vo.getImages();
imagesService.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());
AttrEntity attrServiceById = attrService.getById(attr.getAttrId());
valueEntity.setAttrName(attrServiceById.getAttrName());
valueEntity.setAttrValue(attr.getAttrValues());
valueEntity.setQuickShow(attr.getShowDesc());
valueEntity.setSpuId(spuInfoEntity.getId());
return valueEntity;
}).collect(Collectors.toList());
productAttrValueService.saveProductAttrs(collect);
// 5、保存spu的积分信息: sms_spu_bounds
Bounds bounds = vo.getBounds();
SpuBoundTo spuBoundTo = new SpuBoundTo();
BeanUtils.copyProperties(bounds,spuBoundTo);
spuBoundTo.setSpuId(spuInfoEntity.getId());
R r1 = couponFeignService.saveSpuBounds(spuBoundTo);
if (r1.getCode() != 0){
log.error("远程保存spu积分信息失败");
}
// 6、保存当前spu对应的所有sku信息
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);
// 6.1 sku的基本信息 pms_spu_info
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->{
//TODO 没有选中的图片,路径无需保存
return !StringUtils.isEmpty(entity.getImgUrl());
}).collect(Collectors.toList());
// 6.2 sku的图片信息 pms_sku_images
skuImagesService.saveBatch(imagesEntities);
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());
// 6.3 sku的销售信息: pms_sku_sale_attr_value
skuSaleAttrValueService.saveBatch(skuSaleAttrValueEntities);
// 6.4 sku的优惠、满减等信息: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(new BigDecimal("0") ) ==1){
R r = couponFeignService.saveSkuReduction(skuReductionTo);
if (r.getCode() != 0){
log.error("远程保存sku优惠信息失败");
}
}
});
}
}
@Override
public void saveSkuReduction(SkuReductionTo skuReductionTo) {
//1、保存打折 sms_sku_ladder
SkuLadderEntity skuLadderEntity = new SkuLadderEntity();
BeanUtils.copyProperties(skuReductionTo,skuLadderEntity);
if (skuLadderEntity.getFullCount() >0){
skuLadderService.save(skuLadderEntity);
}
//2、保存满减 sms_sku_full_reduction
SkuFullReductionEntity skuFullReductionEntity = new SkuFullReductionEntity();
BeanUtils.copyProperties(skuReductionTo,skuFullReductionEntity);
if (skuFullReductionEntity.getFullPrice().compareTo(new BigDecimal("0")) ==1){
this.save(skuFullReductionEntity);
}
//sms_member_price
List<MemberPrice> memberPrice = skuReductionTo.getMemberPrice();
List<MemberPriceEntity> collect = memberPrice.stream().map(mem -> {
MemberPriceEntity memberPriceEntity = new MemberPriceEntity();
memberPriceEntity.setSkuId(skuReductionTo.getSkuId());
memberPriceEntity.setMemberLevelId(mem.getId());
memberPriceEntity.setMemberLevelName(mem.getName());
memberPriceEntity.setMemberPrice(mem.getPrice());
memberPriceEntity.setAddOther(1);
return memberPriceEntity;
}).filter(mem->{
return mem.getMemberPrice().compareTo(new BigDecimal("0"))==1;
}).collect(Collectors.toList());
memberPriceService.saveBatch(collect);
}
P93 SPU检索
@RequestMapping("/list")
public R list(@RequestParam Map<String, Object> params){
PageUtils page = spuInfoService.queryPageByCondition(params);
return R.ok().put("page", page);
}
public PageUtils queryPageByCondition(Map<String, Object> params) {
QueryWrapper<SpuInfoEntity> wrapper = new QueryWrapper<>();
String status = (String) params.get("status");
if(!StringUtils.isEmpty(status)){
wrapper.eq("publish_status",status);
}
String key = (String) params.get("key");
if(!StringUtils.isEmpty(key)){
wrapper.and(w->{
w.eq("id",key).or().like("spu_name",key);
});
}
String brandId = (String) params.get("brandId");
if(!StringUtils.isEmpty(brandId) && !"0".equalsIgnoreCase(brandId)){
wrapper.eq("brand_id",brandId);
}
String catelogId = (String) params.get("catelogId");
if(!StringUtils.isEmpty(catelogId) && !"0".equalsIgnoreCase(catelogId)){
wrapper.eq("catalog_id",catelogId);
}
IPage<SpuInfoEntity> page = this.page
(new Query<SpuInfoEntity>().getPage(params),wrapper);
return new PageUtils(page);
}
P94 SKU检索
@RequestMapping("/list")
public R list(@RequestParam Map<String, Object> params){
PageUtils page = skuInfoService.queryPageByCondition(params);
return R.ok().put("page", page);
}
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("sku_id",key).or().like("sku_name",key);
});
}
String catelogId = (String) params.get("catelogId");
if(!StringUtils.isEmpty(catelogId) && !"0".equalsIgnoreCase(catelogId)){
wrapper.eq("catalog_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);
}
String max = (String) params.get("max");
if(!StringUtils.isEmpty(max)){
try {
BigDecimal bigDecimal = new BigDecimal(max);
//等于1表示要大于new BigDecimal("0")中的0才算true
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);
}
P95 整合ware服务
@Override
public PageUtils queryPage(Map<String, Object> params) {
QueryWrapper<WareInfoEntity> wrapper = new QueryWrapper<>();
String key = (String) params.get("key");
if(!StringUtils.isEmpty(key)){
wrapper.and(w->{
w.eq("id",key).or().like("name",key).or().like("address",key).or()
.like("areacode",key);
});
}
IPage<WareInfoEntity> page = this.page(
new Query<WareInfoEntity>().getPage(params),wrapper
);
return new PageUtils(page);
}
P96 查询库存,创建采购需求
查询库存
public PageUtils queryPage(Map<String, Object> params) {
QueryWrapper<WareSkuEntity> wrapper = new QueryWrapper<>();
String skuId = (String) params.get("skuId");
if(!StringUtils.isEmpty(skuId)){
wrapper.eq("sku_id",skuId);
}
String wareId = (String) params.get("wareId");
if(!StringUtils.isEmpty(wareId)){
wrapper.eq("sku_id",wareId);
}
IPage<WareSkuEntity> page = this.page(
new Query<WareSkuEntity>().getPage(params),wrapper
);
return new PageUtils(page);
}
新增库存并不是直接在库存系统里面新增,而是在采购单采购完成之后程序自动加上去的。
多个采购需求合并成一个采购单,采购人员领取采购单,采购完成之后,采购单入库,最后添加库存。
查询采购需求
public PageUtils queryPage(Map<String, Object> params) {
QueryWrapper<PurchaseDetailEntity> wrapper = new QueryWrapper<>();
String key = (String) params.get("key");
if(!StringUtils.isEmpty(key)){
wrapper.and(w->{
w.eq("purchase_id",key).or().eq("sku_id",key);
});
}
String status = (String) params.get("status");
if(!StringUtils.isEmpty(status)){
wrapper.eq("status",status);
}
String wareId = (String) params.get("wareId");
if(!StringUtils.isEmpty(wareId)){
wrapper.eq("ware_id",wareId);
}
IPage<PurchaseDetailEntity> page = this.page(
new Query<PurchaseDetailEntity>().getPage(params),wrapper
);
return new PageUtils(page);
}
P97 合并采购需求
合并采购需求的前提是必须有一个采购单,没有采购单拿什么合并?
需要将未被领取的采购单展示出来-----查询未被领取的采购单
@GetMapping("/unreceive/list")
public R unreceiveList(@RequestParam Map<String, Object> params){
PageUtils page = purchaseService.queryPageUnreceivePurchase(params);
return R.ok().put("page", page);
}
@Override
public PageUtils queryPageUnreceivePurchase(Map<String, Object> params) {
IPage<PurchaseEntity> page = this.page(new Query<PurchaseEntity>().getPage(params),
new QueryWrapper<PurchaseEntity>().eq("status",0).or().eq("status",1));
return new PageUtils(page);
}
合并采购需求
@PostMapping("/merge")
public R merge(@RequestBody MergeVo mergeVo){
purchaseService.mergePurchase(mergeVo);
return R.ok();
}
@Transactional
@Override
public void mergePurchase(MergeVo mergeVo) {
Long purchaseId = mergeVo.getPurchaseId();
if(purchaseId == null){
//没有——>新建
PurchaseEntity purchaseEntity = new PurchaseEntity();
purchaseEntity.setCreateTime(new Date());
purchaseEntity.setUpdateTime(new Date());
purchaseEntity.setStatus(WareConstant.purchaseStatusEnum.CREATED.getCode());
this.save(purchaseEntity);
//需要将新建的采购单Id进行赋值
purchaseId = purchaseEntity.getId();
}
//不为空,进行合并
//TODO 确认采购单状态是0,1才可以合并
List<Long> items = mergeVo.getItems();
Long finalPurchaseId = purchaseId;
List<PurchaseDetailEntity> collect = items.stream().map(i -> {
PurchaseDetailEntity detailEntity = new PurchaseDetailEntity();
detailEntity.setId(i);
detailEntity.setPurchaseId(finalPurchaseId);
detailEntity.setStatus(WareConstant.PurchaseDetailStatusEnum.ASSIGNED.getCode());
return detailEntity;
}).collect(Collectors.toList());
detailService.updateBatchById(collect);
PurchaseEntity purchaseEntity = new PurchaseEntity();
purchaseEntity.setId(purchaseId);
purchaseEntity.setUpdateTime(new Date());
this.updateById(purchaseEntity);
}
P98 领取采购单
//领取采购单
@PostMapping("/received")
public R received(@RequestBody List<Long> ids){
purchaseService.received(ids);
return R.ok();
}
//ids 是采购单id集合
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.CREATED.getCode()
|| item.getStatus() == WareConstant.purchaseStatusEnum.ASSIGNED.getCode()) {
return true;
}
return false;
}).map(item->{
item.setStatus(WareConstant.purchaseStatusEnum.RECEIVE.getCode());
item.setUpdateTime(new Date());
return item;
}).collect(Collectors.toList());
//2、改变采购单的状态
this.updateBatchById(collect);
//3、改变采购项的状态
collect.forEach(item->{
List<PurchaseDetailEntity> entities = detailService.listDetailByPurchaseId(item.getId());
List<PurchaseDetailEntity> purchaseDetailEntities = entities.stream().map(entitie -> {
PurchaseDetailEntity purchaseDetailEntity = new PurchaseDetailEntity();
purchaseDetailEntity.setId(entitie.getId());
purchaseDetailEntity.setStatus(WareConstant.PurchaseDetailStatusEnum.BUYING.getCode());
return purchaseDetailEntity;
}).collect(Collectors.toList());
detailService.updateBatchById(purchaseDetailEntities);
});
}
//按照采购单Id,拿到采购项Id,接着更新状态
public List<PurchaseDetailEntity> listDetailByPurchaseId(Long id) {
List<PurchaseDetailEntity> purchase_id = this.list(new QueryWrapper<PurchaseDetailEntity>()
.eq("purchase_id", id));
return purchase_id;
}
P99 完成采购
@PostMapping("/done")
public R finish(@RequestBody PurchaseDoneVo purchaseDoneVo){
purchaseService.done(purchaseDoneVo);
return R.ok();
}
@Transactional
@Override
public void done(PurchaseDoneVo purchaseDoneVo) {
Long id = purchaseDoneVo.getId();
//2、改变采购项状态
boolean flag = true;
//获得采购项
List<PurchaseItemDoneVo> items = purchaseDoneVo.getItems();
//为后面更新操作作准备
List<PurchaseDetailEntity> updates = new ArrayList<>();
for(PurchaseItemDoneVo item :items){
PurchaseDetailEntity detailEntity = new PurchaseDetailEntity();
if (item.getStatus() == WareConstant.PurchaseDetailStatusEnum.HASERROR.getCode()){
flag = false;
detailEntity.setStatus(item.getStatus());
}else {
//程序执行到这里就是采购成功,改状态
detailEntity.setStatus(WareConstant.PurchaseDetailStatusEnum.FINISH.getCode());
//3、将成功采购的进行入库
PurchaseDetailEntity byId = detailService.getById(item.getItemId());
wareSkuService.addStore(byId.getSkuId(),byId.getWareId(),byId.getSkuNum());
}
detailEntity.setId(item.getItemId());
updates.add(detailEntity);
}
detailService.updateBatchById(updates);
//1、改变采购单状态
//采购单状态并不是一成不变的,要根据采购状态的改变而改变
//如果采购单的状态全部完成那就没问题,如果有一项采购项异常那就是异常
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);
}
public void addStore(Long skuId, Long wareId, Integer skuNum) {
List<WareSkuEntity> wareSkuEntities = wareSkuDao.selectList(new QueryWrapper<WareSkuEntity>().eq("sku_id", skuId).eq("ware_id", wareId));
//判断如果还没有这个库存记录就是新增操作
if (wareSkuEntities == null || wareSkuEntities.size()==0){
WareSkuEntity wareSkuEntity = new WareSkuEntity();
wareSkuEntity.setSkuId(skuId);
wareSkuEntity.setWareId(wareId);
wareSkuEntity.setStock(skuNum);
wareSkuEntity.setStockLocked(0);
//TODO 远程查询sku名字 如果失败,整个事务无需回滚
//TODO 还有什么办法 让异常出现不回滚呢 高级
//自己catch异常,只要没有把异常抛出去,其他功能正常运行
R info = productFeignService.info(skuId);
Map<String,Object> skuInfo = (Map<String, Object>) info.get("skuInfo");
try {
if(info.getCode()==0){
//成功了,就获取,但是如果因为一个冗余字段skuname而造成程序终止就很不值得,所以直接try
wareSkuEntity.setSkuName((String) skuInfo.get("skuName"));
}
}catch (Exception e){
}
wareSkuDao.insert(wareSkuEntity);
}else {
//有库存就更新
wareSkuDao.addStore(skuId,wareId,skuNum);
}
}
//WareDao更新的具体操作
@Mapper
public interface WareSkuDao extends BaseMapper<WareSkuEntity> {
void addStore(@Param("skuId") Long skuId, @Param("wareId") Long wareId, @Param("skuNum") Integer skuNum);
}
<update id="addStore">
UPDATE `wms_ware_sku` SET stock=stock+#{skuNum} WHERE sku_id =#{skuId} AND ware_id=#{wareId}
</update>
P100 SPU 规格维护
获取spu规格
@GetMapping("/base/listforspu/{spuId}")
public R baseAttrlistforspu(@PathVariable("spuId") Long spuId){
//前端需要展示的数据就需要拿到返回值
List<ProductAttrValueEntity> entities = productAttrValueService.baseAttrlistforspu(spuId);
return R.ok().put("data",entities);
}
public List<ProductAttrValueEntity> baseAttrlistforspu(Long spuId) {
List<ProductAttrValueEntity> entities = this.baseMapper.selectList
(new QueryWrapper<ProductAttrValueEntity>().eq("spu_id", spuId));
return entities;
}
修改商品规格
@PostMapping("/update/{spuId}")
public R updateSpuAttr(@PathVariable("spuId") Long spuId,
@RequestBody List<ProductAttrValueEntity> entities){
productAttrValueService.updateSpuAttr(spuId,entities);
return R.ok();
}
@Transactional
@Override
public void updateSpuAttr(Long spuId, List<ProductAttrValueEntity> entities) {
//1、删除这个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);
}
分布式基础篇后端代码完成。