100字范文,内容丰富有趣,生活中的好帮手!
100字范文 > 《谷粒商城》开发实录:070-100 商品服务-发布商品

《谷粒商城》开发实录:070-100 商品服务-发布商品

时间:2024-06-29 14:05:31

相关推荐

《谷粒商城》开发实录:070-100 商品服务-发布商品

070 准备工作

1 名词解释:SPU&SKU

SPUStandard Product Unit(标准化产品单元)是商品信息聚合的最小单位,是一组可复用、易检索的标准化信息的集合,该集合描述了一 个产品的特性。SKUStock Keeping Unit(库存量单位)即库存进出计量的基本单元,可以是以件,盒,托盘等为单位。 SKU 这是对于大型连锁超市 DC (配送中心)物流管理的一个必要的方法。现在已经被引申为产品统一编号的简称,每 种产品均对应有唯一的 SKU 号。

2 前端代码+数据库脚本

百度网盘:提取码2239

3 生成系统菜单

在数据库gulimall-admin中执行SQL脚本文件sys_menus.sql,生成系统菜单。

4 导入前端代码

将下载的前端代码复制到前端项目renren-fast-vue中,风险操作注意备份。

5 接口文档

查看接口文档

071-074 属性分组

涉及业务表:

pms_attr_group 属性分组表pms_attr 属性表pms_attr_attrgroup_relation 属性-属性分组关系表

例如,对一部手机而言,它可以有:

属性分组:机身属性分组:芯片属性:机身长度属性:机身颜色属性:芯片品牌属性-属性分组:机身-机身长度属性-属性分组:机身-机身颜色属性-属性分组:芯片-芯片品牌

关于属性分组的基本增删改查略。

商品分类是三级分类的形式,下面是一套分类回写逻辑:

entity/AttrGroupEntity:

// 数据库中不存在的属性@TableField(exist = false)private Long[] catelogPath;

service/CategoryService:

// 获取分类路径Long[] findCatelogPath(Long catelogId);

service/impl/AttrGroupServiceImpl:

@Overridepublic Long[] findCatelogPath(Long catelogId) {List<Long> paths = new ArrayList<>();List<Long> parentPath = findParentPath(catelogId, paths);Collections.reverse(parentPath);return parentPath.toArray(new Long[parentPath.size()]);}// 递归private List<Long> findParentPath(Long catelogId, List<Long> paths) {paths.add(catelogId);CategoryEntity entity = this.getById(catelogId);if (entity.getParentCid() != 0) {findParentPath(entity.getParentCid(), paths);}return paths;}

bug:三级分类后面跟着一张空白页

原因:第三级分类的children属性是一个空集合。

解决方法:只有当children字段不为空时,才会被放入返回前端页面的json数据中。

075 品牌管理

1 模糊查询

一图流:

2数据库冗余字段同步

对数据库中冗余字段的修改,必须全部更新,或者全部不更新。

所以要在方法前加上事务注解:@Transactional

076-082 平台属性

1 规格参数

增删改查略。

这里有一个bug,请将前端文件attr-add-or-update.vue中的值类型相关代码注释掉,因为在数据库中创建表时这个字段缺失了。不想改表结构了。

2 销售属性

增删改查略。

值得一提的是,在gulimall-common服务中,创建了一个常量类constant/ProductConstant,用来维护gulimall-product服务中的常量。

public class ProductConstant {public enum AttrEnum {ATTR_TYPE_BASE(1, "基本属性"), ATTR_TYPE_SALE(0, "销售属性");private int code;private String msg;AttrEnum(int code, String msg) {this.code = code;this.msg = msg;}public int getCode() {return code;}public String getMsg() {return msg;}}}

3 属性分组

关联规格参数,增删改查略。

083-092 发布商品

在做这一部分之前,建议先把070-2后端代码中product/vo文件夹导入gulimall-product服务。

1PubSub is not defined

进入发布商品页面会出现一个PubSub is not defined的问题。

解决方法:在前端项目中

npminstall --save pubsub-js

在src下的main.js中添加代码(如果在070-4导入前端代码时导入了main.js,跳过这一步)

① import PubSub from 'pubsub-js'

② Vue.prototype.PubSub = PubSub

2 会员等级

解决了PubSub is not defined问题,再次进入发布商品页面,前端会向后端发送一个获取会员等级的请求:http://localhost:88/api/member/memberlevel/list,涉及到了会员服务gulimall-member。

启动gulimall-member服务。

在网关服务gulimall-gateway的配置文件application.yml中配置路由,这次我们将所有之前没有配置的服务补充完整:(注意缩进)

spring:cloud: gateway:routes:- id: product_routeuri: lb://gulimall-productpredicates:- Path=/api/product/**filters:- RewritePath=/api/?(?<segment>.*), /$\{segment} #去掉/api 前缀- id: member_routeuri: lb://gulimall-memberpredicates:- Path=/api/member/**filters:- RewritePath=/api/?(?<segment>.*), /$\{segment}- id: coupon_routeuri: lb://gulimall-couponpredicates:- Path=/api/coupon/**filters:- RewritePath=/api/?(?<segment>.*), /$\{segment}- id: ware_routeuri: lb://gulimall-warepredicates:- Path=/api/ware/**filters:- RewritePath=/api/?(?<segment>.*), /$\{segment}- id: order_routeuri: lb://gulimall-orderpredicates:- Path=/api/order/**filters:- RewritePath=/api/?(?<segment>.*), /$\{segment}- id: third_party_routeuri: lb://gulimall-third-partypredicates:- Path=/api/thirdparty/**filters:- RewritePath=/api/thirdparty/?(?<segment>.*), /$\{segment}# 默认路由- id: admin_routeuri: lb://renren-fast #lb:负载均衡predicates:- Path=/api/** #前端项目发送的请求带有/api 前缀filters:- RewritePath=/api/?(?<segment>.*), /renren-fast/$\{segment} #将/api 前缀改写成/renren-fast

重启gulimall-gateway服务。

随意添加一些会员等级。

3 规格参数

创建接口:根据分类id查询属性及属性分组。原理是在pms_attr 属性表和pms_attr_group 属性分组表中都包含一个catelog_id字段,代码略。

随意添加一些规格参数。

4 分类与品牌的级联关系

在gulimall-product服务中:

新建vo/BrandVo:

