审核时需要把Item和SKU的敏感字段的diff值显示出来,方便审核员审核。
@Service
public class AuditServiceImpl implements AuditService {
...
//查询草稿详情信息
@Override
public 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 itemDiffValues = DiffFieldUtil.buildDiffField(itemInfoDO, null, itemDiffFields);
//skuList diff 存放Map集合
Map> skuDiffFieldsMap = null;
//sku信息
List skuInfoDOList = fullProductData.getSkuInfoDOList();
if (!CollectionUtils.isEmpty(skuInfoDOList)) {
skuDiffFieldsMap = new HashMap<>(skuInfoDOList.size());
for (SkuInfoDO skuInfoDO : skuInfoDOList) {
List skuDiffValues = DiffFieldUtil.buildDiffField(skuInfoDO, null, skuDiffFields);
if (!CollectionUtils.isEmpty(skuDiffValues)) {
skuDiffFieldsMap.put(skuInfoDO.getSkuId(), skuDiffValues);
}
}
}
//填充商品数据变更的差异信息
buildDiffInfo(itemDiffValues, skuDiffFieldsMap, draftDetailDTO);
}
//填充商品数据变更的差异信息
private void buildDiffInfo(List itemDiffValues, Map> 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 itemDiffValues = DiffFieldUtil.buildDiffField(itemInfoDO, oldItemInfoDO, itemDiffFields);
List oldSkuInfoDOList = productInfoRepository.listSkuByItemId(itemInfoDO.getItemId());
List skuInfoDOList = fullProductData.getSkuInfoDOList();
List skuDiffValues;
//skuList diff 存放Map集合
Map> skuDiffFieldsMap = new HashMap<>();
//旧的商品集合转换
Map 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 buildDiffField(Object newObj, Object oldObj, List diffFields) {
//oldObj为null表示新增,如果newObj与oldObj类型不同,则不处理
if (!Objects.isNull(oldObj) && !newObj.getClass().equals(oldObj.getClass())) {
return null;
}
List 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;
}
}
//审批服务
@DubboService(version = "1.0.0", interfaceClass = AuditApi.class, retries = 0)
public class AuditApiImpl implements AuditApi {
@Autowired
private AuditService auditService;
...
@Override
public JsonResult 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 {
//工单id
private Long ticketId;
//审核状态 1-通过 3-拒绝
private Integer auditStatus;
//拒绝原因
private String rejectReason;
//操作人
private Integer operatorUser;
}
@Service
public class AuditServiceImpl implements AuditService {
...
//执行审核
@Transactional
@Override
public 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);
}
}
...
}
//新增/编辑规格请求入参
@Data
public class AttributeRequest implements Serializable {
//规格键信息
private AttributeKeyRequest attributeKeyRequest;
//规格值信息
private List attributeValueRequests;
//操作人
@NotNull(message = "操作人[operateUser]不能为空")
private Integer operateUser;
@Data
public static class AttributeKeyRequest implements Serializable {
//属性key编码
private String keyCode;
//属性key名称
private String keyName;
//扩展字段
private String features;
//排序
private Integer keySort;
//删除标记(1-有效,0-删除)
private Integer delFlag;
}
@Data
public 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 {
@Resource
private AttributeRepository attributeRepository;
//新增/编辑规格键值接口
@Transactional(rollbackFor = Exception.class)
@Override
public 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 attributeValueRequests = attributeRequest.getAttributeValueRequests();
ParamCheckUtil.checkCollectionNonEmpty(attributeValueRequests);
}
...
}
//新增/编辑买手请求入参
@Data
public class BuyerRequest implements Serializable {
private Long id;
//真实姓名
private String realName;
//花名
private String roster;
//买手图像
private String imageUrl;
//介绍
private String description;
//负责的品类ID
private String categoryId;
//删除标记(1-有效,0-删除)
private Integer delFlag;
//操作人
@NotNull(message = "操作人[operateUser]不能为空")
private Integer operateUser;
}
//买手服务
@Service
public class BuyerServiceImpl implements BuyerService {
@Resource
private BuyerRepository buyerRepository;
@Override
public BuyerResultDTO saveBuyer(BuyerRequest buyerRequest) {
//保存买手信息
buyerRepository.saveOrUpdate(buyerRequest);
//返回结果信息
return new BuyerResultDTO(Boolean.TRUE);
}
@Override
public BuyerListDTO getBuyerInfo(QueryBuyerListRequest queryBuyerListRequest) {
List buyerInfoDTOS = buyerRepository.listBuyerInfo(queryBuyerListRequest);
//返回信息
return new BuyerListDTO(buyerInfoDTOS);
}
@Override
public PageResult getBuyerInfoPage(QueryBuyerPageRequest queryBuyerPageRequest) {
return buyerRepository.pageResult(queryBuyerPageRequest);
}
}
//新增/编辑品类请求入参
@Data
public class CategoryRequest implements Serializable {
//id
private Long id;
//品类名称
@NotNull(message = "品类名称[categoryName]不能为空")
private String categoryName;
//父ID(一级类目父ID为0)
private Integer parentId;
//排序(正整数,数字越小越靠前)
@NotNull(message = "排序[categorySort]不能为空")
private Integer categorySort;
//图标icon
private 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 {
@Resource
private CategoryRepository categoryRepository;
@Resource
private CategoryInfoConverter categoryInfoConverter;
//查询品类树
@Override
public List selectTree(QueryCategoryRequest categoryQueryRequest) {
return categoryInfoConverter.converterTreeList(categoryRepository.selectTree(categoryQueryRequest));
}
//查询某个层级下的品类树(默认不带条件查询父类)
@Override
public List selectChild(QueryCategoryRequest categoryQueryRequest) {
//查询某个层级的品类树
List categoryInfoList = categoryRepository.listBy(categoryQueryRequest);
//返回查询结果
return categoryInfoConverter.converterList(categoryInfoList);
}
//保存/修改品类信息
@Override
public CategoryResultDTO saveCategory(CategoryRequest categoryRequest) {
//保存品类树
categoryRepository.saveOrUpdate(categoryRequest);
//返回结果信息
return new CategoryResultDTO(Boolean.TRUE);
}
//查询品类信息列表
@Override
public List selectListByLike(QueryCategoryListRequest categoryListRequest) {
return categoryInfoConverter.converterList(categoryRepository.selectListByLike(categoryListRequest));
}
}
下面以获取前台类目为例,去说明先读缓存再读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 {
@Resource
private FrontCategoryCache frontCategoryStringSource;
@Resource
private FrontCategoryConverter frontCategoryConverter;
//基于通用缓存读写组件,去获取前台类目
@Override
public JsonResult> getFrontCategory(FrontCategoryQuery frontCategoryQuery) {
//入参校验
checkParams(frontCategoryQuery);
List frontCategoryIdList = Arrays.asList(String.valueOf(frontCategoryQuery.getFrontCategoryId()));
//基于通用缓存读写组件,先读缓存再读DB来获取前台类目
Optional> optional = frontCategoryStringSource.listRedisStringData(frontCategoryIdList);
if (!optional.isPresent()) {
JsonResult.buildSuccess();
}
List frontCategoryDTOList = frontCategoryConverter.converterFrontCategoryList(optional.get());
return JsonResult.buildSuccess(frontCategoryDTOList);
}
...
}
//Redis(String)缓存抽象类:是数据对象、是缓存对象
public abstract class AbstractRedisStringCache {
@Resource
private RedisReadWriteManager redisReadWriteManager;
...
//根据关键字批量获取数据
public Optional> listRedisStringData(List keyList) {
if (CollectionUtils.isEmpty(keyList)) {
return Optional.empty();
}
//下面会调用通用缓存读写组件RedisReadWriteManager的listRedisStringDataByCache()方法
//getBOClass()需要子类实现
//getPendingRedisKey()也需要子类实现
//最后的匿名函数中,也使用了多个需要子类实现的方法:getTableFieldsMap()、getStringDatabase()、convertDO2BO()
Optional> boListOpt = redisReadWriteManager.listRedisStringDataByCache(keyList, getBOClass(), this::getRedisKey, (key) -> {
Map tableFieldsMap = getTableFieldsMap(key);
Optional 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 boList = convertDO2BO(Arrays.asList(doOpt.get()));
if (CollectionUtils.isEmpty(boList)) {
return Optional.empty();
}
return Optional.of(boList.get(0));
});
return boListOpt;
}
//获取Redis key
protected String getRedisKey(String key) {
return String.format(getPendingRedisKey(), key);
}
//获取BO对象的Class
protected abstract Class getBOClass();
//获取待处理的Redis Key
protected abstract String getPendingRedisKey();
//关联表字段值
protected abstract Map getTableFieldsMap(String key);
//获取DB读取对象
protected abstract RedisStringDatabase getStringDatabase();
//DO转BO
protected abstract List convertDO2BO(Collection doList);
...
}
@Service("frontCategoryStringSource")
public class FrontCategoryCache extends AbstractRedisStringCache {
@Resource
private FrontCategoryStringDatabase frontCategoryStringDatabase;
...
//获取BO对象的Class
@Override
protected Class getBOClass() {
return FrontCategoryBO.class;
}
//获取待处理的Redis Key
@Override
protected String getPendingRedisKey() {
return AbstractRedisKeyConstants.FRONT_CATEGORY_STRING;
}
@Override
protected RedisStringDatabase getStringDatabase() {
return frontCategoryStringDatabase;
}
//DO转BO
@Override
protected List convertDO2BO(Collection frontCategoryDOList) {
if (CollectionUtils.isEmpty(frontCategoryDOList)) {
return null;
}
List 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 {
...
//获取表数据
@Override
public Optional getTableData(Map tableFieldsMap, String queryType) {
if (tableFieldsMap.containsKey(ID)) {
QueryWrapper queryWrapper = new QueryWrapper<>();
queryWrapper.in("ID", Sets.newHashSet(Integer.valueOf(tableFieldsMap.get(ID).toString())));
List 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 {
@Resource
private RedisCache redisCache;
@Resource
private RedisLock redisLock;
...
//批量获取缓存数据
//@param keyList 关键字列表
//@param clazz 需要将缓存JSON转换的对象
//@param getRedisKeyFunction 获取Redis key的方法
//@param getDbFuction 获取数据源对象的方法
//@return java.util.Optional>
public Optional> listRedisStringDataByCache(List keyList, Class clazz,
Function getRedisKeyFunction, Function> getDbFuction) {
try {
List list = Lists.newArrayList();
List pendingKeyList = keyList.stream().distinct().collect(toList());
List redisKeyList = pendingKeyList.stream().map(getRedisKeyFunction).distinct().collect(toList());
List 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 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 Optional getRedisStringDataByDb(String key, Function getRedisKeyFunction, Function> 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 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(key);
}
}
...
}
FrontCategoryRelationCache和SkuCollectCache这两个缓存类,都继承自抽象类AbstractRedisStringCache,并使用了通用缓存读写组件RedisReadWriteManager。
//商品前台类目服务
@DubboService(version = "1.0.0", interfaceClass = FrontCategoryApi.class, retries = 0)
public class FrontCategoryApiImpl implements FrontCategoryApi {
@Resource
private FrontCategoryRelationCache frontCategoryRelationCache;
@Resource
private SkuCollectCache skuCollectCache;
@Resource
private FrontCategoryConverter frontCategoryConverter;
...
//获取前台类目下的商品列表
@Override
public JsonResult getFrontCategorySkuList(FrontCategoryQuery frontCategoryQuery) {
//入参校验
checkParams(frontCategoryQuery);
List frontCategoryIdList = Arrays.asList(String.valueOf(frontCategoryQuery.getFrontCategoryId()));
//查询前端类目下关联的商品sku信息
Optional> optiona = frontCategoryRelationCache.listRedisStringData(frontCategoryIdList);
if (!optiona.isPresent()) {
JsonResult.buildSuccess();
}
//填充商品的sku信息
List frontCategoryRelationBOS = optiona.get();
List skuIdList = frontCategoryRelationBOS.stream().map(FrontCategoryRelationBO::getParticipateId).collect(Collectors.toList());
Optional> optional = skuCollectCache.listRedisStringData(skuIdList);
if (!optional.isPresent()) {
JsonResult.buildSuccess();
}
List
ItemCollectCache和ProductDetailCache这两个缓存类,都继承自抽象类AbstractRedisStringCache,并使用了通用缓存读写组件RedisReadWriteManager。
@DubboService(version = "1.0.0", interfaceClass = ProductCollectApi.class, retries = 0)
public class ProductCollectApiImpl implements ProductCollectApi {
@Resource
private ItemCollectCache itemCollectCache;
@Resource
private ProductDetailCache productDetailCache;
...
//根据itemId或skuId获取商品信息
@Override
public JsonResult
文章转载自:东阳马生架构
原文链接:商品中心—1.B端建品和C端缓存的技术文档 - 东阳马生架构 - 博客园
体验地址:JNPF快速开发平台