Browse Source

你可能还喜欢推荐店铺的接口

ldz 2 weeks ago
parent
commit
8e6ab09da0

+ 39 - 0
alien-store/src/main/java/shop/alien/store/controller/StoreInfoController.java

@@ -948,6 +948,45 @@ public class StoreInfoController {
         }
     }
 
+    /**
+     * 你可能还喜欢(推荐店铺)
+     * 根据一级分类、二级分类、三级分类进行店铺筛选
+     * 筛选顺序:先三级,然后二级,最后一级
+     *
+     * @param businessSection 一级分类(经营板块)
+     * @param businessTypes 二级分类(经营种类)
+     * @param businessClassify 三级分类(分类)
+     * @param lon 经度
+     * @param lat 纬度
+     * @return R<List<StoreInfoVo>> 店铺信息列表
+     */
+    @ApiOperation("你可能还喜欢(推荐店铺)")
+    @ApiOperationSupport(order = 19)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "businessSection", value = "一级分类(经营板块)", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "businessTypes", value = "二级分类(经营种类)", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "businessClassify", value = "三级分类(分类)", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "lon", value = "经度", dataType = "Double", paramType = "query"),
+            @ApiImplicitParam(name = "lat", value = "纬度", dataType = "Double", paramType = "query")
+    })
+    @GetMapping("/getRecommendedStores")
+    public R<List<StoreInfoVo>> getRecommendedStores(
+            @RequestParam(value = "businessSection", required = true) String businessSection,
+            @RequestParam(value = "businessTypes", required = false) String businessTypes,
+            @RequestParam(value = "businessClassify", required = false) String businessClassify,
+            @RequestParam(value = "lon", required = false) Double lon,
+            @RequestParam(value = "lat", required = false) Double lat) {
+        log.info("StoreInfoController.getRecommendedStores?businessSection={},businessTypes={},businessClassify={},lon={},lat={}", 
+                businessSection, businessTypes, businessClassify, lon, lat);
+        try {
+            List<StoreInfoVo> result = storeInfoService.getRecommendedStores(businessSection, businessTypes, businessClassify, lon, lat);
+            return R.data(result);
+        } catch (Exception e) {
+            log.error("获取推荐店铺异常", e);
+            return R.fail("获取推荐店铺失败,请稍后重试");
+        }
+    }
+
 
 
 }

+ 14 - 0
alien-store/src/main/java/shop/alien/store/service/StoreInfoService.java

@@ -338,4 +338,18 @@ public interface StoreInfoService extends IService<StoreInfo> {
      * @return StoreThreeLevelStructureVo 包含门店信息和三级分类树形结构
      */
     StoreThreeLevelStructureVo getStoreThreeLevelStructure(Integer storeId);
+
+    /**
+     * 你可能还喜欢(推荐店铺)
+     * 根据一级分类、二级分类、三级分类进行店铺筛选
+     * 筛选顺序:先三级,然后二级,最后一级
+     *
+     * @param businessSection 一级分类(经营板块)
+     * @param businessTypes 二级分类(经营种类)
+     * @param businessClassify 三级分类(分类)
+     * @param lon 经度
+     * @param lat 纬度
+     * @return List<StoreInfoVo> 店铺信息列表
+     */
+    List<StoreInfoVo> getRecommendedStores(String businessSection, String businessTypes, String businessClassify, Double lon, Double lat);
 }

+ 233 - 18
alien-store/src/main/java/shop/alien/store/service/impl/StoreInfoServiceImpl.java

@@ -2493,27 +2493,40 @@ public class StoreInfoServiceImpl extends ServiceImpl<StoreInfoMapper, StoreInfo
             );
             
             // 构建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 = new ArrayList<>();
+//            businessSectionIds.add(3); // KTV
+//            businessSectionIds.add(4); // 洗浴汗蒸
+//            businessSectionIds.add(5); // 按摩足浴
+//            businessSectionIds.add(11);// 酒吧
+//
+//            // 添加从字典表查询到的其他类型(如酒吧)
+//            if (!CollectionUtils.isEmpty(storeDictionaries)) {
+//                for (StoreDictionary dict : storeDictionaries) {
+//                    if (StringUtils.isNotEmpty(dict.getDictId())) {
+//                        try {
+//                            Integer dictId = Integer.parseInt(dict.getDictId());
+//                            if (!businessSectionIds.contains(dictId)) {
+//                                businessSectionIds.add(dictId);
+//                            }
+//                        } catch (NumberFormatException e) {
+//                            // 忽略非数字的dictId
+//                        }
+//                    }
+//                }
+//            }
+
+            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);
@@ -3028,6 +3041,208 @@ public class StoreInfoServiceImpl extends ServiceImpl<StoreInfoMapper, StoreInfo
         return storeInfoVoList;
     }
 
+    /**
+     * 你可能还喜欢(推荐店铺)
+     * 根据一级分类、二级分类、三级分类进行店铺筛选
+     * 筛选顺序:先三级,然后二级,最后一级
+     *
+     * @param businessSection 一级分类(经营板块)
+     * @param businessTypes 二级分类(经营种类)
+     * @param businessClassify 三级分类(分类)
+     * @param lon 经度
+     * @param lat 纬度
+     * @return List<StoreInfoVo> 店铺信息列表
+     */
+    @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);
+                        }
+                    }
+                }
+            });
+        }
+        
+        // 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);
+                        }
+                    }
+                }
+            });
+        }
+        
+        // 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);
+            }
+        }
+        
+        // 查询店铺列表
+        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
     public Map<String, Object> getStoreOcrData(String storeId, String imageUrl) {
         Map<String, Object> map = new HashMap<>();