商品中心—9.商品卖家系统的技术文档
大纲
1.卖家和卖家组模型以及业务流程
2.卖家系统数据库表模型与缓存结构设计
3.卖家系统核心接口与代码实现
1.卖家和卖家组模型以及业务流程
(1)卖家模型
(2)卖家组模型
(3)业务流程详解
(1)卖家模型
卖家类型分为:⾃营卖家和POP卖家。
⾃营卖家:类似于京东⾃营卖家。其商品由平台采购,建⽴仓储系统存储,平台⾃⾏上架商品。其最⼩维度为⻔店,如北京市-东城区-⽣鲜⻔店。
POP卖家:第三⽅供货商接⼊平台作为⼀个卖家。其最⼩维度为区域,如北京市-东城区卖家。
卖家树模型可以看成是类目树模型。卖家节点也分为叶子节点和非叶子节点。非叶子节点的卖家节点可以看成是卖家所属的分类,叶子节点的卖家节点才是具体的某一个卖家。所以创建卖家节点之前,必须先根据全国地区编码初始化一次卖家树。初始化完之后,后续创建卖家节点就不需要再初始化卖家树了。
一.自营卖家树体系
二.POP卖家树体系
(2)卖家组模型
卖家组:⼀组具有相同供应属性的卖家组合。注意:POP类型的卖家和⾃营类型的卖家不能同属⼀个卖家组。在⼀个卖家组中,要么全都是⾃营卖家,要么全都是POP卖家。
(3)业务流程详解
一.建立卖家体系
⾃营卖家:根据国家的地区编码建⽴卖家树。其⾮叶⼦节点为虚拟卖家,叶⼦节点为实体卖家,例如东城区⻔店1。
POP卖家:根据国家的地区编码建⽴卖家树。其⾮叶⼦节点为虚拟卖家,叶⼦节点为实体卖家,例如东城区卖家。
二.建立卖家组
⾃营:例如根据地域关系划分,东城区卖家组,⻄城区卖家组。
POP:例如根据地域关系划分,上海卖家组,北京卖家组。
三.绑定卖家与卖家组关系
运营将⼀组卖家与卖家组绑定。注意:绑定时卖家与卖家组类型必须⼀致才可以绑定,否则不能绑定。而且卖家组与卖家是多对多关系,即⼀个卖家组能同时添加多个同⼀⾃营类型卖家或者POP类型卖家,⼀个卖家也可以被添加到多个同⼀类型的卖家组中。
卖家组与卖家建⽴绑定关系后,即可对外提供卖家能⼒。只有当卖家与卖家组绑定关系后,才可以对外提供售卖商品的能⼒。商品需要划分到卖家,⽽卖家也必须⾄少在⼀个卖家组中。
2.卖家系统数据库表模型与缓存结构设计
(1)卖家信息表
(2)卖家组表
(3)卖家与卖家组关系表
(4)卖家账户表
(5)卖家系统ID表
(6)国家⾏政编号信息表
(7)缓存结构设计
(1)卖家信息表
CREATE TABLE `seller_info` ( `id` bigint(40) NOT NULL AUTO_INCREMENT COMMENT '主键', `seller_id` int(10) NOT NULL COMMENT '卖家ID:20开头 + 6位随机数', `seller_code` varchar(64) NOT NULL COMMENT '卖家编码:⾃营指微仓编码(WJ-1001),POP指区域编码(国标编码)', `seller_name` varchar(128) NOT NULL COMMENT '卖家名称', `seller_desc` varchar(512) DEFAULT NULL COMMENT '卖家描述', `seller_type` tinyint(3) NOT NULL COMMENT '卖家类型:1-⾃营,2-POP ', `seller_position` SMALLINT(3) NOT NULL COMMENT '卖家位置:100-全国,200-⼤区,300-省/直辖市,400-城市,500-地区,600-微仓', `seller_status` tinyint(3) NOT NULL COMMENT '卖家状态:1-开店,2-闭店,3-删除', `store_label_list` varchar(10) comment '存储标签(1-常温,2-冷藏,3-冷冻,4-⽔产)', `trial_sale_label` tinyint(1) comment '是否有试销标签(1-试销,0-⾮试销)', `parent_id` bigint(40) DEFAULT '0' COMMENT '⽗卖家ID', `last_node` tinyint(3) NOT NULL DEFAULT '0' COMMENT '叶⼦节点标识:1-是,0-否', `del_flag` tinyint(1) NOT NULL DEFAULT '0' COMMENT '删除标记(1-有效,0-删除)', `create_user` int(10) NOT NULL DEFAULT '0' COMMENT '创建⼈', `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '创建时间', `update_user` int(10) NOT NULL DEFAULT '0' COMMENT '更新⼈', `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=12 DEFAULT CHARSET=utf8 COMMENT='卖家信息表';
(2)卖家组表
create table `seller_group` ( `id` bigint(40) primary key AUTO_INCREMENT COMMENT '主键', `seller_group_id` int(10) not null comment '卖家组ID:30开头 + 4位随机数', `seller_group_name` varchar(64) not null comment '卖家组名称', `seller_group_type` tinyint(3) not null comment '卖家组类型:1-⾃营,2-POP', `seller_group_status` tinyint(3) NOT NULL COMMENT '卖家组状态:1-⽣效,2-⽆效', `del_flag` tinyint(1) NOT NULL DEFAULT '0' COMMENT '删除标记(1-有效,0-删除)', `create_user` int(10) NOT NULL DEFAULT '0' COMMENT '创建⼈', `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '创建时间', `update_user` int(10) NOT NULL DEFAULT '0' COMMENT '更新⼈', `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间' ) ENGINE=InnoDB AUTO_INCREMENT=12 DEFAULT CHARSET=utf8 COMMENT='卖家组表';
(3)卖家与卖家组关系表
create table seller_group_relation( `id` bigint(40) primary key AUTO_INCREMENT COMMENT '主键', `seller_group_id` int(10) not null comment '卖家组ID:30开头 + 4位随机数', `seller_id` int(10) not null comment '卖家ID:20开头 + 6位随机数', `del_flag` tinyint(1) NOT NULL DEFAULT '0' COMMENT '删除标记(1-有效,0-删除)', `create_user` int(10) NOT NULL DEFAULT '0' COMMENT '创建⼈', `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '创建时间', `update_user` int(10) NOT NULL DEFAULT '0' COMMENT '更新⼈', `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间' ) ENGINE=InnoDB AUTO_INCREMENT=12 DEFAULT CHARSET=utf8 COMMENT='卖家与卖家组关系表';
(4)卖家账户表
create table seller_account ( `id` bigint(40) primary key AUTO_INCREMENT COMMENT '主键', `seller_id` int(10) not null comment '卖家ID:20开头+6位随机数', `pay_channel` tinyint(3) not null comment '⽀付渠道:1-银联,2-微信,3-⽀付宝', `account_no` varchar(64) not null comment '卖家结算账户,最终收款账号', `del_flag` tinyint(1) NOT NULL DEFAULT '0' COMMENT '删除标记(1-有效,0-删除)', `create_user` int(10) NOT NULL DEFAULT '0' COMMENT '创建⼈', `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '创建时间', `update_user` int(10) NOT NULL DEFAULT '0' COMMENT '更新⼈', `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间' ) ENGINE=InnoDB AUTO_INCREMENT=12 DEFAULT CHARSET=utf8 COMMENT='卖家账户表';
(5)卖家系统ID表
create table seller_auto_no( `id` bigint(20) primary key auto_increment comment '主键', `create_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '创建时间', `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间' ) ENGINE=InnoDB AUTO_INCREMENT=56 DEFAULT CHARSET=utf8mb4 COMMENT='卖家系统ID表';
(6)国家⾏政编号信息表
CREATE TABLE `national_code` ( `id` int(7) NOT NULL COMMENT '主键', `name` varchar(40) DEFAULT NULL COMMENT '省市区名称', `parent_id` int(7) DEFAULT NULL COMMENT '上级ID', `short_name` varchar(40) DEFAULT NULL COMMENT '简称', `level_type` tinyint(2) DEFAULT NULL COMMENT '级别:0-中国,1-⼤区,2-省/直辖市,3-市,4-区/县', `city_code` varchar(7) DEFAULT NULL COMMENT '城市代码', `zip_code` varchar(7) DEFAULT NULL COMMENT '邮编', `lng` varchar(20) DEFAULT NULL COMMENT '经度', `lat` varchar(20) DEFAULT NULL COMMENT '纬度', `pinyin` varchar(40) DEFAULT NULL COMMENT '拼⾳', `status` enum('0','1') DEFAULT '1' ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='国家⾏政编号信息表';
(7)缓存结构设计
3.卖家系统核心接口与代码实现
(1)卖家接口
(2)卖家组接口
(3)卖家与卖家组关系
(4)卖家账户
(1)卖家接口
一.初始化卖家树接口
二.新增卖家接口
三.更新卖家接口
四.删除卖家接口
五.查询卖家接口
六.查询卖家RPC接口
一.初始化卖家树接口
向接口传⼊卖家类型,然后根据国标数据初始化全国卖家树。卖家树层级为:全国、⼤区、省/直辖市、城市、区/县。
卖家树模型可以看成是类目树模型。卖家节点也分为叶子节点和非叶子节点,非叶子节点的卖家节点可以看成是卖家所属的分类,叶子节点的卖家节点才是具体的某一个卖家。所以创建卖家节点之前,必须先根据全国地区编码初始化一次卖家树。初始化完之后,后续创建卖家节点就不需要再初始化卖家树了。
@Service public class SellerInfoServiceImpl implements SellerInfoService { @Autowired private SellerRepository sellerRepository; ... //卖家树信息初始化接口 @Override @Transactional(rollbackFor = Exception.class) public SellerInfoResultDTO initSellerTreeInfo(Integer sellerType) { //1.生成全国卖家 Long nationwideSellerId = initNationwideSeller(sellerType); //2.生成大区卖家 Map<String, Long> districtMap = initDistrictSeller(nationwideSellerId, sellerType); //3.生成省/直辖市卖家 Map<String, Long> provinceMap = initDiffParentSeller(districtMap, sellerType, NationalCodeLevelEnum.PROVINCE, SellerPositionEnum.PROVINCE); //4.生成城市卖家 Map<String, Long> cityMap = initDiffParentSeller(provinceMap, sellerType, NationalCodeLevelEnum.CITY, SellerPositionEnum.CITY); //5.生成地区卖家 initDiffParentSeller(cityMap, sellerType, NationalCodeLevelEnum.REGION, SellerPositionEnum.REGION); return new SellerInfoResultDTO(Boolean.TRUE); } //1.生成全国卖家 private Long initNationwideSeller(Integer sellerType) { //查询出国家层级数据 List<NationalCodeDO> nationalCodeList = getNationalCode(NationalCodeLevelEnum.NATIONWIDE); //只有一个全国卖家 NationalCodeDO nationalCode = nationalCodeList.get(0); //生成sellerId Long sellerId = sellerRepository.generateSellerId(SkuSellerRelationConstants.SELLER_PREFIX_NO, SELLER_ID_WIDTH); SellerInfoDO sellerInfo = buildSellerInfoDO(sellerType, nationalCode, sellerId); //保存全国卖家 sellerRepository.savelSellerInfoBatch(Collections.singletonList(sellerInfo)); return sellerId; } //查询某地区层级的国标数据 private List<NationalCodeDO> getNationalCode(NationalCodeLevelEnum province) { NationalCodeRequest nationalRequest = new NationalCodeRequest(); nationalRequest.setLevelType(province.getCode()); return nationalCodeRepository.queryNationalCode(nationalRequest); } //构造卖家树节点数据 private SellerInfoDO buildSellerInfoDO(Integer sellerType, NationalCodeDO nationalCode, Long parentId, SellerPositionEnum province) { SellerInfoDO sellerInfo = new SellerInfoDO(); //生成sellerId Long sellerId = sellerRepository.generateSellerId(SkuSellerRelationConstants.SELLER_PREFIX_NO, SELLER_ID_WIDTH); sellerInfo.setSellerId(sellerId); sellerInfo.setSellerCode(String.valueOf(nationalCode.getId())); sellerInfo.setSellerName(nationalCode.getName() + SkuSellerRelationConstants.SELLER_NAME_SUFFIX); sellerInfo.setSellerPosition(province.getCode()); sellerInfo.setSellerType(sellerType); sellerInfo.setSellerStatus(SellerInfoStatusEnum.OPEN_STATUS.getCode()); sellerInfo.setParentId(parentId); sellerInfo.setLastNode(YesOrNoEnum.NO.getCode()); sellerInfo.initCommon(); return sellerInfo; } //2.生成地区卖家 private Map<String, Long> initDistrictSeller(Long nationwideSellerId, Integer sellerType) { //查询出地区层级数据 List<NationalCodeDO> nationalCodeList = getNationalCode(NationalCodeLevelEnum.DISTRICT); //国家以下层级有多个 List<SellerInfoDO> sellerInfoList = nationalCodeList.stream() .map(nationalCode -> buildSellerInfoDO(sellerType, nationalCode, nationwideSellerId, SellerPositionEnum.DISTRICT)) .collect(Collectors.toList()); //批量保存 sellerRepository.savelSellerInfoBatch(sellerInfoList); //转换为Map,key为卖家编码、value为卖家ID return sellerInfoList.stream().collect(Collectors.toMap(SellerInfoDO::getSellerCode, SellerInfoDO::getSellerId)); } //省/直辖市、城市、区县卖家都使用该接口生成 private Map<String, Long> initDiffParentSeller(Map<String, Long> districtMap, Integer sellerType, NationalCodeLevelEnum codeLevelEnum, SellerPositionEnum sellerPositionEnum) { //查询出地区层级数据 List<NationalCodeDO> provinceCodeList = getNationalCode(codeLevelEnum); //需要保存的卖家集合 List<SellerInfoDO> sellerInfoList = provinceCodeList.stream().map(nationalCode -> { //获取该国标的上级卖家ID Long parentId = districtMap.get(String.valueOf(nationalCode.getParentId())); return buildSellerInfoDO(sellerType, nationalCode, parentId, sellerPositionEnum); }).collect(Collectors.toList()); //批量保存 sellerRepository.savelSellerInfoBatch(sellerInfoList); //转换为Map,key为卖家编码、value为卖家ID return sellerInfoList.stream().collect(Collectors.toMap(SellerInfoDO::getSellerCode, SellerInfoDO::getSellerId)); } ... } @Repository public class SellerRepository { ... //生成卖家或卖家组ID,即sellerId、sellerGroupId生成 //sellerId格式:20开头,6位随机数 //sellerGroupId格式:30开头,4位随机数 public Long generateSellerId(String prefix, Integer width) { return Long.parseLong(generateSellerNo(prefix, width)); } //自营卖家叶子结点sellerCode生成 //格式:父卖家(区县)的首拼-4位随机数,如:西湖区 -> XH-1001 //@param prefix 前缀 //@param width 位数 public String generateSellerNo(String prefix, Integer width) { SellerAutoNoDO sellerAutoNoDO = new SellerAutoNoDO(); sellerAutoNoMapper.insert(sellerAutoNoDO); Long autoNo = sellerAutoNoDO.getId(); if (Objects.isNull(autoNo)) { throw new BaseBizException(CommonErrorCodeEnum.SQL_ERROR); } return prefix + IDUtils.genId(autoNo, width); } //批量新增卖家 public void savelSellerInfoBatch(List<SellerInfoDO> sellerInfoList) { List<Long> sellerIdList = sellerInfoList.stream().map(SellerInfoDO::getSellerId).collect(Collectors.toList()); //是否存在该记录 LambdaQueryWrapper<SellerInfoDO> queryWrapper = Wrappers.lambdaQuery(); queryWrapper.in(SellerInfoDO::getSellerId, sellerIdList); int count = sellerInfoMapper.selectCount(queryWrapper); if (count > 0) { throw new BaseBizException(ProductErrorCodeEnum.PARAM_CHECK_ERROR, ProductErrorCodeEnum.PARAM_CHECK_ERROR.getErrorCode()); } //批量插入 count = sellerInfoMapper.insertBatch(sellerInfoList); if (count != sellerInfoList.size()) { throw new BaseBizException(CommonErrorCodeEnum.SQL_ERROR); } } ... }
二.新增卖家接口
新增微仓卖家节点,卖家分为⾃营卖家和POP卖家,只有⾃营卖家才有微仓层级。
@Service public class SellerInfoServiceImpl implements SellerInfoService { //卖家ID,6位序列号 private static final int SELLER_ID_WIDTH = 6; //自营卖家编码,4位序列号 private static final int SELLER_CODE_WIDTH = 4; @Autowired private SellerRepository sellerRepository; ... //新增卖家 @Override @Transactional(rollbackFor = Exception.class) public SellerInfoResultDTO saveSellerInfo(SellerInfoRequest request) { //1.校验参数完整性 checkSaveSellerInfoRequest(request); //2.父卖家是否存在 SellerInfoDO sellerInfo = checkParentSellerExists(request); //3.sellerId生成 Long sellerId = sellerRepository.generateSellerId(SkuSellerRelationConstants.SELLER_PREFIX_NO, SELLER_ID_WIDTH); request.setSellerId(sellerId); //新增卖家的父卖家的sellerCode是国标,找到国标前缀 String sellerCodePrefix = getSellerCodePrefix(sellerInfo); //4.sellerCode生成 String sellerCode = sellerRepository.generateSellerNo(sellerCodePrefix, SELLER_CODE_WIDTH); request.setSellerCode(sellerCode); //5.新增卖家数据 sellerRepository.saveSellerInfo(request); return new SellerInfoResultDTO(request.getSellerId(), Boolean.TRUE); } //获取自营卖家叶子结点的sellerCode前缀 private String getSellerCodePrefix(SellerInfoDO parentSellerInfo) { //父卖家编码 Integer sellerCode; try { sellerCode = Integer.parseInt(parentSellerInfo.getSellerCode()); } catch (NumberFormatException e) { throw new BaseBizException(ProductErrorCodeEnum.PARAM_CHECK_ERROR, ProductErrorCodeEnum.PARAM_CHECK_ERROR.getErrorCode()); } NationalCodeRequest request = new NationalCodeRequest(); request.setId(sellerCode); List<NationalCodeDO> nationalCodeList = nationalCodeRepository.queryNationalCode(request); NationalCodeDO nationalCode = nationalCodeList.get(0); if (Objects.isNull(nationalCode) || StringUtils.isEmpty(nationalCode.getShortName())) { throw new BaseBizException(ProductErrorCodeEnum.PARAM_CHECK_ERROR, ProductErrorCodeEnum.PARAM_CHECK_ERROR.getErrorCode()); } //转换为拼音首字母简写 return BopomofoUtil.initialsTransfer(nationalCode.getShortName()) + SkuSellerRelationConstants.SELF_SELLER_CODE_SEPARATOR; } ... } //卖家信息入参 @Data @Builder @AllArgsConstructor @NoArgsConstructor public class SellerInfoRequest extends PageRequest implements Serializable { //卖家ID集合(限制100个) List<Long> sellerIdList; //卖家ID private Long sellerId; //卖家名称 private String sellerName; //卖家编码 自营指微仓编码(WJ-1001),POP指区域编码(国标编码),上级编码用的是一套国标编码 private String sellerCode; //卖家描述 private String sellerDesc; //卖家位置:100-全国,200-大区,300-省(直辖市),400-城市,500-地区,600-微仓 private Integer sellerPosition; //自营:1,pop:2 private Integer sellerType; //卖家状态:1-开店,2-闭店,3-删除 private Integer sellerStatus; //父卖家ID private Integer parentId; //是否叶子节点 private Integer lastNode; //存储标签(1-常温,2-冷藏,3-冷冻,4-水产) private List<Integer> storeLabelList; //是否有试销标签(1-试销,0-非试销) private Integer trialSaleLabel; } @Repository public class SellerRepository { ... //生成卖家或卖家组ID,即sellerId、sellerGroupId生成 //sellerId格式:20开头,6位随机数 //sellerGroupId格式:30开头,4位随机数 public Long generateSellerId(String prefix, Integer width) { return Long.parseLong(generateSellerNo(prefix, width)); } //自营卖家叶子结点sellerCode生成 //格式:父卖家(区县)的首拼-4位随机数,如:西湖区 -> XH-1001 //@param prefix 前缀 //@param width 位数 public String generateSellerNo(String prefix, Integer width) { SellerAutoNoDO sellerAutoNoDO = new SellerAutoNoDO(); sellerAutoNoMapper.insert(sellerAutoNoDO); Long autoNo = sellerAutoNoDO.getId(); if (Objects.isNull(autoNo)) { throw new BaseBizException(CommonErrorCodeEnum.SQL_ERROR); } return prefix + IDUtils.genId(autoNo, width); } //新增卖家 public void saveSellerInfo(SellerInfoRequest request) { //是否存在该记录 LambdaQueryWrapper<SellerInfoDO> queryWrapper = Wrappers.lambdaQuery(); queryWrapper.eq(SellerInfoDO::getSellerId, request.getSellerId()); int count = sellerInfoMapper.selectCount(queryWrapper); if (count > 0) { throw new BaseBizException(ProductErrorCodeEnum.PARAM_CHECK_ERROR, ProductErrorCodeEnum.PARAM_CHECK_ERROR.getErrorCode()); } //DO对象初始化 SellerInfoDO sellerInfo = initSellerInfoDO(request); //保存到缓存 redisCache.set(SellerRedisKeyConstants.SELLER_INFO_LIST + sellerInfo.getSellerId(), JSON.toJSONString(sellerInfo), -1); //新卖家落库 count = sellerInfoMapper.insert(sellerInfo); if (count <= 0) { throw new BaseBizException(CommonErrorCodeEnum.SQL_ERROR); } } //新增卖家时初始化卖家信息 private SellerInfoDO initSellerInfoDO(SellerInfoRequest request) { SellerInfoDO sellerInfo = sellerInfoConverter.requestToEntity(request); sellerInfo.setSellerPosition(SellerPositionEnum.STORAGE.getCode()); sellerInfo.setSellerType(SellerTypeEnum.SELF.getCode()); sellerInfo.setLastNode(YesOrNoEnum.YES.getCode()); sellerInfo.initCommon(); return sellerInfo; } ... }
三.更新卖家接口
更新卖家的信息,允许对任意层级的卖家进⾏修改,但是仅允许修改:卖家名、卖家描述。
更新卖家状态,仅允许修改城市及城市以下(区县、微仓)卖家的状态。修改某个卖家状态后,该卖家下的⼦卖家状态也将同时修改。
@Service public class SellerInfoServiceImpl implements SellerInfoService { ... //更新卖家信息 //允许修改卖家名、卖家描述和卖家状态字段 //变更卖家状态后,所有子卖家状态也要变更,但仅允许修改城市及以下层级的卖家状态 @Override @Transactional(rollbackFor = Exception.class) public SellerInfoResultDTO updateSellerInfo(SellerInfoRequest request) { //1.校验参数完整性 checkUpdateSellerInfoRequest(request); //2.修改卖家数据 sellerRepository.updateSellerInfo(request); //sellerStatus是否变更 if (Objects.nonNull(request.getSellerStatus())) { //查询出所有的子卖家的sellerId List<Long> sellerIdList = getChildSellerIdList(Collections.singletonList(request.getSellerId()), request.getSellerType()); //批量修改状态 sellerRepository.batchUpdateSellerInfo(sellerIdList, request.getSellerStatus()); } return new SellerInfoResultDTO(request.getSellerId(), Boolean.TRUE); } //获取子卖家的sellerId private List<Long> getChildSellerIdList(List<Long> parentIdList, Integer sellerType) { if (Objects.isNull(sellerType)) { throw new BaseBizException(ProductErrorCodeEnum.PARAM_CHECK_ERROR, ProductErrorCodeEnum.PARAM_CHECK_ERROR.getErrorCode()); } List<Long> sellerIdList = Lists.newArrayList(); List<Long> sellerIds = sellerRepository.getSellerIdListByParentId(parentIdList, sellerType); if (CollectionUtils.isNotEmpty(sellerIds)) { sellerIdList.addAll(sellerIds); sellerIdList.addAll(getChildSellerIdList(sellerIds, sellerType)); } return sellerIdList; } ... } @Repository public class SellerRepository { ... //修改卖家 public void updateSellerInfo(SellerInfoRequest request) { //查询出数据库卖家信息 LambdaQueryWrapper<SellerInfoDO> queryWrapper = Wrappers.lambdaQuery(); queryWrapper.eq(SellerInfoDO::getSellerId, request.getSellerId()); SellerInfoDO sellerInfo = sellerInfoMapper.selectOne(queryWrapper); if (Objects.isNull(sellerInfo)) { throw new BaseBizException(ProductErrorCodeEnum.PARAM_CHECK_ERROR, ProductErrorCodeEnum.PARAM_CHECK_ERROR.getErrorCode()); } //更新到缓存 updateSellerInfoFromCache(request, sellerInfo); //更新到数据库 updateSellerInfoFromDB(request); } //更新卖家信息到数据库 private void updateSellerInfoFromDB(SellerInfoRequest request) { SellerInfoDO requestInfo = sellerInfoConverter.requestToEntity(request); LambdaUpdateWrapper<SellerInfoDO> updateWrapper = Wrappers.lambdaUpdate(); updateWrapper.eq(SellerInfoDO::getSellerId, request.getSellerId()); int count = sellerInfoMapper.update(requestInfo, updateWrapper); if (count <= 0) { throw new BaseBizException(CommonErrorCodeEnum.SQL_ERROR); } } //更新卖家信息到缓存 private void updateSellerInfoFromCache(SellerInfoRequest request, SellerInfoDO sellerInfo) { String redisKey = SellerRedisKeyConstants.SELLER_INFO_LIST + request.getSellerId(); if (StringUtils.isNotEmpty(request.getSellerName())) { sellerInfo.setSellerName(request.getSellerName()); } if (StringUtils.isNotEmpty(request.getSellerDesc())) { sellerInfo.setSellerDesc(request.getSellerDesc()); } if (Objects.nonNull(SellerInfoStatusEnum.getByCode(request.getSellerStatus()))) { sellerInfo.setSellerStatus(request.getSellerStatus()); } redisCache.set(redisKey, JSON.toJSONString(sellerInfo), -1); } //批量修改卖家状态 public void batchUpdateSellerInfo(List<Long> sellerIdList, Integer sellerStatus) { LambdaUpdateWrapper<SellerInfoDO> updateWrapper = Wrappers.lambdaUpdate(); updateWrapper.in(Objects.nonNull(sellerIdList), SellerInfoDO::getSellerId, sellerIdList); updateWrapper.set(SellerInfoDO::getSellerStatus, sellerStatus); int count = sellerInfoMapper.update(null, updateWrapper); if (count <= 0) { throw new BaseBizException(CommonErrorCodeEnum.SQL_ERROR); } } ... }
四.删除卖家接口
批量删除卖家接⼝,仅允许删除微仓卖家,也就是叶⼦结点卖家,⾮叶⼦结点卖家不允许删除。卖家树模型可以看成是类目树模型,卖家节点也分为叶子节点和非叶子节点。非叶子节点的卖家节点可以看成是卖家所属的分类,叶子节点的卖家节点才是具体的某一个卖家。
@Service public class SellerInfoServiceImpl implements SellerInfoService { ... //删除卖家 @Override @Transactional(rollbackFor = Exception.class) public SellerInfoResultDTO deleteSellerInfo(List<Integer> sellerIdList) { //1.是否存在非叶子卖家 if (sellerRepository.checkNonLastSellerExists(sellerIdList)) { throw new BaseBizException(ProductErrorCodeEnum.PARAM_CHECK_ERROR, ProductErrorCodeEnum.PARAM_CHECK_ERROR.getErrorCode()); } //2.删除 sellerRepository.deleteSellerInfo(sellerIdList); return new SellerInfoResultDTO(Boolean.TRUE); } ... } @Repository public class SellerRepository { ... //检查是否存在非叶子卖家 public Boolean checkNonLastSellerExists(List<Integer> sellerIdList) { LambdaQueryWrapper<SellerInfoDO> queryWrapper = Wrappers.lambdaQuery(); queryWrapper.eq(SellerInfoDO::getLastNode, YesOrNoEnum.NO.getCode()); queryWrapper.in(CollectionUtils.isNotEmpty(sellerIdList), SellerInfoDO::getSellerId, sellerIdList); Integer count = sellerInfoMapper.selectCount(queryWrapper); return Objects.nonNull(count) && count > 0; } //删除卖家 public void deleteSellerInfo(List<Integer> sellerIdList) { //删除缓存卖家 List<String> redisKeyList = sellerIdList.stream().map(sellerId -> SellerRedisKeyConstants.SELLER_INFO_LIST + sellerId).collect(toList()); redisCache.delete(redisKeyList); //删除数据库卖家 LambdaUpdateWrapper<SellerInfoDO> updateWrapper = Wrappers.lambdaUpdate(); //条件 updateWrapper.in(SellerInfoDO::getSellerId, sellerIdList); updateWrapper.eq(SellerInfoDO::getLastNode, YesOrNoEnum.YES.getCode()); //删除标志 updateWrapper.set(SellerInfoDO::getDelFlag, YesOrNoEnum.NO.getCode()); int count = sellerInfoMapper.update(null, updateWrapper); if (count <= 0) { throw new BaseBizException(CommonErrorCodeEnum.SQL_ERROR); } } ... }
五.查询卖家接口
根据卖家ID、卖家类型、卖家名、⽗卖家ID等字段,对卖家进⾏查询,该接⼝提供给运营⼈员进⾏操作。如果传⼊卖家ID集合或卖家类型参数,则先尝试从Redis中查询,没有查到再根据其他参数从数据库中查询。
这里使用了简化版的缓存 + DB读写逻辑。
@Service public class SellerInfoServiceImpl implements SellerInfoService { @Autowired private SellerRepository sellerRepository; @Autowired private SellerInfoCache sellerInfoCache; ... //查询卖家,用于运营人员前端界面筛选卖家操作 @Override public List<SellerInfoResponse> querySellerInfo(SellerInfoRequest request) { //校验参数完整性 checkQuerySellerInfoRequest(request); if (CollectionUtils.isEmpty(request.getSellerIdList())) { //根据sellerType获取该类型下的sellerId集合 Optional<List<Long>> sellerIdListOps = getSellerIdListBySellerType(request); sellerIdListOps.ifPresent(request::setSellerIdList); } //根据sellerIdList从缓存或者数据库中查询卖家信息 Optional<List<SellerInfoResponse>> sellerInfoListOps = sellerInfoCache.listRedisStringDataByCache(request.getSellerIdList(), SellerInfoResponse.class, sellerId -> SellerRedisKeyConstants.SELLER_INFO_LIST + sellerId, request, req -> sellerRepository.querySellerInfo(request)); return sellerInfoListOps.orElse(Collections.emptyList()); } ... } @Repository public class SellerRepository { ... //根据条件分页查询出卖家列表 public Optional<List<SellerInfoResponse>> querySellerInfo(SellerInfoRequest request) { LambdaQueryWrapper<SellerInfoDO> queryWrapper = Wrappers.lambdaQuery(); //类型 queryWrapper.eq(Objects.nonNull(request.getSellerType()), SellerInfoDO::getSellerType, request.getSellerType()); //卖家位置(层级) queryWrapper.eq(Objects.nonNull(request.getSellerPosition()), SellerInfoDO::getSellerPosition, request.getSellerPosition()); //状态 queryWrapper.eq(Objects.nonNull(request.getSellerStatus()), SellerInfoDO::getSellerStatus, request.getSellerStatus()); //卖家ID queryWrapper.eq(Objects.nonNull(request.getSellerId()), SellerInfoDO::getSellerId, request.getSellerId()); //卖家编码 queryWrapper.eq(StringUtils.isNotEmpty(request.getSellerCode()), SellerInfoDO::getSellerCode, request.getSellerCode()); //卖家名称 queryWrapper.like(StringUtils.isNotEmpty(request.getSellerName()), SellerInfoDO::getSellerName, request.getSellerName()); //父卖家ID queryWrapper.eq(Objects.nonNull(request.getParentId()), SellerInfoDO::getParentId, request.getParentId()); //卖家ID集合 queryWrapper.in(CollectionUtils.isNotEmpty(request.getSellerIdList()), SellerInfoDO::getSellerId, request.getSellerIdList()); Page<SellerInfoDO> page = new Page<>(request.getPageNo(), request.getPageSize()); Page<SellerInfoDO> pageResult = sellerInfoMapper.selectPage(page, queryWrapper); if (Objects.isNull(pageResult)) { return Optional.of(Collections.emptyList()); } return Optional.of(sellerInfoConverter.listEntityToResponse(pageResult.getRecords())); } ... } @Service("sellerInfoCache") public class SellerInfoCache { @Resource private SellerRepository sellerRepository; @Resource private RedisCache redisCache; @Resource private RedisLock redisLock; ... //批量获取缓存数据 //@param keyList 关键字列表 //@param clazz 需要将缓存JSON转换的对象 //@param getRedisKeyFunction 获取redis key的方法 //@param request 查询数据源对象的参数 //@param getDbFuction 获取数据源对象的方法 public <T> Optional<List<T>> listRedisStringDataByCache(Collection<Long> keyList, Class<T> clazz, Function<Long, String> getRedisKeyFunction, SellerInfoRequest request, Function<SellerInfoRequest, Optional<List<T>>> getDbFuction) { try { List<T> list = Lists.newArrayList(); List<Long> pendingKeyList = keyList.stream().distinct().collect(toList()); List<String> redisKeyList = pendingKeyList.stream().map(getRedisKeyFunction).distinct().collect(toList()); List<String> cacheList = redisCache.mget(redisKeyList); for (int i = 0; i < cacheList.size(); i++) { String cache = cacheList.get(i); //过滤无效缓存 if (EMPTY_OBJECT_STRING.equals(cache)) { continue; } if (StringUtils.isNotBlank(cache)) { T t = JSON.parseObject(cache, clazz); list.add(t); continue; } //缓存没有则读库 Optional<List<T>> optional = getRedisStringDataByDb(pendingKeyList.get(i), request, getRedisKeyFunction, getDbFuction); if (optional.isPresent()) { list.addAll(optional.get()); } } return CollectionUtils.isEmpty(list) ? Optional.empty() : Optional.of(list); } catch (Exception e) { log.error("批量获取缓存数据异常 keyList={},clazz={}", keyList, clazz, e); throw e; } } //读取数据库表数据赋值到Redis public <T> Optional<List<T>> getRedisStringDataByDb(Long key, SellerInfoRequest request, Function<Long, String> getRedisKeyFunction, Function<SellerInfoRequest, Optional<List<T>>> getDbFuction) { if (Objects.isNull(key) || Objects.isNull(request) || Objects.isNull(getDbFuction)) { return Optional.empty(); } try { if (!redisLock.lock(String.valueOf(key))) { return Optional.empty(); } String redisKey = getRedisKeyFunction.apply(key); //从DB中获取数据 Optional<List<T>> optional = getDbFuction.apply(request); if (!optional.isPresent()) { //把空对象暂存到Redis redisCache.setex(redisKey, EMPTY_OBJECT_STRING, RedisKeyUtils.redisKeyRandomTime(INT_EXPIRED_ONE_DAY, TimeUnit.HOURS, NUMBER_24)); log.warn("发生缓存穿透 redisKey={}", redisKey); return optional; } //把表数据对象存到Redis redisCache.setex(redisKey, JSON.toJSONString(optional.get()), RedisKeyUtils.redisKeyRandomTime(INT_EXPIRED_SEVEN_DAYS)); log.info("表数据对象存到redis redisKey={}, data={}", redisKey, optional.get()); return optional; } finally { redisLock.unlock(String.valueOf(key)); } } ... }
六.查询卖家RPC接口
提供给其他服务调⽤的RPC接⼝,仅允许通过卖家ID集合或卖家类型查询。
情况一:如果仅传⼊卖家ID集合,则根据ID查询
情况二:如果同时传⼊卖家ID集合与卖家类型,则根据卖家类型过滤查询到的类型不⼀致的卖家数据
情况三:如果仅传⼊卖家类型,则先查出2⻚卖家ID集合数据,再根据查出的卖家ID查询卖家集合
Redis缓存结构中1⻚存放100条卖家ID,这里也使用了简化版的缓存DB读写逻辑。
@Service public class SellerInfoServiceImpl implements SellerInfoService { @Autowired private SellerInfoCache sellerInfoCache; ... //不同系统间调用RPC接口,查询卖家 //卖家系统提供给外部系统调用的卖家查询接口,只允许通过sellerIdList或者sellerType两个参数进行查询 @Override public List<SellerInfoResponse> querySellerInfoForRPC(SellerInfoRequest request) { //参数校验,RPC接口根据sellerIdList和sellerType查询 checkQuerySellerInfoRequestByRPC(request); //如果未传入sellerIdList if (CollectionUtils.isEmpty(request.getSellerIdList())) { //根据sellerType获取该类型下的sellerId集合 Optional<List<Long>> sellerIdListOps = getSellerIdListBySellerType(request); //如果类型下没有sellerId数据,直接返回空卖家集合 if (!sellerIdListOps.isPresent()) { return Collections.emptyList(); } //将根据sellerType查询到的sellerIdList数据set到request中 request.setSellerIdList(sellerIdListOps.get()); } //根据sellerIdList从缓存或者数据库中查询卖家信息 Optional<List<SellerInfoResponse>> sellerInfoListOps = sellerInfoCache.listRedisStringDataByCache(request.getSellerIdList(), SellerInfoResponse.class, sellerId -> SellerRedisKeyConstants.SELLER_INFO_LIST + sellerId, //根据sellerId从DB中获取SellerInfo数据 sellerId -> sellerRepository.querySellerInfoBySellerId(sellerId)); if (!sellerInfoListOps.isPresent()) { return Collections.emptyList(); } //过滤类型不一致的卖家信息 return filterAccordantSellerInfo(sellerInfoListOps.get(), request); } ... } @Repository public class SellerRepository { ... //根据sellerId查询SellerInfo数据 public Optional<SellerInfoResponse> querySellerInfoBySellerId(Long sellerId) { LambdaQueryWrapper<SellerInfoDO> queryWrapper = Wrappers.lambdaQuery(); queryWrapper.eq(Objects.nonNull(sellerId), SellerInfoDO::getSellerId, sellerId); return Optional.ofNullable(sellerInfoConverter.entityToResponse(sellerInfoMapper.selectOne(queryWrapper))); } } @Service("sellerInfoCache") public class SellerInfoCache { @Resource private SellerRepository sellerRepository; @Resource private RedisCache redisCache; @Resource private RedisLock redisLock; ... //批量获取缓存数据 //@param keyList 关键字列表 //@param clazz 需要将缓存JSON转换的对象 //@param getRedisKeyFunction 获取redis key的方法 //@param getDbFuction 获取数据源对象的方法 public <T> Optional<List<T>> listRedisStringDataByCache(Collection<Long> keyList, Class<T> clazz, Function<Long, String> getRedisKeyFunction, Function<Long, Optional<T>> getDbFuction) { try { List<T> list = Lists.newArrayList(); List<Long> pendingKeyList = keyList.stream().distinct().collect(toList()); List<String> redisKeyList = pendingKeyList.stream().map(getRedisKeyFunction).distinct().collect(toList()); List<String> cacheList = redisCache.mget(redisKeyList); if (CollectionUtils.isEmpty(cacheList)) { for (Long pendingKey : pendingKeyList) { //缓存没有则读库 Optional<T> optional = getRedisStringDataByDb(pendingKey, getRedisKeyFunction, getDbFuction); if (optional.isPresent()) { list.add(optional.get()); } } return CollectionUtils.isEmpty(list) ? Optional.empty() : Optional.of(list); } for (int i = 0; i < cacheList.size(); i++) { String cache = cacheList.get(i); //过滤无效缓存 if (EMPTY_OBJECT_STRING.equals(cache)) { continue; } if (StringUtils.isNotBlank(cache)) { T t = JSON.parseObject(cache, clazz); list.add(t); continue; } //缓存没有则读库 Optional<T> optional = getRedisStringDataByDb(pendingKeyList.get(i), getRedisKeyFunction, getDbFuction); if (optional.isPresent()) { list.add(optional.get()); } } return CollectionUtils.isEmpty(list) ? Optional.empty() : Optional.of(list); } catch (Exception e) { log.error("批量获取缓存数据异常 keyList={},clazz={}", keyList, clazz, e); throw e; } } //读取数据库表数据赋值到redis public <T> Optional<T> getRedisStringDataByDb(Long key, Function<Long, String> getRedisKeyFunction, Function<Long, Optional<T>> getDbFuction) { if (Objects.isNull(key) || Objects.isNull(getDbFuction)) { return Optional.empty(); } try { if (!redisLock.lock(String.valueOf(key))) { return Optional.empty(); } String redisKey = getRedisKeyFunction.apply(key); Optional<T> optional = getDbFuction.apply(key); if (!optional.isPresent()) { //把空对象暂存到Redis redisCache.setex(redisKey, EMPTY_OBJECT_STRING, RedisKeyUtils.redisKeyRandomTime(INT_EXPIRED_ONE_DAY, TimeUnit.HOURS, NUMBER_24)); log.warn("发生缓存穿透 redisKey={}", redisKey); return optional; } //把表数据对象存到Redis redisCache.setex(redisKey, JSON.toJSONString(optional.get()), RedisKeyUtils.redisKeyRandomTime(INT_EXPIRED_SEVEN_DAYS)); log.info("表数据对象存到redis redisKey={}, data={}", redisKey, optional.get()); return optional; } finally { redisLock.unlock(String.valueOf(key)); } } ... }
(2)卖家组接口
一.新增卖家组接口
二.更新卖家组接口
三.查询卖家组接口
四.删除卖家组接口
一.新增卖家组接口
新增⼀个卖家组,⼀个卖家组原则上不允许圈定不同区域的卖家,卖家组只限定其下的卖家类型要与卖家组保持⼀致。
@Service public class SellerGroupServiceImpl implements SellerGroupService { //卖家组,4位序列号 private static final int GROUP_WIDTH = 4; @Autowired private SellerRepository sellerRepository; ... //新增卖家组 @Override @Transactional(rollbackFor = Exception.class) public SellerGroupResultDTO saveSellerGroupInfo(SellerGroupRequest request) { //1.参数检查 checkSaveSellerGroupRequest(request); //2.sellerGroupId生成 Long sellerGroupId = sellerRepository.generateSellerId(SkuSellerRelationConstants.SELLER_GROUP_PREFIX_NO, GROUP_WIDTH); request.setSellerGroupId(sellerGroupId); //3.新增卖家组数据 sellerRepository.saveSellerGroup(request); return new SellerGroupResultDTO(request.getSellerGroupId(), Boolean.TRUE); } ... } //卖家组入参 @Data @Builder @AllArgsConstructor @NoArgsConstructor public class SellerGroupRequest extends PageRequest implements Serializable { //卖家组ID private Long sellerGroupId; //卖家组名称 private String sellerGroupName; //卖家组类型:1-自营,2-POP private Integer sellerGroupType; //卖家组ID集合(限制100个) private List<Long> sellerGroupIdList; //卖家组状态:1-有效,0-无效 private Integer sellerGroupStatus; //是否需要卖家信息 private Boolean needSellerInfo = false; } @Repository public class SellerRepository { ... //生成卖家或卖家组ID,即sellerId、sellerGroupId生成 //sellerId格式:20开头,6位随机数 //sellerGroupId格式:30开头,4位随机数 public Long generateSellerId(String prefix, Integer width) { return Long.parseLong(generateSellerNo(prefix, width)); } //自营卖家叶子结点sellerCode生成 //格式:父卖家(区县)的首拼-4位随机数,如:西湖区 -> XH-1001 //@param prefix 前缀 //@param width 位数 public String generateSellerNo(String prefix, Integer width) { SellerAutoNoDO sellerAutoNoDO = new SellerAutoNoDO(); sellerAutoNoMapper.insert(sellerAutoNoDO); Long autoNo = sellerAutoNoDO.getId(); if (Objects.isNull(autoNo)) { throw new BaseBizException(CommonErrorCodeEnum.SQL_ERROR); } return prefix + IDUtils.genId(autoNo, width); } //新增卖家组 public void saveSellerGroup(SellerGroupRequest request) { //是否存在该记录 LambdaQueryWrapper<SellerGroupDO> queryWrapper = Wrappers.lambdaQuery(); queryWrapper.eq(SellerGroupDO::getSellerGroupId, request.getSellerGroupId()); int count = sellerGroupMapper.selectCount(queryWrapper); if (count > 0) { throw new BaseBizException(ProductErrorCodeEnum.PARAM_CHECK_ERROR, ProductErrorCodeEnum.PARAM_CHECK_ERROR.getErrorCode()); } //DO对象初始化 SellerGroupDO sellerGroup = sellerGroupConverter.requestToEntity(request); sellerGroup.initCommon(); //新卖家落库 count = sellerGroupMapper.insert(sellerGroup); if (count <= 0) { throw new BaseBizException(CommonErrorCodeEnum.SQL_ERROR); } } ... }
二.更新卖家组接口
只允许更新卖家组的名称和状态,不允许修改卖家组的类型。
@Service public class SellerGroupServiceImpl implements SellerGroupService { @Autowired private SellerRepository sellerRepository; ... //更新卖家组信息 @Override @Transactional(rollbackFor = Exception.class) public SellerGroupResultDTO updateSellerGroupInfo(SellerGroupRequest request) { //1.参数检查 checkUpdateSellerGroupRequest(request); //2.修改卖家组数据 sellerRepository.updateSellerGroup(request); return new SellerGroupResultDTO(request.getSellerGroupId(), Boolean.TRUE); } ... } @Repository public class SellerRepository { ... //修改卖家组数据,卖家组类型禁止修改 public void updateSellerGroup(SellerGroupRequest request) { LambdaUpdateWrapper<SellerGroupDO> updateWrapper = Wrappers.lambdaUpdate(); updateWrapper.set(StringUtils.isNotEmpty(request.getSellerGroupName()), SellerGroupDO::getSellerGroupName, request.getSellerGroupName()); updateWrapper.set(Objects.nonNull(request.getSellerGroupStatus()), SellerGroupDO::getSellerGroupStatus, request.getSellerGroupStatus()); updateWrapper.eq(SellerGroupDO::getSellerGroupId, request.getSellerGroupId()); int count = sellerGroupMapper.update(null, updateWrapper); if (count <= 0) { throw new BaseBizException(CommonErrorCodeEnum.SQL_ERROR); } } ... }
三.查询卖家组接口
批量查询出卖家组,允许携带卖家组下的卖家信息。
@Service public class SellerGroupServiceImpl implements SellerGroupService { @Autowired private SellerRepository sellerRepository; ... //查询卖家组 @Override public PageResult<SellerGroupResponse> querySellerGroupInfo(SellerGroupRequest request) { //1.校验参数完整性 checkQuerySellerGroupRequest(request); //2.根据条件分页查询出卖家组 PageResult<SellerGroupResponse> sellerGroupResponsePage = sellerRepository.querySellerGroup(request); //3.判断是否需要获取卖家组对应的卖家信息 if (!request.getNeedSellerInfo()) { return sellerGroupResponsePage; } //4.返回带有卖家数据的响应结果 return sellerRepository.querySellerInfoByGroupList(sellerGroupResponsePage); } ... } @Repository public class SellerRepository { ... //查询卖家组 public PageResult<SellerGroupResponse> querySellerGroup(SellerGroupRequest request) { LambdaQueryWrapper<SellerGroupDO> queryWrapper = Wrappers.lambdaQuery(); //卖家组ID queryWrapper.eq(Objects.nonNull(request.getSellerGroupId()), SellerGroupDO::getSellerGroupId, request.getSellerGroupId()); //卖家组名称 queryWrapper.like(StringUtils.isNotEmpty(request.getSellerGroupName()), SellerGroupDO::getSellerGroupName, request.getSellerGroupName()); //卖家组状态 queryWrapper.eq(Objects.nonNull(request.getSellerGroupStatus()), SellerGroupDO::getSellerGroupStatus, request.getSellerGroupStatus()); //卖家组ID集合 queryWrapper.in(CollectionUtils.isNotEmpty(request.getSellerGroupIdList()), SellerGroupDO::getSellerGroupId, request.getSellerGroupIdList()); queryWrapper.eq(SellerGroupDO::getDelFlag, YesOrNoEnum.YES.getCode()); Page<SellerGroupDO> page = new Page<>(request.getPageNo(), request.getPageSize()); Page<SellerGroupDO> pageResult = sellerGroupMapper.selectPage(page, queryWrapper); if (Objects.isNull(pageResult)) { return new PageResult<>(); } List<SellerGroupDO> groupList = pageResult.getRecords(); List<SellerGroupResponse> sellerGroupResponseList = sellerGroupConverter.listEntityToResponse(groupList); return new PageResult<>(sellerGroupResponseList); } //根据group信息查询对应的卖家信息 public PageResult<SellerGroupResponse> querySellerInfoByGroupList(PageResult<SellerGroupResponse> page) { //提取groupIdList,用于查询对应的卖家数据信息 List<Long> groupIdList = page.getContent().stream().map(SellerGroupResponse::getSellerGroupId).collect(Collectors.toList()); //查询出卖家数据与卖家组id映射关系 Map<Long, List<SellerInfoDTO>> sellerInfoMap = querySellerInfoBySellerIdList(groupIdList); //组装卖家数据到响应数据体中 List<SellerGroupResponse> sellerGroupResponseList = page.getContent().stream() .peek(sellerGroupResponse -> sellerGroupResponse.setSellerInfoDTOList(sellerInfoMap.get(sellerGroupResponse.getSellerGroupId()))) .collect(Collectors.toList()); return new PageResult<>(sellerGroupResponseList); } //根据卖家组ID集合查询出对应的卖家集合 private Map<Long, List<SellerInfoDTO>> querySellerInfoBySellerIdList(List<Long> groupIdList) { //查询卖家与卖家组关系集合 List<SellerGroupRelationDTO> relationList = querySellerGroupRelationInfo(groupIdList); //卖家ID和卖家组ID的映射关系 Map<Long, Long> sellerGroupIdRelationMap = new HashMap<>(); //卖家ID集合 List<Long> sellerIdList = new ArrayList<>(); for (SellerGroupRelationDTO relation : relationList) { sellerGroupIdRelationMap.put(relation.getSellerId(), relation.getSellerGroupId()); sellerIdList.add(relation.getSellerId()); } SellerInfoRequest request = new SellerInfoRequest(); request.setSellerIdList(sellerIdList); //根据条件分页查询出卖家列表 Optional<List<SellerInfoResponse>> sellerInfoResponseListOps = querySellerInfo(request); if (!sellerInfoResponseListOps.isPresent()) { return Collections.emptyMap(); } List<SellerInfoResponse> sellerInfoResponseList = sellerInfoResponseListOps.get(); Map<Long, List<SellerInfoDTO>> result = new HashMap<>(); for (SellerInfoResponse sellerInfoResponse : sellerInfoResponseList) { Long groupId = sellerGroupIdRelationMap.get(sellerInfoResponse.getSellerId()); List<SellerInfoDTO> sellerInfoList = result.get(groupId); if (CollectionUtils.isEmpty(sellerInfoList)) { sellerInfoList = new ArrayList<>(); } sellerInfoList.add(sellerInfoConverter.responseToDTO(sellerInfoResponse)); result.put(groupId, sellerInfoList); } return result; } //查询卖家与卖家关系集合 private List<SellerGroupRelationDTO> querySellerGroupRelationInfo(List<Long> groupIdList) { List<SellerGroupRelationDO> relationList = new ArrayList<>(); //一次最大查询200个数据,多个分页查询,这里做数据切割 List<List<Long>> splitList = DataCuttingUtil.dataCuttingString(groupIdList, ProductConstants.QUERY_ITEM_MAX_COUNT); for (List<Long> groupIds : splitList) { LambdaQueryWrapper<SellerGroupRelationDO> queryWrapper = Wrappers.lambdaQuery(); queryWrapper.in(Objects.nonNull(groupIds), SellerGroupRelationDO::getSellerGroupId, groupIds); List<SellerGroupRelationDO> relations = sellerGroupRelationMapper.selectList(queryWrapper); if (!CollectionUtils.isEmpty(relations)) { relationList.addAll(relations); } } return sellerGroupRelationConverter.listEntityToDTO(relationList); } //根据条件分页查询出卖家列表 public Optional<List<SellerInfoResponse>> querySellerInfo(SellerInfoRequest request) { LambdaQueryWrapper<SellerInfoDO> queryWrapper = Wrappers.lambdaQuery(); //类型 queryWrapper.eq(Objects.nonNull(request.getSellerType()), SellerInfoDO::getSellerType, request.getSellerType()); //卖家位置(层级) queryWrapper.eq(Objects.nonNull(request.getSellerPosition()), SellerInfoDO::getSellerPosition, request.getSellerPosition()); //状态 queryWrapper.eq(Objects.nonNull(request.getSellerStatus()), SellerInfoDO::getSellerStatus, request.getSellerStatus()); //卖家ID queryWrapper.eq(Objects.nonNull(request.getSellerId()), SellerInfoDO::getSellerId, request.getSellerId()); //卖家编码 queryWrapper.eq(StringUtils.isNotEmpty(request.getSellerCode()), SellerInfoDO::getSellerCode, request.getSellerCode()); //卖家名称 queryWrapper.like(StringUtils.isNotEmpty(request.getSellerName()), SellerInfoDO::getSellerName, request.getSellerName()); //父卖家ID queryWrapper.eq(Objects.nonNull(request.getParentId()), SellerInfoDO::getParentId, request.getParentId()); //卖家ID集合 queryWrapper.in(CollectionUtils.isNotEmpty(request.getSellerIdList()), SellerInfoDO::getSellerId, request.getSellerIdList()); Page<SellerInfoDO> page = new Page<>(request.getPageNo(), request.getPageSize()); Page<SellerInfoDO> pageResult = sellerInfoMapper.selectPage(page, queryWrapper); if (Objects.isNull(pageResult)) { return Optional.of(Collections.emptyList()); } return Optional.of(sellerInfoConverter.listEntityToResponse(pageResult.getRecords())); } ... }
四.删除卖家组接口
根据传⼊的sellerGroupIdList(卖家组ID集合)批量删除卖家组。
@Service public class SellerGroupServiceImpl implements SellerGroupService { @Autowired private SellerRepository sellerRepository; ... //删除卖家组 @Override @Transactional(rollbackFor = Exception.class) public SellerGroupResultDTO deleteSellerGroupInfo(List<Long> sellerGroupIdList) { sellerRepository.deleteSellerGroup(sellerGroupIdList); return new SellerGroupResultDTO(Boolean.TRUE); } ... } @Repository public class SellerRepository { ... //删除卖家组 public void deleteSellerGroup(List<Long> sellerGroupIdList) { LambdaUpdateWrapper<SellerGroupDO> updateWrapper = Wrappers.lambdaUpdate(); //条件 updateWrapper.in(SellerGroupDO::getSellerGroupId, sellerGroupIdList); //删除标志 updateWrapper.set(SellerGroupDO::getDelFlag, YesOrNoEnum.NO.getCode()); int count = sellerGroupMapper.update(null, updateWrapper); if (count <= 0) { throw new BaseBizException(CommonErrorCodeEnum.SQL_ERROR); } } ... }
(3)卖家与卖家组关系
一.新增卖家与卖家组关系
二.查询卖家与卖家组关系
三.删除卖家与卖家组关系
一.新增卖家与卖家组关系
新增卖家与卖家关系时,需要保证卖家与卖家组的类型是⼀样的,否则⽆法添加。
@Service public class SellerGroupRelationServiceImpl implements SellerGroupRelationService { @Autowired private SellerRepository sellerRepository; ... //新增卖家组与卖家关系 @Override public SellerGroupRelationResultDTO saveSellerGroupRelationInfo(SellerGroupRelationRequest request) { //参数检查 checkSaveSellerGroupRelationRequest(request); //卖家ID类型是否有和卖家组的类型不一致的 checkAndGetSellerTypeConsistency(request); //过滤掉已经添加过相同关系的卖家与卖家组关系 request = filterIfExist(request); //批量插入 log.info("request:{}", JSON.toJSONString(request)); sellerRepository.saveSellerGroupRelationInfo(request); SellerGroupRelationResultDTO result = new SellerGroupRelationResultDTO(request.getSellerGroupId(), request.getSellerIdList(), Boolean.TRUE); return result; } private SellerGroupRelationRequest filterIfExist(SellerGroupRelationRequest request) { SellerGroupRelationRequest relationRequest = SellerGroupRelationRequest.builder().sellerIdList(request.getSellerIdList()).sellerGroupId(request.getSellerGroupId()).build(); List<SellerGroupRelationDTO> sellerGroupRelationDTOS = sellerRepository.querySellerGroupRelationInfo(relationRequest); List<Long> existSellerIds = sellerGroupRelationDTOS.stream().map(relation -> relation.getSellerId()).collect(Collectors.toList()); List<Long> filteredSellerIds = request.getSellerIdList().stream().filter(sellerId -> !existSellerIds.contains(sellerId)).collect(Collectors.toList()); log.info("filteredSellerIds:{}", JSON.toJSONString(filteredSellerIds)); return SellerGroupRelationRequest.builder().sellerGroupId(request.getSellerGroupId()).sellerIdList(filteredSellerIds).build(); } ... } //卖家与卖家组关系入参 @Data @Builder @AllArgsConstructor @NoArgsConstructor public class SellerGroupRelationRequest extends PageRequest implements Serializable { //卖家组ID @NotNull(message = "sellerGroupId不能为空") private Long sellerGroupId; //卖家id列表 @Size(min = 1, max = 100, message = "sellerIdList不能为空,且最多同时操作200个") private List<Long> sellerIdList; //卖家组ID集合 private List<Long> sellerGroupIdList; } @Repository public class SellerRepository { ... //根据卖家组id列表,卖家组类型查询卖家与卖家关系 public List<SellerGroupRelationDTO> querySellerGroupRelationInfo(SellerGroupRelationRequest request) { LambdaQueryWrapper<SellerGroupRelationDO> queryWrapper = Wrappers.lambdaQuery(); queryWrapper.eq(Objects.nonNull(request.getSellerGroupId()), SellerGroupRelationDO::getSellerGroupId, request.getSellerGroupId()); //分页查询存放数据的总集合 List<SellerGroupRelationDO> results = new ArrayList<>(); Page<SellerGroupRelationDO> page = new Page<>(request.getPageNo(), request.getPageSize()); //查询分页数据 Page<SellerGroupRelationDO> pageResult = sellerGroupRelationMapper.selectPage(page, queryWrapper); results.addAll(pageResult.getRecords()); return sellerGroupRelationConverter.convertAccountsTODTO(results); } //批量保存卖家与卖家组信息 public void saveSellerGroupRelationInfo(SellerGroupRelationRequest request) { //经过前置sellerId过滤,sellerId为空说明本批次的relation关系已经保存过 //直接返回即可 if (CollectionUtils.isEmpty(request.getSellerIdList())) { return; } List<SellerGroupRelationDO> relationList = request.getSellerIdList().stream().map(sellerId -> { SellerGroupRelationDO relation = new SellerGroupRelationDO(); relation.setSellerGroupId(request.getSellerGroupId()); relation.setSellerId(sellerId); relation.initCommon(); return relation; }).collect(Collectors.toList()); Integer count = sellerGroupRelationMapper.insertBatch(relationList); if (Objects.isNull(count) || count <= 0) { throw new BaseBizException(CommonErrorCodeEnum.SQL_ERROR); } } ... }
二.查询卖家与卖家组关系
根据传⼊的sellerGroupId(卖家组ID)查询卖家与卖家组关系。
@Service public class SellerGroupRelationServiceImpl implements SellerGroupRelationService { @Autowired private SellerRepository sellerRepository; ... //查询卖家组与卖家关系信息 @Override public List<SellerGroupRelationDTO> querySellerGroupRelationInfo(SellerGroupRelationRequest request) { return sellerRepository.querySellerGroupRelationInfo(request); } ... } @Repository public class SellerRepository { ... //根据卖家组id列表,卖家组类型查询卖家与卖家关系 public List<SellerGroupRelationDTO> querySellerGroupRelationInfo(SellerGroupRelationRequest request) { LambdaQueryWrapper<SellerGroupRelationDO> queryWrapper = Wrappers.lambdaQuery(); queryWrapper.eq(Objects.nonNull(request.getSellerGroupId()), SellerGroupRelationDO::getSellerGroupId, request.getSellerGroupId()); //分页查询存放数据的总集合 List<SellerGroupRelationDO> results = new ArrayList<>(); Page<SellerGroupRelationDO> page = new Page<>(request.getPageNo(), request.getPageSize()); //查询分页数据 Page<SellerGroupRelationDO> pageResult = sellerGroupRelationMapper.selectPage(page, queryWrapper); results.addAll(pageResult.getRecords()); return sellerGroupRelationConverter.convertAccountsTODTO(results); } ... }
三.删除卖家与卖家组关系
根据sellerGroupId(卖家组ID)和sellerIdList(卖家ID)删除卖家与卖家组关系。
@Service public class SellerGroupRelationServiceImpl implements SellerGroupRelationService { @Autowired private SellerRepository sellerRepository; ... //根据卖家id和卖家组id删除卖家与卖家组关系 @Override @Transactional(rollbackFor = Exception.class) public Boolean deleteBySellerId(SellerGroupRelationRequest request) { //校验参数 checkSaveSellerGroupRelationRequest(request); return sellerRepository.deleteRelationBySellerId(request.getSellerIdList(), request.getSellerGroupId()); } ... } @Repository public class SellerRepository { ... //逻辑删除卖家与卖家组关系 public Boolean deleteRelationBySellerId(List<Long> sellerIds, Long sellerGroupId) { int count = sellerGroupRelationMapper.deleteBySellerId(sellerIds, sellerGroupId, YesOrNoEnum.NO.getCode()); if (count <= 0) { throw new ProductBizException(CommonErrorCodeEnum.SQL_ERROR); } return true; } ... }
(4)卖家账户
一.新增卖家账户
二.更新支付渠道
三.查询卖家的所有账户信息
四.删除一个卖家账户信息
一.新增卖家账户
新增卖家与卖家关系时,需保证卖家与卖家组类型是⼀样,否则⽆法添加。
@Service public class SellerAccountServiceImpl implements SellerAccountService { @Autowired private SellerRepository sellerRepository; ... //保存一个用户账号信息 @Override @Transactional(rollbackFor = Exception.class) public Boolean saveSellerAccountInfo(SellerAccountRequest request) { //校验参数是否为null checkAccountParam(request); return sellerRepository.saveSellerAccountInfo(request); } ... } @Repository public class SellerRepository { ... //保存一个卖家账户信息 public Boolean saveSellerAccountInfo(SellerAccountRequest request) { SellerAccountDO accountDO = sellerAccountConverter.convertToDO(request); //初始化默认参数 accountDO.initCommon(); Integer count = sellerAccountMapper.insert(accountDO); if (count <= 0) { throw new ProductBizException(CommonErrorCodeEnum.SQL_ERROR); } return true; } ... }
二.更新支付渠道
修改某个卖家账户下的⽀付渠道。
@Service public class SellerAccountServiceImpl implements SellerAccountService { @Autowired private SellerRepository sellerRepository; ... //更新支付渠道信息 @Override @Transactional(rollbackFor = Exception.class) public Boolean updateSellerAccountInfo(SellerAccountRequest request) { //校验参数是否为null checkAccountParam(request); return sellerRepository.updateSellerAccountInfo(request); } ... } @Repository public class SellerRepository { ... //更新卖家账户的支付渠道信息 public Boolean updateSellerAccountInfo(SellerAccountRequest request) { SellerAccountDO accountDO = sellerAccountConverter.convertToDO(request); //根据卖家id查询账户信息 LambdaQueryWrapper<SellerAccountDO> queryWrapper = Wrappers.lambdaQuery(); queryWrapper.eq(SellerAccountDO::getSellerId, accountDO.getSellerId()); queryWrapper.eq(SellerAccountDO::getAccountNo, accountDO.getAccountNo()); queryWrapper.eq(SellerAccountDO::getDelFlag, YesOrNoEnum.YES.getCode()); SellerAccountDO sellerAccountDO = sellerAccountMapper.selectOne(queryWrapper); if (Objects.isNull(sellerAccountDO)) { throw new BaseBizException("该卖家的待更新账户信息不存在"); } //设置更新字段,执行更新操作 sellerAccountDO.setPayChannel(accountDO.getPayChannel()); sellerAccountDO.setUpdateTime(new Date()); int count = sellerAccountMapper.updateById(sellerAccountDO); if (count <= 0) { throw new ProductBizException(CommonErrorCodeEnum.SQL_ERROR); } return true; } ... }
三.查询卖家的所有账户信息
根据sellerId(卖家ID)查询该卖家的所有⽀付渠道。
@Service public class SellerAccountServiceImpl implements SellerAccountService { @Autowired private SellerRepository sellerRepository; ... //根据卖家id查询卖家全部的账号信息 @Override public List<SellerAccountDTO> queryAccountsBySellerId(Long sellerId) { return sellerRepository.queryAccountsBySellerId(sellerId); } ... } @Repository public class SellerRepository { ... //根据sellerId查询所有卖家相关的账号 public List<SellerAccountDTO> queryAccountsBySellerId(Long sellerId) { LambdaQueryWrapper<SellerAccountDO> queryWrapper = Wrappers.lambdaQuery(); queryWrapper.eq(SellerAccountDO::getSellerId, sellerId); queryWrapper.eq(SellerAccountDO::getDelFlag, YesOrNoEnum.YES.getCode()); List<SellerAccountDO> accountDOS = sellerAccountMapper.selectList(queryWrapper); //如果查询结果为空集合或者null if (CollectionUtils.isEmpty(accountDOS)) { return new ArrayList<>(); } return sellerAccountConverter.convertAccountsTODTO(accountDOS); } ... }
四.删除一个卖家账户信息
删除某个卖家账户。
@Service public class SellerAccountServiceImpl implements SellerAccountService { @Autowired private SellerRepository sellerRepository; ... //删除一个卖家账号 @Override @Transactional(rollbackFor = Exception.class) public Boolean deleteAccount(SellerAccountRequest request) { //校验参数是否为null checkAccountParam(request); return sellerRepository.deleteAccount(request); } ... } @Repository public class SellerRepository { ... //逻辑删除一个账号 public Boolean deleteAccount(SellerAccountRequest request) { SellerAccountDO accountDO = sellerAccountConverter.convertToDO(request); //根据卖家id查询账户信息 LambdaQueryWrapper<SellerAccountDO> queryWrapper = Wrappers.lambdaQuery(); queryWrapper.eq(SellerAccountDO::getSellerId, accountDO.getSellerId()); queryWrapper.eq(SellerAccountDO::getAccountNo, accountDO.getAccountNo()); queryWrapper.eq(SellerAccountDO::getDelFlag, YesOrNoEnum.YES.getCode()); SellerAccountDO sellerAccountDO = sellerAccountMapper.selectOne(queryWrapper); if (Objects.isNull(sellerAccountDO)) { throw new BaseBizException("待删除账户信息不存在"); } //设置账号信息为失效 sellerAccountDO.setDelFlag(YesOrNoEnum.NO.getCode()); sellerAccountDO.setUpdateTime(new Date()); int count = sellerAccountMapper.updateById(sellerAccountDO); if (count <= 0) { throw new ProductBizException(CommonErrorCodeEnum.SQL_ERROR); } return true; } ... }
详细介绍后端技术栈的基础内容,包括但不限于:MySQL原理和优化、Redis原理和应用、JVM和G1原理和优化、RocketMQ原理应用及源码、Kafka原理应用及源码、ElasticSearch原理应用及源码、JUC源码、Netty源码、zk源码、Dubbo源码、Spring源码、Spring Boot源码、SCA源码、分布式锁源码、分布式事务、分库分表和TiDB、大型商品系统、大型订单系统等