记得上下班打卡 | git大法好,push需谨慎

Commit 4e23bb64 authored by 姜秀龙's avatar 姜秀龙

sqb sku只展示关联的

parent 155ea2c4
...@@ -76,8 +76,13 @@ public class GoblinFrontController { ...@@ -76,8 +76,13 @@ public class GoblinFrontController {
@GetMapping("getGoodsDetail") @GetMapping("getGoodsDetail")
@ApiOperation("获得商品详情") @ApiOperation("获得商品详情")
public ResponseDto<GoblinFrontGoodDetailVo> getGoodsDetail(@RequestParam(name = "spuId", required = true) String spuId) throws ParseException { @ApiImplicitParams({
GoblinFrontGoodDetailVo vo = goblinFrontService.getGoodsDetail(spuId); @ApiImplicitParam(paramType = "query", required = false, dataType = "String", name = "performancesId", value = "演出ID,可选;收钱吧商品(spuType=33)时传入则仅返回该演出已关联的 SKU")
})
public ResponseDto<GoblinFrontGoodDetailVo> getGoodsDetail(
@RequestParam(name = "spuId", required = true) String spuId,
@RequestParam(name = "performancesId", required = false) String performancesId) throws ParseException {
GoblinFrontGoodDetailVo vo = goblinFrontService.getGoodsDetail(spuId, performancesId);
if(vo==null){ if(vo==null){
return ResponseDto.failure("商品不存在"); return ResponseDto.failure("商品不存在");
} }
......
...@@ -79,6 +79,69 @@ public class GoblinFrontServiceImpl implements GoblinFrontService { ...@@ -79,6 +79,69 @@ public class GoblinFrontServiceImpl implements GoblinFrontService {
return vo != null && vo.getSpuType() == 33; return vo != null && vo.getSpuType() == 33;
} }
/**
* 单场次演出下全部关联关系(status=1):先读 Redis {@link GoblinRedisUtils#getSqbPerformanceGoodsListCache},
* 未命中再查库并回写,与 {@link #getPerformanceSelectGoods} 一致。
*/
private List<GoblinSqbPerformanceGoods> loadSqbPerformanceGoodsRelationsFromCacheOrDb(String performancesId) {
if (StringUtil.isBlank(performancesId)) {
return new ArrayList<>();
}
String pid = performancesId.trim();
List<GoblinSqbPerformanceGoods> relations = goblinRedisUtils.getSqbPerformanceGoodsListCache(pid);
if (relations == null) {
relations = goblinSqbPerformanceGoodsMapper.selectList(
new LambdaQueryWrapper<GoblinSqbPerformanceGoods>()
.eq(GoblinSqbPerformanceGoods::getPerformancesId, pid)
.eq(GoblinSqbPerformanceGoods::getStatus, 1)
.orderByAsc(GoblinSqbPerformanceGoods::getSort, GoblinSqbPerformanceGoods::getMid));
if (relations == null) {
relations = new ArrayList<>();
}
goblinRedisUtils.setSqbPerformanceGoodsListCache(pid, relations);
}
return relations;
}
/**
* 收钱吧演出-SKU 关联(status=1)。<br>
* 有 performancesId 时与列表接口共用 Redis 缓存,仅内存过滤当前 spu 的 skuId;<br>
* 无 performancesId 时(详情并集)当前无按 SPU 的缓存结构,仍查库。
*/
private Set<String> loadSqbLinkedSkuIdSet(String spuId, String performancesId) {
if (StringUtil.isBlank(spuId)) {
return Collections.emptySet();
}
String spu = spuId.trim();
if (StringUtil.isNotBlank(performancesId)) {
List<GoblinSqbPerformanceGoods> relations = loadSqbPerformanceGoodsRelationsFromCacheOrDb(performancesId);
LinkedHashSet<String> ids = new LinkedHashSet<>();
for (GoblinSqbPerformanceGoods r : relations) {
if (r == null || StringUtil.isBlank(r.getSkuId())) {
continue;
}
if (StringUtil.isBlank(r.getSpuId()) || !spu.equals(r.getSpuId().trim())) {
continue;
}
ids.add(r.getSkuId().trim());
}
return ids;
}
LambdaQueryWrapper<GoblinSqbPerformanceGoods> w = new LambdaQueryWrapper<GoblinSqbPerformanceGoods>()
.eq(GoblinSqbPerformanceGoods::getSpuId, spu)
.eq(GoblinSqbPerformanceGoods::getStatus, 1);
List<GoblinSqbPerformanceGoods> rows = goblinSqbPerformanceGoodsMapper.selectList(w);
LinkedHashSet<String> ids = new LinkedHashSet<>();
if (rows != null) {
for (GoblinSqbPerformanceGoods r : rows) {
if (r != null && StringUtil.isNotBlank(r.getSkuId())) {
ids.add(r.getSkuId().trim());
}
}
}
return ids;
}
@Override @Override
public ArrayList<GoblinFrontBannerVo> getListBanner() { public ArrayList<GoblinFrontBannerVo> getListBanner() {
List<GoblinFrontBanner> list = goblinRedisUtils.getListBanner(); List<GoblinFrontBanner> list = goblinRedisUtils.getListBanner();
...@@ -323,6 +386,13 @@ public class GoblinFrontServiceImpl implements GoblinFrontService { ...@@ -323,6 +386,13 @@ public class GoblinFrontServiceImpl implements GoblinFrontService {
* 前台展示用:仅包含未删除且已上架的 SKU,并填充库存、限购可买数等(与 {@link #getGoodsDetail} 一致) * 前台展示用:仅包含未删除且已上架的 SKU,并填充库存、限购可买数等(与 {@link #getGoodsDetail} 一致)
*/ */
private ArrayList<GoblinGoodsSkuInfoDetailVo> buildOnShelfSkuDetailList(GoblinGoodsInfoVo goblinGoodsInfoVo) { private ArrayList<GoblinGoodsSkuInfoDetailVo> buildOnShelfSkuDetailList(GoblinGoodsInfoVo goblinGoodsInfoVo) {
return buildOnShelfSkuDetailList(goblinGoodsInfoVo, null);
}
/**
* @param allowedSkuIds 非 null 时仅保留集合内的 SKU(用于收钱吧演出关联 SKU、spuType=33 详情)
*/
private ArrayList<GoblinGoodsSkuInfoDetailVo> buildOnShelfSkuDetailList(GoblinGoodsInfoVo goblinGoodsInfoVo, Set<String> allowedSkuIds) {
ArrayList<GoblinGoodsSkuInfoDetailVo> list = ObjectUtil.goblinGoodsSkuInfoDetailVos(); ArrayList<GoblinGoodsSkuInfoDetailVo> list = ObjectUtil.goblinGoodsSkuInfoDetailVos();
if (goblinGoodsInfoVo == null) { if (goblinGoodsInfoVo == null) {
return list; return list;
...@@ -334,6 +404,9 @@ public class GoblinFrontServiceImpl implements GoblinFrontService { ...@@ -334,6 +404,9 @@ public class GoblinFrontServiceImpl implements GoblinFrontService {
} }
Integer buyCount = 0; Integer buyCount = 0;
for (String sku : skuIdList) { for (String sku : skuIdList) {
if (allowedSkuIds != null && !allowedSkuIds.contains(sku)) {
continue;
}
String userId = CurrentUtil.getCurrentUid(); String userId = CurrentUtil.getCurrentUid();
if (StringUtils.isNotBlank(userId)) { if (StringUtils.isNotBlank(userId)) {
buyCount = goblinRedisUtils.getSkuCountByUid(userId, sku); buyCount = goblinRedisUtils.getSkuCountByUid(userId, sku);
...@@ -370,6 +443,13 @@ public class GoblinFrontServiceImpl implements GoblinFrontService { ...@@ -370,6 +443,13 @@ public class GoblinFrontServiceImpl implements GoblinFrontService {
* 获得商品详情 * 获得商品详情
*/ */
public GoblinFrontGoodDetailVo getGoodsDetail(String spuId) { public GoblinFrontGoodDetailVo getGoodsDetail(String spuId) {
return getGoodsDetail(spuId, null);
}
/**
* @param performancesId 可选;spuType=33 时传入则只返回该演出已关联的 SKU,不传则返回该商品在任意演出下已关联 SKU 的并集
*/
public GoblinFrontGoodDetailVo getGoodsDetail(String spuId, String performancesId) {
GoblinFrontGoodDetailVo goblinFrontGoodDetailVo = GoblinFrontGoodDetailVo.getNew(); GoblinFrontGoodDetailVo goblinFrontGoodDetailVo = GoblinFrontGoodDetailVo.getNew();
GoblinGoodsInfoVo goblinGoodsInfoVo = goblinRedisUtils.getGoodsInfoVo(spuId); GoblinGoodsInfoVo goblinGoodsInfoVo = goblinRedisUtils.getGoodsInfoVo(spuId);
GoblinGoodsInfoDetailVo goblinGoodsInfoDetailVo = GoblinGoodsInfoDetailVo.getNew(); GoblinGoodsInfoDetailVo goblinGoodsInfoDetailVo = GoblinGoodsInfoDetailVo.getNew();
...@@ -378,7 +458,11 @@ public class GoblinFrontServiceImpl implements GoblinFrontService { ...@@ -378,7 +458,11 @@ public class GoblinFrontServiceImpl implements GoblinFrontService {
BeanUtils.copyProperties(goblinGoodsInfoVo, goblinGoodsInfoDetailVo); BeanUtils.copyProperties(goblinGoodsInfoVo, goblinGoodsInfoDetailVo);
goblinFrontGoodDetailVo.setGoblinGoodsInfoVo(goblinGoodsInfoDetailVo); goblinFrontGoodDetailVo.setGoblinGoodsInfoVo(goblinGoodsInfoDetailVo);
// int limit= getStockCount(goblinGoodsInfoVo.getStoreId()); // int limit= getStockCount(goblinGoodsInfoVo.getStoreId());
ArrayList<GoblinGoodsSkuInfoDetailVo> list = buildOnShelfSkuDetailList(goblinGoodsInfoVo); Set<String> sqbSkuAllow = null;
if (isSqbSpuGoods(goblinGoodsInfoVo)) {
sqbSkuAllow = loadSqbLinkedSkuIdSet(spuId, performancesId);
}
ArrayList<GoblinGoodsSkuInfoDetailVo> list = buildOnShelfSkuDetailList(goblinGoodsInfoVo, sqbSkuAllow);
//goblinGoodsInfoVo //goblinGoodsInfoVo
GoblinStoreInfoVo goblinStoreInfoVo = this.getStore(goblinGoodsInfoVo.getStoreId()); GoblinStoreInfoVo goblinStoreInfoVo = this.getStore(goblinGoodsInfoVo.getStoreId());
if (null != goblinStoreInfoVo) { if (null != goblinStoreInfoVo) {
...@@ -1150,44 +1234,37 @@ public class GoblinFrontServiceImpl implements GoblinFrontService { ...@@ -1150,44 +1234,37 @@ public class GoblinFrontServiceImpl implements GoblinFrontService {
} }
} }
// 1. 尝试从 Redis 缓存获取全量已关联商品关系(Raw Relations)提高性能 // 1. 全量关联关系:Redis → DB → 回写(与 loadSqbLinkedSkuIdSet 有演出 ID 时共用)
List<GoblinSqbPerformanceGoods> relations = goblinRedisUtils.getSqbPerformanceGoodsListCache(performancesId); List<GoblinSqbPerformanceGoods> relations = loadSqbPerformanceGoodsRelationsFromCacheOrDb(performancesId);
if (relations == null) {
// 2. 缓存失效,查询数据库获取“原始关联关系”
relations = goblinSqbPerformanceGoodsMapper.selectList(
new LambdaQueryWrapper<GoblinSqbPerformanceGoods>()
.eq(GoblinSqbPerformanceGoods::getPerformancesId, performancesId)
.eq(GoblinSqbPerformanceGoods::getStatus, 1)
.orderByAsc(GoblinSqbPerformanceGoods::getSort, GoblinSqbPerformanceGoods::getMid));
if (relations == null) {
relations = new ArrayList<>();
}
// 3. 将关联关系(SPU 列表及其结算价/排序等)写入 Redis 缓存
goblinRedisUtils.setSqbPerformanceGoodsListCache(performancesId, relations);
}
// 4. 每次请求都实时校验商品状态(delFlg, shelvesStatus 等) // 4. 每次请求都实时校验商品状态(delFlg, shelvesStatus 等)
// 这样如果商品在 SPU 层面下架,虽然关联关系还在缓存,但这里会动态过滤掉 // 这样如果商品在 SPU 层面下架,虽然关联关系还在缓存,但这里会动态过滤掉
ArrayList<GoblinSqbPerformanceGoodsInfoVo> allGoods = new ArrayList<GoblinSqbPerformanceGoodsInfoVo>(); ArrayList<GoblinSqbPerformanceGoodsInfoVo> allGoods = new ArrayList<GoblinSqbPerformanceGoodsInfoVo>();
if (!CollectionUtils.isEmpty(relations)) { if (!CollectionUtils.isEmpty(relations)) {
// 说明:relations 关联的是 SKU(同一 SPU 可能多条),下单侧需要完整 SKU 关系,所以 Redis 缓存必须保留全量 // relations 按 SKU 关联:同一 SPU 可多条;列表按 SPU 去重展示,但 SKU 列表只保留本演出已关联的 SKU
// 前端“推荐商品列表”展示按 SPU 去重:取排序最靠前的一条关联作为该 SPU 的展示代表。 LinkedHashMap<String, GoblinSqbPerformanceGoods> firstRelBySpu = new LinkedHashMap<>();
LinkedHashMap<String, GoblinSqbPerformanceGoods> uniqueSpuRelMap = new LinkedHashMap<>(); LinkedHashMap<String, LinkedHashSet<String>> linkedSkusBySpu = new LinkedHashMap<>();
for (GoblinSqbPerformanceGoods rel : relations) { for (GoblinSqbPerformanceGoods rel : relations) {
if (rel == null || StringUtil.isBlank(rel.getSpuId())) { if (rel == null || StringUtil.isBlank(rel.getSpuId())) {
continue; continue;
} }
if (!uniqueSpuRelMap.containsKey(rel.getSpuId())) { firstRelBySpu.putIfAbsent(rel.getSpuId(), rel);
uniqueSpuRelMap.put(rel.getSpuId(), rel); if (StringUtil.isNotBlank(rel.getSkuId())) {
linkedSkusBySpu.computeIfAbsent(rel.getSpuId(), k -> new LinkedHashSet<>()).add(rel.getSkuId().trim());
} }
} }
for (GoblinSqbPerformanceGoods rel : uniqueSpuRelMap.values()) { for (GoblinSqbPerformanceGoods rel : firstRelBySpu.values()) {
if (rel == null || StringUtil.isBlank(rel.getSpuId())) { if (rel == null || StringUtil.isBlank(rel.getSpuId())) {
continue; continue;
} }
LinkedHashSet<String> linkedSkuIds = linkedSkusBySpu.get(rel.getSpuId());
if (linkedSkuIds == null) {
linkedSkuIds = new LinkedHashSet<>();
}
if (linkedSkuIds.isEmpty()) {
continue;
}
// 从 Redis SPU 缓存获取详情(SPU 详情由其他逻辑负责更新缓存,这里始终拿最新状态) // 从 Redis SPU 缓存获取详情(SPU 详情由其他逻辑负责更新缓存,这里始终拿最新状态)
GoblinGoodsInfoVo goodsInfoVo = goblinRedisUtils.getGoodsInfoVo(rel.getSpuId()); GoblinGoodsInfoVo goodsInfoVo = goblinRedisUtils.getGoodsInfoVo(rel.getSpuId());
if (goodsInfoVo == null) { if (goodsInfoVo == null) {
...@@ -1209,11 +1286,11 @@ public class GoblinFrontServiceImpl implements GoblinFrontService { ...@@ -1209,11 +1286,11 @@ public class GoblinFrontServiceImpl implements GoblinFrontService {
GoblinSqbPerformanceGoodsInfoVo frontGoods = new GoblinSqbPerformanceGoodsInfoVo(); GoblinSqbPerformanceGoodsInfoVo frontGoods = new GoblinSqbPerformanceGoodsInfoVo();
BeanUtils.copyProperties(goodsInfoVo, frontGoods); BeanUtils.copyProperties(goodsInfoVo, frontGoods);
BigDecimal normalPrice = resolveGoodsSellPrice(goodsInfoVo); BigDecimal normalPrice = resolveGoodsSellPrice(goodsInfoVo, linkedSkuIds);
frontGoods.setSellPrice(normalPrice); frontGoods.setSellPrice(normalPrice);
frontGoods.setPrice(normalPrice); frontGoods.setPrice(normalPrice);
frontGoods.setSettlementPrice(rel.getSettlementPrice()); frontGoods.setSettlementPrice(rel.getSettlementPrice());
frontGoods.setGoblinGoodsSkuInfoVolist(buildOnShelfSkuDetailList(goodsInfoVo)); frontGoods.setGoblinGoodsSkuInfoVolist(buildOnShelfSkuDetailList(goodsInfoVo, linkedSkuIds));
allGoods.add(frontGoods); allGoods.add(frontGoods);
} }
} }
...@@ -1233,7 +1310,7 @@ public class GoblinFrontServiceImpl implements GoblinFrontService { ...@@ -1233,7 +1310,7 @@ public class GoblinFrontServiceImpl implements GoblinFrontService {
return respVo; return respVo;
} }
private BigDecimal resolveGoodsSellPrice(GoblinGoodsInfoVo goodsInfoVo) { private BigDecimal resolveGoodsSellPrice(GoblinGoodsInfoVo goodsInfoVo, Set<String> allowedSkuIds) {
if (goodsInfoVo == null) { if (goodsInfoVo == null) {
return null; return null;
} }
...@@ -1244,6 +1321,9 @@ public class GoblinFrontServiceImpl implements GoblinFrontService { ...@@ -1244,6 +1321,9 @@ public class GoblinFrontServiceImpl implements GoblinFrontService {
BigDecimal minSkuPrice = null; BigDecimal minSkuPrice = null;
if (!CollectionUtils.isEmpty(goodsInfoVo.getSkuIdList())) { if (!CollectionUtils.isEmpty(goodsInfoVo.getSkuIdList())) {
for (String skuId : goodsInfoVo.getSkuIdList()) { for (String skuId : goodsInfoVo.getSkuIdList()) {
if (allowedSkuIds != null && !allowedSkuIds.contains(skuId)) {
continue;
}
GoblinGoodsSkuInfoVo skuInfoVo = goblinRedisUtils.getGoodsSkuInfoVo(skuId); GoblinGoodsSkuInfoVo skuInfoVo = goblinRedisUtils.getGoodsSkuInfoVo(skuId);
if (skuInfoVo == null) { if (skuInfoVo == null) {
continue; continue;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment