项目分析过程,已经学习思路记录

2023-10-21 19:10

本文主要是介绍项目分析过程,已经学习思路记录,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

作为电商产品经理,你不得不知----电商后台系统-商品中心 - 知乎 (zhihu.com)

raw.githubusercontent.com/elastic/elasticsearch/7.4/docs/src/test/resources/accounts.json

document.querySelector ('bwp-video').playbackRate =0.8

应该区分产品的类型spu,和产品的类型的实例sku,gitee.com/agoni_no/gulimall

后台管理的平台属性

    商品系统商品分类维护0

开始:

    商品种类管理0:是对商品唯一种类归类,通过三级分类的形式去组织查找种类产品信息,该产品类型描述产品的功能特性,是一个抽象层,就产品的spu,一个该类型下的实例产品称位sku,sku具有商品的规格基本属性即分组属性,以及产品的销售属性,即具体区别的分组对应的详细属性,这里的商品种类分级通过字段来实现,level分为父子孙三级,任意一种都可以改变三者关系,在前端的展示,就是根据所有的种类的集合对象通过strem流进行分组,所有level=1的为一级显示为,为二的判断父类的的对象加入该一级分组的实体的chlidren子集,完成所有搜集并显示前端。

商品管理 1

   品牌id,商品名称,商品log,介绍,显示状态,检索首字母,排序,操作等视图

这是商品简单的增删改查,包括商品的关联分类是在已经存在的分类基础上添加的。比如小米这个商品它对应分类是手机,当修改的售后,同样需要商品分类的三级菜单回显修改传入.

