商品中心—1.B端建品和C端缓存的技术文档二

大纲

1.商品中心的专业术语

2.商品中心的基本业务系统

3.商品中心整体架构设计以及运行流程

4.商品B端—商品编码生成逻辑

5.商品B端—商品核心数据模型

6.商品B端—转换建品请求数据为商品模型数据

7.商品B端—商品建品时商品编号补全与审核配置

8.商品B端—商品审核前的草稿数据保存逻辑

9.商品B端—不需审核的建品流程持久化逻辑

10.商品B端—审核工单分页列表和商品草稿查询

11.商品B端—商品审核时的敏感字段diff计算逻辑

12.商品B端—对草稿中的商品进行审核的逻辑

13.商品B端—商品属性+买手+品类的数据维护

14.商品C端—通用缓存读写组件的实现逻辑

15.商品C端—接口代码实现逻辑

10.商品B端—审核工单分页列表和商品草稿查询

//审批服务
@DubboService(version = "1.0.0", interfaceClass = AuditApi.class, retries = 0)
public class AuditApiImpl implements AuditApi {@Autowiredprivate AuditService auditService;@Overridepublic JsonResult<PageResult<AuditInfoDTO>> getTodoList(QueryTodoListRequest request) {try {//审核工单分页列表PageResult<AuditInfoDTO> todoList = auditService.getTodoList(request);return JsonResult.buildSuccess(todoList);} catch (ProductBizException e) {log.error("biz error: request={}", JSON.toJSONString(request), e);return JsonResult.buildError(e.getErrorCode(), e.getErrorMsg());} catch (Exception e) {log.error("system error: request={}", JSON.toJSONString(request), e);return JsonResult.buildError(e.getMessage());}}@Overridepublic JsonResult<DraftDetailDTO> getDraftDetail(QueryDraftRequest request) {try {//商品草稿查询DraftDetailDTO draftDetailDTO = auditService.getDraftDetail(request);return JsonResult.buildSuccess(draftDetailDTO);} catch (ProductBizException e) {log.error("biz error: request={}", JSON.toJSONString(request), e);return JsonResult.buildError(e.getErrorCode(), e.getErrorMsg());} catch (Exception e) {log.error("system error: request={}", JSON.toJSONString(request), e);return JsonResult.buildError(e.getMessage());}}...
}@Service
public class AuditServiceImpl implements AuditService {...//获取审核的代办列表@Overridepublic PageResult<AuditInfoDTO> getTodoList(QueryTodoListRequest queryTodoListRequest) {//获取用户审核角色AuditorListConfigDO auditor = productAuditRepository.getAuditorRuleByUserId(queryTodoListRequest.getUserId());//返回待办列表return productAuditRepository.pageResult(queryTodoListRequest, auditor);}//查询草稿详情信息@Overridepublic DraftDetailDTO getDraftDetail(QueryDraftRequest queryDraftRequest) {//草稿详情信息DraftDetailDTO draftDetailDTO = productAuditRepository.getDraftDetail(queryDraftRequest.getTicketId());//构建需要比较不同的字段数据buildDiffChangeField(draftDetailDTO);return draftDetailDTO;}//构建需要比较不同的字段的数据private void buildDiffChangeField(DraftDetailDTO draftDetailDTO) {//草稿主表信息DraftMainDTO draftMainDTO = draftDetailDTO.getDraftMainDTO();//修改后的商品数据FullProductData fullProductData = JSON.parseObject(draftMainDTO.getFeatures(), FullProductData.class);//商品新增时,item版本号是0,草稿表中的版本号是item表中的版本号加1//所以此时判断草稿表中的版本号是小于等于1表示新增数据if (draftMainDTO.getVersionId() <= 1) {buildAddDiff(fullProductData, draftDetailDTO);} else {buildUpdateDiff(fullProductData, draftDetailDTO);}}...
}//商品审核 资源管理
@Repository
public class ProductAuditRepository {...//获取用户审核角色public AuditorListConfigDO getAuditorRuleByUserId(Integer userId) {LambdaQueryWrapper<AuditorListConfigDO> queryWrapper = Wrappers.lambdaQuery();queryWrapper.eq(AuditorListConfigDO::getAuditorId, userId);AuditorListConfigDO auditorListConfigDO = auditorListConfigMapper.selectOne(queryWrapper);//判断是否查询到对应的权限信息if (Objects.isNull(auditorListConfigDO)) {throw new ProductBizException(AuditExceptionCode.USER_AUDIT_RULE_NULL);}return auditorListConfigDO;}//获取用户可审核的详细列表public PageResult<AuditInfoDTO> pageResult(QueryTodoListRequest queryTodoListRequest, AuditorListConfigDO auditor) {LambdaQueryWrapper<AuditInfoDO> queryWrapper = Wrappers.lambdaQuery();queryWrapper.eq(AuditInfoDO::getTicketStatus, AuditStatusEnum.UNAUDITED.getCode());Page<AuditInfoDO> page = new Page<>(queryTodoListRequest.getPageNum(), queryTodoListRequest.getPageSize());Integer auditorRole = auditor.getAuditorRole();//不是拥有所有审核权限,则增加限定条件,指定是建品审核或者是价格审核if (!Objects.equals(AuditorRoleEnum.ADMIN.getCode(), auditorRole)) {queryWrapper.eq(AuditInfoDO::getTicketType, auditorRole);}//根据角色查询待办列表return auditConverter.converterPageResult(auditInfoMapper.selectPage(page, queryWrapper));}//查询草稿明细信息public DraftDetailDTO getDraftDetail(Long ticketId) {//1.查询草稿主表信息DraftMainDTO draftMainDTO = auditConverter.convertDTO(getByTicketId(ticketId));//2.查询草稿图片列表信息List<DraftImgDTO> draftImgDTOS = getByDraft(draftMainDTO);//返回草稿的主体信息return new DraftDetailDTO(draftMainDTO, draftImgDTOS);}...
}

11.商品B端—商品审核时的敏感字段diff计算逻辑

审核时需要把Item和SKU的敏感字段的diff值显示出来,方便审核员审核。

@Service
public class AuditServiceImpl implements AuditService {...//查询草稿详情信息@Overridepublic DraftDetailDTO getDraftDetail(QueryDraftRequest queryDraftRequest) {//草稿详情信息DraftDetailDTO draftDetailDTO = productAuditRepository.getDraftDetail(queryDraftRequest.getTicketId());//构建需要比较不同的字段数据buildDiffChangeField(draftDetailDTO);return draftDetailDTO;}//构建需要比较不同的字段的数据private void buildDiffChangeField(DraftDetailDTO draftDetailDTO) {//草稿主表信息DraftMainDTO draftMainDTO = draftDetailDTO.getDraftMainDTO();//修改后的商品数据FullProductData fullProductData = JSON.parseObject(draftMainDTO.getFeatures(), FullProductData.class);//商品新增时,item版本号是0,草稿表中的版本号是item表中的版本号加1//所以此时判断草稿表中的版本号是小于等于1表示新增数据if (draftMainDTO.getVersionId() <= 1) {buildAddDiff(fullProductData, draftDetailDTO);} else {buildUpdateDiff(fullProductData, draftDetailDTO);}}//填充新增的 商品差异变化信息private void buildAddDiff(FullProductData fullProductData, DraftDetailDTO draftDetailDTO) {//item信息ItemInfoDO itemInfoDO = fullProductData.getItemInfoDO();List<DiffValue> itemDiffValues = DiffFieldUtil.buildDiffField(itemInfoDO, null, itemDiffFields);//skuList diff 存放Map集合Map<String, List<DiffValue>> skuDiffFieldsMap = null;//sku信息List<SkuInfoDO> skuInfoDOList = fullProductData.getSkuInfoDOList();if (!CollectionUtils.isEmpty(skuInfoDOList)) {skuDiffFieldsMap = new HashMap<>(skuInfoDOList.size());for (SkuInfoDO skuInfoDO : skuInfoDOList) {List<DiffValue> skuDiffValues = DiffFieldUtil.buildDiffField(skuInfoDO, null, skuDiffFields);if (!CollectionUtils.isEmpty(skuDiffValues)) {skuDiffFieldsMap.put(skuInfoDO.getSkuId(), skuDiffValues);}}}//填充商品数据变更的差异信息buildDiffInfo(itemDiffValues, skuDiffFieldsMap, draftDetailDTO);}//填充商品数据变更的差异信息private void buildDiffInfo(List<DiffValue> itemDiffValues, Map<String, List<DiffValue>> skuDiffFieldsMap, DraftDetailDTO draftDetailDTO) {//item变更字段if (!CollectionUtils.isEmpty(itemDiffValues)) {draftDetailDTO.setItemDiffFields(itemDiffValues);}//sku变更字段if (!CollectionUtils.isEmpty(skuDiffFieldsMap)) {draftDetailDTO.setSkuDiffFields(skuDiffFieldsMap);}}//填充修改的 商品差异变化信息private void buildUpdateDiff(FullProductData fullProductData, DraftDetailDTO draftDetailDTO) {//item信息ItemInfoDO itemInfoDO = fullProductData.getItemInfoDO();//先查询修改前itemInfoDO和修改前的skuInfoDOList,再比较变更值ItemInfoDO oldItemInfoDO = productInfoRepository.getItemByItemId(itemInfoDO.getItemId());List<DiffValue> itemDiffValues = DiffFieldUtil.buildDiffField(itemInfoDO, oldItemInfoDO, itemDiffFields);List<SkuInfoDO> oldSkuInfoDOList = productInfoRepository.listSkuByItemId(itemInfoDO.getItemId());List<SkuInfoDO> skuInfoDOList = fullProductData.getSkuInfoDOList();List<DiffValue> skuDiffValues;//skuList diff 存放Map集合Map<String, List<DiffValue>> skuDiffFieldsMap = new HashMap<>();//旧的商品集合转换Map<String, SkuInfoDO> oldMap = oldSkuInfoDOList.stream().collect(Collectors.toMap(SkuInfoDO::getSkuId, e -> e));for (SkuInfoDO skuInfoDO : skuInfoDOList) {if (oldMap.containsKey(skuInfoDO.getSkuId())) {SkuInfoDO oldSkuInfoDO = oldMap.get(skuInfoDO.getSkuId());skuDiffValues = DiffFieldUtil.buildDiffField(skuInfoDO, oldSkuInfoDO, skuDiffFields);if (!CollectionUtils.isEmpty(skuDiffValues)) {skuDiffFieldsMap.put(skuInfoDO.getSkuId(), skuDiffValues);}}}//填充修改的商品信息buildDiffInfo(itemDiffValues, skuDiffFieldsMap, draftDetailDTO);}...
}public class DiffFieldUtil {public static List<DiffValue> buildDiffField(Object newObj, Object oldObj, List<String> diffFields) {//oldObj为null表示新增,如果newObj与oldObj类型不同,则不处理if (!Objects.isNull(oldObj) && !newObj.getClass().equals(oldObj.getClass())) {return null;}List<DiffValue> diffValues = new ArrayList<>();Field[] newObjFields = newObj.getClass().getDeclaredFields();Field[] oldObjFields = null;if (!Objects.isNull(oldObj)) {oldObjFields = oldObj.getClass().getDeclaredFields();}for (int i = 0; i < newObjFields.length; i++) {Field newObjField = newObjFields[i];//需要比较当前字段String fieldName = newObjField.getName();if (diffFields.contains(fieldName)) {try {Object newValue = newObjField.get(fieldName);if (Objects.isNull(oldObjFields) || !Objects.equals(oldObjFields[i].get(fieldName), newValue)) {DiffValue diffValue = new DiffValue();diffValue.setField(fieldName);diffValue.setOldValue(Objects.isNull(oldObjFields) ? null : oldObjFields[i].get(fieldName));diffValue.setNewValue(newValue);diffValues.add(diffValue);}} catch (IllegalAccessException e) {log.error("获取字段值失败", e);}}}return diffValues;}
}

12.商品B端—对草稿中的商品进行审核的逻辑

//审批服务
@DubboService(version = "1.0.0", interfaceClass = AuditApi.class, retries = 0)
public class AuditApiImpl implements AuditApi {@Autowiredprivate AuditService auditService;...@Overridepublic JsonResult<ExecAuditDTO> execAudit(AuditRequest request) {try {ExecAuditDTO execAuditDTO = auditService.execAudit(request);return JsonResult.buildSuccess(execAuditDTO);} catch (ProductBizException e) {log.error("biz error: request={}", JSON.toJSONString(request), e);return JsonResult.buildError(e.getErrorCode(), e.getErrorMsg());} catch (Exception e) {log.error("system error: request={}", JSON.toJSONString(request), e);return JsonResult.buildError(e.getMessage());}}
}//审核请求入参
@Data
public class AuditRequest extends BaseEntity implements Serializable {//工单idprivate Long ticketId;//审核状态 1-通过 3-拒绝private Integer auditStatus;//拒绝原因private String rejectReason;//操作人private Integer operatorUser;
}@Service
public class AuditServiceImpl implements AuditService {...//执行审核@Transactional@Overridepublic ExecAuditDTO execAudit(AuditRequest auditRequest) {//验证是否有可以审核,并填充审核信息AuditInfoDTO auditInfoDTO = productAuditRepository.checkAudit(auditRequest);//执行审核execGoodsAudit(auditRequest, auditInfoDTO);//处理审核的信息DB变更productAuditRepository.updateAudit(auditRequest, auditInfoDTO);return new ExecAuditDTO(Boolean.TRUE);}//商品审核private void execGoodsAudit(AuditRequest auditRequest, AuditInfoDTO auditInfoDTO) {DraftMainDTO draftMainDTO = auditInfoDTO.getDraftMainDTO();Integer ticketType = auditInfoDTO.getTicketType();//如果是审批通过,则需要更改正式表的数据if (Objects.equals(auditRequest.getAuditStatus(), AuditStatusEnum.PASS.getCode())) {FullProductData fullProductData = JSON.parseObject(draftMainDTO.getFeatures(), FullProductData.class);//建品审核if (Objects.equals(ticketType, AuditTypeEnum.GOODS.getCode())) {fullProductData.getItemInfoDO().setVersionId(draftMainDTO.getVersionId());//产品信息入库;版本号小于等于1,表示新增,否则表示修改if (fullProductData.getItemInfoDO().getVersionId() <= 1) {productInfoRepository.saveItemInfo(fullProductData);} else {productInfoRepository.updateItemInfo(fullProductData);}} else if (Objects.equals(ticketType, AuditTypeEnum.PRICE.getCode())) {SkuInfoDO skuInfoDO = fullProductData.getSkuInfoDOList().get(0);productInfoRepository.saveRecord(skuInfoDO);}}}...
}//商品审核 资源管理
@Repository
public class ProductAuditRepository {...//验证是否可审核,并返回审核对象public AuditInfoDTO checkAudit(AuditRequest auditRequest) {Long ticketId = auditRequest.getTicketId();//查询审核工单AuditInfoDO auditInfoDO = auditInfoMapper.selectById(ticketId);if (Objects.isNull(auditInfoDO)) {throw new ProductBizException(AuditExceptionCode.USER_AUDIT_INFO_NULL);}AuditInfoDTO auditInfoDTO = auditConverter.convertAuditDTO(auditInfoDO);//获取审核工单的详情DraftMainDO draftMainDO = getByTicketId(ticketId);if (Objects.isNull(draftMainDO)) {throw new ProductBizException(AuditExceptionCode.USER_AUDIT_INFO_NULL.getErrorCode(), "审核工单详情信息不存在");}//验证权限是否满足AuditorListConfigDO auditorListConfigDO = getAuditorRuleByUserId(auditRequest.getOperatorUser());if (Objects.isNull(auditorListConfigDO)) {throw new ProductBizException(AuditExceptionCode.USER_AUDIT_RULE_NULL);}//不是超级审核权限,并且拥有的审核权限与审核类型不一致if (!Objects.equals(AuditorRoleEnum.ADMIN.getCode(), auditorListConfigDO.getAuditorRole())&& !Objects.equals(draftMainDO.getTicketType(), auditorListConfigDO.getAuditorRole())) {throw new ProductBizException(ProductErrorCodeEnum.AUDIT_ERROR);}auditInfoDTO.setDraftMainDTO(auditConverter.convertDTO(draftMainDO));return auditInfoDTO;}//修改审核信息public void updateAudit(AuditRequest auditRequest, AuditInfoDTO auditInfoDTO) {DraftMainDTO draftMainDTO = auditInfoDTO.getDraftMainDTO();//软删除草稿表数据deleteDraftMain(draftMainDTO);//修改审核表信息updateAudit(auditInfoDTO, auditRequest);//新增审核历史记录saveAuditHistory(auditRequest);}//逻辑删除草稿表数据private void deleteDraftMain(DraftMainDTO draftMainDTO) {DraftMainDO draftMainDO = auditConverter.converterDO(draftMainDTO);draftMainDO.setDelFlag(DelFlagEnum.DISABLED.getCode());//草稿表数据删除int count = draftMainMapper.updateById(draftMainDO);if (count <= 0) {throw new ProductBizException(AuditExceptionCode.AUDIT_SQL);}}//修改审核表信息private void updateAudit(AuditInfoDTO auditInfoDTO, AuditRequest auditRequest) {AuditInfoDO auditInfoDO = auditConverter.convertAuditDO(auditInfoDTO);auditInfoDO.setTicketStatus(auditRequest.getAuditStatus());auditInfoDO.setUpdateUser(auditRequest.getOperatorUser());auditInfoDO.setUpdateTime(new Date());int count = this.auditInfoMapper.updateById(auditInfoDO);if (count <= 0) {throw new ProductBizException(AuditExceptionCode.AUDIT_SQL);}}//新增审核历史记录private void saveAuditHistory(AuditRequest auditRequest) {AuditHistoryDO auditHistoryDO = auditConverter.converterHistoryDO(auditRequest);auditHistoryDO.initCommon();int count = this.auditHistoryMapper.insert(auditHistoryDO);if (count <= 0) {throw new ProductBizException(AuditExceptionCode.AUDIT_SQL);}}...
}

13.商品B端—商品属性 + 买手 + 品类的数据维护

(1)商品属性数据维护

(2)买手数据维护

(3)品类数据维护

(1)商品属性数据维护

//新增/编辑规格请求入参
@Data
public class AttributeRequest implements Serializable {//规格键信息private AttributeKeyRequest attributeKeyRequest;//规格值信息private List<AttributeValueRequest> attributeValueRequests;//操作人@NotNull(message = "操作人[operateUser]不能为空")private Integer operateUser;@Datapublic static class AttributeKeyRequest implements Serializable {//属性key编码private String keyCode;//属性key名称private String keyName;//扩展字段private String features;//排序private Integer keySort;//删除标记(1-有效,0-删除)private Integer delFlag;}@Datapublic static class AttributeValueRequest implements Serializable {//属性key编码private String keyCode;//属性value名称private String valueName;//扩展字段private String features;//排序private Integer valueSort;//删除标记(1-有效,0-删除)private Integer delFlag;}
}//规格服务
@Service
public class AttributeServiceImpl implements AttributeService {@Resourceprivate AttributeRepository attributeRepository;//新增/编辑规格键值接口@Transactional(rollbackFor = Exception.class)@Overridepublic AttributeResultDTO saveAttribute(AttributeRequest attributeRequest) {//入参检查this.checkAttributeRequestParam(attributeRequest);//保存规格信息attributeRepository.saveAttribute(attributeRequest);//返回结果return new AttributeResultDTO(Boolean.TRUE);}//入参检查private void checkAttributeRequestParam(AttributeRequest attributeRequest) {ParamCheckUtil.checkObjectNonNull(attributeRequest);//规格键信息AttributeRequest.AttributeKeyRequest attributeKeyRequest = attributeRequest.getAttributeKeyRequest();ParamCheckUtil.checkObjectNonNull(attributeKeyRequest);//规格值信息List<AttributeRequest.AttributeValueRequest> attributeValueRequests = attributeRequest.getAttributeValueRequests();ParamCheckUtil.checkCollectionNonEmpty(attributeValueRequests);}...
}

(2)买手数据维护

//新增/编辑买手请求入参
@Data
public class BuyerRequest implements Serializable {private Long id;//真实姓名private String realName;//花名private String roster;//买手图像private String imageUrl;//介绍private String description;//负责的品类IDprivate String categoryId;//删除标记(1-有效,0-删除)private Integer delFlag;//操作人@NotNull(message = "操作人[operateUser]不能为空")private Integer operateUser;
}//买手服务
@Service
public class BuyerServiceImpl implements BuyerService {@Resourceprivate BuyerRepository buyerRepository;@Overridepublic BuyerResultDTO saveBuyer(BuyerRequest buyerRequest) {//保存买手信息buyerRepository.saveOrUpdate(buyerRequest);//返回结果信息return new BuyerResultDTO(Boolean.TRUE);}@Overridepublic BuyerListDTO getBuyerInfo(QueryBuyerListRequest queryBuyerListRequest) {List<BuyerInfoDTO> buyerInfoDTOS = buyerRepository.listBuyerInfo(queryBuyerListRequest);//返回信息return new BuyerListDTO(buyerInfoDTOS);}@Overridepublic PageResult<BuyerInfoDTO> getBuyerInfoPage(QueryBuyerPageRequest queryBuyerPageRequest) {return buyerRepository.pageResult(queryBuyerPageRequest);}
}

(3)品类数据维护

//新增/编辑品类请求入参
@Data
public class CategoryRequest implements Serializable {//idprivate Long id;//品类名称@NotNull(message = "品类名称[categoryName]不能为空")private String categoryName;//父ID(一级类目父ID为0)private Integer parentId;//排序(正整数,数字越小越靠前)@NotNull(message = "排序[categorySort]不能为空")private Integer categorySort;//图标iconprivate String icon;//目录是否展示(1-是,0-否)private Integer showMark;//是否是末级类目@NotNull(message = "末级类目[lastFlag]不能为空")private Integer lastFlag;//渠道(1-每日生鲜、2-美团、3-饿了么、4-淘鲜达、5-招商银行)@NotNull(message = "渠道[channel]不能为空")private Integer channel;//卖家类型(1-自营,2-POP)@NotNull(message = "卖家类型[sellerType]不能为空")private Integer sellerType;//扩展字段private String feature;//删除标记(1-有效,0-删除)private Integer delFlag;//操作人@NotNull(message = "操作人[operateUser]不能为空")private Integer operateUser;
}//商品品类信息
@Service
public class CategoryInfoServiceImpl implements CategoryInfoService {@Resourceprivate CategoryRepository categoryRepository;@Resourceprivate CategoryInfoConverter categoryInfoConverter;//查询品类树@Overridepublic List<CategoryInfoTreeDTO> selectTree(QueryCategoryRequest categoryQueryRequest) {return categoryInfoConverter.converterTreeList(categoryRepository.selectTree(categoryQueryRequest));}//查询某个层级下的品类树(默认不带条件查询父类)@Overridepublic List<CategoryInfoDTO> selectChild(QueryCategoryRequest categoryQueryRequest) {//查询某个层级的品类树List<CategoryInfoDO> categoryInfoList = categoryRepository.listBy(categoryQueryRequest);//返回查询结果return categoryInfoConverter.converterList(categoryInfoList);}//保存/修改品类信息@Overridepublic CategoryResultDTO saveCategory(CategoryRequest categoryRequest) {//保存品类树categoryRepository.saveOrUpdate(categoryRequest);//返回结果信息return new CategoryResultDTO(Boolean.TRUE);}//查询品类信息列表@Overridepublic List<CategoryInfoDTO> selectListByLike(QueryCategoryListRequest categoryListRequest) {return categoryInfoConverter.converterList(categoryRepository.selectListByLike(categoryListRequest));}
}

14.商品C端—通用缓存读写组件的实现逻辑

下面以获取前台类目为例,去说明先读缓存再读DB的通用缓存读写组件的逻辑。

FrontCategoryCache继承自Redis缓存抽象类AbstractRedisStringCache,这个抽象类中会有一个模版方法listRedisStringData(),该方法可以根据关键字来批量获取数据,并且会调用通用缓存读写组件的listRedisStringDataByCache()方法。

其中,listRedisStringDataByCache()方法需要传入两个方法:一个是获取Redis的key的方法,一个是从DB查询数据的方法。

//商品前台类目服务
@DubboService(version = "1.0.0", interfaceClass = FrontCategoryApi.class, retries = 0)
public class FrontCategoryApiImpl implements FrontCategoryApi {@Resourceprivate FrontCategoryCache frontCategoryStringSource;@Resourceprivate FrontCategoryConverter frontCategoryConverter;//基于通用缓存读写组件,去获取前台类目@Overridepublic JsonResult<List<FrontCategoryDTO>> getFrontCategory(FrontCategoryQuery frontCategoryQuery) {//入参校验checkParams(frontCategoryQuery);List<String> frontCategoryIdList = Arrays.asList(String.valueOf(frontCategoryQuery.getFrontCategoryId()));//基于通用缓存读写组件,先读缓存再读DB来获取前台类目Optional<List<FrontCategoryBO>> optional = frontCategoryStringSource.listRedisStringData(frontCategoryIdList);if (!optional.isPresent()) {JsonResult.buildSuccess();}List<FrontCategoryDTO> frontCategoryDTOList = frontCategoryConverter.converterFrontCategoryList(optional.get());return JsonResult.buildSuccess(frontCategoryDTOList);}...
}//Redis(String)缓存抽象类:<DO>是数据对象、<BO>是缓存对象
public abstract class AbstractRedisStringCache<DO, BO> {@Resourceprivate RedisReadWriteManager redisReadWriteManager;...//根据关键字批量获取数据public Optional<List<BO>> listRedisStringData(List<String> keyList) {if (CollectionUtils.isEmpty(keyList)) {return Optional.empty();}//下面会调用通用缓存读写组件RedisReadWriteManager的listRedisStringDataByCache()方法//getBOClass()需要子类实现//getPendingRedisKey()也需要子类实现//最后的匿名函数中,也使用了多个需要子类实现的方法:getTableFieldsMap()、getStringDatabase()、convertDO2BO()Optional<List<BO>> boListOpt = redisReadWriteManager.listRedisStringDataByCache(keyList, getBOClass(), this::getRedisKey, (key) -> {Map<String, Object> tableFieldsMap = getTableFieldsMap(key);Optional<DO> doOpt;try {doOpt = getStringDatabase().getTableData(tableFieldsMap, queryType());} catch (Exception e) {log.error("根据关键字批量获取数据出现异常 key={},paramMap={}", key, tableFieldsMap, e);return Optional.empty();}if (!doOpt.isPresent()) {return Optional.empty();}List<BO> boList = convertDO2BO(Arrays.asList(doOpt.get()));if (CollectionUtils.isEmpty(boList)) {return Optional.empty();}return Optional.of(boList.get(0));});return boListOpt;}//获取Redis keyprotected String getRedisKey(String key) {return String.format(getPendingRedisKey(), key);}//获取BO对象的Classprotected abstract Class<BO> getBOClass();//获取待处理的Redis Keyprotected abstract String getPendingRedisKey();//关联表字段值protected abstract Map<String, Object> getTableFieldsMap(String key);//获取DB读取对象protected abstract RedisStringDatabase<DO> getStringDatabase();//DO转BOprotected abstract List<BO> convertDO2BO(Collection<DO> doList);...
}@Service("frontCategoryStringSource")
public class FrontCategoryCache extends AbstractRedisStringCache<FrontCategoryDO, FrontCategoryBO> {@Resourceprivate FrontCategoryStringDatabase frontCategoryStringDatabase;...//获取BO对象的Class@Overrideprotected Class<FrontCategoryBO> getBOClass() {return FrontCategoryBO.class;}//获取待处理的Redis Key@Overrideprotected String getPendingRedisKey() {return AbstractRedisKeyConstants.FRONT_CATEGORY_STRING;}@Overrideprotected RedisStringDatabase<FrontCategoryDO> getStringDatabase() {return frontCategoryStringDatabase;}//DO转BO@Overrideprotected List<FrontCategoryBO> convertDO2BO(Collection<FrontCategoryDO> frontCategoryDOList) {if (CollectionUtils.isEmpty(frontCategoryDOList)) {return null;}List<FrontCategoryBO> result = Lists.newArrayList();for (FrontCategoryDO frontCategoryDO : frontCategoryDOList) {FrontCategoryBO frontCategoryBO = new FrontCategoryBO();BeanUtils.copyProperties(frontCategoryDO, frontCategoryBO);result.add(frontCategoryBO);}return result;}...
}@Service("frontCategoryStringDatabase")
public class FrontCategoryStringDatabase extends AbstractRedisStringDatabase<FrontCategoryDO> {...//获取表数据@Overridepublic Optional<FrontCategoryDO> getTableData(Map<String, Object> tableFieldsMap, String queryType) {if (tableFieldsMap.containsKey(ID)) {QueryWrapper<FrontCategoryDO> queryWrapper = new QueryWrapper<>();queryWrapper.in("ID", Sets.newHashSet(Integer.valueOf(tableFieldsMap.get(ID).toString())));List<FrontCategoryDO> frontCategoryDOList = frontCategoryMapper.selectList(queryWrapper);if (!CollectionUtils.isEmpty(frontCategoryDOList)) {FrontCategoryDO doBase = frontCategoryDOList.get(0);if (Objects.equals(DelFlagEnum.EFFECTIVE.getCode(), doBase.getDelFlag())) {return Optional.of(doBase);}}return Optional.empty();}throw new UnsupportedOperationException();}...
}//通用缓存读写组件
@Service
public class RedisReadWriteManager {@Resourceprivate RedisCache redisCache;@Resourceprivate RedisLock redisLock;...//批量获取缓存数据//@param keyList             关键字列表//@param clazz               需要将缓存JSON转换的对象//@param getRedisKeyFunction 获取Redis key的方法//@param getDbFuction        获取数据源对象的方法//@return java.util.Optional<java.util.List<T>>public <T> Optional<List<T>> listRedisStringDataByCache(List<String> keyList, Class<T> clazz, Function<String, String> getRedisKeyFunction, Function<String, Optional<T>> getDbFuction) {try {List<T> list = Lists.newArrayList();List<String> 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<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;}}//查询数据库表的数据并赋值到Redispublic <T> Optional<T> getRedisStringDataByDb(String key, Function<String, String> getRedisKeyFunction, Function<String, Optional<T>> getDbFuction) {if (StringUtils.isEmpty(key) || Objects.isNull(getDbFuction)) {return Optional.empty();}try {//使用分布式锁if (!redisLock.lock(key)) {return Optional.empty();}String redisKey = getRedisKeyFunction.apply(key);Optional<T> optional = getDbFuction.apply(key);if (!optional.isPresent()) {//把空对象暂存到RedisredisCache.setex(redisKey, EMPTY_OBJECT_STRING, RedisKeyUtils.redisKeyRandomTime(INT_EXPIRED_ONE_DAY, TimeUnit.HOURS, NUMBER_24));log.warn("发生缓存穿透 redisKey={}", redisKey);return optional;}//把表数据对象存到RedisredisCache.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(key);}}...
}

15.商品C端—接口代码实现逻辑

(1)获取前台类目下的商品列表

(2)获取商品信息和详情接口

(1)获取前台类目下的商品列表

FrontCategoryRelationCache和SkuCollectCache这两个缓存类,都继承自抽象类AbstractRedisStringCache,并使用了通用缓存读写组件RedisReadWriteManager。

//商品前台类目服务
@DubboService(version = "1.0.0", interfaceClass = FrontCategoryApi.class, retries = 0)
public class FrontCategoryApiImpl implements FrontCategoryApi {@Resourceprivate FrontCategoryRelationCache frontCategoryRelationCache;@Resourceprivate SkuCollectCache skuCollectCache;@Resourceprivate FrontCategoryConverter frontCategoryConverter;...//获取前台类目下的商品列表@Overridepublic JsonResult<FrontCategorySkuRelationDTO> getFrontCategorySkuList(FrontCategoryQuery frontCategoryQuery) {//入参校验checkParams(frontCategoryQuery);List<String> frontCategoryIdList = Arrays.asList(String.valueOf(frontCategoryQuery.getFrontCategoryId()));//查询前端类目下关联的商品sku信息Optional<List<FrontCategoryRelationBO>> optiona = frontCategoryRelationCache.listRedisStringData(frontCategoryIdList);if (!optiona.isPresent()) {JsonResult.buildSuccess();}//填充商品的sku信息List<FrontCategoryRelationBO> frontCategoryRelationBOS = optiona.get();List<String> skuIdList = frontCategoryRelationBOS.stream().map(FrontCategoryRelationBO::getParticipateId).collect(Collectors.toList());Optional<List<SkuInfoBO>> optional = skuCollectCache.listRedisStringData(skuIdList);if (!optional.isPresent()) {JsonResult.buildSuccess();}List<Object> skuList = frontCategoryConverter.converterObjectList(optional.get());return JsonResult.buildSuccess(new FrontCategorySkuRelationDTO(skuList));}...
}@Service("frontCategoryRelationCache")
public class FrontCategoryRelationCache extends AbstractRedisStringCache<FrontCategoryRelationDO, FrontCategoryRelationBO> {@Resourceprivate FrontCategoryRelationStringDatabase frontCategoryRelationStringDatabase;@Overrideprotected Class<FrontCategoryRelationBO> getBOClass() {return FrontCategoryRelationBO.class;}@Overrideprotected String getPendingRedisKey() {return AbstractRedisKeyConstants.FRONT_CATEGORY_ITEM_RELATION_SET;}@Overrideprotected RedisStringDatabase<FrontCategoryRelationDO> getStringDatabase() {return frontCategoryRelationStringDatabase;}...
}@Service("frontCategoryRelationStringDatabase")
public class FrontCategoryRelationStringDatabase extends AbstractRedisStringDatabase<FrontCategoryRelationDO> {...@Overridepublic Optional<FrontCategoryRelationDO> getTableData(Map<String, Object> tableFieldsMap, String queryType) {if (tableFieldsMap.containsKey(FRONT_CATEGORY_ID)) {List<FrontCategoryRelationDO> frontCategoryDOList = frontCategoryMapper.queryFrontCategoryList(Arrays.asList(Long.valueOf(tableFieldsMap.get(FRONT_CATEGORY_ID).toString())));if (!CollectionUtils.isEmpty(frontCategoryDOList)) {FrontCategoryRelationDO doBase = frontCategoryDOList.get(0);if (Objects.equals(DelFlagEnum.EFFECTIVE.getCode(), doBase.getDelFlag())) {return Optional.of(doBase);}}return Optional.empty();}throw new UnsupportedOperationException();}...
}//Redis(string)缓存抽象类:<DO>数据对象、<BO>缓存对象
public abstract class AbstractRedisStringCache<DO, BO> {@Resourceprivate RedisReadWriteManager redisReadWriteManager;...//根据关键字批量获取数据public Optional<List<BO>> listRedisStringData(List<String> keyList) {if (CollectionUtils.isEmpty(keyList)) {return Optional.empty();}//下面会调用通用缓存读写组件RedisReadWriteManager的listRedisStringDataByCache()方法//getBOClass()需要子类实现//getPendingRedisKey()也需要子类实现//最后的匿名函数中,也使用了多个需要子类实现的方法:getTableFieldsMap()、getStringDatabase()、convertDO2BO()Optional<List<BO>> boListOpt = redisReadWriteManager.listRedisStringDataByCache(keyList, getBOClass(), this::getRedisKey, (key) -> {Map<String, Object> tableFieldsMap = getTableFieldsMap(key);Optional<DO> doOpt;try {doOpt = getStringDatabase().getTableData(tableFieldsMap, queryType());} catch (Exception e) {log.error("根据关键字批量获取数据出现异常 key={},paramMap={}", key, tableFieldsMap, e);return Optional.empty();}if (!doOpt.isPresent()) {return Optional.empty();}List<BO> boList = convertDO2BO(Arrays.asList(doOpt.get()));if (CollectionUtils.isEmpty(boList)) {return Optional.empty();}return Optional.of(boList.get(0));});return boListOpt;}//获取Redis keyprotected String getRedisKey(String key) {return String.format(getPendingRedisKey(), key);}...
}

(2)获取商品信息和详情接口

ItemCollectCache和ProductDetailCache这两个缓存类,都继承自抽象类AbstractRedisStringCache,并使用了通用缓存读写组件RedisReadWriteManager。

@DubboService(version = "1.0.0", interfaceClass = ProductCollectApi.class, retries = 0)
public class ProductCollectApiImpl implements ProductCollectApi {@Resourceprivate ItemCollectCache itemCollectCache;@Resourceprivate ProductDetailCache productDetailCache;...//根据itemId或skuId获取商品信息@Overridepublic JsonResult<Map<String, ProductCollectDTO>> getProductCollect(ProductCollectQuery productCollectQuery) {if (Objects.isNull(productCollectQuery) || CollectionUtils.isEmpty(productCollectQuery.getProductIdList())) {return JsonResult.buildError(ProductErrorCodeEnum.PARAM_ERROR.getErrorCode(), ProductErrorCodeEnum.PARAM_ERROR.getErrorMsg());}if (productCollectQuery.getProductIdList().size() > BaseConstants.LIMIT_100) {return JsonResult.buildError(ProductErrorCodeEnum.PRODUCT_LIMIT_ERROR.getErrorCode(), ProductErrorCodeEnum.PRODUCT_LIMIT_ERROR.getErrorMsg());}Set<String> productIdSet = Sets.newHashSet(productCollectQuery.getProductIdList());Set<String> itemIdSet = productIdSet.stream().filter(NumberUtils::isItem).collect(Collectors.toSet());List<ItemInfoBO> itemInfoBOList = Lists.newArrayList();if (!CollectionUtils.isEmpty(itemIdSet)) {Optional<List<ItemInfoBO>> itemOptional = itemCollectCache.listRedisStringData(Lists.newArrayList(itemIdSet));if (itemOptional.isPresent()) {itemInfoBOList = itemOptional.get();}}//获取sku相关信息ProductBO productBO = buildSkuInfoList(productCollectQuery, itemInfoBOList);return JsonResult.buildSuccess(buildProductCollect(productBO.getItemInfoBOList(), productBO.getSkuInfoBOList(), productBO.getPriceBOList()));}//根据skuId获取商品详情@Overridepublic JsonResult<ProductDetailDTO> getProductDetail(ProductDetailQuery productDetailQuery) {if (Objects.isNull(productDetailQuery) || Objects.isNull(productDetailQuery.getSkuId())) {return JsonResult.buildError(ProductErrorCodeEnum.PARAM_ERROR.getErrorCode(), ProductErrorCodeEnum.PARAM_ERROR.getErrorMsg());}List<String> productIdList = Arrays.asList(productDetailQuery.getSkuId());Optional<List<ProductDetailBO>> optional = productDetailCache.listRedisStringData(productIdList);if (optional.isPresent()) {List<ProductDetailBO> productDetailBOS = optional.get();ProductDetailDTO productDetailDTO = productDetailConverter.converterDetail(productDetailBOS.get(0));return JsonResult.buildSuccess(productDetailDTO);}return JsonResult.buildSuccess();}...
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.pswp.cn/web/83071.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

网络之交换机

定义与作用 交换机是一种为所连接的IT设备提供网络通信的设备&#xff0c;主要作用是转发传输数据&#xff0c;实现网络设备之间的通信互联&#xff0c;还能对网络进行分段和隔离&#xff0c;划分多个虚拟网段&#xff0c;提高网络安全性&#xff0c;以及对不同端口、用户和应用…

AI不会杀死创作,但会杀死平庸

作为一个敲了8年Java代码的普通本科程序员&#xff0c;日常主要泡在会议后台管理系统的开发里。从2023年底被朋友拽着试了第一把AI工具到现在&#xff0c;电脑手机上的AI软件比外卖App还多——写代码的Copilot、画时序图的工具、聊天的ChatGPT、Deepseek&#xff0c;基本市面上…

Golang——8、协程和管道

协程和管道 1、协程1.1、进程、线程和协程1.2、goroutine的使用以及sync.WaitGroup1.3、启动多个协程1.4、设置Golang并行运行的时候占用的cup数量1.5、goroutine统计素数 2、管道2.1、管道的操作2.2、协程和管道协同2.3、单向管道2.4、多路复用之select2.5、解决协程中出现的异…

深入理解Python内置模块及第三方库的使用与管理

Python 内置模块与第三方库 在 Python 编程中&#xff0c;模块和库是帮助开发者高效实现各种功能的基础工具。Python 提供了丰富的内置模块以及第三方库&#xff0c;能够支持从基础的文件操作到复杂的数据分析和机器学习等任务。本篇文章将深入介绍 Python 的内置模块与第三方…

二分查找-P2249 【深基13.例1】查找

文章目录 参考代码二分标准模板 题目来源-洛谷网 参考代码 #include<bits/stdc.h> using namespace std; const int N 1e65; int m,n,a[N],b; int find(int t) {int l1,rn;while(l<r){int mid(lr)/2;//防止溢出 mid l (r-l) /2 ;if(a[mid]>t) rmid;//中间值比…

手写muduo网络库(一):项目构建和时间戳、日志库

引言 本文作为手写 muduo 网络库系列开篇&#xff0c;聚焦项目基础框架搭建与核心基础工具模块设计。通过解析 CMake 工程结构设计、目录规划原则&#xff0c;结合时间戳与日志系统的架构&#xff0c;为后续网络库开发奠定工程化基础。文中附完整 CMake 配置示例及模块代码。 …

NLP学习路线图(三十二): 模型压缩与优化

一、 核心压缩与优化技术详解 1. 知识蒸馏:智慧的传承(Knowledge Distillation, KD) 核心思想:“师授徒业”。训练一个庞大、高性能但笨重的“教师模型”(Teacher Model),让其指导训练一个轻量级的“学生模型”(Student Model)。学生模型学习模仿教师模型的输出行为(…

vue前端字典映射

1.界面展示 2.图中状态字段接收的数据如下 3.代码转换&#xff0c;添加计算属性代码 再在绑定属性的地方做转换 computed: {statusMap() {return {"-1": "已退号",1: "挂号",2: "接诊",3: "已完诊",};},},<m-input:spa…

基于 llama-factory进行模型微调

# GLM4-9B-chat Lora 微调. 介绍如何基于 llama-factory 框架&#xff0c;对 glm-4-9b-chat 模型进行 Lora 微调。Lora 是一种高效微调方法&#xff0c;深入了解其原理可参见博客&#xff1a;[知乎|深入浅出 Lora](https://zhuanlan.zhihu.com/p/650197598)。 ## 环境配置 在完…

不到 2 个月,OpenAI 火速用 Rust 重写 AI 编程工具。尤雨溪也觉得 Rust 香!

一、OpenAI 用 Rust 重写 Codex CLI OpenAI 已用 Rust 语言重写了其 AI 命令行编程工具 Codex CLI&#xff0c;理由是此举能提升性能和安全性&#xff0c;同时避免对 Node.js 的依赖。他们认为 Node.js “可能让部分用户感到沮丧或成为使用障碍”。 Codex 是一款实验性编程代理…

Go 并发编程深度指南

Go 并发编程深度指南 Go 语言以其内置的并发原语而闻名&#xff0c;通过 goroutine 和 channel 提供了一种高效、安全的并发编程模型。本文将全面解析 Go 的并发机制及其实际应用。 核心概念&#xff1a;Goroutines 和 Channels 1. Goroutines (协程) Go 的轻量级线程实现&…

vue和uniapp聊天页面右侧滚动条自动到底部

1.vue右侧滚动条自动到底部 <div ref"newMessage1"></div> <!-- 定义<div ref"newMessage1"></div>与<div v-for”item in list“>循环同级定义-->定义方法 scrollToBottomCenter(){this.$nextTick(() > {this.$re…

iOS 项目怎么构建稳定性保障机制?一次系统性防错经验分享(含 KeyMob 工具应用)

崩溃、内存飙升、后台任务未释放、页面卡顿、日志丢失——稳定性问题&#xff0c;不一定会立刻崩&#xff0c;但一旦积累&#xff0c;就是“上线后救不回来的代价”。 稳定性保障不是某个工具的功能&#xff0c;而是一套贯穿开发、测试、上线全流程的“观测分析防范”机制。 …

JMeter函数整理

"_csvRead"函数 csvRead函数是从外部读取参数&#xff0c;csvRead函数可以从一个文件中读取多个参数。 下面具体讲一下如何使用csvread函数&#xff1a; 1.新建一个csv或者text文件&#xff0c;里面保存要读取的参数&#xff0c;每个参数间用逗号相隔。每行表示每一组…

深入理解 React Hooks

在当今的 React 开发中,Hooks 已经成为构建函数组件的核心工具。自 React 16.8 版本引入以来,Hooks 彻底改变了开发者编写 React 组件的方式,使得状态管理和副作用处理变得更加简洁和直观。本文将全面介绍 React 提供的各种 Hooks,从基础的 useState 和 useEffect,到高级的…

Doris-2:单虚拟机上非docker化安装Doris实验环境

Doris-2:单虚拟机上非docker化安装Doris实验环境 1.安装1.1.环境说明1.2.基础准备1.2.1.JDK1.2.2.操作系统配置(使用root或者有权账户)1.2.2.1.修改环境变量1.2.2.2.修改虚拟内存区域1.2.2.3.关闭swap1.2.2.4.关闭防火墙1.2.2.5.创建用户和组1.3.安装doris1.3.1.解压1.3.2.配置…

C# SqlSugar:依赖注入与仓储模式实践

C# SqlSugar&#xff1a;依赖注入与仓储模式实践 在 C# 的应用开发中&#xff0c;数据库操作是必不可少的环节。为了让数据访问层更加简洁、高效且易于维护&#xff0c;许多开发者会选择成熟的 ORM&#xff08;对象关系映射&#xff09;框架&#xff0c;SqlSugar 就是其中备受…

Razor编程中@Helper的用法大全

文章目录 第一章&#xff1a;Helper基础概念1.1 Helper的定义与作用1.2 Helper的基本语法结构1.3 Helper与HtmlHelper的区别 第二章&#xff1a;基础Helper用法2.1 无参数Helper2.2 带简单参数的Helper2.3 带默认值的参数2.4 使用模型作为参数 第三章&#xff1a;高级Helper用法…

Python-正则表达式(re 模块)

目录 一、re 模块的使用过程二、正则表达式的字符匹配1. 匹配开头结尾2. 匹配单个字符3. 匹配多个字符4. 匹配分组5. Python 代码示例 三、re 模块的函数1. 函数一览表2. Python 代码示例1&#xff09;search 与 finditer2&#xff09;findall3&#xff09;sub4&#xff09;spl…

前端知识导图

前端知识导图 参考&#xff1a;字节标准 前端知识导图 通用基础 1、编程语言 HTML CSS JS TS 2、计算机基础 计算机网略 数据结构 算法&#xff1a;二分查找、十大排序、二叉树先中后和层次遍历、集合交并集、leetcode刷题经验 编译构建 webpack & vite 应用基础 开…