From 5b27cac6b09bc42bd535dfcbc6cf143ff45805c8 Mon Sep 17 00:00:00 2001 From: Jerry Yan <792602257@qq.com> Date: Fri, 21 Nov 2025 21:43:37 +0800 Subject: [PATCH] =?UTF-8?q?feat(service):=20=E4=BC=98=E5=8C=96=E5=95=86?= =?UTF-8?q?=E5=93=81=E6=9F=A5=E8=AF=A2=E9=80=BB=E8=BE=91=E5=B9=B6=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E5=88=86=E7=BB=84=E6=9F=A5=E8=AF=A2=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在 SourceMapper 中新增 queryGroupedByFaceAndType 方法,支持按 faceId 和 type 分组查询 - 调整 orderBiz.isBuy 方法的参数顺序,统一调用格式 - 修改 GoodsServiceImpl 中源素材查询逻辑,使用新分组方法减少循环嵌套 - 简化源素材去重及过滤禁用类型的处理流程 - 提前获取景区配置信息,避免重复查询 - 优化代码结构,提升可读性和维护性 --- .../com/ycwl/basic/mapper/SourceMapper.java | 7 ++ .../service/mobile/impl/GoodsServiceImpl.java | 102 +++++++++--------- src/main/resources/mapper/SourceMapper.xml | 47 +++++++- 3 files changed, 100 insertions(+), 56 deletions(-) diff --git a/src/main/java/com/ycwl/basic/mapper/SourceMapper.java b/src/main/java/com/ycwl/basic/mapper/SourceMapper.java index b2e248dd..065da456 100644 --- a/src/main/java/com/ycwl/basic/mapper/SourceMapper.java +++ b/src/main/java/com/ycwl/basic/mapper/SourceMapper.java @@ -67,6 +67,13 @@ public interface SourceMapper { List queryByRelation(SourceReqQuery sourceReqQuery); + /** + * 按 faceId 和 type 分组查询源素材,每组返回最新的一条记录 + * @param sourceReqQuery 查询参数 + * @return 分组后的素材列表 + */ + List queryGroupedByFaceAndType(SourceReqQuery sourceReqQuery); + SourceEntity querySameVideo(Long faceSampleId, Long deviceId); int hasRelationTo(Long memberId, Long sourceId, int type); diff --git a/src/main/java/com/ycwl/basic/service/mobile/impl/GoodsServiceImpl.java b/src/main/java/com/ycwl/basic/service/mobile/impl/GoodsServiceImpl.java index b1709441..1327735f 100644 --- a/src/main/java/com/ycwl/basic/service/mobile/impl/GoodsServiceImpl.java +++ b/src/main/java/com/ycwl/basic/service/mobile/impl/GoodsServiceImpl.java @@ -310,7 +310,7 @@ public class GoodsServiceImpl implements GoodsService { goodsDetailVO.setFaceId(entity.getFaceId()); goodsDetailVO.setIsBuy(entity.getIsBuy()); if (Integer.valueOf(0).equals(entity.getIsBuy())) { - IsBuyRespVO buy = orderBiz.isBuy(userId, videoRespVO.getScenicId(), 0, videoId); + IsBuyRespVO buy = orderBiz.isBuy(videoRespVO.getScenicId(), userId, entity.getFaceId(), 0, videoId); if (!buy.isBuy()) { PriceObj priceObj = orderBiz.queryPrice(videoRespVO.getScenicId(), 0, videoId); if (priceObj.isFree()) { @@ -584,7 +584,7 @@ public class GoodsServiceImpl implements GoodsService { goodsUrlVO.setCreateTime(source.getCreateTime()); return goodsUrlVO; }).collect(Collectors.toList()); - IsBuyRespVO isBuy = orderBiz.isBuy(face.getMemberId(), face.getScenicId(), query.getSourceType(), face.getId()); + IsBuyRespVO isBuy = orderBiz.isBuy(face.getScenicId(), face.getMemberId(), face.getId(), query.getSourceType(), face.getId()); if (!isBuy.isBuy()) { ScenicConfigEntity scenicConfig = scenicRepository.getScenicConfig(face.getScenicId()); if (scenicConfig != null && ((scenicConfig.getAntiScreenRecordType() & 2) == 0)) { @@ -676,7 +676,7 @@ public class GoodsServiceImpl implements GoodsService { } return true; }).count(); - IsBuyRespVO isBuy = orderBiz.isBuy(face.getMemberId(), face.getScenicId(), query.getSourceType(), face.getId()); + IsBuyRespVO isBuy = orderBiz.isBuy(face.getScenicId(), face.getMemberId(), face.getId(), query.getSourceType(), face.getId()); if (count > 0) { if (!isBuy.isBuy()) { return Collections.emptyList(); @@ -842,17 +842,14 @@ public class GoodsServiceImpl implements GoodsService { return ApiResponse.success(Collections.emptyList()); } + // 获取景区配置 + ScenicConfigManager scenicConfig = scenicRepository.getScenicConfigManager(scenicId); + // 使用 LinkedHashMap 按 goodsType-goodsId 去重 Map goodsMap = new LinkedHashMap<>(); // 循环查询每个 faceId 的商品 for (Long faceId : faceIds) { - // 构造查询参数 - GoodsReqQuery query = new GoodsReqQuery(); - query.setFaceId(faceId); - query.setIsBuy(isBuy); - query.setScenicId(scenicId); - // 查询成片 vlog (goodsType = 0) VideoReqQuery videoReqQuery = new VideoReqQuery(); videoReqQuery.setScenicId(scenicId); @@ -880,55 +877,52 @@ public class GoodsServiceImpl implements GoodsService { goodsMap.put(key, goodsPageVO); } } + } - // 查询源素材 (goodsType = 1/2) - SourceReqQuery sourceReqQuery = new SourceReqQuery(); - sourceReqQuery.setScenicId(scenicId); - sourceReqQuery.setIsBuy(isBuy); - sourceReqQuery.setFaceId(faceId); + // 查询源素材 (goodsType = 1/2) - 使用新的 GROUP BY 方法 + SourceReqQuery sourceReqQuery = new SourceReqQuery(); + sourceReqQuery.setScenicId(scenicId); + sourceReqQuery.setIsBuy(isBuy); + sourceReqQuery.setFaceIds(faceIds); - List sourceList = sourceMapper.queryByRelation(sourceReqQuery); - ScenicConfigManager scenicConfig = scenicRepository.getScenicConfigManager(scenicId); + // 使用 queryGroupedByFaceAndType 方法,数据库已经按 faceId+type 分组 + List sourceList = sourceMapper.queryGroupedByFaceAndType(sourceReqQuery); - // 按 faceId 和 type 分组处理 - sourceList.stream() - .collect(Collectors.groupingBy(SourceRespVO::getFaceId)) - .forEach((sourceFaceId, goods) -> { - goods.stream() - .collect(Collectors.groupingBy(SourceRespVO::getType)) - .forEach((type, sourcesByType) -> { - // 根据景区配置过滤禁用的素材类型 - boolean isDisabled = false; - if (Integer.valueOf(1).equals(type)) { - isDisabled = Boolean.TRUE.equals(scenicConfig.getBoolean("disable_source_video")); - } else if (Integer.valueOf(2).equals(type)) { - isDisabled = Boolean.TRUE.equals(scenicConfig.getBoolean("disable_source_image")); - } + // 遍历分组后的结果,每个 faceId+type 组合只有一条记录 + for (SourceRespVO source : sourceList) { + Integer type = source.getType(); + Long sourceFaceId = source.getFaceId(); - if (!isDisabled) { - String key = type + "-" + sourceFaceId; // goodsType=type, goodsId=faceId(源素材用faceId作为ID) - if (!goodsMap.containsKey(key)) { - GoodsPageVO goodsPageVO = new GoodsPageVO(); - goodsPageVO.setFaceId(sourceFaceId); - goodsPageVO.setGoodsType(type); - if (type == 1) { - goodsPageVO.setGoodsName("录像集"); - goodsPageVO.setTemplateCoverUrl(scenicConfig.getString("video_cover_url")); - } else if (type == 2) { - goodsPageVO.setGoodsName("照片集"); - goodsPageVO.setTemplateCoverUrl(scenicConfig.getString("photo_cover_url")); - } else { - goodsPageVO.setGoodsName("未知商品"); - } - if (StringUtils.isBlank(goodsPageVO.getTemplateCoverUrl())) { - goodsPageVO.setTemplateCoverUrl(sourcesByType.getFirst().getUrl()); - } - goodsPageVO.setScenicId(scenicId); - goodsMap.put(key, goodsPageVO); - } - } - }); - }); + // 根据景区配置过滤禁用的素材类型 + boolean isDisabled = false; + if (Integer.valueOf(1).equals(type)) { + isDisabled = Boolean.TRUE.equals(scenicConfig.getBoolean("disable_source_video")); + } else if (Integer.valueOf(2).equals(type)) { + isDisabled = Boolean.TRUE.equals(scenicConfig.getBoolean("disable_source_image")); + } + + if (!isDisabled) { + String key = type + "-" + sourceFaceId; // goodsType=type, goodsId=faceId(源素材用faceId作为ID) + if (!goodsMap.containsKey(key)) { + GoodsPageVO goodsPageVO = new GoodsPageVO(); + goodsPageVO.setFaceId(sourceFaceId); + goodsPageVO.setGoodsType(type); + if (type == 1) { + goodsPageVO.setGoodsName("录像集"); + goodsPageVO.setTemplateCoverUrl(scenicConfig.getString("video_cover_url")); + } else if (type == 2) { + goodsPageVO.setGoodsName("照片集"); + goodsPageVO.setTemplateCoverUrl(scenicConfig.getString("photo_cover_url")); + } else { + goodsPageVO.setGoodsName("未知商品"); + } + if (StringUtils.isBlank(goodsPageVO.getTemplateCoverUrl())) { + goodsPageVO.setTemplateCoverUrl(source.getUrl()); + } + goodsPageVO.setScenicId(scenicId); + goodsMap.put(key, goodsPageVO); + } + } } // 返回去重后的商品列表 diff --git a/src/main/resources/mapper/SourceMapper.xml b/src/main/resources/mapper/SourceMapper.xml index e2278194..5442a530 100644 --- a/src/main/resources/mapper/SourceMapper.xml +++ b/src/main/resources/mapper/SourceMapper.xml @@ -265,15 +265,58 @@ select so.id, ms.face_id, ms.scenic_id, ms.type, so.thumb_url, so.url, ms.is_free, so.create_time, so.update_time,ms.is_buy from member_source ms left join source so on ms.source_id = so.id - + where - ms.member_id = #{memberId} and so.id + ms.member_id = #{memberId} and so.id is not null and ms.face_id = #{faceId} and ms.type = #{type} and ms.scenic_id = #{scenicId} and ms.is_buy = #{isBuy} order by so.create_time desc + + +