feat(mobile): 实现基于人脸ID的商品列表查询功能

- 修改AppFaceController中list方法,将scenicId转换为Long类型传递
- 在AppGoodsController中注入FaceService,并在goodsList接口中调用faceService获取人脸列表
- 更新FaceMapper中的listByScenicAndUserId方法签名,统一scenicId参数类型为Long
- GoodsServiceImpl中新增listGoodsByFaceIdList方法,实现根据人脸ID列表查询相关商品逻辑
- 商品查询支持按成片vlog和源素材分类展示,并去重处理
- 优化GoodsService接口,增加listGoodsByFaceIdList方法定义
- OrderMapper.xml
This commit is contained in:
2025-11-21 20:49:05 +08:00
parent d0d238d31d
commit cd8ae491e2
6 changed files with 124 additions and 8 deletions

View File

@@ -59,7 +59,7 @@ public class AppFaceController {
public ApiResponse<List<FaceRespVO>> list(@PathVariable("scenicId") String scenicId) {
JwtInfo worker = JwtTokenUtil.getWorker();
Long userId = worker.getUserId();
List<FaceRespVO> list = faceService.listByUser(userId, scenicId);
List<FaceRespVO> list = faceService.listByUser(userId, Long.parseLong(scenicId));
return ApiResponse.success(list);
}

View File

@@ -4,7 +4,9 @@ import com.ycwl.basic.annotation.IgnoreToken;
import com.ycwl.basic.exception.CheckTokenException;
import com.ycwl.basic.model.jwt.JwtInfo;
import com.ycwl.basic.model.mobile.goods.*;
import com.ycwl.basic.model.pc.face.resp.FaceRespVO;
import com.ycwl.basic.service.mobile.GoodsService;
import com.ycwl.basic.service.pc.FaceService;
import com.ycwl.basic.utils.ApiResponse;
import com.ycwl.basic.utils.JwtTokenUtil;
import org.springframework.beans.factory.annotation.Autowired;
@@ -24,11 +26,17 @@ public class AppGoodsController {
@Autowired
private GoodsService goodsService;
@Autowired
private FaceService faceService;
// 商品列表
@PostMapping("/goodsList")
public ApiResponse<List<GoodsPageVO>> goodsList(@RequestBody GoodsReqQuery query) {
return goodsService.goodsList(query);
JwtInfo worker = JwtTokenUtil.getWorker();
Long userId = worker.getUserId();
List<FaceRespVO> faceRespVOS = faceService.listByUser(userId, query.getScenicId());
List<Long> faceIds = faceRespVOS.stream().map(FaceRespVO::getId).toList();
return goodsService.listGoodsByFaceIdList(faceIds, query.getIsBuy(), query.getScenicId());
}
// 源素材(原片/照片)商品列表

View File

@@ -35,7 +35,7 @@ public interface FaceMapper {
FaceRespVO findLastFaceByUserId(String userId);
FaceRespVO findLastFaceByScenicAndUserId(Long scenicId, Long userId);
List<FaceRespVO> listByScenicAndUserId(String scenicId, Long userId);
List<FaceRespVO> listByScenicAndUserId(Long scenicId, Long userId);
List<FaceEntity> listUnpaidEntityBeforeDate(Long scenicId, Date endDate);
}

View File

@@ -57,4 +57,6 @@ public interface GoodsService {
* @return 视频更新检查结果
*/
VideoUpdateCheckVO checkVideoUpdate(Long videoId);
ApiResponse<List<GoodsPageVO>> listGoodsByFaceIdList(List<Long> faceIds, Integer isBuy, Long scenicId);
}

View File

@@ -66,6 +66,7 @@ import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.LinkedHashMap;
import java.util.stream.Collectors;
/**
@@ -102,10 +103,6 @@ public class GoodsServiceImpl implements GoodsService {
@Autowired
private CouponBiz couponBiz;
@Autowired
private SourceRepository sourceRepository;
@Autowired
private TemplateBiz templateBiz;
@Autowired
private VideoUpdateConfig videoUpdateConfig;
@Autowired
private MemberRelationRepository memberRelationRepository;
@@ -834,5 +831,109 @@ public class GoodsServiceImpl implements GoodsService {
return result;
}
@Override
public ApiResponse<List<GoodsPageVO>> listGoodsByFaceIdList(List<Long> faceIds, Integer isBuy, Long scenicId) {
// 参数校验
if (faceIds == null || faceIds.isEmpty()) {
return ApiResponse.success(Collections.emptyList());
}
if (scenicId == null) {
return ApiResponse.success(Collections.emptyList());
}
// 使用 LinkedHashMap 按 goodsType-goodsId 去重
Map<String, GoodsPageVO> 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);
videoReqQuery.setIsBuy(isBuy);
videoReqQuery.setFaceId(faceId);
List<VideoRespVO> videoList = videoMapper.queryByRelation(videoReqQuery);
for (VideoRespVO videoRespVO : videoList) {
String key = "0-" + videoRespVO.getId(); // goodsType=0, goodsId=videoId
if (!goodsMap.containsKey(key)) {
GoodsPageVO goodsPageVO = new GoodsPageVO();
goodsPageVO.setGoodsName(videoRespVO.getTemplateName());
goodsPageVO.setScenicId(videoRespVO.getScenicId());
try {
ScenicV2DTO scenic = scenicRepository.getScenicBasic(videoRespVO.getScenicId());
goodsPageVO.setScenicName(scenic.getName());
} catch (Exception e) {
goodsPageVO.setScenicName("");
}
goodsPageVO.setGoodsType(0);
goodsPageVO.setFaceId(videoRespVO.getFaceId());
goodsPageVO.setGoodsId(videoRespVO.getId());
goodsPageVO.setTemplateName(videoRespVO.getTemplateName());
goodsPageVO.setTemplateCoverUrl(videoRespVO.getTemplateCoverUrl());
goodsMap.put(key, goodsPageVO);
}
}
// 查询源素材 (goodsType = 1/2)
SourceReqQuery sourceReqQuery = new SourceReqQuery();
sourceReqQuery.setScenicId(scenicId);
sourceReqQuery.setIsBuy(isBuy);
sourceReqQuery.setFaceId(faceId);
List<SourceRespVO> sourceList = sourceMapper.queryByRelation(sourceReqQuery);
ScenicConfigManager scenicConfig = scenicRepository.getScenicConfigManager(scenicId);
// 按 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"));
}
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);
}
}
});
});
}
// 返回去重后的商品列表
List<GoodsPageVO> resultList = new ArrayList<>(goodsMap.values());
return ApiResponse.success(resultList);
}
}

View File

@@ -404,6 +404,11 @@
o.scenic_id
from `order` AS o
left join face f on o.face_id = f.id
<where>
<if test="memberId != null">
and o.member_id = #{memberId}
</if>
</where>
order by o.create_at desc
</select>
<select id="appDetail" resultMap="AppBaseResultMap">