|
|
@@ -381,6 +381,7 @@ public class StoreInfoServiceImpl extends ServiceImpl<StoreInfoMapper, StoreInfo
|
|
|
} else if (CommonConstant.FOOD_LICENCE_NOT_EXPIRED_STATUS.equals(foodLicenceWhetherExpiredStatus)) {//经营许可证筛选状态 2 查询食品经营许可证未到期 距离到期时间大于30天以上
|
|
|
queryWrapper.gt("a.food_licence_expiration_time", nowDay.plusDays(31));
|
|
|
}
|
|
|
+ //查出所有店铺
|
|
|
IPage<StoreInfoVo> storeInfoVoPage = storeInfoMapper.getStoreInfoVoPage(iPage, queryWrapper);
|
|
|
List<StoreInfoVo> records = storeInfoVoPage.getRecords();
|
|
|
if (!records.isEmpty()) {
|
|
|
@@ -957,8 +958,12 @@ public class StoreInfoServiceImpl extends ServiceImpl<StoreInfoMapper, StoreInfo
|
|
|
|
|
|
@Override
|
|
|
public List<StoreDictionaryVo> getBusinessSectionTypes(String parentId) {
|
|
|
- StoreDictionary businessSection = storeDictionaryMapper.selectOne(new LambdaQueryWrapper<StoreDictionary>().eq(StoreDictionary::getTypeName, "business_section").eq(StoreDictionary::getDictId, parentId));
|
|
|
- List<StoreDictionary> storeDictionaries = storeDictionaryMapper.selectList(new LambdaQueryWrapper<StoreDictionary>().eq(StoreDictionary::getParentId, businessSection.getId()));
|
|
|
+ StoreDictionary businessSection = storeDictionaryMapper.selectOne(new LambdaQueryWrapper<StoreDictionary>()
|
|
|
+ .eq(StoreDictionary::getTypeName, "business_section")
|
|
|
+ .eq(StoreDictionary::getDictId, parentId)
|
|
|
+ .isNull(StoreDictionary::getParentId));
|
|
|
+ List<StoreDictionary> storeDictionaries = storeDictionaryMapper.selectList(new LambdaQueryWrapper<StoreDictionary>()
|
|
|
+ .eq(StoreDictionary::getParentId, businessSection.getId()));
|
|
|
List<StoreDictionaryVo> voList = new ArrayList<>();
|
|
|
for (StoreDictionary storeDictionary : storeDictionaries) {
|
|
|
StoreDictionaryVo vo = new StoreDictionaryVo();
|
|
|
@@ -2479,7 +2484,7 @@ public class StoreInfoServiceImpl extends ServiceImpl<StoreInfoMapper, StoreInfo
|
|
|
} else {
|
|
|
// 如果没有指定businessType,则查询所有四种类型的店铺
|
|
|
// 需要查询字典表获取所有四种类型的dictId
|
|
|
- List<String> storeTypeNames = Arrays.asList("酒吧", "KTV", "洗浴汗蒸", "按摩足浴");
|
|
|
+ List<String> storeTypeNames = Arrays.asList("酒吧", "KTV", "洗浴汗蒸", "按摩足疗");
|
|
|
List<StoreDictionary> storeDictionaries = storeDictionaryMapper.selectList(
|
|
|
new LambdaQueryWrapper<StoreDictionary>()
|
|
|
.eq(StoreDictionary::getTypeName, "business_section")
|
|
|
@@ -2487,28 +2492,18 @@ public class StoreInfoServiceImpl extends ServiceImpl<StoreInfoMapper, StoreInfo
|
|
|
.eq(StoreDictionary::getDeleteFlag, 0)
|
|
|
);
|
|
|
|
|
|
- // 构建business_section的ID列表
|
|
|
- List<Integer> businessSectionIds = new ArrayList<>();
|
|
|
- businessSectionIds.add(3); // KTV
|
|
|
- businessSectionIds.add(4); // 洗浴汗蒸
|
|
|
- businessSectionIds.add(5); // 按摩足浴
|
|
|
- businessSectionIds.add(14);// 酒吧
|
|
|
-
|
|
|
- // 添加从字典表查询到的其他类型(如酒吧)
|
|
|
- if (!CollectionUtils.isEmpty(storeDictionaries)) {
|
|
|
- for (StoreDictionary dict : storeDictionaries) {
|
|
|
- if (StringUtils.isNotEmpty(dict.getDictId())) {
|
|
|
+ List<Integer> businessSectionIds = storeDictionaries.stream()
|
|
|
+ .filter(dict -> StringUtils.isNotEmpty(dict.getDictId()))
|
|
|
+ .map(StoreDictionary::getDictId)
|
|
|
+ .map(dictId -> {
|
|
|
try {
|
|
|
- Integer dictId = Integer.parseInt(dict.getDictId());
|
|
|
- if (!businessSectionIds.contains(dictId)) {
|
|
|
- businessSectionIds.add(dictId);
|
|
|
- }
|
|
|
+ return Integer.parseInt(dictId);
|
|
|
} catch (NumberFormatException e) {
|
|
|
- // 忽略非数字的dictId
|
|
|
+ return null;
|
|
|
}
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
+ })
|
|
|
+ .filter(Objects::nonNull)
|
|
|
+ .collect(Collectors.toList());
|
|
|
|
|
|
// 使用business_section字段进行筛选
|
|
|
queryWrapper.in("a.business_section", businessSectionIds);
|
|
|
@@ -2896,9 +2891,15 @@ public class StoreInfoServiceImpl extends ServiceImpl<StoreInfoMapper, StoreInfo
|
|
|
* @return IPage<StoreInfoVo>
|
|
|
*/
|
|
|
@Override
|
|
|
- public List<StoreInfoVo> getMoreRecommendedStores(Double lon , Double lat, int category, int subcategory, int threeCategory) {
|
|
|
+ public List<StoreInfoVo> getMoreRecommendedStores(Double lon , Double lat, String businessSection, String businessTypes, String businessClassify) {
|
|
|
+ // 参数校验
|
|
|
+ if (lon == null || lat == null) {
|
|
|
+ log.warn("获取更多推荐店铺失败,经纬度为空");
|
|
|
+ return Collections.emptyList();
|
|
|
+ }
|
|
|
+
|
|
|
QueryWrapper<StoreInfoVo> queryWrapper = new QueryWrapper<>();
|
|
|
- queryWrapper.eq("a.delete_flag", 0).eq("b.delete_flag", 0).orderByDesc("a.created_time");
|
|
|
+ queryWrapper.eq("a.delete_flag", 0).eq("b.delete_flag", 0);
|
|
|
//如果查询未过期
|
|
|
// 获取当前时刻
|
|
|
Date currentDate = new Date();
|
|
|
@@ -2912,7 +2913,36 @@ public class StoreInfoServiceImpl extends ServiceImpl<StoreInfoMapper, StoreInfo
|
|
|
// 加上 30 天
|
|
|
calendar.add(Calendar.DAY_OF_MONTH, 30);
|
|
|
|
|
|
- List<StoreInfoVo> storeInfoVoList = storeInfoMapper.getMoreRecommendedStores(queryWrapper);
|
|
|
+ // 构建一级分类
|
|
|
+ if(StringUtils.isNotEmpty(businessSection)){
|
|
|
+ queryWrapper.eq("a.business_section", businessSection);
|
|
|
+ // 构建二级分类
|
|
|
+ if(StringUtils.isNotEmpty(businessTypes)){
|
|
|
+ queryWrapper.eq("a.business_types", businessTypes);
|
|
|
+ // 构建三级分类
|
|
|
+ if(StringUtils.isNotEmpty(businessClassify)){
|
|
|
+ // 解析businessClassify参数(格式:1,2,3)
|
|
|
+ String[] classifyArray = businessClassify.split(",");
|
|
|
+ // 使用FIND_IN_SET函数检查数据库字段是否包含参数中的任何一个值
|
|
|
+ queryWrapper.and(wrapper -> {
|
|
|
+ for (int i = 0; i < classifyArray.length; i++) {
|
|
|
+ String classify = classifyArray[i].trim();
|
|
|
+ if (StringUtils.isNotEmpty(classify)) {
|
|
|
+ if (i == 0) {
|
|
|
+ wrapper.apply("FIND_IN_SET({0}, a.business_classify) > 0", classify);
|
|
|
+ } else {
|
|
|
+ wrapper.or().apply("FIND_IN_SET({0}, a.business_classify) > 0", classify);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 构建position参数(格式:经度,纬度)
|
|
|
+ String position = lon + "," + lat;
|
|
|
+ List<StoreInfoVo> storeInfoVoList = storeInfoMapper.getMoreRecommendedStores(queryWrapper, position);
|
|
|
if (CollectionUtils.isEmpty(storeInfoVoList)) {
|
|
|
return Collections.emptyList();
|
|
|
}
|
|
|
@@ -2925,10 +2955,11 @@ public class StoreInfoServiceImpl extends ServiceImpl<StoreInfoMapper, StoreInfo
|
|
|
|
|
|
|
|
|
// 计算平均分和评价
|
|
|
- Map<Object, List<Map<String, Object>>> avgScoreMap = new HashMap<>();
|
|
|
+ Map<String, List<Map<String, Object>>> avgScoreMap = new HashMap<>();
|
|
|
Map<Integer, List<StoreComment>> commentMap = new HashMap<>();
|
|
|
|
|
|
- avgScoreMap = storeEvaluationMapper.allStoreAvgScore().stream().collect(Collectors.groupingBy(o -> o.get("store_id")));
|
|
|
+ // 注意:需要将store_id转换为String类型,与后续containsKey判断保持一致
|
|
|
+ avgScoreMap = storeEvaluationMapper.allStoreAvgScore().stream().collect(Collectors.groupingBy(o -> o.get("store_id").toString()));
|
|
|
commentMap = storeCommentMapper.selectList(new QueryWrapper<StoreComment>().eq("business_type", "5").eq("delete_flag", 0)).stream().collect(Collectors.groupingBy(StoreComment::getStoreId));
|
|
|
|
|
|
|
|
|
@@ -2982,65 +3013,211 @@ public class StoreInfoServiceImpl extends ServiceImpl<StoreInfoMapper, StoreInfo
|
|
|
}
|
|
|
|
|
|
}
|
|
|
- if (lon == null || lat == null) {
|
|
|
- log.warn("获取更多推荐店铺失败,经纬度为空");
|
|
|
- return Collections.emptyList();
|
|
|
- }
|
|
|
-
|
|
|
- return filterStoresWithinDistance(storeInfoVoList, lon, lat);
|
|
|
+
|
|
|
+ // SQL已经实现了距离过滤和排序,直接返回结果
|
|
|
+ return storeInfoVoList;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * 根据距离过滤并排序店铺列表
|
|
|
+ * 你可能还喜欢(推荐店铺)
|
|
|
+ * 根据一级分类、二级分类、三级分类进行店铺筛选
|
|
|
+ * 筛选顺序:先三级,然后二级,最后一级
|
|
|
*
|
|
|
- * @param storeInfoVoList 原始店铺列表
|
|
|
- * @param lon 用户经度
|
|
|
- * @param lat 用户纬度
|
|
|
- * @return 距离范围内的店铺列表
|
|
|
+ * @param businessSection 一级分类(经营板块)
|
|
|
+ * @param businessTypes 二级分类(经营种类)
|
|
|
+ * @param businessClassify 三级分类(分类)
|
|
|
+ * @param lon 经度
|
|
|
+ * @param lat 纬度
|
|
|
+ * @return List<StoreInfoVo> 店铺信息列表
|
|
|
*/
|
|
|
- private List<StoreInfoVo> filterStoresWithinDistance(List<StoreInfoVo> storeInfoVoList, double lon, double lat) {
|
|
|
- List<StoreInfoVo> filteredList = storeInfoVoList.stream()
|
|
|
- .filter(store -> isWithinDistance(store, lon, lat, StoreInfoServiceImpl.DEFAULT_DISTANCE_METER))
|
|
|
- .sorted(Comparator.comparingDouble(StoreInfoVo::getDistance3))
|
|
|
- .collect(Collectors.toList());
|
|
|
-
|
|
|
- if (log.isInfoEnabled()) {
|
|
|
- log.info("距离筛选完成,原始店铺数量={},筛选后店铺数量={},筛选距离={}米",
|
|
|
- storeInfoVoList.size(), filteredList.size(), StoreInfoServiceImpl.DEFAULT_DISTANCE_METER);
|
|
|
+ @Override
|
|
|
+ public List<StoreInfoVo> getRecommendedStores(String businessSection, String businessTypes, String businessClassify, Double lon, Double lat) {
|
|
|
+ log.info("StoreInfoServiceImpl.getRecommendedStores?businessSection={},businessTypes={},businessClassify={},lon={},lat={}",
|
|
|
+ businessSection, businessTypes, businessClassify, lon, lat);
|
|
|
+
|
|
|
+ QueryWrapper<StoreInfoVo> queryWrapper = new QueryWrapper<>();
|
|
|
+ // 基础条件:未删除、已启用
|
|
|
+ queryWrapper.eq("a.delete_flag", 0)
|
|
|
+ .eq("b.delete_flag", 0)
|
|
|
+ .ne("a.business_status", 99) // 过滤永久关门的店铺
|
|
|
+ .ne("a.store_status", 0); // 过滤禁用的店铺
|
|
|
+
|
|
|
+ // 按照三级->二级->一级的顺序筛选
|
|
|
+
|
|
|
+ // 1. 先按三级分类(business_classify)筛选
|
|
|
+ if (StringUtils.isNotEmpty(businessClassify)) {
|
|
|
+ // 解析businessClassify参数(格式:1,2,3)
|
|
|
+ String[] classifyArray = businessClassify.split(",");
|
|
|
+ // 使用FIND_IN_SET函数检查数据库字段是否包含参数中的任何一个值
|
|
|
+ queryWrapper.and(wrapper -> {
|
|
|
+ for (int i = 0; i < classifyArray.length; i++) {
|
|
|
+ String classify = classifyArray[i].trim();
|
|
|
+ if (StringUtils.isNotEmpty(classify)) {
|
|
|
+ if (i == 0) {
|
|
|
+ wrapper.apply("FIND_IN_SET({0}, a.business_classify) > 0", classify);
|
|
|
+ } else {
|
|
|
+ wrapper.or().apply("FIND_IN_SET({0}, a.business_classify) > 0", classify);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ });
|
|
|
}
|
|
|
- return filteredList;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 判断店铺是否在指定距离范围内
|
|
|
- *
|
|
|
- * @param store 店铺信息
|
|
|
- * @param lon 用户经度
|
|
|
- * @param lat 用户纬度
|
|
|
- * @param maxDistance 最大距离(米)
|
|
|
- * @return true 表示在范围内
|
|
|
- */
|
|
|
- private boolean isWithinDistance(StoreInfoVo store, double lon, double lat, int maxDistance) {
|
|
|
- if (StringUtils.isEmpty(store.getStorePosition())) {
|
|
|
- return false;
|
|
|
+
|
|
|
+ // 2. 然后按二级分类(business_types)筛选
|
|
|
+ if (StringUtils.isNotEmpty(businessTypes)) {
|
|
|
+ // business_types可能是逗号分隔的字符串,使用FIND_IN_SET处理
|
|
|
+ String[] typesArray = businessTypes.split(",");
|
|
|
+ queryWrapper.and(wrapper -> {
|
|
|
+ for (int i = 0; i < typesArray.length; i++) {
|
|
|
+ String type = typesArray[i].trim();
|
|
|
+ if (StringUtils.isNotEmpty(type)) {
|
|
|
+ if (i == 0) {
|
|
|
+ wrapper.apply("FIND_IN_SET({0}, a.business_types) > 0", type);
|
|
|
+ } else {
|
|
|
+ wrapper.or().apply("FIND_IN_SET({0}, a.business_types) > 0", type);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ });
|
|
|
}
|
|
|
-
|
|
|
- String[] positionArray = store.getStorePosition().split(",");
|
|
|
- if (positionArray.length != 2) {
|
|
|
- log.warn("店铺坐标格式异常,storeId={},storePosition={}", store.getId(), store.getStorePosition());
|
|
|
- return false;
|
|
|
+
|
|
|
+ // 3. 最后按一级分类(business_section)筛选
|
|
|
+ if (StringUtils.isNotEmpty(businessSection)) {
|
|
|
+ try {
|
|
|
+ Integer sectionId = Integer.parseInt(businessSection.trim());
|
|
|
+ queryWrapper.eq("a.business_section", sectionId);
|
|
|
+ } catch (NumberFormatException e) {
|
|
|
+ log.warn("一级分类参数格式错误: {}", businessSection);
|
|
|
+ }
|
|
|
}
|
|
|
-
|
|
|
- try {
|
|
|
- double storeLon = Double.parseDouble(positionArray[0].trim());
|
|
|
- double storeLat = Double.parseDouble(positionArray[1].trim());
|
|
|
- double distanceMeters = DistanceUtil.haversineCalculateDistance(lon, lat, storeLon, storeLat) * 1000;
|
|
|
- store.setDistance3(distanceMeters);
|
|
|
- return distanceMeters <= maxDistance;
|
|
|
- } catch (NumberFormatException ex) {
|
|
|
- log.warn("店铺坐标解析失败,storeId={},storePosition={},error={}", store.getId(), store.getStorePosition(), ex.getMessage());
|
|
|
- return false;
|
|
|
+
|
|
|
+ // 查询店铺列表
|
|
|
+ List<StoreInfoVo> storeInfoVoList;
|
|
|
+ if (lon != null && lat != null) {
|
|
|
+ // 如果提供了经纬度,使用带距离计算的查询方法
|
|
|
+ String position = lon + "," + lat;
|
|
|
+ queryWrapper.isNotNull("a.store_position")
|
|
|
+ .ne("a.store_position", "");
|
|
|
+ // 按距离排序
|
|
|
+ queryWrapper.orderByAsc("dist");
|
|
|
+ storeInfoVoList = storeInfoMapper.getStoreInfoVoListNew(queryWrapper, position);
|
|
|
+ } else {
|
|
|
+ // 如果没有提供经纬度,使用普通查询方法
|
|
|
+ queryWrapper.orderByDesc("a.created_time");
|
|
|
+ storeInfoVoList = storeInfoMapper.getStoreInfoVoList(queryWrapper);
|
|
|
}
|
|
|
+
|
|
|
+ if (CollectionUtils.isEmpty(storeInfoVoList)) {
|
|
|
+ return Collections.emptyList();
|
|
|
+ }
|
|
|
+
|
|
|
+ // 处理店铺信息,设置评分等
|
|
|
+ // 提前查询所有需要的字典数据
|
|
|
+ List<StoreInfoVo> collect = storeInfoVoList.stream()
|
|
|
+ .filter(record -> StringUtils.isNotEmpty(record.getStoreType()))
|
|
|
+ .collect(Collectors.toList());
|
|
|
+ Set<String> allTypes = collect.stream()
|
|
|
+ .map(StoreInfoVo::getStoreType)
|
|
|
+ .flatMap(type -> Arrays.stream(type.split(",")))
|
|
|
+ .collect(Collectors.toSet());
|
|
|
+
|
|
|
+ List<StoreDictionary> storeDictionaries = storeDictionaryMapper.selectList(
|
|
|
+ new LambdaQueryWrapper<StoreDictionary>()
|
|
|
+ .eq(StoreDictionary::getTypeName, "storeType")
|
|
|
+ .isNull(StoreDictionary::getParentId)
|
|
|
+ .in(!allTypes.isEmpty(), StoreDictionary::getDictId, allTypes));
|
|
|
+ Map<String, String> typeMap = storeDictionaries.stream()
|
|
|
+ .collect(Collectors.toMap(StoreDictionary::getDictId, StoreDictionary::getDictDetail));
|
|
|
+
|
|
|
+ // 计算平均分和评价
|
|
|
+ Map<String, List<Map<String, Object>>> avgScoreMap = new HashMap<>();
|
|
|
+ Map<Integer, List<StoreComment>> commentMap = new HashMap<>();
|
|
|
+
|
|
|
+ avgScoreMap = storeEvaluationMapper.allStoreAvgScore().stream()
|
|
|
+ .collect(Collectors.groupingBy(o -> o.get("store_id").toString()));
|
|
|
+ commentMap = storeCommentMapper.selectList(
|
|
|
+ new QueryWrapper<StoreComment>()
|
|
|
+ .eq("business_type", "5")
|
|
|
+ .eq("delete_flag", 0))
|
|
|
+ .stream()
|
|
|
+ .collect(Collectors.groupingBy(StoreComment::getStoreId));
|
|
|
+
|
|
|
+ for (StoreInfoVo record : storeInfoVoList) {
|
|
|
+ // 处理类型
|
|
|
+ if (StringUtils.isNotEmpty(record.getStoreType())) {
|
|
|
+ String[] types = record.getStoreType().split(",");
|
|
|
+ List<String> typeDetails = Arrays.stream(types)
|
|
|
+ .map(typeMap::get)
|
|
|
+ .filter(Objects::nonNull)
|
|
|
+ .collect(Collectors.toList());
|
|
|
+ record.setStoreTypeStr(String.join(",", typeDetails));
|
|
|
+ record.setStoreTypeList(Arrays.asList(types));
|
|
|
+ }
|
|
|
+
|
|
|
+ // 处理经纬度
|
|
|
+ if (StringUtils.isNotEmpty(record.getStorePosition())) {
|
|
|
+ String[] split = record.getStorePosition().split(",");
|
|
|
+ if (split.length >= 2) {
|
|
|
+ record.setStorePositionLongitude(split[0]);
|
|
|
+ record.setStorePositionLatitude(split[1]);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 如果提供了经纬度,处理距离信息
|
|
|
+ if (lon != null && lat != null && StringUtils.isNotEmpty(record.getStorePosition())) {
|
|
|
+ try {
|
|
|
+ // 手动计算距离(单位:公里)
|
|
|
+ String[] positionArray = record.getStorePosition().split(",");
|
|
|
+ if (positionArray.length >= 2) {
|
|
|
+ double storeLon = Double.parseDouble(positionArray[0].trim());
|
|
|
+ double storeLat = Double.parseDouble(positionArray[1].trim());
|
|
|
+ // 计算距离(单位:公里)
|
|
|
+ double distanceKm = DistanceUtil.haversineCalculateDistance(lon, lat, storeLon, storeLat);
|
|
|
+ record.setDistance(distanceKm);
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("计算店铺距离失败,storeId={},error={}", record.getId(), e.getMessage());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 处理到期状态
|
|
|
+ Date expirationTime = record.getExpirationTime();
|
|
|
+ if (expirationTime != null) {
|
|
|
+ Date currentDate = new Date();
|
|
|
+ Calendar now = Calendar.getInstance();
|
|
|
+ Date nowCurrentDate = now.getTime();
|
|
|
+ now.add(Calendar.DAY_OF_YEAR, 30);
|
|
|
+ Date thirtyDaysLater = now.getTime();
|
|
|
+
|
|
|
+ if (expirationTime.after(currentDate)) {
|
|
|
+ record.setExpiredState("0");
|
|
|
+ if ((expirationTime.after(nowCurrentDate) || expirationTime.equals(nowCurrentDate))
|
|
|
+ && expirationTime.before(thirtyDaysLater)) {
|
|
|
+ record.setExpiredState("1");
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ record.setExpiredState("2");
|
|
|
+ }
|
|
|
+
|
|
|
+ LocalDate nowLocal = LocalDate.now();
|
|
|
+ LocalDate expDate = expirationTime.toInstant()
|
|
|
+ .atZone(ZoneId.systemDefault())
|
|
|
+ .toLocalDate();
|
|
|
+ long daysToExpire = ChronoUnit.DAYS.between(nowLocal, expDate);
|
|
|
+ record.setDaysToExpire(daysToExpire);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 设置店铺得分和总评论数
|
|
|
+ if (avgScoreMap.containsKey(String.valueOf(record.getId()))) {
|
|
|
+ record.setAvgScore(String.valueOf(
|
|
|
+ avgScoreMap.get(String.valueOf(record.getId())).get(0).get("avg_score")));
|
|
|
+ }
|
|
|
+ if (commentMap.containsKey(record.getId())) {
|
|
|
+ record.setTotalNum(String.valueOf(commentMap.get(record.getId()).size()));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return storeInfoVoList;
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
@@ -3362,7 +3539,7 @@ public class StoreInfoServiceImpl extends ServiceImpl<StoreInfoMapper, StoreInfo
|
|
|
@Override
|
|
|
public List<StoreDictionaryVo> getLeisureEntertainmentCategories() {
|
|
|
// 定义四种主分类名称
|
|
|
- List<String> mainCategoryNames = Arrays.asList("酒吧", "KTV", "洗浴汗蒸", "按摩足浴");
|
|
|
+ List<String> mainCategoryNames = Arrays.asList("酒吧", "KTV", "洗浴汗蒸", "按摩足疗");
|
|
|
|
|
|
// 查询所有主分类(business_section类型)
|
|
|
List<StoreDictionary> allMainCategories = storeDictionaryMapper.selectList(
|
|
|
@@ -3396,6 +3573,7 @@ public class StoreInfoServiceImpl extends ServiceImpl<StoreInfoMapper, StoreInfo
|
|
|
new LambdaQueryWrapper<StoreDictionary>()
|
|
|
.in(StoreDictionary::getParentId, mainCategoryIds)
|
|
|
.eq(StoreDictionary::getDeleteFlag, 0)
|
|
|
+ .in(StoreDictionary::getTypeName, "business_section","business_type","business_classify")
|
|
|
);
|
|
|
|
|
|
// 按parentId分组
|
|
|
@@ -3424,4 +3602,184 @@ public class StoreInfoServiceImpl extends ServiceImpl<StoreInfoMapper, StoreInfo
|
|
|
|
|
|
return result;
|
|
|
}
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public List<StoreDictionary> getAllBusinessSection() {
|
|
|
+ // 查询所有经营种类数据
|
|
|
+ LambdaQueryWrapper<StoreDictionary> queryWrapper = new LambdaQueryWrapper<>();
|
|
|
+ queryWrapper.in(StoreDictionary::getTypeName, "business_section","business_type","business_classify");
|
|
|
+ queryWrapper.eq(StoreDictionary::getDeleteFlag, 0);
|
|
|
+ queryWrapper.orderByAsc(StoreDictionary::getSortId);
|
|
|
+ List<StoreDictionary> storeDictionaryList = storeDictionaryMapper.selectList(queryWrapper);
|
|
|
+
|
|
|
+ // 构建三级树形结构
|
|
|
+ return buildTreeOptimized(storeDictionaryList);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 构建树形结构(优化版)
|
|
|
+ *
|
|
|
+ * @param flatList 扁平列表
|
|
|
+ * @return 树形结构列表
|
|
|
+ */
|
|
|
+ private List<StoreDictionary> buildTreeOptimized(List<StoreDictionary> flatList) {
|
|
|
+ if (flatList == null || flatList.isEmpty()) {
|
|
|
+ return new ArrayList<>();
|
|
|
+ }
|
|
|
+
|
|
|
+ // 创建三个存储结构
|
|
|
+ Map<Integer, StoreDictionary> nodeMap = new HashMap<>(); // ID到节点的映射
|
|
|
+ Map<Integer, List<StoreDictionary>> parentChildMap = new HashMap<>(); // 父ID到子节点列表的映射
|
|
|
+ List<StoreDictionary> result = new ArrayList<>(); // 结果列表
|
|
|
+
|
|
|
+ // 填充nodeMap和parentChildMap
|
|
|
+ for (StoreDictionary entity : flatList) {
|
|
|
+ Integer id = entity.getId();
|
|
|
+ Integer parentId = entity.getParentId();
|
|
|
+
|
|
|
+ // 存入节点映射
|
|
|
+ nodeMap.put(id, entity);
|
|
|
+
|
|
|
+ // 初始化子节点列表
|
|
|
+ entity.setStoreDictionaryList(new ArrayList<>());
|
|
|
+
|
|
|
+ // 如果是根节点(parentId为null或0),直接添加到结果
|
|
|
+ if (parentId == null || parentId == 0) {
|
|
|
+ result.add(entity);
|
|
|
+ } else {
|
|
|
+ // 否则,记录父子关系
|
|
|
+ parentChildMap.computeIfAbsent(parentId, k -> new ArrayList<>()).add(entity);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 建立父子关系
|
|
|
+ for (StoreDictionary entity : flatList) {
|
|
|
+ Integer id = entity.getId();
|
|
|
+ if (parentChildMap.containsKey(id)) {
|
|
|
+ entity.getStoreDictionaryList().addAll(parentChildMap.get(id));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public StoreThreeLevelStructureVo getStoreThreeLevelStructure(Integer storeId) {
|
|
|
+ // 1. 根据门店ID查询门店信息
|
|
|
+ StoreInfo storeInfo = storeInfoMapper.selectById(storeId);
|
|
|
+ if (storeInfo == null) {
|
|
|
+ log.warn("门店不存在,storeId={}", storeId);
|
|
|
+ StoreThreeLevelStructureVo vo = new StoreThreeLevelStructureVo();
|
|
|
+ vo.setStoreId(storeId);
|
|
|
+ vo.setStoreName(null);
|
|
|
+ vo.setThreeLevelStructure(new ArrayList<>());
|
|
|
+ return vo;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 2. 获取门店表里的三级分类信息
|
|
|
+ Integer businessSection = storeInfo.getBusinessSection(); // 一级:经营板块ID
|
|
|
+ String businessTypes = storeInfo.getBusinessTypes(); // 二级:经营种类IDs(逗号分隔,fine_food类型)
|
|
|
+ String businessClassify = storeInfo.getBusinessClassify(); // 三级:分类IDs(逗号分隔)
|
|
|
+
|
|
|
+ // 3. 查询一级分类(business_section - 经营板块)
|
|
|
+ // business_section存储的是dictId(字符串),需要转换为String
|
|
|
+ List<StoreDictionary> allDicts = new ArrayList<>();
|
|
|
+ StoreDictionary firstLevelDict = null;
|
|
|
+ if (businessSection != null) {
|
|
|
+ firstLevelDict = storeDictionaryMapper.selectOne(
|
|
|
+ new LambdaQueryWrapper<StoreDictionary>()
|
|
|
+ .eq(StoreDictionary::getDictId, String.valueOf(businessSection))
|
|
|
+ .eq(StoreDictionary::getTypeName, "business_section")
|
|
|
+ .eq(StoreDictionary::getDeleteFlag, 0)
|
|
|
+ );
|
|
|
+ if (firstLevelDict != null) {
|
|
|
+ allDicts.add(firstLevelDict);
|
|
|
+ log.debug("查询到一级分类: id={}, dictId={}, dictDetail={}",
|
|
|
+ firstLevelDict.getId(), firstLevelDict.getDictId(), firstLevelDict.getDictDetail());
|
|
|
+ } else {
|
|
|
+ log.warn("未查询到一级分类,businessSection={}", businessSection);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 4. 查询二级分类(business_type - 根据门店表里的business_types字段,存储的是dictId)
|
|
|
+ List<StoreDictionary> secondLevelDicts = new ArrayList<>();
|
|
|
+ if (StringUtils.isNotEmpty(businessTypes) && firstLevelDict != null) {
|
|
|
+ // 解析二级分类dictId列表(business_types存储的是dictId字符串)
|
|
|
+ String[] typeDictIds = businessTypes.split(",");
|
|
|
+ List<String> typeDictIdList = new ArrayList<>();
|
|
|
+ for (String typeDictId : typeDictIds) {
|
|
|
+ if (StringUtils.isNotEmpty(typeDictId.trim())) {
|
|
|
+ typeDictIdList.add(typeDictId.trim());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 查询二级分类,需要满足:
|
|
|
+ // 1. dict_id 在门店配置的经营种类dictId列表中
|
|
|
+ // 2. type_name = "business_type"
|
|
|
+ // 3. parent_id = 一级分类的id(不是dictId)
|
|
|
+ if (!typeDictIdList.isEmpty() && firstLevelDict != null) {
|
|
|
+ LambdaQueryWrapper<StoreDictionary> secondLevelWrapper = new LambdaQueryWrapper<>();
|
|
|
+ secondLevelWrapper.in(StoreDictionary::getDictId, typeDictIdList);
|
|
|
+ secondLevelWrapper.eq(StoreDictionary::getTypeName, "business_type");
|
|
|
+ secondLevelWrapper.eq(StoreDictionary::getParentId, firstLevelDict.getId());
|
|
|
+ secondLevelWrapper.eq(StoreDictionary::getDeleteFlag, 0);
|
|
|
+// secondLevelWrapper.orderByAsc(StoreDictionary::getSortId);
|
|
|
+ secondLevelDicts = storeDictionaryMapper.selectList(secondLevelWrapper);
|
|
|
+ allDicts.addAll(secondLevelDicts);
|
|
|
+ log.debug("查询到二级分类数量: {}, dictIds={}", secondLevelDicts.size(), typeDictIdList);
|
|
|
+ } else {
|
|
|
+ log.warn("无法查询二级分类: typeDictIdList为空或一级分类不存在, typeDictIdList={}, firstLevelDict={}",
|
|
|
+ typeDictIdList, firstLevelDict != null ? firstLevelDict.getId() : null);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 5. 查询三级分类(business_classify - 根据门店表里的business_classify字段,存储的是dictId)
|
|
|
+ if (!secondLevelDicts.isEmpty() && StringUtils.isNotEmpty(businessClassify)) {
|
|
|
+ // 解析三级分类dictId列表(business_classify存储的是dictId字符串)
|
|
|
+ String[] classifyDictIds = businessClassify.split(",");
|
|
|
+ List<String> classifyDictIdList = new ArrayList<>();
|
|
|
+ for (String classifyDictId : classifyDictIds) {
|
|
|
+ if (StringUtils.isNotEmpty(classifyDictId.trim())) {
|
|
|
+ classifyDictIdList.add(classifyDictId.trim());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 查询三级分类,需要满足:
|
|
|
+ // 1. dict_id 在门店配置的分类dictId列表中
|
|
|
+ // 2. parent_id 在二级分类的id列表中(不是dictId)
|
|
|
+ // 3. type_name = "business_classify"
|
|
|
+ if (!classifyDictIdList.isEmpty() && !secondLevelDicts.isEmpty()) {
|
|
|
+ Set<Integer> secondLevelIds = secondLevelDicts.stream()
|
|
|
+ .map(StoreDictionary::getId)
|
|
|
+ .collect(Collectors.toSet());
|
|
|
+
|
|
|
+ List<StoreDictionary> thirdLevelDicts = storeDictionaryMapper.selectList(
|
|
|
+ new LambdaQueryWrapper<StoreDictionary>()
|
|
|
+ .in(StoreDictionary::getDictId, classifyDictIdList)
|
|
|
+ .in(StoreDictionary::getParentId, secondLevelIds)
|
|
|
+ .eq(StoreDictionary::getTypeName, "business_classify")
|
|
|
+ .eq(StoreDictionary::getDeleteFlag, 0)
|
|
|
+// .orderByAsc(StoreDictionary::getSortId)
|
|
|
+ );
|
|
|
+ allDicts.addAll(thirdLevelDicts);
|
|
|
+ log.debug("查询到三级分类数量: {}, dictIds={}, parentIds={}",
|
|
|
+ thirdLevelDicts.size(), classifyDictIdList, secondLevelIds);
|
|
|
+ } else {
|
|
|
+ log.warn("无法查询三级分类: classifyDictIdList为空或二级分类为空, classifyDictIdList={}, secondLevelDicts.size={}",
|
|
|
+ classifyDictIdList, secondLevelDicts.size());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 6. 构建树形结构(根据parent_id关系组装)
|
|
|
+ List<StoreDictionary> treeStructure = buildTreeOptimized(allDicts);
|
|
|
+
|
|
|
+ // 7. 构建返回对象
|
|
|
+ StoreThreeLevelStructureVo vo = new StoreThreeLevelStructureVo();
|
|
|
+ vo.setStoreId(storeInfo.getId());
|
|
|
+ vo.setStoreName(storeInfo.getStoreName());
|
|
|
+ vo.setThreeLevelStructure(treeStructure);
|
|
|
+
|
|
|
+ return vo;
|
|
|
+ }
|
|
|
+
|
|
|
}
|