@Datapublic class BrandVo {// 品牌idprivate Long brandId;// 品牌名称private String brandName;}

controller/CategoryBrandRelationController:

@GetMapping("/brands/list")public R relationBrandsList(@RequestParam(value = "catId") Long catId) {List<BrandEntity> vos = categoryBrandRelationService.getBrandsByCatId(catId);List<BrandVo> brandVoList = 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", brandVoList);}

service/CategoryBrandRelationService:

List<BrandEntity> getBrandsByCatId(Long catId);

service/impl/CategoryBrandRelationServiceImpl:

@Overridepublic List<BrandEntity> getBrandsByCatId(Long catId) {List<CategoryBrandRelationEntity> catelogId = relationDao.selectList(new QueryWrapper<CategoryBrandRelationEntity>().eq("catelog_id", catId));List<BrandEntity> entityList = catelogId.stream().map(item -> {Long brandId = item.getBrandId();BrandEntity entity = brandService.getById(brandId);return entity;}).collect(Collectors.toList());return entityList;}

分类与品牌的级联关系创建完成。

5 发布商品

按照页面提示输入商品基本信息:

选择规格参数:

选择销售属性:

编辑SKU信息:

6 保存商品信息

这个功能,业务繁琐但并不复杂:

1 保存spu基本信息: pms_spu_info

2 保存spu的描述图片: pms_spu_info_desc

3 保存spu的图片集: pms_spu_images

4 保存spu的规格参数: pms_product_attr_value

5 保存spu的积分信息: gulimall_sms -> sms_spu_bounds

6 保存当前spu对应的所有sku信息

6.1 sku的基本信息: pms_sku_info

6.2 sku的图片信息: pms_sku_images

6.3 sku的销售属性信息: pms_sku_sale_attr_value

6.4 sku的优惠、满减等信息: gulimall_sms -> sms_sku_ladder

sms_sku_full_reduction

sms_member_price

保存商品信息的操作是事务性的,如果想在debug过程中看到数据库中的实时数据变化,可以设置当前会话的事务隔离级别为读未提交:

SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;

在此隔离级别下,数据库允许脏读。

controller/SpuInfoController:

@RequestMapping("/save")public R save(@RequestBody SpuSaveVo vo) {spuInfoService.saveSpuInfo(vo);return R.ok();}

service/SpuInfoService:

void saveSpuInfo(SpuSaveVo vo);void saveBaseSpuInfo(SpuInfoEntity infoEntity);

service/impl/SpuInfoServiceImpl:

@Transactional@Overridepublic void saveSpuInfo(SpuSaveVo vo) {// 1 保存spu基本信息: pms_spu_infoSpuInfoEntity infoEntity = new SpuInfoEntity();BeanUtils.copyProperties(vo, infoEntity);infoEntity.setCreateTime(new Date());infoEntity.setUpdateTime(new Date());this.saveBaseSpuInfo(infoEntity);// 2 保存spu的描述图片: pms_spu_info_descList<String> decript = vo.getDecript();SpuInfoDescEntity descEntity = new SpuInfoDescEntity();descEntity.setSpuId(infoEntity.getId());descEntity.setDecript(String.join(",", decript));spuInfoDescService.saveSpuInfoDesc(descEntity);// 3 保存spu的图片集: pms_spu_imagesList<String> images = vo.getImages();spuImagesService.saveImages(infoEntity.getId(), images);// 4 保存spu的规格参数: pms_product_attr_valueList<BaseAttrs> baseAttrs = vo.getBaseAttrs();List<ProductAttrValueEntity> productAttrValueEntities = baseAttrs.stream().map(attr -> {ProductAttrValueEntity valueEntity = new ProductAttrValueEntity();valueEntity.setAttrId(attr.getAttrId());AttrEntity entity = attrService.getById(attr.getAttrId());valueEntity.setAttrName(entity.getAttrName());valueEntity.setAttrValue(attr.getAttrValues());valueEntity.setQuickShow(attr.getShowDesc());valueEntity.setSpuId(infoEntity.getId());return valueEntity;}).collect(Collectors.toList());productAttrValueService.saveProductAttr(productAttrValueEntities);// 5 保存spu的积分信息: gulimall_sms -> sms_spu_boundsBounds bounds = vo.getBounds();SpuBoundTo spuBoundTo = new SpuBoundTo();BeanUtils.copyProperties(bounds, spuBoundTo);spuBoundTo.setSpuId(infoEntity.getId());R saveSpuBounds = couponFeignService.saveSpuBounds(spuBoundTo);if (saveSpuBounds.getCode() != 0) {log.error("远程保存spu积分信息失败");}// 6 保存当前spu对应的所有sku信息List<Skus> skues = vo.getSkus();if (skues != null && skues.size() > 0) {skues.forEach(item -> {// 6.1 sku的基本信息: pms_sku_infoSkuInfoEntity skuInfoEntity = new SkuInfoEntity();BeanUtils.copyProperties(item, skuInfoEntity);skuInfoEntity.setBrandId(infoEntity.getBrandId());skuInfoEntity.setCatalogId(infoEntity.getCatalogId());skuInfoEntity.setSaleCount(0L);skuInfoEntity.setSpuId(infoEntity.getId());String defaultImg = "";for (Images image : item.getImages()) {if (image.getDefaultImg() == 1) {defaultImg = image.getImgUrl();}}skuInfoEntity.setSkuDefaultImg(defaultImg);skuInfoService.saveSkuInfo(skuInfoEntity);// 6.2 sku的图片信息: pms_sku_imagesLong 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 -> {return !StringUtils.isEmpty(entity.getImgUrl());}).collect(Collectors.toList());skuImagesService.saveBatch(imagesEntities);// 6.3 sku的销售属性信息: pms_sku_sale_attr_valueList<Attr> attr = item.getAttr();List<SkuSaleAttrValueEntity> skuSaleAttrValueEntities = attr.stream().map(a -> {SkuSaleAttrValueEntity attrValueEntity = new SkuSaleAttrValueEntity();BeanUtils.copyProperties(a, attrValueEntity);attrValueEntity.setSkuId(skuId);return attrValueEntity;}).collect(Collectors.toList());skuSaleAttrValueService.saveBatch(skuSaleAttrValueEntities);// 6.4 sku的优惠、满减等信息: gulimall_sms -> sms_sku_ladder// sms_sku_full_reduction// sms_member_priceSkuReductionTo skuReductionTo = new SkuReductionTo();BeanUtils.copyProperties(item, skuReductionTo);skuReductionTo.setSkuId(skuId);List<mon.to.MemberPrice> memberPrices = new ArrayList<>();item.getMemberPrice().forEach(memberPrice -> {mon.to.MemberPrice mp = new mon.to.MemberPrice();mp.setId(memberPrice.getId());mp.setName(memberPrice.getName());mp.setPrice(memberPrice.getPrice());memberPrices.add(mp);});skuReductionTo.setMemberPrice(memberPrices);if (skuReductionTo.getFullCount() > 0 || skuReductionTo.getFullPrice().compareTo(new BigDecimal("0")) == 1) {R saveSkuReduction = couponFeignService.saveSkuReduction(skuReductionTo);if (saveSkuReduction.getCode() != 0) {log.error("远程保存sku优惠信息失败");}}});}}@Overridepublic void saveBaseSpuInfo(SpuInfoEntity infoEntity) {this.baseMapper.insert(infoEntity);}

// 2 保存spu的描述图片: pms_spu_info_desc

entity/SpuInfoDescEntity:

@TableId(type = IdType.INPUT)private Long spuId;

service/SpuInfoDescService:

void saveSpuInfoDesc(SpuInfoDescEntity descEntity);

service/impl/SpuInfoDescServiceImpl:

@Overridepublic void saveSpuInfoDesc(SpuInfoDescEntity descEntity) {this.baseMapper.insert(descEntity);}

// 3 保存spu的图片集: pms_spu_images

service/SpuImagesService:

void saveImages(Long id, List<String> images);

service/impl/SpuImagesServiceImpl:

@Overridepublic void saveImages(Long id, List<String> images) {if (images == null || images.size() == 0) {return;} else {List<SpuImagesEntity> entityList = images.stream().map(img -> {SpuImagesEntity spuImagesEntity = new SpuImagesEntity();spuImagesEntity.setSpuId(id);spuImagesEntity.setImgUrl(img);return spuImagesEntity;}).collect(Collectors.toList());this.saveBatch(entityList);}}

// 4 保存spu的规格参数: pms_product_attr_value

service/productAttrValueService:

void saveProductAttr(List<ProductAttrValueEntity> entityList);

service/impl/ProductAttrValueServiceImpl:

public void saveProductAttr(List<ProductAttrValueEntity> entityList) {this.saveBatch(entityList);}

// 5 保存spu的积分信息: gulimall_sms -> sms_spu_bounds

feign/CouponFeignService:

@FeignClient("gulimall-coupon")public interface CouponFeignService {@PostMapping("/coupon/spubounds/save")R saveSpuBounds(@RequestBody SpuBoundTo spuBoundTo);}

coupon服务,controller/SpuBoundsController:

@PostMapping("/save")public R save(@RequestBody SpuBoundsEntity spuBounds) {spuBoundsService.save(spuBounds);return R.ok();}

common服务,工具类R:

public Integer getCode() {return (Integer) this.get("code");}

// 6.1 sku的基本信息: pms_sku_info

service/SkuInfoService:

void saveSkuInfo(SkuInfoEntity skuInfoEntity);

service/impl/SkuInfoServiceImpl:

@Overridepublic void saveSkuInfo(SkuInfoEntity skuInfoEntity) {this.baseMapper.insert(skuInfoEntity);}

// 6.4 sku的优惠、满减等信息: gulimall_sms -> sms_sku_ladder

// sms_sku_full_reduction

// sms_member_price

feign/CouponFeignService:

@PostMapping("/coupon/skufullreduction/saveinfo")R saveSkuReduction(@RequestBody SkuReductionTo skuReductionTo);

coupon服务,controller/SkuFullReductionController:

@PostMapping("/saveinfo")public R saveInfo(@RequestBody SkuReductionTo skuReductionTo) {skuFullReductionService.saveSkuReduction(skuReductionTo);return R.ok();}

coupon服务,service/SkuFullReductionService:

void saveSkuReduction(SkuReductionTo skuReductionTo);

coupon服务,service/impl/SkuFullReductionServiceImpl:

@Overridepublic void saveSkuReduction(SkuReductionTo skuReductionTo) {SkuLadderEntity skuLadderEntity = new SkuLadderEntity();skuLadderEntity.setSkuId(skuReductionTo.getSkuId());skuLadderEntity.setFullCount(skuReductionTo.getFullCount());skuLadderEntity.setDiscount(skuReductionTo.getDiscount());skuLadderEntity.setAddOther(skuReductionTo.getCountStatus());if (skuReductionTo.getFullCount() > 0) {skuLadderService.save(skuLadderEntity);}SkuFullReductionEntity skuFullReductionEntity = new SkuFullReductionEntity();BeanUtils.copyProperties(skuReductionTo, skuFullReductionEntity);if (skuFullReductionEntity.getFullPrice().compareTo(new BigDecimal("0")) == 1) {this.save(skuFullReductionEntity);}List<MemberPrice> memberPrice = skuReductionTo.getMemberPrice();List<MemberPriceEntity> collect = memberPrice.stream().map(item -> {MemberPriceEntity memberPriceEntity = new MemberPriceEntity();memberPriceEntity.setSkuId(skuReductionTo.getSkuId());memberPriceEntity.setMemberLevelId(item.getId());memberPriceEntity.setMemberLevelName(item.getName());memberPriceEntity.setMemberPrice(item.getPrice());memberPriceEntity.setAddOther(1);return memberPriceEntity;}).filter(item -> {return item.getMemberPrice().compareTo(new BigDecimal("0")) == 1;}).collect(Collectors.toList());memberPriceService.saveBatch(collect);}

7SPU与SKU的条件查询

略。

095-099 仓库管理

1 为各个服务创建配置文件夹

在各个服务中,创建config/MybatisConfig文件:(注意修改@MapperScan的路径)

@Configuration@EnableTransactionManagement@MapperScan("com.atguigu.gulimall.product.dao")public class MybatisConfig {// 引入分页插件@Beanpublic PaginationInterceptor paginationInterceptor() {PaginationInterceptor paginationInterceptor = new PaginationInterceptor();return paginationInterceptor;}}

2 仓库维护

已自动生成增删改查代码。

在这里遇到了一个bug:因为id在数据库中的存储类型为最大长度 20位的bigint类型,当id位数很长时(简单测了一下可能是超过17位),在传到前端时会发生精度丢失。

所以在做数据库设计时,最好认真地设计一下主键,主键自增你把握不住。

这里我使用了当前时间作为主键:

SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmmss");String id = dateFormat.format(new Date(System.currentTimeMillis()));wareInfo.setId(Long.parseLong(id));

3 商品库存

增删改查,根据仓库id或skuId查询。略。

4 商品采购

采购流程:

100 规格维护

增删改查略。

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。