需要分页处理添加配置类,加上@EnableTransactionManagement开启事务。

 @Beanpublic PaginationInterceptor paginationInterceptor() {PaginationInterceptor paginationInterceptor = new PaginationInterceptor();// 设置请求的页面,true为回到首页,false为继续请��paginationInterceptor.setOverflow(true);// 设置最大page数paginationInterceptor.setLimit(1000);return paginationInterceptor;}

商品管理里面,默认进入管理页面为查询一页展示数据,通过搜索框可以查询其他数据,可以对展示数据的修改,删除操作。

商品的添加较为麻烦使用到了阿里云的文件上传系统将图片文件保存到远程服务器,使用非对称加密,通过发放的公钥兑换自己的公钥加上已经有的私钥,前端通过请求文件上传,获取到签名字段,直接请求阿里云完成图片上传功能,验证签名解决跨域后完成上传。

关联分类功能  (清楚品牌列表下的功能,向中间表商品分类和品牌关联表中加入id,和对应的name属性,好处避免大数据关联查询,在添加中间表品牌和商品分类中会用到各自的mapper对象完成实体结合关联类CategoryBrandRelationEntity的分段数据查询,查到各自的name属性维护到中间表中去代码如下)

        每一个品牌都有所属的分类,比如华为、小米、vivo为手机分类下的品牌,一个品牌可以关联多个分类,比如小米关联了手机,电视 、手表等分类,一个品牌对应多个分类,一个分类可以对多个品牌,整体是一个多对多的关系。所以使用中间关联表category_brand_relation来维护多对多关系。

当点击该商品关联分类时会请求分类产品品牌关联表加上brandId查出品牌下的所有分类,即获取品牌的关联分类(文档地址15、获取品牌关联的分类 - 谷粒商城)该分类就是分类品牌关联表的实体对象,它封装了详细信息,返回前端显示。品牌下的所有关联分类查询完成。

     @Overridepublic List<CategoryBrandRelationEntity> getBrandCateRelation(Long brandId) {//baseMapper相当于代理类,mybassPlus通过自定义的服务实现类,把生成的代理对象封装到//ServiceImp中去,第一个参数就是类型,定义了该类型的变量baseMapper,这是生成的代理对象List<CategoryBrandRelationEntity> relationEntityList = baseMapper.selectList(new QueryWrapper<CategoryBrandRelationEntity>().eq("brand_id", brandId));return relationEntityList;}

新增关联关系时会发送品牌id和分类id到关联表中,比如华为-》手机分类;华为-》电脑分类;小米-》手机;小米-》电脑,那么这些品牌所对应的商品分类就可以衍生出多种产品如手机分类:小米-》手机-》(小米5、小米7、小米11),小米11为spu,青春版对应sku是具体实例,定义了销售属性如颜色,尺寸。除此之外中间表还要保存brand_name、catelog_name添加两个冗余字段,避免做关联查询,多表关联影响性能。

  /*** 保存 品牌和分类的id和名称** @param categoryBrandRelation*/@Transactional@Overridepublic void saveIdAndName(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());baseMapper.insert(categoryBrandRelation);}

完成后在品牌列表中点击关联如选择华为品牌关联到(手机/手机通讯/手机;家用电器/大家电)分类中就生成两个表中已经存在的记录建立关系。生成记录到关联表中,其中对商品分类的修改和品牌的修改,都需要同步到中间表防止无效的数据存在。

    属性分组管理 2

    规格参数管理 3

    销售属性管理 4

开始属性分组管理 2

属性分组-商品规格属性-销售属性 三级分类 进行关联

商品类别以三级分类的形式组织和搜索,特定分类下的商品,携带着产品分类类别id查找属性分组表中对应唯一类别商品的多个id属性,从属性分组表获取id后,去属性分组和属性关联表中查找具体的属性,这时一个分组id找出多个属性,在用关联的属性id,去查属性的具体信息。(通过中间表的形式较少联合查询),一个商品是一个spuid,一个spuid对应一组分组属性,一种分组属性对应多个属性类别和属性值(如屏幕组->(属性大小:12、清晰度:20))->cpu组->(cpu型号:七零、cpu核心数:7等),先去查商品的分类,进行三级展示,通过分类找出该产品的sku的规格基础属性即分组属性,更具分组找到该组下的所有属性字段和属性值,有了属性id再去sku-arry关联表中获取具体属性的sku产品的属性值。

在属性管理对象中,依赖商品分类查询即商品分类的三级菜单,当点击商品即spu时会传如catgoryId去到属性关联表中的属性分组表,属性分组表中有属性id,再通过属性id查出每组属性的多个商品属性字段

用户没有通过三级分类,去点击,而是通过搜索框输入,就会传一个key的属性字段,此时是自定义查询,用于多条件模糊查询,比如属性组id或属性组名称满足都能查出结果,通过构建查询条件。QueryWrapper<AttrGroupEntity> wrapper = new QueryWrapper<>();来构建多条件查询

        wrapper.and((obj) -> {obj.eq("attr_group_id", key).or().like("attr_group_name", key);});if (!StringUtils.isEmpty(key)) {wrapper.and((obj) -> {obj.eq("attr_group_id", key).or().like("attr_group_name", key);});}if (catelogId == 0) {IPage<AttrGroupEntity> page = this.page(new Query<AttrGroupEntity>().getPage(params),wrapper);return new PageUtils(page);} else {//如果是三级分类去查,传入了id就按id查找,否则如上查出所有商品以及对应的属性组wrapper.eq("catelog_id", catelogId);IPage<AttrGroupEntity> page = this.page(new Query<AttrGroupEntity>().getPage(params),wrapper);return new PageUtils(page);}

构造查询wrapper要注意在where后的=用eq方法在加and时并列上(多条件满足)就使用and方法加函数式接口条件判断,obj是表中要匹配的每个字段。

让后通过当前Service的实现类this.page的功能去查询出结果集,并把结果其和查询的条件封装成QueryUtil返回给前端,前端查询/product/attrgroup/listt/225?page=1&key=aaa;就能查到特点商品的分组信息,包括sku的销售属性,sku的基本规格属性。

目前没有数据,就在arrGroup_id中添加catelog_id中添加225三级菜单对应商品种类id,并对应属性组名字为主体,添加多个属性组(主体,基本信息)商品种类id都是255,完成一对多查询,当三级菜单点击后会查出结果,并把数据展示到右侧的表格当中,如果直接搜索是通过key字段在查属性组表中模糊查询,id字段和属性分组名字查到都可,没有categoryId就查出上面这些,包括不同商品的分组属性信息

分组属性的添加

        通过级联选择器选择来维护添加属性分组,属于当前产品种类的哪一个sku,在该产品实例下定义添加分组,一组分组可能对应多个组内属性,如先择手机分类(分类和商品关系是一对多,对属性来书是一对一的分组,分组对多多),也就是同一个手机种类下有相同的属性分组,不同手机有相同的分组,分组和产品id的约束可能,组内可能有不同的小的属性,他们的属性对应着销售属性。

添加分组属性:

 查出商品的三级分类,在三级分类的基础上,把商品回显,添加属性组时添加该商品属性所属于的的catelogId,属性组名,和属性组生成iD,对于组内的属性,是实例共有或特有的。

这个时候属性组内该种类的商品就有了一个分组属性,比如手机对应的categorId=255就多了一条属性记录,在添加分组属性时也会出现商品分类的三级菜单,修改同样如此。

修改回显问题,当修改该属性分组时,无法回显商品所属的产品种类,只能得到产品种类id255,需要去categoryService查出所对应的商品信息对象CategoryEntity,判断该对象是否有父类对象,递归形式获取,把id加入集合,有该集合关系后就在前端已经存在的数据集中获取指定id的商品父名称/商品子名称/商品孙子名称回显成功。该回显组件是一个数组,当属性组添加时会收集父类商品id/子类商品id/孙子商品id,我们要的是孙子商品id,但修改回显时需要这些id所对应的路径,即产品种类名称,当回显后又回到了默认的熟悉的商品三级分类,且还是手机选项上,我们可以重新选择一个种类,将屏幕属性改为三级分类改为移动数据,那么属性分组中对应的商品种类CategoryId由255变为231,然后属性页面,重新展示数据集,默认查所有属性组记录。如图

p76:

完成规格属性 

        在属性规格页面中添加属性, 属性名入网型号,属性参数(可选类型规格参数,销售参数),在所属分类中又出现商品分类的三级菜单,选择手机,在所属分组中出现该分类对应的属性分组表中的字段,这里是数组形式给出它的分类下的属性组的所有选项,这里选择基本属性,添加,添加成功,发现属性记录表中有数据,属性和属性分组表的中间表却没有值,也就是AttrEntity,没有关联上AttrAttrgroupRelationEntity对象,中间表也就没有数据,即便前端传的有,解决是在AttrEntity加字段attrGtoup并加上@TableField(exist=false)表示实体表不存在的字段。这里推荐使用VO对象做封装避免对数据库表对应的实体做任何的修改。VO视图对象用于封装前端的数据,TO用于微服务中传输的对象,BO是多个mapper查询结果汇总返回前端。修改后,重写属性添加逻辑如下:

  @Transactional@Overridepublic void saveVo(AttrVo attr) {AttrEntity attrEntity = new AttrEntity();if (attrEntity != null) {//spring提供的将对象相同字段的值进行赋值BeanUtils.copyProperties(attr, attrEntity);}baseMapper.insert(attrEntity);// 保存分组关系if (attr.getAttrType() == ProductConstant.AttrEnum.ATTR_TYPE_BASE.getCode() && attr.getAttrGroupId() != null) {AttrAttrgroupRelationEntity groupEntity = new AttrAttrgroupRelationEntity();groupEntity.setAttrGroupId(attr.getAttrGroupId());groupEntity.setAttrId(attrEntity.getAttrId());attrAttrgroupRelationDao.insert(groupEntity);}}

重新添加规格属性页面中添加属性上市年份,选择分类为手机,属性组为屏幕。最终在属性和属性分组的中间表attr_attrGroup_relation中生成一条数据,注意点这个是中间表不要和属性分组表attr_group弄混,想查看多个表中的关系可以到数据库中查看。

attr表

当要展示商品的属性时先展示属性分组,再展示分组下的多个属性比如分组1为屏幕,该组下有上市年份属性,对应值1999. 继续完成规格参数页面的删改分页查功能能,这些比较简单,拿分页查询为例,当查询带有分类条件,查特点商品下的属性就动态拼接上,没有时不查询,然后获取查询的key,在当前属性表中多字段模糊查询,注意查询条件后面的and拼接当作为一个()时表明后面还有条件,此时QueryWapper就用and的接口编程里面传当前wapper对象继续条件判断相当于where  t1 and (t2 and t3) and t4,加括号就是做整体条件判断。分页查询实现如下:

 @Overridepublic AttrResVo getAttrInfo(Long attrId) {AttrEntity attrEntity = baseMapper.selectById(attrId);AttrResVo attrResVo = new AttrResVo();BeanUtils.copyProperties(attrEntity, attrResVo);AttrAttrgroupRelationEntity relationEntity = attrAttrgroupRelationDao.selectOne(new QueryWrapper<AttrAttrgroupRelationEntity>().eq("attr_id", attrEntity.getAttrId()));// 将attrGroupId 和 GroupName 返回if (relationEntity != null) {attrResVo.setAttrGroupId(relationEntity.getAttrGroupId());AttrGroupEntity attrGroupEntity = attrGroupDao.selectById(relationEntity.getAttrGroupId());if (attrGroupEntity != null) {attrResVo.setGroupName(attrGroupEntity.getAttrGroupName());}}// 将catelogPath和 catlogName 返回Long[] categoryPath = categoryService.findCategoryPath(attrEntity.getCatelogId());attrResVo.setCatelogPath(categoryPath);CategoryEntity categoryEntity = categoryDao.selectById(attrEntity.getCatelogId());if (categoryEntity != null) {attrResVo.setCatelogName(categoryEntity.getName());}return attrResVo;}

 查询属性时携带了其他表的字段如商品分类表的分类名称,分组表的分组名,再封装一个VO继承属性Attr_Entity并添加上catelogName;groupName;字段,在查询出基础表的结果后,再去根据属性所属的分类字段查出分类名,和当前属性id在属性和属性分组表的中间表中属性id所对应的属性组id,有了 属性组id就能查出给属性属于哪个分组名称。

属性修改功能:

       两个重要的点,所属分类是一个三级选择器,当点击修改时,要求数据回显,定义一个查询属性的接口,里面扩展封装返回的VO对象包含分类的数组型路径以及分类下的分组属性的id对应的name进行回显,当点击修改时触发查询回显数据,查出属性的基础字段,在根据管理属性查询其他字段封装成VO返回前端,问题点,完整的路径是数组id的形式如何显示三级分类的?其实这是一个局部请求左侧有一个三级菜单,前端有js数组对象,要什么结果就从js结果集中获取,回显时只是选择器选择了这条属性对应的唯一内容,主要强调的默认展示。没有携带过多内容信息,在分类选择器上完全可以用js的数据,然后属性分组也是js保存好的,动态回显,回显后完成后台修改,修改获取前端VO拷贝基础表对象,完成修改,其他属性交给中关联表去查,比如拿属性id去查中间表attr_attrGroup_relation,获取属性组id到属性组表更新前端对象的相关属性,问题当前没有属性分组id的话,也就是拿属性id去属性属性分组关联表中查出0条,当修改时没有数据可以修改,这里当查到结果大于0表示修改,查出结果等于0表示添加,商品分类不需要处理,没有关联关系,只是所属关系,修改了就没有所属关系,其他无字段处理

p79:完成销售属性

  前面完成的是规格属性,这里完成销售属性,属性都存放在attr表中用字段attr_type来区分,0销售属性,1基本属性 2即是销售属性又是基本属性2可以放弃使用。

完成销售属性页面数据查询,销售属性和规格属性基本相同,只是字段不同,这里使用动态的路径变量去区分,这两个查询共用一个方法,注意点销售属性是不存在分组的,也就是在查询中,避免走属性规格一样的查中间表条件,对于添加CatelogId查询,是三级分类菜单,是一个可选值的前端视图js对象,该对象的用户的选择参数,定性地加入请求中,如果你没选择默认值0,后端忽略,如果选择分类就只查分类,如果选择的非三级菜单,就默认查所有,分类菜单的意义就是方便分类定位,其实有用的是孙子分类,Math.ceil向上取整,判断是销售还是规格属性用到了枚举类型

  @Overridepublic PageUtils queryByCid(Map<String, Object> params, Long catelogId, String attrType) {QueryWrapper<AttrEntity> wrapper = new QueryWrapper<>();String key = (String) params.get("key");if (!StringUtils.isEmpty(key)) {wrapper.and((obj) -> {obj.eq("attr_id", key).or().like("attr_name", key);});}PageUtils pageUtils = null;IPage<AttrEntity> page = null;if (catelogId != 0) {wrapper.eq("catelog_id", catelogId);}wrapper.eq("attr_type", "base".equalsIgnoreCase(attrType) ? ProductConstant.AttrEnum.ATTR_TYPE_BASE.getCode() : ProductConstant.AttrEnum.ATTR_TYPE_SALE.getCode());page = this.page(new Query<AttrEntity>().getPage(params),wrapper);pageUtils = new PageUtils(page);List<AttrEntity> records = page.getRecords();// 获取当前页所有结果集需要将groupName 和 catelogName 一同返回List<AttrResVo> attrResVoList = records.stream().map((attrEntity) -> {AttrResVo attrResVo = new AttrResVo();BeanUtils.copyProperties(attrEntity, attrResVo);//可能用到可能用不到,所以放在这里AttrAttrgroupRelationEntity relationEntity = attrAttrgroupRelationDao.selectOne(new QueryWrapper<AttrAttrgroupRelationEntity>().eq("attr_id", attrEntity.getAttrId()));if ("base".equalsIgnoreCase(attrType)) {//带属性组名称的规格属性查询,销售属性不带就不查询if (relationEntity != null && relationEntity.getAttrGroupId() != null) {AttrGroupEntity attrGroupEntity = attrGroupDao.selectById(relationEntity.getAttrGroupId());attrResVo.setGroupName(attrGroupEntity.getAttrGroupName());}}CategoryEntity categoryEntity = categoryDao.selectById(attrEntity.getCatelogId());if (categoryEntity != null) {attrResVo.setCatelogName(categoryEntity.getName());}return attrResVo;}).collect(Collectors.toList());pageUtils.setList(attrResVoList);return pageUtils;}

wrapper.and相当于where 对当前条件加()让后在拼接一个and ,(内部条件)and形式,查询、更新有不同的wrapper,上面的查询唯一标识是类路径上的参数判别,如果是base表明是规格参数(基本属性)属性,三元判断否则就是销售属性完成条件查询,在保存属性时销售属性不用属性分组,不用关联中间表,获取spu规格,spu是产品的描述对象,并不是实例对象,实例对象对应sku,规格参数外,也有属性参数。(目前属性和属性组有中间表,商品分类和品牌有中间表)

在修改时只修改base类型的分组属性的中间表,销售属性不修改,查询的时候也是base有关联关系表要查带上属性id查中间表的属性分组id,然后在属性分组表中年查出name字段,分类可以直接查询,无中间表。添加属性时可以添加多个值,选择类型为销售属性时不会向中间表中建立双表关联,添加一个,在attr_attrgroup_relation表中无attrId,无attr_attrgruopId,不用维护关系,添加删除都要判断,修改是可以修改所属的属性类型(这是难点和复杂点)。

查询当前属性对应的属性组的其他属性,也就是先获取属性去中间表中查找。

p81:

查询当前属性组可以用来绑定的其他属性,并且要求属性同是一个分类级别下没有被其他分组绑定的属性,解决思想,根据分类id在当前属性组表查出分类的所有属性主属性,包括当前的属性组,会返回一个attrgroupEntity集合,通过stream搜集他们的id,在attr_attrgroup_alation中查出所有组中的属性id,最终获取当该分类下所有已经绑定了的属性,查出未绑定的属性,就直接查属性表条件在同一个分类并且不在上面id中的属性,完成查询返回前端的选择器上可以选择没有绑定的属性,选择后会在非未绑定选择器的下方属性已经绑定的属性列表。点击关联时会显示已经关联的属性,并且有移除的功能,它是简单的中间表维护的关系,直接删除中间表,移除后新增关联关系会看到刚才移除的属性。

p83 商品的发布流程(难点可以是核心点)

流程 基本属性-》规格属性-》销售属性-》sku属性-》保存成功

发布商品

基本属性-商品的描述、选择分类、选择品牌、商品重量、设置积分、成长值、商品介绍图片、商品图集。

规格参数:

销售属性:颜色(可以选择也可以创建自定义颜色)内存4g、8g、16g、版本8+256g等

sku:当选择了黑色和银河色并且有8+255、8+128就会形成组合式的sku,比如黑色+8+255/128,

银河色+8+255/128生成四条sku信息,在该面板会额外添加副标题、价格展开设置更多,会有折扣信息满多少件打多少折,设置满多少元减多少元。选择图集可以选择图片默认展示信息,保存商品就会在商品表中添加四条记录。前面录入spu信息,后面录入了sku信息最终是一个大json串,发送到后端保存。商品的图集是针对sku的,是产品实例具体的图片信息。

s

基本信息需要跨服务查询,

获取分类关联的所有品牌

当选择商品分类后就要查询商品的分类id所关联的品牌,在下面的选择器上可以共选择。

涉及点:

        商品分类表category、品牌表、商品分类和商品品牌表,现在我们要拿到商品分类id去查中间表,获取中间实体,在通过stream进行映射返回出关联的所有品牌id集合,再用品牌接口批量查数据库,代码实现如下:

 /*** 根据catelogId获取品牌信息** @param catId* @return*/@Overridepublic List<BrandEntity> getBrandByCatlogId(Long catId) {List<CategoryBrandRelationEntity> brandRelationEntityList = baseMapper.selectList(new QueryWrapper<CategoryBrandRelationEntity>().eq("catelog_id", catId));List<Long> brandIds = brandRelationEntityList.stream().map(item -> {return item.getBrandId();}).collect(Collectors.toList());List<BrandEntity> brandEntityList = brandDao.selectBatchIds(brandIds);return brandEntityList;}

它仅仅是录入基本信息时实时数据回显到选择器上,选择你添加商品的品牌,下一步到规格参数

规格参数的录入:进入下一步时,就根据分类去查询分类下的属性所有分组,分组下关联的所有属性。每个属性组对应一个视图,如图在属性组中查到了主体、基本信息、主芯片、分别对应页面去填写该组内属性字段的值,这些属性根据商品是否需要,可以选择性的录入。

在录取基础属性的时候,同样是查询的方式去先查询基础属性的值,然后放入选择器中共用户选择,下一步进入销售属性,会产生多个sku实例,主要是前面选择的做了笛卡尔积,4个颜色,两个版本,就形成8个sku,在这里可以修改标题信息,方便后面做搜索,修改副标题等,最右侧可以查看之前选择的信息,比如会员优惠具体信息,银牌会员金牌会员有优惠,普通会员无优惠,还可以上传每一个sku的图片集,包括正面图片,背面图片等信息。

前端会生成大量的json数据我们解析这些数据,定义一个对象来接收这些信息,把json转为java的VO对象,当出入到后端后,对相应的数据进行处理,这里注意VO的属性的数据类型对于小数的一定使用BigDecimal。防止属性拷贝的时候数据类型不一致导致数据精度丢失。

保存产品的录入信息,调用SpuInfoController下的save方法,使用post请求让spring

mvc自动封装@RequestBody SpuSaveVo对象,保存分两个步骤,一个是保存spu基本信息,一个保存spu的图片集,数以当前有两个表spu_images,spu_info表,使用spu_info_desc关联表的方式,在查询时将数据组合一起,该表只有spu_id,decript两个字段,我们先保存spu的基本信息,让后保存spu的描述信息,通过描述信息定位spu的图片集信息,并保存他们。1保存spu的基本信息,在spu_info2保存spu的图片描述在spu_info_desc,3保存spu的图片集在表spu_image4保存spu的规格参数(去操作另外product_attr_value表它的spu_id,属性id,属性名,属性值信息)5:保存spu的积分信息sms_spu_bounds;6保存当前sku对应的所有sku信息,有很多;6.1sku的基本信息:sku_info;6.2;sku的图片地址信息,默认图片信息sku_images表中6.3sku的销售属性信息sku_sale_attr_value;6.4sku的优惠信息,满减等信息,跨库gulimall_sms-》sms_sku_ladder(),sms_sku_full_reduction满多少减多少表,sms_member_price会员表,整个保存完就成功

这个地方对整体的流程都清除了可以不看视屏,直接去看代码,代码看懂跳过这部分,难点就是设计表之间的关系,sku,spu到底解决什么问题。要理解流程就要弄清他们实体直接的关系,商品模块,现在要弄清15个实体都是如何依赖的,再谈具体的小细节。

,在这之上有如保存sku信息,就要对8个具体产品实例的sku信息进行保存,先保存sku的基础表信息,从前端VO集合收集,然后批量插入数据,然后再处理关联表所属的图片资源,知道自己的sku的id集合去向pms_spu_info关联表中加入图片数据,批量保存后完成资源定位,

接下来处理sku的销售属性,也就是前端会提交过来多个属性对象,这些属性对象就有销售属性,销售属性不仅要自身保存外,还要保存在sku_sale_attr_value(表明属性对应的sku,或sku对应具体的属性)

属性要和sku关联采用中间表pms_sku_sale_attr_value的形式,当有属性的时候插入,也要根据属性id,属性名,在中间表插入,并同时插入sku,这样就确定关系。sku和attr是同时存在才具有完整性,由于sku要保存销售属性,产品实例会有多个销售属性,这时通过中间表pms_sku_sale_attr_value,只需要加skuid,和attr_id,就能得到一个skuid,对多个销售属性,也就形成属性组的概念,而属性组又有自己所属,

spu不仅仅是存储单元,而且是优惠活动的处理单元。

商品发布处理商品(巨大的抽象体,不以商品对象为存储)之积分数据保存,需要跨积分服务,这里使用@FeignClient("服务名"),接口方法上加请求注解

要远程保存成长积分和购物积分,就要从VO中解析获取Bound,是前端json解析的对象信息载体,电商中商品是宏观的抽象,是一个大熔炉,spu是产品单元是具体类型的实体,sku是类型实体的一个唯一表现,

关于优惠卷的实体有核心字段像一个把锁来控制优惠系统是否生效

SpuBoundsEntity 下的Integer work,控制产品单元下优惠生效情况[1111(四个状态位,从右到左);0 - 无优惠,成长积分是否赠送;1 - 无优惠,购物积分是否赠送;2 - 有优惠,成长积分是否赠送;3 - 有优惠,购物积分是否赠送【状态位0:不赠送,1:赠送】]

对应成长积分,和购买后获得积分,这些是随商品创建而录入,商品的积分是产品的类型唯一单元,它的积分是在改类型的实体上生效,所以积分必须保存spu产品标准类型id的信息通过TO传输优惠服务系统。

保存sku的优惠信息(区别spu和sku等具有的优惠特性,sku有成长积分,购买积分,sku有优惠打折信息,sku是具体的实体,spu是类型描述体)sku信息保存,就要在优惠服务,保存它的优惠信息,将当前的sku分装成TO传输到优惠系统完成SkuFullReductionEntity 的MemberPriceService和SkuLadderDao服务的优惠信息保存,(两种情况满减打折,和打折),具体步骤如下

1保存满减打折,会员价,或阶梯型打折,获取折扣信息对像服务,创建要保存的实体,将接收的数据属性注入,对需要计算打几折的字段进行计算,我们只保存sku的打折信息的保存,拿到sku的优惠信息后SkuFullReductionEntity,调用它的服务,进行保存,统一打折处理交给SkuFullReductionEntity对象完成,sku的打折信息由SkuFullReductionServiceImpll对象保存,分为 ;2 sms_sku_full_reduction;3 保存会员价格 sms_member_price,具体的实现:

1,sms_sku_ladder为阶梯价格表, 1保存满减价格 sms_sku_ladder

2,sms_sku_full_reduction商品满减信息表, 保存满减信息 ,保存sku时必须保存

3,会员也要维护会员所具有的优惠信息, 保存会员价格 sms_member_price表,对应实体MemberPriceEntity(关键字段,sku具体的商品,会员等级,会员对应价格,是否优惠可以叠加)以及用到的MemberPrice工具收集数据,

@Transactional@Overridepublic void saveSkuReduction(SkuReductionTo skuReductionTo) {// 保存满减价格 sms_sku_ladderSkuLadderEntity skuLadderEntity = new SkuLadderEntity();skuLadderEntity.setSkuId(skuReductionTo.getSkuId());skuLadderEntity.setFullCount(skuReductionTo.getFullCount());skuLadderEntity.setDiscount(skuReductionTo.getDiscount());skuLadderEntity.setAddOther(skuReductionTo.getCountStatus());skuLadderDao.insert(skuLadderEntity);// 保存满减信息 sms_sku_full_reductionSkuFullReductionEntity skuFullReductionEntity = new SkuFullReductionEntity();skuFullReductionEntity.setAddOther(skuReductionTo.getCountStatus());skuFullReductionEntity.setFullPrice(skuReductionTo.getFullPrice());skuFullReductionEntity.setReducePrice(skuReductionTo.getReducePrice());skuFullReductionEntity.setSkuId(skuReductionTo.getSkuId());baseMapper.insert(skuFullReductionEntity);// 保存会员价格 sms_member_priceList<MemberPrice> memberPrice = skuReductionTo.getMemberPrice();List<MemberPriceEntity> memberPriceEntityList = memberPrice.stream().map(item -> {MemberPriceEntity memberPriceEntity = new MemberPriceEntity();memberPriceEntity.setAddOther(skuReductionTo.getCountStatus());memberPriceEntity.setMemberLevelId(item.getId());memberPriceEntity.setMemberLevelName(item.getName());memberPriceEntity.setMemberPrice(item.getPrice());memberPriceEntity.setSkuId(skuReductionTo.getSkuId());return memberPriceEntity;}).filter(item -> {return (item.getMemberPrice().compareTo(new BigDecimal("0")) == 1);}).collect(Collectors.toList());memberPriceService.saveBatch(memberPriceEntityList);}

就完成持久化,另外一种是满减少打折,还是一句话只存sku的信息,不用考虑优化的使用情况,只需要持久sku,spu等所有的信息才算商品的发布成功,里面有优化的细节,包括前端对全量url的提交,当为空时不提交,在远程保存时还有判断具体是否有打折无打折不提交,会员无优惠不提交,无满减不提交保存,商品发布任务已经完成,具体细节看图片

基础属性设置:

规则属性:

销售属性设置:

sku信息设置:标题副标题在选择sku时详情的标题副标题。

sku的在>头有更多设置(前面的规格属性,销售属性是关于spu,前面商品名称是spu的名称),如下图:

商品维护模块:发布商品;SPU管理;商品管理

SPU管理:前面完成了商品的发布,现在完成spu的管理?spu是什么?

spu查询,spu是产品具体的实体类型,是标准化单元,它是商品实例的类型实例,是实例的非具体化表现,查询sku,根据多个条件并的条件查询,只提供多条件查询,比如分类、品牌名、新建状态,关键子key查。where(id=?or spu_id=?)and publier_status=? and beand_id=? and category_id=?,spu管理目前只完成查询,与sku做同步剩余功能待完成,

接下来完成Sku管理(商品查询):在价格查询的时候注意商品价格默认为的时候,做条件判断只查价格大于0。

。支持商品价格范围搜素,更多上传图片、参与秒杀满减设置、折扣设置、会员设置、库存设置、优惠劵等信息,这是对sku产品的具有的活动功能的设置。

完成库存功能:库存系统-》仓库维护、库存工作单、商品库存、采购单维护(采购需求、采购单)

gulimall-ware首先清除几个表,或者实体,

1 4 4商品库存表:wms_ware_sku(sku_id,wareId仓库id,stock库存数,skuName,stockLocked锁定库存)

2 库存工作单:wms_ware_order_task(orderId,order_sn, consignee收获人,consigneeTel收货人电话,deliveryAddress配送地址,orderComment订单备注,paymentWay付款方式(1在线付款,货到付款),taskStatus任务状态,orderBody订单描述,trackingNo物流单号,wareId仓储id,taskComment工作单备注)

3 库存工作单详情:wms_ware_order_task_detail(sku_id,sku_name,skuNum购买个数,taskId工作单id)

4 仓储信息wms_ware_info(name仓库名称,address仓库地址,areacode区域编码)同时要设置网关,对于网关的用法有待提高.

5采购信息表:wms_purchase(assigneeId采购人id,assigneeName采购人名,priority优先级,status状态,wareId仓库id,amount总金额)

6采购详情表:wms_purchase_detail(purchaseId采购单id,skuNum采购数量,skuPrice采购金额,wareId仓库id,status状态[0新建,1已分配,2正在采购,3已完成,4采购失败])

p96:查询商品库存:是结合已经存在的仓库和skuID去查询,库存管理对采购功能并不是直接在改页面下完成,而是分子处理方式,人工,加其他系统预警等来完成库存的添加 ;如当采购人员采购商品完成,在进库时扫描入库,库存是不可操作的,只有商品入库时才会有改修改操作,库存是由采购单来填充,采购单维护分采购需求,采购单,由采购需求决定采购单,采购需求由人工创建或库存报警创建,采购需求可以对多个采购需求生成采购单,先有采购需求再有采购订单,。

库存服务,项目逆向工程就具备仓储服务的采购单维护功能的基本查找,采购单分为了,采购需求、采购单进行了流程细化,采购需求自身数据库表逆向工程有采购需求的创建搜索删除功能,需要在搜索时添加模糊搜索修改,接下来就是采购单的处理,采购单是由采购需求的多个单所合并而成,

在创基采购单后,可以给采购单分配人员,合并多个需求采购单需要查询采购单进行合并选择器的回显,指定合并到哪一个采购单,这些采购单要求是没有被领取,其状态status必须是0或者1,新建或刚分配人,查询很简单QueryWapper后跟ep条件,如果在合并需求采购单时没有选择采购单就会新建采购单,并完成合并,需求采购单会被解析关联到采购单,插入到采购单的采购单详情的id完成关联关系,PurchaseEntity和PurchaseDetailEntity 关联,将采购的PurchaseDetailEntity采购需求单绑定采购单上分配给菜购人员,领取采购单(可以使用mq完成通知功能),领取采购单后要在采购详情表中更新采购项状态,表示当前需求采购单已经合并成采购单并且已经分配并由采购员接收,这时采购详情中的采购项就是正在采购状态(区分采购单的状态)(需求采购单之上指明它所属的采购单并不会在需求采后单页面中有变化,只是它的归属处理字段有了值),采购员领取所属于的采购单,采购单中归属的采购项的状态也会发生改变

当采购人完成采购单后,会继续更改采购需求中的采购项的状态,如果采后人确认都采购完成则采购单完成,否则采购单是未完成状态,并且失败的采购想会添加失败原因,(采购项是否失败是根据前端采购员将采购项状态以VO形式传参封装成实体用来更新状态,包括失败的信息)以及该采购单的采购成功率比,1更新采购详情中的采购项的状态,如果采购成功并同时入库 2如果采购项全部完成则跟新采购单状态完成  3,所有更新完成将成功的采购项入库操作(wms_ware_sku)(sku最小入库单元也是库存单元) 加库存的具体操作对应数据库如下:

首先找到具体sku产品id,以及所属的库存id,如果没有就是添加操作创建新的库存sku单位。

现在模拟采购员领取采购单,采购单采购项改变状态,库存系统只有一个sku为1号铲平,采购单里面有1,4产品,当前仓库系统没有4号,现在采购员回应采购系统采购情况,向接口发送数据

此时采购单4已经完成状态,参构单项也是已完成,1号库存累加1号新填数量 4号sku库存没有,就创建4号库存单元,并且指定库存数量,在库存数量展示时还有库存的name需要展示,所以还要去查skuId对应的产品名,当前是仓库系统,需要Figen远程服务调用获取,发送的方式有两种1直接给服务发送请求2给网关发送请求,在获取name时发生远程调用失败我们try掉不发生事务回滚。

仓库管理,采购功能完成。

回看商品系统,商品维护,spu管理商品的信息,它有一个规格,点击它出现规格维护如图:

用来修改库存单元商品的规格属性(spu属性组)sku的基础属性,我们要求它能回显出来,所以要添加查询接口,它有product_attr_value为商品(sku)的基本属性保存了spu_id,attr_id,attr_name,arr_value。包括快速展示属性quick_show等信息,查询规格属性并回显

查询商品属性规格是根据spu去查找它是定义sku的类型实例的,是以spu向外暴漏,是sku的定位器。

这是spu管理,是产品的sku的直接所数的标准产品单元,比如小米七,小米六,它不是具体的sku而是产品的一个标准类型,它有名称、描述、商品分类、品牌、重量、上架状态、等产品类型上的特点,完成回显后,可以修改,如下图,这里要批量更新spu的属性

这篇关于项目分析过程,已经学习思路记录的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/256361

相关文章

HarmonyOS学习(七)——UI(五)常用布局总结

自适应布局 1.1、线性布局(LinearLayout) 通过线性容器Row和Column实现线性布局。Column容器内的子组件按照垂直方向排列,Row组件中的子组件按照水平方向排列。 属性说明space通过space参数设置主轴上子组件的间距,达到各子组件在排列上的等间距效果alignItems设置子组件在交叉轴上的对齐方式,且在各类尺寸屏幕上表现一致,其中交叉轴为垂直时,取值为Vert

Ilya-AI分享的他在OpenAI学习到的15个提示工程技巧

Ilya(不是本人,claude AI)在社交媒体上分享了他在OpenAI学习到的15个Prompt撰写技巧。 以下是详细的内容: 提示精确化:在编写提示时,力求表达清晰准确。清楚地阐述任务需求和概念定义至关重要。例:不用"分析文本",而用"判断这段话的情感倾向:积极、消极还是中性"。 快速迭代:善于快速连续调整提示。熟练的提示工程师能够灵活地进行多轮优化。例:从"总结文章"到"用

浅析Spring Security认证过程

类图 为了方便理解Spring Security认证流程,特意画了如下的类图,包含相关的核心认证类 概述 核心验证器 AuthenticationManager 该对象提供了认证方法的入口,接收一个Authentiaton对象作为参数; public interface AuthenticationManager {Authentication authenticate(Authenti

作业提交过程之HDFSMapReduce

作业提交全过程详解 (1)作业提交 第1步:Client调用job.waitForCompletion方法,向整个集群提交MapReduce作业。 第2步:Client向RM申请一个作业id。 第3步:RM给Client返回该job资源的提交路径和作业id。 第4步:Client提交jar包、切片信息和配置文件到指定的资源提交路径。 第5步:Client提交完资源后,向RM申请运行MrAp

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

学习hash总结

2014/1/29/   最近刚开始学hash,名字很陌生,但是hash的思想却很熟悉,以前早就做过此类的题,但是不知道这就是hash思想而已,说白了hash就是一个映射,往往灵活利用数组的下标来实现算法,hash的作用:1、判重;2、统计次数;

零基础学习Redis(10) -- zset类型命令使用

zset是有序集合,内部除了存储元素外,还会存储一个score,存储在zset中的元素会按照score的大小升序排列,不同元素的score可以重复,score相同的元素会按照元素的字典序排列。 1. zset常用命令 1.1 zadd  zadd key [NX | XX] [GT | LT]   [CH] [INCR] score member [score member ...]

【机器学习】高斯过程的基本概念和应用领域以及在python中的实例

引言 高斯过程(Gaussian Process,简称GP)是一种概率模型,用于描述一组随机变量的联合概率分布,其中任何一个有限维度的子集都具有高斯分布 文章目录 引言一、高斯过程1.1 基本定义1.1.1 随机过程1.1.2 高斯分布 1.2 高斯过程的特性1.2.1 联合高斯性1.2.2 均值函数1.2.3 协方差函数(或核函数) 1.3 核函数1.4 高斯过程回归(Gauss

透彻!驯服大型语言模型(LLMs)的五种方法,及具体方法选择思路

引言 随着时间的发展,大型语言模型不再停留在演示阶段而是逐步面向生产系统的应用,随着人们期望的不断增加,目标也发生了巨大的变化。在短短的几个月的时间里,人们对大模型的认识已经从对其zero-shot能力感到惊讶,转变为考虑改进模型质量、提高模型可用性。 「大语言模型(LLMs)其实就是利用高容量的模型架构(例如Transformer)对海量的、多种多样的数据分布进行建模得到,它包含了大量的先验

【学习笔记】 陈强-机器学习-Python-Ch15 人工神经网络(1)sklearn

系列文章目录 监督学习:参数方法 【学习笔记】 陈强-机器学习-Python-Ch4 线性回归 【学习笔记】 陈强-机器学习-Python-Ch5 逻辑回归 【课后题练习】 陈强-机器学习-Python-Ch5 逻辑回归(SAheart.csv) 【学习笔记】 陈强-机器学习-Python-Ch6 多项逻辑回归 【学习笔记 及 课后题练习】 陈强-机器学习-Python-Ch7 判别分析 【学