而在data中defaultProps:{children:"children",label:"name"}6.1.5删除数据----逻辑删除1、前端代码node与data在element-ui的tree中,有2个非常重要的属性node代表当前节点(是否展开等信息,element-ui自带属性)data是节点数据,是自己的数据。data从哪里来:前面ajax发送请求,拿到data,赋值给menus属性,而menus属性绑定到标签的data属性。而node是ui的默认规则
在每一个菜单后面添加append,delete
点击按钮时,不进行菜单的打开合并:expand-on-click-node="false"
当没有子菜单时,才可以显示delete按钮。当为一级、二级菜单时,才显示append按钮
添加多选框show-checkbox
设置node-key=""标识每一个节点的不同
在学习的过程中,看到老师使用TODO才知道IDEA有一个类似备忘录的功能。
我们使用mybatis-plus中的逻辑删除语法:
1)、配置全局逻辑删除规则
application.yml中
mybatis-plus:mapper-locations:classpath*:/mapper/**/*.xmlglobal-config:db-config:id-type:auto#主键自增logic-delete-value:1#1表示删除logic-not-delete-value:0#0表示未删除注意:这里有一个坑,数据库中我们最开始设置的是1:未删除,0:删除。这个坑马上解决。
配置之后,我们可以继续使用APIFox进行测试,实际测试成功。为了验证,我们也可以在application.yml设置一个全局打印日志,将sql语句打印出来。
==>Preparing:UPDATEpms_categorySETshow_status=0WHEREcat_idIN()ANDshow_status=1==>Parameters:1000(Long)<==Updates:1打印的sql语句,可以看到删除其实是更新。
测试删除数据,打开postman或者是APIFox都可以(推荐使用APIFox)
delete请求传入的是数组,所以我们使用json数据。
删除1434,之后从1---0,即逻辑删除正确。
发送的请求:delete
ajax的get请求会被缓存,就不会请求服务器了。
所以我们在url后面拼接个date,让他每次都请求服务器
注意:
前端向后端发送post请求和get请求。对于这个我们可以设置一个自定义的代码块。文件->首选项->用户片段,以后我们就可以通过快捷键直接进行输出了。
导出的data中"dialogVisible=false"
点击确认或者取消后的逻辑都是@click=“dialogVisible=false”关闭会话
1、新增Edit按钮:复制之前的append
2、查看controller,发现updata方法是由id进行更新的,所以data中的category中新增catId
3、增加、修改的时候也修改图标和计量单位,所以data的category新增inco,productUnit
4、新建edit方法,用来绑定Edit按钮。新建editCategory方法,用来绑定对话框的确定按钮。
5、复用对话框:
7、编辑editCategory方法:
controller之中的更新是动态更新,根据id,发回去什么值修改什么值,所以把要修改的数据发回后端就好。成功之后发送提示消息,展开刚才的菜单。8、编辑之后,再点击添加,发现会回显刚才编辑的信息。所以在append方法中重置回显的信息。
@RestController@RequestMapping("product/category")publicclassCategoryController{@AutowiredprivateCategoryServicecategoryService;/***修改分类*/@RequestMapping("/update/sort")//@RequiresPermissions("product:category:update")publicRupdate(@RequestBodyCategoryEntity[]category){categoryService.updateBatchById(Arrays.asList(category));returnR.ok();}}前端发送请求:
这次要用到的代码是通过renren-generator代码生成器中生成的前端代码。在前面中如果我们不小心进行删除了,可以通过idea自带的恢复功能进行恢复。
步骤:
这里提一嘴,我们可以将es6语法检查关闭。
在列表中添加自定义列:中间加标签。可以通过Scopedslot可以获取到row,column,$index和store(table内部的状态管理)的数据
修改开关状态,发送修改请求
数据库中showStatus是01,开关默认值是true/false。所以在开关中设置:active-value="1"、:inactive-value="0"属性,与数据库同步
这里我们选用服务端签名后直传进行文件上传功能。
根据官网的文档,我们可以直接在项目中引入依赖进行安装
这个依赖是最原始的。配置什么要写一大堆。
com.aliyun.ossaliyun-sdk-oss3.15.0更推荐我们使用的是封装好的,这样配置可以直接在application.yml中。
在gulimall-common中引入上述依赖。
创建子账户,获取AccessKeyID、AccessKeySecret
我们将文件上传或者以后的短信验证这些第三方服务抽取出来放到一个专门的第三方微服务的工程项目中。gulimall-third-party
com.alibaba.cloudspring-cloud-starter-alicloud-oss2.2.0.RELEASE微服务端口设置为:30000启动测试启动thrid-party报错:Ossendpointcan‘tbeempty.
加入以下这个依赖之后就可以启动成功。
在test文件夹下的主测试类进行测试。
测试成功。
阿里云OSS存储服务中对于服务器签名直传这部分的文档。链接在下面:
我们参考这个文档创建属于我们自己的配置。
在网关中进行配置
-id:third_party_routeuri:lb://gulimall-third-partypredicates:-Path=/api/thirdparty/**filters:-RewritePath=/api/thirdparty/(.*),/$\{segment}重启微服务,进行访问。如下图,能获取到签名信息。
singleUpload.vue是单文件上传,multiUploca.vue是多文件上传。
点击上传,拿到police.阿里云服务器验证,上传。
在课件资源中将老师编写好的单文件上传和多文件上传一起复制到renren-fast-vue下的src\upload文件夹下。
测试完成。
新增品牌,发现在品牌logo下面显示的是地址。应该显示图片。
记得修改OSScontroller中的返回值,因为前端的数据要求是在data.
el-form中rules表示校验规则
为了定义这些错误状态码,我们可以单独定义一个常量类,用来存储这些错误状态码。
/****错误码和错误信息定义类*1.错误码定义规则为5为数字*2.前两位表示业务场景,最后三位表示错误码。例如:100001。10:通用001:系统未知异常*3.维护错误码后需要维护错误描述,将他们定义为枚举形式*错误码列表:*10:通用*001:参数格式校验*11:商品*12:订单*13:购物车*14:物流*/publicenumBizCodeEnum{UNKNOW_EXEPTION(10000,"系统未知异常"),VALID_EXCEPTION(10001,"参数格式校验失败");privateintcode;privateStringmsg;BizCodeEnum(intcode,Stringmsg){this.code=code;this.msg=msg;}publicintgetCode(){returncode;}publicStringgetMsg(){returnmsg;}}在product里面新建类GulimallExceptionControllerAdvice,用来集中处理所有异常
/***集中处理所有异常*///@ControllerAdvice(basePackages="com.atguigu.gulimall.product.controller")//@ResponseBody@Slf4j@RestControllerAdvice(basePackages="com.atguigu.gulimall.product.controller")publicclassGulimallExceptionControllerAdvice{@ExceptionHandler(value=MethodArgumentNotValidException.class)publicRhandleValidException(MethodArgumentNotValidExceptione){log.error("数据校验出现问题{},异常类型:{}",e.getMessage(),e.getClass());BindingResultbindingResult=e.getBindingResult();MaperrorMap=newHashMap<>();bindingResult.getFieldErrors().forEach((fieldError->{errorMap.put(fieldError.getField(),fieldError.getDefaultMessage());}));returnR.error(BizCodeEnume.VAILD_EXCEPTION.getCode(),BizCodeEnume.VAILD_EXCEPTION.getMsg()).put("data",errorMap);}@ExceptionHandler(value=Throwable.class)publicRhandleException(Throwablethrowable){log.error("错误:",throwable);returnR.error(BizCodeEnume.UNKNOWN_EXCEPTION.getCode(),BizCodeEnume.UNKNOWN_EXCEPTION.getMsg());}}测试结果
在gulimall-common中新建valid包,里面新建两个空接口AddGroup,UpdateGroup用来分组
给校验注解,标注上groups,指定什么情况下才需要进行校验
如:指定在更新和添加的时候,都需要进行校验
@NotEmpty @NotBlank(message="品牌名必须非空",groups={UpdateGroup.class,AddGroup.class}) privateStringname;在这种情况下,没有指定分组的校验注解,默认是不起作用的。想要起作用就必须要加groups。
@RequestMapping("/save")publicRsave(@Valided({AddGroup.class})@RequestBodyBrandEntitybrand){...}默认情况下,在分组校验情况下,没有指定指定分组的校验注解,将不会生效,它只会在不分组的情况下生效4、自定义校验gulimall-common中
publicclassListValueConstraintValidatorimplementsConstraintValidator{privateSetset=newHashSet<>();@Overridepublicvoidinitialize(ListValueconstraintAnnotation){int[]value=constraintAnnotation.vals();for(inti:value){set.add(i);}}@OverridepublicbooleanisValid(Integervalue,ConstraintValidatorContextcontext){returnset.contains(value);}}关联自定义的校验器和自定义的校验注解(可以指定多个不同的校验器,适配不同类型的校验)关联校验器和校验注解:在校验注解的@Constraint注解上关联校验器
校验注解添加到showStatus上,进行测试
是商品信息聚合的最小单位,是一组可复用、易检索的标准化信息的集合,该集合描述了一个产品的特性。
iphoneX是SPU、MI8是SPUiphoneX64G黑曜石是SKUMI88+64G+黑色是SKU
个人理解:
商品介绍、规格包装这些都是spu,每一个spu下的所有的sku都具备同样的属性。
颜色和内存都是销售属性,因为这个是决定每一个sku的不同的价格的。决定sku的属性我们叫做销售属性。每一个sku的商品介绍的图片和文字这些都是一样的。
基本属性就是指的主体中的型号或者是长度等。这些都是基本属性。
如下图,这个是属性分组的效果展示。
从老师给的课件资源中找到sys_menus.sql,将其复制后,在数据库gulimall_admin中进行找到sys_menus这张表进行完善。即可出现。
注意这里有一个坑:就是看清楚sql文件中的语句是否和你数据库中对应的表的名称一样,否则不管运行多少次sql都不会生效。
Can'tresolve'./attrgroup-add-or-update'in'C:\Users\hxld\Desktop\renren-fast-vue\src\views\modules\product'
解决办法:
原来是绝对路径,后面改为相对路径即可。错误原因是因为版本问题可能。
我们要实现的功能是点击左侧,右侧表格对应显示。
父子组件传递数据:category.vue点击时,引用它的attgroup.vue能感知到,然后通知到add-or-update。
在category.vue中的树形控件绑定点击事件@node-click="nodeclick"
node-click方法中有三个参数(data,node,component),data表示当前数据,node为elementui封装的数据
点击之后向父组件发送事件:this.$emit("tree-node-click",...)…为参数
//组件绑定事件//methods中新增方法 nodeclick(data,node,component){console.log("子组件categroy的节点被点击:",data,node,component);this.$emit("tree-node-click",data,node,component);//向父组件发送tree-node-click事件}父组件接收事件//引用的组件,可能会发散tree-node-click事件,当接收到时,触发父组件的treenodeclick方法//methods中新增treenodeclick方法,验证父组件是否接收到treenodeclick(data,node,component){console.log("attrgroup感知到category的节点被点击:",data,node,component);console.log("刚才被点击的菜单Id",data.catId);},3、启动测试
接口地址:/product/attrgroup/list/{catelogId}
@RequestParam:获取请求参数的值,和方法形参绑定,并自动赋值
@PathVariable:获取路径参数中的值,和方法形参绑定,并自动赋值
修改前端代码
treenodeclick(data,node,component){//必须是三级分类,才显示属性if(data.catLevel==3){this.catId=data.catId;this.getDataList();}},4、数据库中新增数据,进行测试。6.3.5属性分组新增功能新增时,父id改换为选择框
我们发现可以选择分类,但是分类显示的是空白,这个是因为显示的属性是label,通过props属性进行绑定
原因是:el-cascader绑定的dataForm.catelogId是一个数组,其中包含选择框的父节点id和自己的id。而我们要提交的只是他自己的id。
我们要设置选择进行修改的时候将原来本属于这个原信息回显出来。
@ConfigurationpublicclassMybatisPlusConfig{//最新版@BeanpublicMybatisPlusInterceptormybatisPlusInterceptor(){MybatisPlusInterceptorinterceptor=newMybatisPlusInterceptor();interceptor.addInnerInterceptor(newPaginationInnerInterceptor(DbType.H2));returninterceptor;}}上面这个是最新版本的分页插件的配置信息。
下面这个是老师课件上的配置信息
@Configuration@EnableTransactionManagement//开启事务@MapperScan("com.atguigu.gulimall.product.dao")publicclassMyBatisConfig{//引入分页插件显示页码@BeanpublicPaginationInterceptorpaginationInterceptor(){PaginationInterceptorpaginationInterceptor=newPaginationInterceptor();//设置请求的页面大于最大页后操作,true调回到首页,false继续请求,默认falsepaginationInterceptor.setOverflow(true);//设置最大单页限制数量,默认500条,-1不受限制paginationInterceptor.setLimit(1000);returnpaginationInterceptor;}}6.3.8关联分类1、前端代码拷贝这一部分前端代码我们直接将老师发的课件的全部复制到我们自己的renren-fast-vue文件夹下.
根据传过来的brandId,查找所有的分类信息
CategoryBrandRelationController
保存的时候,前端传过来brandid和categoryid,存储的时候还要存brandName和categoryName,所以在保存之前进行查找.
/***保存*/@RequestMapping("/save")publicRsave(@RequestBodyCategoryBrandRelationEntitycategoryBrandRelation){ categoryBrandRelationService.saveDetail(categoryBrandRelation);returnR.ok();}CategoryBrandRelationServicevoidsaveDetail(CategoryBrandRelationEntitycategoryBrandRelation);CategoryBrandRelationServiceImpl@OverridepublicvoidsaveDetail(CategoryBrandRelationEntitycategoryBrandRelation){LongbrandId=categoryBrandRelation.getBrandId();LongcatelogId=categoryBrandRelation.getCatelogId();//查询详细名字和分类名字BrandEntitybrandEntity=brandDao.selectById(brandId);CategoryEntitycategoryEntity=categoryDao.selectById(catelogId);categoryBrandRelation.setBrandName(brandEntity.getName());categoryBrandRelation.setCatelogName(categoryEntity.getName());this.save(categoryBrandRelation);}4、要对品牌(分类)名字进行修改时,品牌分类关系表之中的名字也要进行修改品牌名字修改同时修改关系表数据BrandController
@RequestMapping("/update")//@RequiresPermissions("product:brand:update")publicRupdate(@Validated(value={UpdateGroup.class})@RequestBodyBrandEntitybrand){ brandService.updateByIdDetail(brand);returnR.ok();}brandServiceImpl
@AutowiredCategoryBrandRelationServicecategoryBrandRelationService;@Transactional@OverridepublicvoidupdateByIdDetail(BrandEntitybrand){//保证冗余字段的数据一致this.updateById(brand);if(!StringUtils.isEmpty(brand.getName())){//如果修改了名字,则品牌分类关系表之中的名字也要修改categoryBrandRelationService.updateBrand(brand.getBrandId(),brand.getName());//TODO更新其他关联}}CategoryBrandRelationServiceImpl
@OverridepublicvoidupdateBrand(LongbrandId,Stringname){CategoryBrandRelationEntitycategoryBrandRelationEntity=newCategoryBrandRelationEntity();categoryBrandRelationEntity.setBrandName(name);categoryBrandRelationEntity.setBrandId(brandId);this.update(categoryBrandRelationEntity,newQueryWrapper().eq("brand_id",brandId));}分类名字修改同时修改关系表数据CategoryController
/***修改*/@RequestMapping("/update")//@RequiresPermissions("product:category:update")publicRupdate(@RequestBodyCategoryEntitycategory){ categoryService.updateCascade(category);returnR.ok();}CategroyServiceImpl
@AutowiredCategoryBrandRelationServicecategoryBrandRelationService; @Transactional@OverridepublicvoidupdateCascade(CategoryEntitycategory){this.updateById(category);categoryBrandRelationService.updateCategory(category.getCatId(),category.getName());}CategoryBrandRelationDao
voidupdateCategroy(@Param("catId")LongcatId,@Param("name")Stringname);CateBrandRelationDao.xml
UPDATE`pms_category_brand_relation`SETcatelog_name=#{name}WHEREcatelog_id=#{catId}6.4规格参数模糊查询全部
规格参数新增时,请求的URL:RequestURL:/product/attr/save现在的情况是,它在保存的时候,只是保存了attr,并没有保存attrgroup,为了解决这个问题,我们新建了一个vo/AttrVo,在原AttrEntity基础上增加了attrGroupId字段,使得保存新增数据的时候,也保存了它们之间的关系。
通过"BeanUtils.copyProperties(attr,attrEntity);"能够实现在两个Bean之间拷贝数据,但是两个Bean的字段要相同
当有新增字段时,我们往往会在entity实体类中新建一个字段,并标注数据库中不存在该字段,然而这种方式并不规范。比较规范的做法是,新建一个vo文件夹,将每种不同的对象,按照它的功能进行了划分。
查看前端返回的数据,发现比数据库中的attr多了attrGroupId字段,所以新建AttrVo
上面的保存的save方法只是逆向生成代码中最简单的,没有任何附加作用,所以我们进行修改。
所属分组修改成功,数据库中已经成功,但是页面不显示的问题?
目前暂时解决办法是在:规格参数那新增的时候选择到销售属性。在规格参数中新增的时候选择到销售属性即可。
我们不难发现获取销售属性和规格参数的差别就是sale和base的区别,其他后面的都是差不多的,所以我们可以在路径变量中添加{attrType},使得同时用一个方法查询销售属性和规格参数。
注意点:销售属性,没有分组信息,所以我们在复用方法的时候要进行判断到底是销售属性还是规格参数。
当新增/修改规格参数时,会在attrAttrGroupRelation表之中新增数据,但是销售属性没有分组信息。所以在新增/修改时,进行判断
在Common中新建类ProductConstant,用来商品服务中的保存常量
@GetMapping("/{attrgroupId}/noattr/relation")publicRattrNoRelation(@PathVariable("attrgroupId")LongattrgroupId,@RequestParamMapparams){PageUtilspage=attrService.getNoRelationAttr(params,attrgroupId);returnR.ok().put("page",page);}2、AttrServiceImpl当前分组只能关联自己所属分类里面的所有属性当前分组只能关联别的分组没有引用的属性-当前分类下的其他分组
-这些分组关联的属性
-从当前分类的所有属性中移除这些属性
注册到服务注册中心的套路和配置相应的网关路由前面已经详细讲解过,这里不再赘述。
在用户系统中的会员等级中添加上面的几个等级名称。
请求参数catId不是路径变量,所以我们使用@RequestParam("catId")从请求中获取。
因为响应数据不是page分页数据,所以我们直接使用list即可。
返回的数据不是entity中的所有数据,我们就可以通过封装为vo来进行解决返回数据的问题。
前端项目中没有引入pubsub.js,这个是只要按照老师进度,都会范的错误,接下来进行解决。
下载.exe安装包进行安装
切换版本之后将本地前端项目renren-fast-vue中的node-modules这个文件夹给删除就好了。后面这个node-modules文件夹中的内容会在你进行任何Npm操作时自动生成。
进行npminstall--savepubsub-js之后继续报错,查看报错原因:node-sass版本问题。
再次执行安装之后不报错了。
如下图:
这里面的快速展示我们可以不需要全部都选,因为有一些无关紧要的设置我们可以不在商品介绍页面进行展示。还有一些销售属性的attrType是2,既是销售属性也是规格参数,所以有些可以选,有些可以不选。
注意
因为我们在数据库中设计某些表的时候都是设置了主键的,我们拿pms_attr这张表为例。如果我们后期想要进行数据的添加(直接导入老师sql中的数据,避免我们自己手动添加出错),但是因为设置了主键的原因,你的attr_id这个主键是会不连续的。这个即使删除数据也没办法。那么如何解决下面的这个问题呢?
从接口文档中的响应数据来看,我们发现返回的data数据中有一个attrs[]数组列表,返回的数据是AttrGroupEntity上增加了一个attrEntity.所以我们新建一个AttrGroupWithAttrsVo
我们可以在发布商品的时候设置一些折扣优惠信息。
当发布商品的所有信息都选择好之后,我们查看控制台,得到一串json数据。将其拷贝,通过json工具将我们得到的数据进行抽取,按照我们指定的包名和总的vo名,生成相应的java实体类。
微调vo:我们查看生成的代码,发现有get和set方法,我们这个地方可以将这些方法进行去掉,然后使用lombok中的@Data注解即可。把所有id字段改成Long类型,把所有double类型改成BigDecimal类型。
真实项目中需要进行校验,校验是当传过来的数据不符合之后,给前端返回相应的异常错误代码。
这个地方为了防止和老师所讲代码有差别,直接将老师课件中的代码复制到product包下的vo包中即可。
保存商品涉及到多个表之间的关系。我们先要搞清楚到底需要保存哪些东西?
保存spu基本信息---pms_spu_info
保存spu的描述图片---pms_spu_info_desc
保存spu的图片集---pms_spu_images
保存spu的规格参数---pms_product_attr_value
保存spu的积分信息---gulimall_sms->sms_spu_bounds
保存spu对应的所有sku信息
-sku的基本信息---pms_sku_info
-sku的图片信息---pms_sku_images
-sku的销售属性信息---pms_sku_sale_attr_value
-sku的优惠、满减等信息---gulimall_sms->sms_sku_ladder/sms_sku_full_reduction/sms_member_price
这些基础是要将数据库中表的字段要熟悉
SpuInfoController
/***保存*/@RequestMapping("/save")publicRsave(@RequestBodySpuSaveVovo){// spuInfoService.save(spuInfo);spuInfoService.saveSpuInfo(vo);returnR.ok();}
(此处代码是下一步debug之后完整可正常运行的代码,和老师讲课正常章节有所出入)
1.SpuInfoDescServiceImpl@OverridepublicvoidsaveSpuInfoDesc(SpuInfoDescEntitydescEntity){this.baseMapper.insert(descEntity);}2.SpuImagesServiceImpl@OverridepublicvoidsaveImages(Longid,Listimages){if(images==null||images.size()==0){}else{Listcollect=images.stream().map(img->{SpuImagesEntityspuImagesEntity=newSpuImagesEntity();spuImagesEntity.setSpuId(id);spuImagesEntity.setImgUrl(img);returnspuImagesEntity;}).collect(Collectors.toList());this.saveBatch(collect);}}3.ProductAttrValueServiceImpl@OverridepublicvoidsaveProductAttr(Listcollect){this.saveBatch(collect);}4.SkuInfoServiceImpl@OverridepublicvoidsaveSkuInfo(SkuInfoEntityskuInfoEntity){this.baseMapper.insert(skuInfoEntity);}7、创建TO对于远程调用,其实就是跨表操作。我们可以创建一个TO来做远程调用
@DatapublicclassSkuReductionTo{privateLongskuId;privateintfullCount;privateBigDecimaldiscount;privateintcountStatus;privateBigDecimalfullPrice;privateBigDecimalreducePrice;privateintpriceStatus;privateListmemberPrice;}同时将json自动生成的memberPrice这个vo也复制到common中
在product包中新建fegin.CouponFeignService用来远程调用Coupon服务
一共调用了两个服务"coupon/spubounds/save"和"coupon/skufullreduction/saveInfo"
这样可以大大减少内存占用。
我们在进行debug的时候,因为我们在上面设置了事务的原因,而mysql默认是可重复读(REPEATABLEREAD),所以我们可以暂时设置隔离级别。
如果我们使用@Transactional,不指定隔离级别,就会使用数据库的默认隔离级别
publicIntegergetCode(){ return(Integer)this.get("code"); }出现问题,保存sku图片时,有些图片是没有路径的,没有路径的图片,无需保存。解决办法:在收集图片的时候进行过滤
解决方法:在保存之前做判断,过滤掉小于等于0的无意义信息(不贴代码了),要注意的是判断BigDecimal进行判断时,要用compareTo函数。如果是普通的,就不需要做判断。
下面举例:
保存的时候出现上面这个原因,我们去控制台中查看得知是调用远程服务超时导致。因为会去nacos中进行寻找,我们所要做的就是等待feign稳定即可。
按照华为mate30pro新增方法,新增一个apple11到数据库中。
我们可以在配置文件中进行设置:
否则点击相应的菜单报404错误
我们打开控制台,点击查询,查看发出的url
然后根据这个url我们进行仓库模糊查询功能的实现。
@RequestMapping("/list")//@RequiresPermissions("ware:wareinfo:list")publicRlist(@RequestParamMapparams){PageUtilspage=wareInfoService.queryPageByCondition(params);returnR.ok().put("page",page);}WareInfoServiceImpl.java(模糊查询)@OverridepublicPageUtilsqueryPageByCondition(Mapparams){QueryWrapperwrapper=newQueryWrapper<>();Stringkey=(String)params.get("key");if(!StringUtils.isNullOrEmpty(key)){wrapper.eq("id",key).or().like("name",key).or().like("address",key).or().like("areacode",key);}IPagepage=this.page(newQuery().getPage(params),wrapper);returnnewPageUtils(page);}设置日志输出级别,方便查看sql语句logging:level:com.xmh:debug6、进行测试测试成功,控制台中的sql如下:
WareSkuServiceImpl
/***skuId:1*wareId:2*@paramparams*@return*/@OverridepublicPageUtilsqueryPage(Mapparams){QueryWrapperqueryWrapper=newQueryWrapper<>();StringskuId=(String)params.get("skuId");if(!StringUtils.isEmpty(skuId)){queryWrapper.eq("sku_id",skuId);}StringwareId=(String)params.get("wareId");if(!StringUtils.isEmpty(wareId)){queryWrapper.eq("ware_id",wareId);}IPagepage=this.page(newQuery().getPage(params),queryWrapper);returnnewPageUtils(page);}这个模糊查询比较简单,自动生成的方法不需要重新修改,只需要在方法实现类中添加一些查询即可。
采购单可以通过采购需求进行创建,一种是指定某个采购人员(在系统管理-管理员中创建),一种是不指定,直接将多个采购需求自动合并成采购单。
测试成功
可以选择需要合并的采购需求,合并到整单,也可以不选择整单id,自动创建新的采购单。
@DatapublicclassMergerVo{privateLongpurchaseId;//整单idprivateListitems;//合并项集合}2、思路梳理分配,就是修改【采购需求】里对应的【采购单id、采购需求状态】,即purchase_detail表
并且不能重复分配采购需求给不同的采购单,如果还没去采购,或者采购失败,就可以修改
在配置文件中对json进行格式化
jackson:date-format:yyyy-MM-ddHH:mm:ss测试成功:
采购单分配给了采购人员,采购人员在手机端领取采购单,此时的采购单应该为新建或已分配状态,在采购人员领取后采购单的状态变为已领取,采购需求的状态变为正在采购。
我们可以使用自己的接口调试工具进行测试。本次调试使用apifox.
@DatapublicclassPurchaseItemDoneVo{privateLongitemId;privateIntegerstatus;privateStringreason;}PurchaseDoneVo
因为这个方法我们在gulimall-product微服务中已经写过了,所以我们直接使用最简单的方法(直接复制controller中的方法)进行远程调用。
对于第四步中的更新库存这个地方,我们使用需要新增一个addStock方法。
当我们点击规格后,不仅会回显原来的规格参数,我们可能还需要队某些规格参数进行修改。
@PostMapping("/update/{spuId}")publicRupdateSpuAttr(@PathVariable("spuId")LongspuId,@RequestBodyListentities){productAttrValueService.updateSpuAttr(spuId,entities);returnR.ok();}2、ProductAttrValueServiceImpl因为修改的时候,有新增有修改有删除。所以就先把spuId对应的所有属性都删了,再新增.