|
|
@@ -11,6 +11,7 @@ import com.baomidou.mybatisplus.core.toolkit.StringUtils;
|
|
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
|
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
|
|
import lombok.RequiredArgsConstructor;
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
import org.springframework.beans.BeanUtils;
|
|
|
import org.springframework.beans.factory.annotation.Value;
|
|
|
import org.springframework.data.geo.Point;
|
|
|
@@ -71,9 +72,12 @@ import java.util.stream.Collectors;
|
|
|
* @since 2024-12-05
|
|
|
*/
|
|
|
@Service
|
|
|
+@Slf4j
|
|
|
@RequiredArgsConstructor
|
|
|
@Transactional
|
|
|
public class StoreInfoServiceImpl extends ServiceImpl<StoreInfoMapper, StoreInfo> implements StoreInfoService {
|
|
|
+ private static final int DEFAULT_DISTANCE_METER = 1000;
|
|
|
+
|
|
|
|
|
|
private final String DEFAULT_PASSWORD = "123456";
|
|
|
|
|
|
@@ -145,6 +149,9 @@ public class StoreInfoServiceImpl extends ServiceImpl<StoreInfoMapper, StoreInfo
|
|
|
|
|
|
private final RestTemplate restTemplate;
|
|
|
|
|
|
+ private final StoreImgService storeImgService;
|
|
|
+
|
|
|
+
|
|
|
/** 商户证照历史记录数据访问对象 */
|
|
|
private final StoreLicenseHistoryMapper licenseHistoryMapper;
|
|
|
|
|
|
@@ -210,9 +217,16 @@ public class StoreInfoServiceImpl extends ServiceImpl<StoreInfoMapper, StoreInfo
|
|
|
List<StoreImg> albumUrlList = storeImgMapper.selectList(albumUrlQueryWrapper);
|
|
|
storeMainInfoVo.setAlbumUrl(albumUrlList.stream().sorted(Comparator.comparing(StoreImg::getImgSort)).map(StoreImg::getImgUrl).collect(Collectors.toList()));
|
|
|
//推荐菜
|
|
|
- storeMainInfoVo.setRecommendUrl(storeMenuMapper.getStoreMenuList(id, 1).stream().sorted(Comparator.comparing(StoreMenuVo::getImgSort)).collect(Collectors.toList()));
|
|
|
+ storeMainInfoVo.setRecommendUrl(storeMenuMapper.getStoreMenuList(id, 1,1).stream().sorted(Comparator.comparing(StoreMenuVo::getImgSort)).collect(Collectors.toList()));
|
|
|
//菜单
|
|
|
- storeMainInfoVo.setMenuUrl(storeMenuMapper.getStoreMenuList(id, 0).stream().sorted(Comparator.comparing(StoreMenuVo::getImgSort)).collect(Collectors.toList()));
|
|
|
+ storeMainInfoVo.setMenuUrl(storeMenuMapper.getStoreMenuList(id, 0,1).stream().sorted(Comparator.comparing(StoreMenuVo::getImgSort)).collect(Collectors.toList()));
|
|
|
+
|
|
|
+ // todo 需要根据门店类型来判断
|
|
|
+ // 推荐酒水
|
|
|
+ storeMainInfoVo.setRecommendBeverageUrl(storeMenuMapper.getStoreMenuList(id, 1,1).stream().sorted(Comparator.comparing(StoreMenuVo::getImgSort)).collect(Collectors.toList()));
|
|
|
+ //酒单
|
|
|
+ storeMainInfoVo.setBeverageUrl(storeMenuMapper.getStoreMenuList(id, 0,1).stream().sorted(Comparator.comparing(StoreMenuVo::getImgSort)).collect(Collectors.toList()));
|
|
|
+
|
|
|
//门店标签
|
|
|
storeMainInfoVo.setStoreLabel(storeLabelMapper.selectOne(new LambdaQueryWrapper<StoreLabel>().eq(StoreLabel::getStoreId, id)));
|
|
|
//营业时间
|
|
|
@@ -860,6 +874,16 @@ public class StoreInfoServiceImpl extends ServiceImpl<StoreInfoMapper, StoreInfo
|
|
|
storeImg.setImgUrl(storeInfoDto.getFoodLicenceUrl());
|
|
|
storeImgMapper.insert(storeImg);
|
|
|
}
|
|
|
+ //存入店铺娱乐经营许可证图片
|
|
|
+ if (StringUtils.isNotEmpty(storeInfoDto.getEntertainmentLicenceUrl())) {
|
|
|
+ StoreImg storeImg = new StoreImg();
|
|
|
+ storeImg.setStoreId(storeInfo.getId());
|
|
|
+ storeImg.setImgType(26);
|
|
|
+ storeImg.setImgSort(0);
|
|
|
+ storeImg.setImgDescription("娱乐经营许可证审核通过图片");
|
|
|
+ storeImg.setImgUrl(storeInfoDto.getEntertainmentLicenceUrl());
|
|
|
+ storeImgMapper.insert(storeImg);
|
|
|
+ }
|
|
|
|
|
|
//初始化标签数据
|
|
|
LambdaQueryWrapper<TagStoreRelation> tagStoreRelationLambdaQueryWrapper = new LambdaQueryWrapper<>();
|
|
|
@@ -1490,6 +1514,14 @@ public class StoreInfoServiceImpl extends ServiceImpl<StoreInfoMapper, StoreInfo
|
|
|
if (!storeDictionaries.isEmpty()) {
|
|
|
result.setBusinessStatusStr(storeDictionaries.get(0).getDictDetail());
|
|
|
}
|
|
|
+ // TODO 之后修改********** 正常OcrType由前端传存储ocr表要加新字段。传参要由前端传。
|
|
|
+ // 查询并设置各类证件OCR信息
|
|
|
+ result.setJyxkz(convertOcrResultToJson(storeUser.getId(), "BUSINESS_LICENSE", "营业执照", true));
|
|
|
+ result.setIdcardFace(convertOcrResultToJson(storeUser.getId(), "ID_CARD", "face", true));
|
|
|
+ result.setIdcardBack(convertOcrResultToJson(storeUser.getId(), "ID_CARD", "back", true));
|
|
|
+ result.setFoodLicence(convertOcrResultToJson(storeUser.getId(), "FOOD_MANAGE_LICENSE", null, true));
|
|
|
+ result.setEntertainmentLicence(convertOcrResultToJson(storeUser.getId(), "BUSINESS_LICENSE", "娱乐", false));
|
|
|
+
|
|
|
return result;
|
|
|
}
|
|
|
|
|
|
@@ -2615,15 +2647,8 @@ public class StoreInfoServiceImpl extends ServiceImpl<StoreInfoMapper, StoreInfo
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
- public Map<String, Object> getStoreOcrData(String storeUserId, String imageUrl) {
|
|
|
+ public Map<String, Object> getStoreOcrData(String imageUrl, String merchantName) {
|
|
|
Map<String, Object> map = new HashMap<>();
|
|
|
- LambdaQueryWrapper<OcrImageUpload> queryWrapper = new LambdaQueryWrapper<>();
|
|
|
- queryWrapper.eq(OcrImageUpload::getStoreUserId, storeUserId)
|
|
|
- .eq(OcrImageUpload::getOcrType, OcrTypeEnum.BUSINESS_LICENSE.getCode());
|
|
|
- OcrImageUpload ocrImageUploads = ocrImageUploadMapper.selectOne(queryWrapper);
|
|
|
- if(ocrImageUploads== null){
|
|
|
- throw new RuntimeException("未找到OCI识别数据!");
|
|
|
- }
|
|
|
String accessToken = aiAuthTokenUtil.getAccessToken();
|
|
|
|
|
|
HttpHeaders aiHeaders = new HttpHeaders();
|
|
|
@@ -2633,9 +2658,7 @@ public class StoreInfoServiceImpl extends ServiceImpl<StoreInfoMapper, StoreInfo
|
|
|
List<String> imageUrls = new ArrayList<>();
|
|
|
imageUrls.add(imageUrl);
|
|
|
jsonBody.put("image_urls", imageUrls);
|
|
|
- String ocrResult = ocrImageUploads.getOcrResult();
|
|
|
- JSONObject jsonObject = JSONObject.parseObject(ocrResult);
|
|
|
- jsonBody.put("merchant_name", jsonObject.get("companyName"));
|
|
|
+ jsonBody.put("merchant_name", merchantName);
|
|
|
|
|
|
HttpEntity<Map<String, Object>> request = new HttpEntity<>(jsonBody, aiHeaders);
|
|
|
ResponseEntity<String> response = null;
|
|
|
@@ -2673,4 +2696,2010 @@ public class StoreInfoServiceImpl extends ServiceImpl<StoreInfoMapper, StoreInfo
|
|
|
}
|
|
|
return map;
|
|
|
}
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取活动banner图
|
|
|
+ */
|
|
|
+
|
|
|
+ public List<StoreImg> getBannerUrl(String storeId){
|
|
|
+ LambdaQueryWrapper<StoreImg> queryWrapper = new LambdaQueryWrapper<StoreImg>()
|
|
|
+ .eq(StoreImg::getStoreId, Integer.parseInt(storeId))
|
|
|
+ .eq(StoreImg::getImgType, 26)
|
|
|
+ .eq(StoreImg::getDeleteFlag, 0);
|
|
|
+ return storeImgMapper.selectList(queryWrapper);
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取活动详情banner图
|
|
|
+ */
|
|
|
+
|
|
|
+ public List<StoreImg> getBannerUrlInfo(String storeId, Integer businessId){
|
|
|
+ LambdaQueryWrapper<StoreImg> queryWrapper = new LambdaQueryWrapper<StoreImg>()
|
|
|
+ .eq(StoreImg::getStoreId, Integer.parseInt(storeId))
|
|
|
+ .eq(StoreImg::getImgType, 27)
|
|
|
+ .eq(StoreImg::getDeleteFlag, 0)
|
|
|
+ .eq(StoreImg::getBusinessId, businessId);
|
|
|
+ return storeImgMapper.selectList(queryWrapper);
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public IPage<StoreInfoVo> getLifeServicesByDistance(Double lon, Double lat, Double distance, Integer sortType, Integer businessType, Integer categoryId, int pageNum, int pageSize) {
|
|
|
+ // 参数校验
|
|
|
+ if (lon == null || lat == null) {
|
|
|
+ throw new IllegalArgumentException("经纬度参数不能为空");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 智能检测:如果参数可能传反了(lat超出范围但lon在范围内),给出提示
|
|
|
+ if (Math.abs(lat) > 90 && Math.abs(lon) <= 90) {
|
|
|
+ throw new IllegalArgumentException(
|
|
|
+ String.format("参数可能传反了!当前值: lon=%s, lat=%s。经度范围: [-180, 180],纬度范围: [-90, 90]。请检查参数顺序。", lon, lat)
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ // 校验经纬度范围:经度 [-180, 180],纬度 [-90, 90]
|
|
|
+ if (lon < -180 || lon > 180) {
|
|
|
+ throw new IllegalArgumentException("经度参数超出有效范围 [-180, 180],当前值: " + lon);
|
|
|
+ }
|
|
|
+ if (lat < -90 || lat > 90) {
|
|
|
+ throw new IllegalArgumentException("纬度参数超出有效范围 [-90, 90],当前值: " + lat);
|
|
|
+ }
|
|
|
+ final int finalSortType = (sortType == null || sortType < 1 || sortType > 3) ? 1 : sortType; // 默认智能排序
|
|
|
+ if (pageNum <= 0) {
|
|
|
+ pageNum = 1;
|
|
|
+ }
|
|
|
+ if (pageSize <= 0) {
|
|
|
+ pageSize = 10;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 构建查询条件
|
|
|
+ QueryWrapper<StoreInfoVo> queryWrapper = new QueryWrapper<>();
|
|
|
+
|
|
|
+ // 如果传入了dictId,根据字典id查询经营板块、经营种类、分类并匹配店铺
|
|
|
+ if (categoryId != null) {
|
|
|
+ // 根据categoryId查询字典表记录
|
|
|
+ StoreDictionary dict = storeDictionaryMapper.selectById(categoryId);
|
|
|
+ if (dict == null || dict.getDeleteFlag() == 1) {
|
|
|
+ throw new IllegalArgumentException("字典id不存在或已删除: " + categoryId);
|
|
|
+ }
|
|
|
+
|
|
|
+ String typeName = dict.getTypeName();
|
|
|
+ String dictIdStr =dict.getDictId();
|
|
|
+
|
|
|
+ if ("business_section".equals(typeName)) {
|
|
|
+ // 如果是经营板块,直接匹配business_section
|
|
|
+ try {
|
|
|
+ Integer sectionId = Integer.parseInt(dictIdStr);
|
|
|
+ queryWrapper.eq("a.business_section", sectionId);
|
|
|
+ } catch (NumberFormatException e) {
|
|
|
+ throw new IllegalArgumentException("经营板块dictId格式错误: " + dictIdStr);
|
|
|
+ }
|
|
|
+ } else if ("business_type".equals(typeName)) {
|
|
|
+ // 如果是经营种类,需要:
|
|
|
+ // 1. 向上查找父级(经营板块)
|
|
|
+ // 2. 匹配business_section和business_types
|
|
|
+ StoreDictionary parentDict = storeDictionaryMapper.selectById(dict.getParentId());
|
|
|
+ if (parentDict == null || !"business_section".equals(parentDict.getTypeName())) {
|
|
|
+ throw new IllegalArgumentException("经营种类的父级不是经营板块,categoryId: " + categoryId);
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ Integer sectionId = Integer.parseInt(parentDict.getDictId());
|
|
|
+ queryWrapper.eq("a.business_section", sectionId);
|
|
|
+ // 使用FIND_IN_SET匹配business_types字段(逗号分隔)
|
|
|
+ queryWrapper.apply("FIND_IN_SET({0}, a.business_types) > 0", dictIdStr);
|
|
|
+ } catch (NumberFormatException e) {
|
|
|
+ throw new IllegalArgumentException("经营板块或经营种类dictId格式错误");
|
|
|
+ }
|
|
|
+ } else if ("business_classify".equals(typeName)) {
|
|
|
+ // 如果是分类,需要:
|
|
|
+ // 1. 向上查找父级(经营种类)
|
|
|
+ // 2. 向上查找祖父级(经营板块)
|
|
|
+ // 3. 匹配business_section、business_types和business_classify
|
|
|
+ StoreDictionary parentDict = storeDictionaryMapper.selectById(dict.getParentId());
|
|
|
+ if (parentDict == null || !"business_type".equals(parentDict.getTypeName())) {
|
|
|
+ throw new IllegalArgumentException("分类的父级不是经营种类,categoryId: " + categoryId);
|
|
|
+ }
|
|
|
+
|
|
|
+ StoreDictionary grandParentDict = storeDictionaryMapper.selectById(parentDict.getParentId());
|
|
|
+ if (grandParentDict == null || !"business_section".equals(grandParentDict.getTypeName())) {
|
|
|
+ throw new IllegalArgumentException("经营种类的父级不是经营板块,categoryId: " + categoryId);
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ Integer sectionId = Integer.parseInt(grandParentDict.getDictId());
|
|
|
+ queryWrapper.eq("a.business_section", sectionId);
|
|
|
+ // 使用FIND_IN_SET匹配business_types字段
|
|
|
+ queryWrapper.apply("FIND_IN_SET({0}, a.business_types) > 0", parentDict.getDictId());
|
|
|
+ // 使用FIND_IN_SET匹配business_classify字段
|
|
|
+ queryWrapper.apply("FIND_IN_SET({0}, a.business_classify) > 0", dictIdStr);
|
|
|
+ } catch (NumberFormatException e) {
|
|
|
+ throw new IllegalArgumentException("经营板块、经营种类或分类dictId格式错误");
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ throw new IllegalArgumentException("不支持的字典类型: " + typeName + ", categoryId: " + categoryId);
|
|
|
+ }
|
|
|
+ } else if (businessType != null) {
|
|
|
+ // 如果指定了businessType,则根据传入的数值进行筛选
|
|
|
+ // 直接使用传入的数值作为business_section进行筛选
|
|
|
+ // KTV=3、洗浴汗蒸=4、按摩足浴=5,酒吧的数值未知(由调用方传入)
|
|
|
+ queryWrapper.eq("a.business_section", businessType);
|
|
|
+ } else {
|
|
|
+ // 如果没有指定businessType,则查询所有两种类型的店铺
|
|
|
+ // 需要查询字典表获取所有四种类型的dictId
|
|
|
+ List<String> storeTypeNames = Arrays.asList("丽人美发","运动健身");
|
|
|
+ List<StoreDictionary> storeDictionaries = storeDictionaryMapper.selectList(
|
|
|
+ new LambdaQueryWrapper<StoreDictionary>()
|
|
|
+ .eq(StoreDictionary::getTypeName, "business_section")
|
|
|
+ .in(StoreDictionary::getDictDetail, storeTypeNames)
|
|
|
+ .eq(StoreDictionary::getDeleteFlag, 0)
|
|
|
+ );
|
|
|
+
|
|
|
+ List<Integer> businessSectionIds = storeDictionaries.stream()
|
|
|
+ .filter(d -> StringUtils.isNotEmpty(d.getDictId()))
|
|
|
+ .map(StoreDictionary::getDictId)
|
|
|
+ .map(dictIdStr -> {
|
|
|
+ try {
|
|
|
+ return Integer.parseInt(dictIdStr);
|
|
|
+ } catch (NumberFormatException e) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ })
|
|
|
+ .filter(Objects::nonNull)
|
|
|
+ .collect(Collectors.toList());
|
|
|
+
|
|
|
+ // 使用business_section字段进行筛选
|
|
|
+ queryWrapper.in("a.business_section", businessSectionIds);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 过滤已删除的门店
|
|
|
+ queryWrapper.eq("a.delete_flag", 0);
|
|
|
+ // 过滤注销的门店
|
|
|
+ queryWrapper.eq("a.logout_flag", 0);
|
|
|
+ // 过滤禁用的门店
|
|
|
+ queryWrapper.ne("a.store_status", 0);
|
|
|
+ // 过滤永久关门的店铺
|
|
|
+ queryWrapper.ne("a.business_status", 99);
|
|
|
+ // 过滤过期的经营许可证
|
|
|
+ queryWrapper.ge("a.entertainment_licence_expiration_time", new Date());
|
|
|
+
|
|
|
+ // 距离优先模式:只显示10公里内且3.5星以上的店铺
|
|
|
+ final Double finalDistance;
|
|
|
+ if (finalSortType == 3) {
|
|
|
+ queryWrapper.ge("a.score_avg", 3.5);
|
|
|
+ if (distance == null || distance <= 0) {
|
|
|
+ finalDistance = 10.0; // 默认10公里
|
|
|
+ } else if (distance > 10) {
|
|
|
+ finalDistance = 10.0; // 距离优先最多10公里
|
|
|
+ } else {
|
|
|
+ finalDistance = distance;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ finalDistance = distance;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 先按距离排序获取所有数据(用于计算距离)
|
|
|
+ queryWrapper.orderByAsc("dist");
|
|
|
+
|
|
|
+ // 创建分页对象(先获取更多数据用于排序计算)
|
|
|
+ IPage<StoreInfoVo> page = new Page<>(1, 1000); // 先获取足够多的数据
|
|
|
+ IPage<StoreInfoVo> storeInfoIPage = storeInfoMapper.getPageForDistance(page, lon + "," + lat, queryWrapper);
|
|
|
+ List<StoreInfoVo> storeInfoVoList = storeInfoIPage.getRecords();
|
|
|
+
|
|
|
+ // 如果指定了距离范围,进行距离筛选
|
|
|
+ if (finalDistance != null && finalDistance > 0) {
|
|
|
+ storeInfoVoList = storeInfoVoList.stream()
|
|
|
+ .filter(store -> {
|
|
|
+ String distStr = store.getDist();
|
|
|
+ if (distStr == null || distStr.isEmpty()) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ double storeDistance = Double.parseDouble(distStr);
|
|
|
+ return storeDistance <= finalDistance;
|
|
|
+ } catch (NumberFormatException e) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ })
|
|
|
+ .collect(Collectors.toList());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 计算时间范围
|
|
|
+ LocalDateTime now = LocalDateTime.now();
|
|
|
+ LocalDateTime sevenDaysAgo = now.minusDays(7);
|
|
|
+ LocalDateTime thirtyDaysAgo = now.minusDays(30);
|
|
|
+ DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
|
|
+ String sevenDaysAgoStr = sevenDaysAgo.format(formatter);
|
|
|
+ String thirtyDaysAgoStr = thirtyDaysAgo.format(formatter);
|
|
|
+
|
|
|
+ // 提取所有门店ID
|
|
|
+ List<Integer> storeIds = storeInfoVoList.stream().map(StoreInfoVo::getId).collect(Collectors.toList());
|
|
|
+
|
|
|
+ if (CollectionUtils.isEmpty(storeIds)) {
|
|
|
+ // 如果没有符合条件的店铺,返回空结果
|
|
|
+ IPage<StoreInfoVo> emptyPage = new Page<>(pageNum, pageSize);
|
|
|
+ emptyPage.setRecords(new ArrayList<>());
|
|
|
+ emptyPage.setTotal(0);
|
|
|
+ return emptyPage;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 查询近7天销量(订单数)
|
|
|
+ Map<Integer, Long> sales7DaysMap = new HashMap<>();
|
|
|
+ if (finalSortType == 1) { // 智能排序需要销量数据
|
|
|
+ LambdaQueryWrapper<LifeUserOrder> orderWrapper = new LambdaQueryWrapper<>();
|
|
|
+ orderWrapper.in(LifeUserOrder::getStoreId, storeIds)
|
|
|
+ .and(w -> w.and(w1 -> w1.eq(LifeUserOrder::getStatus, 7)
|
|
|
+ .ge(LifeUserOrder::getFinishTime, sevenDaysAgoStr))
|
|
|
+ .or(w2 -> w2.eq(LifeUserOrder::getStatus, 1)
|
|
|
+ .ge(LifeUserOrder::getPayTime, sevenDaysAgoStr)))
|
|
|
+ .eq(LifeUserOrder::getDeleteFlag, 0);
|
|
|
+ List<LifeUserOrder> orders7Days = lifeUserOrderMapper.selectList(orderWrapper);
|
|
|
+ Map<Integer, Long> tempSalesMap = orders7Days.stream()
|
|
|
+ .filter(order -> order.getStoreId() != null)
|
|
|
+ .collect(Collectors.groupingBy(
|
|
|
+ order -> Integer.parseInt(order.getStoreId()),
|
|
|
+ Collectors.counting()));
|
|
|
+ sales7DaysMap.putAll(tempSalesMap);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 查询近30天好评数(score >= 4.5)
|
|
|
+ Map<Integer, Long> goodComment30DaysMap = new HashMap<>();
|
|
|
+ // 查询近7天评论数
|
|
|
+ Map<Integer, Long> comment7DaysMap = new HashMap<>();
|
|
|
+ // 查询总评论数
|
|
|
+ Map<Integer, Long> totalCommentMap = new HashMap<>();
|
|
|
+
|
|
|
+ if (finalSortType == 2) { // 好评优先需要评论数据
|
|
|
+ // 近30天好评数
|
|
|
+ LambdaQueryWrapper<StoreComment> goodCommentWrapper = new LambdaQueryWrapper<>();
|
|
|
+ goodCommentWrapper.in(StoreComment::getStoreId, storeIds)
|
|
|
+ .eq(StoreComment::getBusinessType, 5)
|
|
|
+ .eq(StoreComment::getDeleteFlag, 0)
|
|
|
+ .isNull(StoreComment::getReplyId)
|
|
|
+ .ge(StoreComment::getScore, 4.5)
|
|
|
+ .ge(StoreComment::getCreatedTime, thirtyDaysAgoStr);
|
|
|
+ List<StoreComment> goodComments30Days = storeCommentMapper.selectList(goodCommentWrapper);
|
|
|
+ Map<Integer, Long> tempGoodCommentMap = goodComments30Days.stream()
|
|
|
+ .collect(Collectors.groupingBy(StoreComment::getStoreId, Collectors.counting()));
|
|
|
+ goodComment30DaysMap.putAll(tempGoodCommentMap);
|
|
|
+
|
|
|
+ // 近7天评论数
|
|
|
+ LambdaQueryWrapper<StoreComment> comment7DaysWrapper = new LambdaQueryWrapper<>();
|
|
|
+ comment7DaysWrapper.in(StoreComment::getStoreId, storeIds)
|
|
|
+ .eq(StoreComment::getBusinessType, 5)
|
|
|
+ .eq(StoreComment::getDeleteFlag, 0)
|
|
|
+ .isNull(StoreComment::getReplyId)
|
|
|
+ .ge(StoreComment::getCreatedTime, sevenDaysAgoStr);
|
|
|
+ List<StoreComment> comments7Days = storeCommentMapper.selectList(comment7DaysWrapper);
|
|
|
+ Map<Integer, Long> tempComment7DaysMap = comments7Days.stream()
|
|
|
+ .collect(Collectors.groupingBy(StoreComment::getStoreId, Collectors.counting()));
|
|
|
+ comment7DaysMap.putAll(tempComment7DaysMap);
|
|
|
+
|
|
|
+ // 总评论数
|
|
|
+ LambdaQueryWrapper<StoreComment> totalCommentWrapper = new LambdaQueryWrapper<>();
|
|
|
+ totalCommentWrapper.in(StoreComment::getStoreId, storeIds)
|
|
|
+ .eq(StoreComment::getBusinessType, 5)
|
|
|
+ .eq(StoreComment::getDeleteFlag, 0)
|
|
|
+ .isNull(StoreComment::getReplyId);
|
|
|
+ List<StoreComment> totalComments = storeCommentMapper.selectList(totalCommentWrapper);
|
|
|
+ Map<Integer, Long> tempTotalCommentMap = totalComments.stream()
|
|
|
+ .collect(Collectors.groupingBy(StoreComment::getStoreId, Collectors.counting()));
|
|
|
+ totalCommentMap.putAll(tempTotalCommentMap);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 获取全部店铺的评分
|
|
|
+ Map<String, List<Map<String, Object>>> avgScoreMap = storeEvaluationMapper.allStoreAvgScore().stream()
|
|
|
+ .collect(Collectors.groupingBy(o -> o.get("store_id").toString()));
|
|
|
+ Map<String, List<Map<String, Object>>> avgPriceMap = lifeUserOrderMapper.allStoreAvgPrice().stream()
|
|
|
+ .collect(Collectors.groupingBy(o -> o.get("store_id").toString()));
|
|
|
+
|
|
|
+ // 计算综合得分并排序
|
|
|
+ List<StoreInfoVo> sortedList = storeInfoVoList.stream()
|
|
|
+ .map(store -> {
|
|
|
+ // 获取基础评分(score_avg × 2,标准化为0-10分)
|
|
|
+ Double scoreAvg = store.getScoreAvg() != null ? store.getScoreAvg() : 0.0;
|
|
|
+ double baseScore = Math.min(scoreAvg * 2, 10.0); // 基础评分,最高10分
|
|
|
+
|
|
|
+ // 获取距离
|
|
|
+ double storeDistance = 999999;
|
|
|
+ try {
|
|
|
+ String distStr = store.getDist();
|
|
|
+ if (distStr != null && !distStr.isEmpty()) {
|
|
|
+ storeDistance = Double.parseDouble(distStr);
|
|
|
+ }
|
|
|
+ } catch (NumberFormatException e) {
|
|
|
+ // 忽略
|
|
|
+ }
|
|
|
+
|
|
|
+ double finalScore = 0.0;
|
|
|
+
|
|
|
+ if (finalSortType == 1) {
|
|
|
+ // 智能排序:综合评分×50% + 近7天销量×30% + 距离得分×20%
|
|
|
+ // 综合评分(基础评分)
|
|
|
+ double scorePart = baseScore * 0.5;
|
|
|
+
|
|
|
+ // 近7天销量(需要标准化,假设最大销量为100)
|
|
|
+ long sales7Days = sales7DaysMap.getOrDefault(store.getId(), 0L);
|
|
|
+ double salesScore = Math.min(sales7Days / 100.0 * 10, 10.0); // 标准化到0-10
|
|
|
+ double salesPart = salesScore * 0.3;
|
|
|
+
|
|
|
+ // 距离得分(距离越近得分越高,10公里内计算)
|
|
|
+ double distanceScore = storeDistance <= 10 ? (10 - storeDistance) / 10.0 * 10 : 0;
|
|
|
+ double distancePart = distanceScore * 0.2;
|
|
|
+
|
|
|
+ finalScore = scorePart + salesPart + distancePart;
|
|
|
+ } else if (finalSortType == 2) {
|
|
|
+ // 好评优先:综合评分×50% + 近30天好评数×35% + 近7天新评占比×15%
|
|
|
+ double scorePart = baseScore * 0.5;
|
|
|
+
|
|
|
+ // 近30天好评数(需要标准化,假设最大好评数为50)
|
|
|
+ long goodComment30Days = goodComment30DaysMap.getOrDefault(store.getId(), 0L);
|
|
|
+ double goodCommentScore = Math.min(goodComment30Days / 50.0 * 10, 10.0);
|
|
|
+ double goodCommentPart = goodCommentScore * 0.35;
|
|
|
+
|
|
|
+ // 近7天新评占比
|
|
|
+ long comment7Days = comment7DaysMap.getOrDefault(store.getId(), 0L);
|
|
|
+ long totalComment = totalCommentMap.getOrDefault(store.getId(), 1L); // 避免除0
|
|
|
+ double newCommentRatio = (double) comment7Days / totalComment;
|
|
|
+ double newCommentPart = newCommentRatio * 10 * 0.15; // 占比转换为0-10分
|
|
|
+
|
|
|
+ finalScore = scorePart + goodCommentPart + newCommentPart;
|
|
|
+ } else if (finalSortType == 3) {
|
|
|
+ // 距离优先:距离得分 = (10 - 实际距离) × 80% + 基础评分 × 20%(10公里内计算)
|
|
|
+ if (storeDistance <= 10) {
|
|
|
+ double distanceScore = (10 - storeDistance) / 10.0 * 10; // 标准化到0-10
|
|
|
+ double distancePart = distanceScore * 0.8;
|
|
|
+ double scorePart = baseScore * 0.2;
|
|
|
+ finalScore = distancePart + scorePart;
|
|
|
+ } else {
|
|
|
+ finalScore = -1; // 超出范围,不展示
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 设置综合得分(用于排序)
|
|
|
+ store.setDistance(storeDistance);
|
|
|
+ // 使用反射或扩展字段存储finalScore,这里我们使用一个临时字段
|
|
|
+ // 由于StoreInfoVo没有finalScore字段,我们使用distance字段临时存储,排序后再恢复
|
|
|
+ return new Object[] { store, finalScore };
|
|
|
+ })
|
|
|
+ .filter(item -> {
|
|
|
+ // 距离优先模式:过滤掉超出范围的
|
|
|
+ if (finalSortType == 3) {
|
|
|
+ return ((Double) item[1]) >= 0;
|
|
|
+ }
|
|
|
+ return true;
|
|
|
+ })
|
|
|
+ .sorted((a, b) -> Double.compare((Double) b[1], (Double) a[1])) // 按得分降序
|
|
|
+ .map(item -> (StoreInfoVo) item[0])
|
|
|
+ .collect(Collectors.toList());
|
|
|
+
|
|
|
+ // 分页处理
|
|
|
+ long total = sortedList.size();
|
|
|
+ int start = (pageNum - 1) * pageSize;
|
|
|
+ int end = Math.min(start + pageSize, sortedList.size());
|
|
|
+ List<StoreInfoVo> pagedList;
|
|
|
+ if (start < sortedList.size()) {
|
|
|
+ pagedList = sortedList.subList(start, end);
|
|
|
+ } else {
|
|
|
+ pagedList = new ArrayList<>();
|
|
|
+ }
|
|
|
+
|
|
|
+ // 查询每个店铺的最新代金券(只显示一个,最新创建的)
|
|
|
+ Map<Integer, LifeCoupon> latestCouponMap = new HashMap<>();
|
|
|
+ if (!CollectionUtils.isEmpty(pagedList)) {
|
|
|
+ List<Integer> pagedStoreIds = pagedList.stream().map(StoreInfoVo::getId).collect(Collectors.toList());
|
|
|
+ // 查询所有店铺的代金券,按创建时间降序
|
|
|
+ LambdaQueryWrapper<LifeCoupon> couponWrapper = new LambdaQueryWrapper<>();
|
|
|
+ couponWrapper.in(LifeCoupon::getStoreId, pagedStoreIds.stream().map(String::valueOf).collect(Collectors.toList()))
|
|
|
+ .eq(LifeCoupon::getStatus, CouponStatusEnum.ONGOING.getCode())
|
|
|
+ .eq(LifeCoupon::getType, 1) // 代金券类型
|
|
|
+ .eq(LifeCoupon::getDeleteFlag, 0)
|
|
|
+ .orderByDesc(LifeCoupon::getCreatedTime);
|
|
|
+ List<LifeCoupon> allCoupons = lifeCouponMapper.selectList(couponWrapper);
|
|
|
+
|
|
|
+ // 为每个店铺只保留最新创建的一个代金券
|
|
|
+ for (LifeCoupon coupon : allCoupons) {
|
|
|
+ if (coupon.getStoreId() != null) {
|
|
|
+ try {
|
|
|
+ Integer storeId = Integer.parseInt(coupon.getStoreId());
|
|
|
+ // 如果该店铺还没有代金券,或者当前代金券更新,则更新
|
|
|
+ if (!latestCouponMap.containsKey(storeId)) {
|
|
|
+ latestCouponMap.put(storeId, coupon);
|
|
|
+ }
|
|
|
+ } catch (NumberFormatException e) {
|
|
|
+ // 忽略无效的storeId
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 设置评分、平均消费和代金券
|
|
|
+ for (StoreInfoVo store : pagedList) {
|
|
|
+ if (avgScoreMap.containsKey(String.valueOf(store.getId()))) {
|
|
|
+ store.setAvgScore(new BigDecimal(avgScoreMap.get(String.valueOf(store.getId())).get(0).get("avg_score").toString())
|
|
|
+ .setScale(1, RoundingMode.HALF_UP).toString());
|
|
|
+ store.setTotalNum(avgScoreMap.get(String.valueOf(store.getId())).get(0).get("total_num").toString());
|
|
|
+ } else {
|
|
|
+ store.setAvgScore("0");
|
|
|
+ store.setTotalNum("0");
|
|
|
+ }
|
|
|
+
|
|
|
+ if (avgPriceMap.containsKey(String.valueOf(store.getId()))) {
|
|
|
+ store.setAvgPrice(avgPriceMap.get(String.valueOf(store.getId())).get(0).get("avg_price").toString());
|
|
|
+ } else {
|
|
|
+ store.setAvgPrice("0");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 设置最新代金券(只显示一个)
|
|
|
+ if (latestCouponMap.containsKey(store.getId())) {
|
|
|
+ LifeCoupon latestCoupon = latestCouponMap.get(store.getId());
|
|
|
+ LifeCouponVo couponVo = new LifeCouponVo();
|
|
|
+ BeanUtils.copyProperties(latestCoupon, couponVo);
|
|
|
+ // 只设置一个代金券到列表中
|
|
|
+ List<LifeCouponVo> couponList = new ArrayList<>();
|
|
|
+ couponList.add(couponVo);
|
|
|
+ store.setCouponList(couponList);
|
|
|
+ } else {
|
|
|
+ // 如果没有代金券,设置为空列表
|
|
|
+ store.setCouponList(new ArrayList<>());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 创建分页对象
|
|
|
+ IPage<StoreInfoVo> resultPage = new Page<>(pageNum, pageSize);
|
|
|
+ resultPage.setRecords(pagedList);
|
|
|
+ resultPage.setTotal(total);
|
|
|
+ resultPage.setCurrent(pageNum);
|
|
|
+ resultPage.setSize(pageSize);
|
|
|
+ resultPage.setPages((total + pageSize - 1) / pageSize);
|
|
|
+
|
|
|
+ return resultPage;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 查询OCR图片上传记录并转换为JSONObject
|
|
|
+ *
|
|
|
+ * @param storeUserId 店铺用户ID
|
|
|
+ * @param ocrType OCR类型
|
|
|
+ * @param likeKeyword 模糊查询关键词,可为null
|
|
|
+ * @param includeImageUrl 是否包含imageUrl字段
|
|
|
+ * @return JSONObject,如果查询结果为空则返回空的JSONObject
|
|
|
+ */
|
|
|
+ private com.alibaba.fastjson2.JSONObject convertOcrResultToJson(Integer storeUserId, String ocrType, String likeKeyword, boolean includeImageUrl) {
|
|
|
+ LambdaQueryWrapper<OcrImageUpload> wrapper = new LambdaQueryWrapper<OcrImageUpload>()
|
|
|
+ .eq(OcrImageUpload::getStoreUserId, storeUserId)
|
|
|
+ .eq(OcrImageUpload::getOcrType, ocrType)
|
|
|
+ .orderByDesc(OcrImageUpload::getCreateTime)
|
|
|
+ .last("limit 1");
|
|
|
+
|
|
|
+ if (StringUtils.isNotEmpty(likeKeyword)) {
|
|
|
+ wrapper.like(OcrImageUpload::getOcrResult, likeKeyword);
|
|
|
+ }
|
|
|
+
|
|
|
+ OcrImageUpload ocrImageUpload = ocrImageUploadMapper.selectOne(wrapper);
|
|
|
+
|
|
|
+ if (ocrImageUpload == null || StringUtils.isEmpty(ocrImageUpload.getOcrResult())) {
|
|
|
+ return new com.alibaba.fastjson2.JSONObject();
|
|
|
+ }
|
|
|
+
|
|
|
+ com.alibaba.fastjson2.JSONObject jsonObject = com.alibaba.fastjson2.JSONObject.parseObject(ocrImageUpload.getOcrResult());
|
|
|
+ if (includeImageUrl && StringUtils.isNotEmpty(ocrImageUpload.getImageUrl())) {
|
|
|
+ jsonObject.put("imageUrl", ocrImageUpload.getImageUrl());
|
|
|
+ }
|
|
|
+
|
|
|
+ return jsonObject;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 你可能还喜欢(推荐店铺)
|
|
|
+ * 根据一级分类、二级分类、三级分类进行店铺筛选
|
|
|
+ * 筛选顺序:先三级,然后二级,最后一级
|
|
|
+ *
|
|
|
+ * @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;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 构建树形结构(优化版)
|
|
|
+ *
|
|
|
+ * @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 List<StoreDictionaryVo> getLeisureEntertainmentCategories() {
|
|
|
+ // 定义四种主分类名称
|
|
|
+ List<String> mainCategoryNames = Arrays.asList("酒吧", "KTV", "洗浴汗蒸", "按摩足疗");
|
|
|
+
|
|
|
+ // 查询所有主分类(business_section类型)
|
|
|
+ List<StoreDictionary> allMainCategories = storeDictionaryMapper.selectList(
|
|
|
+ new LambdaQueryWrapper<StoreDictionary>()
|
|
|
+ .eq(StoreDictionary::getTypeName, "business_section")
|
|
|
+ .in(StoreDictionary::getDictDetail, mainCategoryNames)
|
|
|
+ .eq(StoreDictionary::getDeleteFlag, 0)
|
|
|
+ );
|
|
|
+
|
|
|
+ // 构建主分类列表,包含"全部"选项
|
|
|
+ List<StoreDictionaryVo> result = new ArrayList<>();
|
|
|
+
|
|
|
+ // 添加"全部"选项
|
|
|
+ StoreDictionaryVo allCategory = new StoreDictionaryVo();
|
|
|
+ allCategory.setId(0);
|
|
|
+ allCategory.setDictId("0");
|
|
|
+ allCategory.setDictDetail("全部");
|
|
|
+ allCategory.setTypeName("business_section");
|
|
|
+ allCategory.setSubDataList(new ArrayList<>());
|
|
|
+ result.add(allCategory);
|
|
|
+
|
|
|
+ // 查询所有子分类(根据parent_id关联)
|
|
|
+ Map<Integer, List<StoreDictionary>> subCategoryMap = new HashMap<>();
|
|
|
+ if (!CollectionUtils.isEmpty(allMainCategories)) {
|
|
|
+ List<Integer> mainCategoryIds = allMainCategories.stream()
|
|
|
+ .map(StoreDictionary::getId)
|
|
|
+ .collect(Collectors.toList());
|
|
|
+
|
|
|
+ // 查询所有子分类
|
|
|
+ List<StoreDictionary> allSubCategories = storeDictionaryMapper.selectList(
|
|
|
+ new LambdaQueryWrapper<StoreDictionary>()
|
|
|
+ .in(StoreDictionary::getParentId, mainCategoryIds)
|
|
|
+ .eq(StoreDictionary::getDeleteFlag, 0)
|
|
|
+ .in(StoreDictionary::getTypeName, "business_section","business_type","business_classify")
|
|
|
+ );
|
|
|
+
|
|
|
+ // 按parentId分组
|
|
|
+ subCategoryMap = allSubCategories.stream()
|
|
|
+ .collect(Collectors.groupingBy(StoreDictionary::getParentId));
|
|
|
+ }
|
|
|
+
|
|
|
+ // 构建主分类及其子分类
|
|
|
+ for (StoreDictionary mainCategory : allMainCategories) {
|
|
|
+ StoreDictionaryVo mainVo = new StoreDictionaryVo();
|
|
|
+ BeanUtils.copyProperties(mainCategory, mainVo);
|
|
|
+
|
|
|
+ // 获取该主分类下的子分类
|
|
|
+ List<StoreDictionary> subCategories = subCategoryMap.getOrDefault(mainCategory.getId(), new ArrayList<>());
|
|
|
+ List<StoreDictionaryVo> subVoList = new ArrayList<>();
|
|
|
+
|
|
|
+ for (StoreDictionary subCategory : subCategories) {
|
|
|
+ StoreDictionaryVo subVo = new StoreDictionaryVo();
|
|
|
+ BeanUtils.copyProperties(subCategory, subVo);
|
|
|
+ subVoList.add(subVo);
|
|
|
+ }
|
|
|
+
|
|
|
+ mainVo.setSubDataList(subVoList);
|
|
|
+ result.add(mainVo);
|
|
|
+ }
|
|
|
+
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public IPage<StoreInfoVo> getSpecialTypeStoresByDistance(Double lon, Double lat, Double distance, Integer sortType, Integer businessType, Integer categoryId, int pageNum, int pageSize) {
|
|
|
+ // 参数校验
|
|
|
+ if (lon == null || lat == null) {
|
|
|
+ throw new IllegalArgumentException("经纬度参数不能为空");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 智能检测:如果参数可能传反了(lat超出范围但lon在范围内),给出提示
|
|
|
+ if (Math.abs(lat) > 90 && Math.abs(lon) <= 90) {
|
|
|
+ throw new IllegalArgumentException(
|
|
|
+ String.format("参数可能传反了!当前值: lon=%s, lat=%s。经度范围: [-180, 180],纬度范围: [-90, 90]。请检查参数顺序。", lon, lat)
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ // 校验经纬度范围:经度 [-180, 180],纬度 [-90, 90]
|
|
|
+ if (lon < -180 || lon > 180) {
|
|
|
+ throw new IllegalArgumentException("经度参数超出有效范围 [-180, 180],当前值: " + lon);
|
|
|
+ }
|
|
|
+ if (lat < -90 || lat > 90) {
|
|
|
+ throw new IllegalArgumentException("纬度参数超出有效范围 [-90, 90],当前值: " + lat);
|
|
|
+ }
|
|
|
+ final int finalSortType = (sortType == null || sortType < 1 || sortType > 3) ? 1 : sortType; // 默认智能排序
|
|
|
+ if (pageNum <= 0) {
|
|
|
+ pageNum = 1;
|
|
|
+ }
|
|
|
+ if (pageSize <= 0) {
|
|
|
+ pageSize = 10;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 构建查询条件
|
|
|
+ QueryWrapper<StoreInfoVo> queryWrapper = new QueryWrapper<>();
|
|
|
+
|
|
|
+ // 如果传入了dictId,根据字典id查询经营板块、经营种类、分类并匹配店铺
|
|
|
+ if (categoryId != null) {
|
|
|
+ // 根据categoryId查询字典表记录
|
|
|
+ StoreDictionary dict = storeDictionaryMapper.selectById(categoryId);
|
|
|
+ if (dict == null || dict.getDeleteFlag() == 1) {
|
|
|
+ throw new IllegalArgumentException("字典id不存在或已删除: " + categoryId);
|
|
|
+ }
|
|
|
+
|
|
|
+ String typeName = dict.getTypeName();
|
|
|
+ String dictIdStr =dict.getDictId();
|
|
|
+
|
|
|
+ if ("business_section".equals(typeName)) {
|
|
|
+ // 如果是经营板块,直接匹配business_section
|
|
|
+ try {
|
|
|
+ Integer sectionId = Integer.parseInt(dictIdStr);
|
|
|
+ queryWrapper.eq("a.business_section", sectionId);
|
|
|
+ } catch (NumberFormatException e) {
|
|
|
+ throw new IllegalArgumentException("经营板块dictId格式错误: " + dictIdStr);
|
|
|
+ }
|
|
|
+ } else if ("business_type".equals(typeName)) {
|
|
|
+ // 如果是经营种类,需要:
|
|
|
+ // 1. 向上查找父级(经营板块)
|
|
|
+ // 2. 匹配business_section和business_types
|
|
|
+ StoreDictionary parentDict = storeDictionaryMapper.selectById(dict.getParentId());
|
|
|
+ if (parentDict == null || !"business_section".equals(parentDict.getTypeName())) {
|
|
|
+ throw new IllegalArgumentException("经营种类的父级不是经营板块,categoryId: " + categoryId);
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ Integer sectionId = Integer.parseInt(parentDict.getDictId());
|
|
|
+ queryWrapper.eq("a.business_section", sectionId);
|
|
|
+ // 使用FIND_IN_SET匹配business_types字段(逗号分隔)
|
|
|
+ queryWrapper.apply("FIND_IN_SET({0}, a.business_types) > 0", dictIdStr);
|
|
|
+ } catch (NumberFormatException e) {
|
|
|
+ throw new IllegalArgumentException("经营板块或经营种类dictId格式错误");
|
|
|
+ }
|
|
|
+ } else if ("business_classify".equals(typeName)) {
|
|
|
+ // 如果是分类,需要:
|
|
|
+ // 1. 向上查找父级(经营种类)
|
|
|
+ // 2. 向上查找祖父级(经营板块)
|
|
|
+ // 3. 匹配business_section、business_types和business_classify
|
|
|
+ StoreDictionary parentDict = storeDictionaryMapper.selectById(dict.getParentId());
|
|
|
+ if (parentDict == null || !"business_type".equals(parentDict.getTypeName())) {
|
|
|
+ throw new IllegalArgumentException("分类的父级不是经营种类,categoryId: " + categoryId);
|
|
|
+ }
|
|
|
+
|
|
|
+ StoreDictionary grandParentDict = storeDictionaryMapper.selectById(parentDict.getParentId());
|
|
|
+ if (grandParentDict == null || !"business_section".equals(grandParentDict.getTypeName())) {
|
|
|
+ throw new IllegalArgumentException("经营种类的父级不是经营板块,categoryId: " + categoryId);
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ Integer sectionId = Integer.parseInt(grandParentDict.getDictId());
|
|
|
+ queryWrapper.eq("a.business_section", sectionId);
|
|
|
+ // 使用FIND_IN_SET匹配business_types字段
|
|
|
+ queryWrapper.apply("FIND_IN_SET({0}, a.business_types) > 0", parentDict.getDictId());
|
|
|
+ // 使用FIND_IN_SET匹配business_classify字段
|
|
|
+ queryWrapper.apply("FIND_IN_SET({0}, a.business_classify) > 0", dictIdStr);
|
|
|
+ } catch (NumberFormatException e) {
|
|
|
+ throw new IllegalArgumentException("经营板块、经营种类或分类dictId格式错误");
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ throw new IllegalArgumentException("不支持的字典类型: " + typeName + ", categoryId: " + categoryId);
|
|
|
+ }
|
|
|
+ } else if (businessType != null) {
|
|
|
+ // 如果指定了businessType,则根据传入的数值进行筛选
|
|
|
+ // 直接使用传入的数值作为business_section进行筛选
|
|
|
+ // KTV=3、洗浴汗蒸=4、按摩足浴=5,酒吧的数值未知(由调用方传入)
|
|
|
+ queryWrapper.eq("a.business_section", businessType);
|
|
|
+ } else {
|
|
|
+ // 如果没有指定businessType,则查询所有四种类型的店铺
|
|
|
+ // 需要查询字典表获取所有四种类型的dictId
|
|
|
+ List<String> storeTypeNames = Arrays.asList("酒吧", "KTV", "洗浴汗蒸", "按摩足疗");
|
|
|
+ List<StoreDictionary> storeDictionaries = storeDictionaryMapper.selectList(
|
|
|
+ new LambdaQueryWrapper<StoreDictionary>()
|
|
|
+ .eq(StoreDictionary::getTypeName, "business_section")
|
|
|
+ .in(StoreDictionary::getDictDetail, storeTypeNames)
|
|
|
+ .eq(StoreDictionary::getDeleteFlag, 0)
|
|
|
+ );
|
|
|
+
|
|
|
+ List<Integer> businessSectionIds = storeDictionaries.stream()
|
|
|
+ .filter(d -> StringUtils.isNotEmpty(d.getDictId()))
|
|
|
+ .map(StoreDictionary::getDictId)
|
|
|
+ .map(dictIdStr -> {
|
|
|
+ try {
|
|
|
+ return Integer.parseInt(dictIdStr);
|
|
|
+ } catch (NumberFormatException e) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ })
|
|
|
+ .filter(Objects::nonNull)
|
|
|
+ .collect(Collectors.toList());
|
|
|
+
|
|
|
+ // 使用business_section字段进行筛选
|
|
|
+ queryWrapper.in("a.business_section", businessSectionIds);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 过滤已删除的门店
|
|
|
+ queryWrapper.eq("a.delete_flag", 0);
|
|
|
+ // 过滤注销的门店
|
|
|
+ queryWrapper.eq("a.logout_flag", 0);
|
|
|
+ // 过滤禁用的门店
|
|
|
+ queryWrapper.ne("a.store_status", 0);
|
|
|
+ // 过滤永久关门的店铺
|
|
|
+ queryWrapper.ne("a.business_status", 99);
|
|
|
+ // 过滤过期的经营许可证
|
|
|
+ queryWrapper.ge("a.entertainment_licence_expiration_time", new Date());
|
|
|
+
|
|
|
+ // 距离优先模式:只显示10公里内且3.5星以上的店铺
|
|
|
+ final Double finalDistance;
|
|
|
+ if (finalSortType == 3) {
|
|
|
+ queryWrapper.ge("a.score_avg", 3.5);
|
|
|
+ if (distance == null || distance <= 0) {
|
|
|
+ finalDistance = 10.0; // 默认10公里
|
|
|
+ } else if (distance > 10) {
|
|
|
+ finalDistance = 10.0; // 距离优先最多10公里
|
|
|
+ } else {
|
|
|
+ finalDistance = distance;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ finalDistance = distance;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 先按距离排序获取所有数据(用于计算距离)
|
|
|
+ queryWrapper.orderByAsc("dist");
|
|
|
+
|
|
|
+ // 创建分页对象(先获取更多数据用于排序计算)
|
|
|
+ IPage<StoreInfoVo> page = new Page<>(1, 1000); // 先获取足够多的数据
|
|
|
+ IPage<StoreInfoVo> storeInfoIPage = storeInfoMapper.getPageForDistance(page, lon + "," + lat, queryWrapper);
|
|
|
+ List<StoreInfoVo> storeInfoVoList = storeInfoIPage.getRecords();
|
|
|
+
|
|
|
+ // 如果指定了距离范围,进行距离筛选
|
|
|
+ if (finalDistance != null && finalDistance > 0) {
|
|
|
+ storeInfoVoList = storeInfoVoList.stream()
|
|
|
+ .filter(store -> {
|
|
|
+ String distStr = store.getDist();
|
|
|
+ if (distStr == null || distStr.isEmpty()) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ double storeDistance = Double.parseDouble(distStr);
|
|
|
+ return storeDistance <= finalDistance;
|
|
|
+ } catch (NumberFormatException e) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ })
|
|
|
+ .collect(Collectors.toList());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 计算时间范围
|
|
|
+ LocalDateTime now = LocalDateTime.now();
|
|
|
+ LocalDateTime sevenDaysAgo = now.minusDays(7);
|
|
|
+ LocalDateTime thirtyDaysAgo = now.minusDays(30);
|
|
|
+ DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
|
|
+ String sevenDaysAgoStr = sevenDaysAgo.format(formatter);
|
|
|
+ String thirtyDaysAgoStr = thirtyDaysAgo.format(formatter);
|
|
|
+
|
|
|
+ // 提取所有门店ID
|
|
|
+ List<Integer> storeIds = storeInfoVoList.stream().map(StoreInfoVo::getId).collect(Collectors.toList());
|
|
|
+
|
|
|
+ if (CollectionUtils.isEmpty(storeIds)) {
|
|
|
+ // 如果没有符合条件的店铺,返回空结果
|
|
|
+ IPage<StoreInfoVo> emptyPage = new Page<>(pageNum, pageSize);
|
|
|
+ emptyPage.setRecords(new ArrayList<>());
|
|
|
+ emptyPage.setTotal(0);
|
|
|
+ return emptyPage;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 查询近7天销量(订单数)
|
|
|
+ Map<Integer, Long> sales7DaysMap = new HashMap<>();
|
|
|
+ if (finalSortType == 1) { // 智能排序需要销量数据
|
|
|
+ LambdaQueryWrapper<LifeUserOrder> orderWrapper = new LambdaQueryWrapper<>();
|
|
|
+ orderWrapper.in(LifeUserOrder::getStoreId, storeIds)
|
|
|
+ .and(w -> w.and(w1 -> w1.eq(LifeUserOrder::getStatus, 7)
|
|
|
+ .ge(LifeUserOrder::getFinishTime, sevenDaysAgoStr))
|
|
|
+ .or(w2 -> w2.eq(LifeUserOrder::getStatus, 1)
|
|
|
+ .ge(LifeUserOrder::getPayTime, sevenDaysAgoStr)))
|
|
|
+ .eq(LifeUserOrder::getDeleteFlag, 0);
|
|
|
+ List<LifeUserOrder> orders7Days = lifeUserOrderMapper.selectList(orderWrapper);
|
|
|
+ Map<Integer, Long> tempSalesMap = orders7Days.stream()
|
|
|
+ .filter(order -> order.getStoreId() != null)
|
|
|
+ .collect(Collectors.groupingBy(
|
|
|
+ order -> Integer.parseInt(order.getStoreId()),
|
|
|
+ Collectors.counting()));
|
|
|
+ sales7DaysMap.putAll(tempSalesMap);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 查询近30天好评数(score >= 4.5)
|
|
|
+ Map<Integer, Long> goodComment30DaysMap = new HashMap<>();
|
|
|
+ // 查询近7天评论数
|
|
|
+ Map<Integer, Long> comment7DaysMap = new HashMap<>();
|
|
|
+ // 查询总评论数
|
|
|
+ Map<Integer, Long> totalCommentMap = new HashMap<>();
|
|
|
+
|
|
|
+ if (finalSortType == 2) { // 好评优先需要评论数据
|
|
|
+ // 近30天好评数
|
|
|
+ LambdaQueryWrapper<StoreComment> goodCommentWrapper = new LambdaQueryWrapper<>();
|
|
|
+ goodCommentWrapper.in(StoreComment::getStoreId, storeIds)
|
|
|
+ .eq(StoreComment::getBusinessType, 5)
|
|
|
+ .eq(StoreComment::getDeleteFlag, 0)
|
|
|
+ .isNull(StoreComment::getReplyId)
|
|
|
+ .ge(StoreComment::getScore, 4.5)
|
|
|
+ .ge(StoreComment::getCreatedTime, thirtyDaysAgoStr);
|
|
|
+ List<StoreComment> goodComments30Days = storeCommentMapper.selectList(goodCommentWrapper);
|
|
|
+ Map<Integer, Long> tempGoodCommentMap = goodComments30Days.stream()
|
|
|
+ .collect(Collectors.groupingBy(StoreComment::getStoreId, Collectors.counting()));
|
|
|
+ goodComment30DaysMap.putAll(tempGoodCommentMap);
|
|
|
+
|
|
|
+ // 近7天评论数
|
|
|
+ LambdaQueryWrapper<StoreComment> comment7DaysWrapper = new LambdaQueryWrapper<>();
|
|
|
+ comment7DaysWrapper.in(StoreComment::getStoreId, storeIds)
|
|
|
+ .eq(StoreComment::getBusinessType, 5)
|
|
|
+ .eq(StoreComment::getDeleteFlag, 0)
|
|
|
+ .isNull(StoreComment::getReplyId)
|
|
|
+ .ge(StoreComment::getCreatedTime, sevenDaysAgoStr);
|
|
|
+ List<StoreComment> comments7Days = storeCommentMapper.selectList(comment7DaysWrapper);
|
|
|
+ Map<Integer, Long> tempComment7DaysMap = comments7Days.stream()
|
|
|
+ .collect(Collectors.groupingBy(StoreComment::getStoreId, Collectors.counting()));
|
|
|
+ comment7DaysMap.putAll(tempComment7DaysMap);
|
|
|
+
|
|
|
+ // 总评论数
|
|
|
+ LambdaQueryWrapper<StoreComment> totalCommentWrapper = new LambdaQueryWrapper<>();
|
|
|
+ totalCommentWrapper.in(StoreComment::getStoreId, storeIds)
|
|
|
+ .eq(StoreComment::getBusinessType, 5)
|
|
|
+ .eq(StoreComment::getDeleteFlag, 0)
|
|
|
+ .isNull(StoreComment::getReplyId);
|
|
|
+ List<StoreComment> totalComments = storeCommentMapper.selectList(totalCommentWrapper);
|
|
|
+ Map<Integer, Long> tempTotalCommentMap = totalComments.stream()
|
|
|
+ .collect(Collectors.groupingBy(StoreComment::getStoreId, Collectors.counting()));
|
|
|
+ totalCommentMap.putAll(tempTotalCommentMap);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 获取全部店铺的评分
|
|
|
+ Map<String, List<Map<String, Object>>> avgScoreMap = storeEvaluationMapper.allStoreAvgScore().stream()
|
|
|
+ .collect(Collectors.groupingBy(o -> o.get("store_id").toString()));
|
|
|
+ Map<String, List<Map<String, Object>>> avgPriceMap = lifeUserOrderMapper.allStoreAvgPrice().stream()
|
|
|
+ .collect(Collectors.groupingBy(o -> o.get("store_id").toString()));
|
|
|
+
|
|
|
+ // 计算综合得分并排序
|
|
|
+ List<StoreInfoVo> sortedList = storeInfoVoList.stream()
|
|
|
+ .map(store -> {
|
|
|
+ // 获取基础评分(score_avg × 2,标准化为0-10分)
|
|
|
+ Double scoreAvg = store.getScoreAvg() != null ? store.getScoreAvg() : 0.0;
|
|
|
+ double baseScore = Math.min(scoreAvg * 2, 10.0); // 基础评分,最高10分
|
|
|
+
|
|
|
+ // 获取距离
|
|
|
+ double storeDistance = 999999;
|
|
|
+ try {
|
|
|
+ String distStr = store.getDist();
|
|
|
+ if (distStr != null && !distStr.isEmpty()) {
|
|
|
+ storeDistance = Double.parseDouble(distStr);
|
|
|
+ }
|
|
|
+ } catch (NumberFormatException e) {
|
|
|
+ // 忽略
|
|
|
+ }
|
|
|
+
|
|
|
+ double finalScore = 0.0;
|
|
|
+
|
|
|
+ if (finalSortType == 1) {
|
|
|
+ // 智能排序:综合评分×50% + 近7天销量×30% + 距离得分×20%
|
|
|
+ // 综合评分(基础评分)
|
|
|
+ double scorePart = baseScore * 0.5;
|
|
|
+
|
|
|
+ // 近7天销量(需要标准化,假设最大销量为100)
|
|
|
+ long sales7Days = sales7DaysMap.getOrDefault(store.getId(), 0L);
|
|
|
+ double salesScore = Math.min(sales7Days / 100.0 * 10, 10.0); // 标准化到0-10
|
|
|
+ double salesPart = salesScore * 0.3;
|
|
|
+
|
|
|
+ // 距离得分(距离越近得分越高,10公里内计算)
|
|
|
+ double distanceScore = storeDistance <= 10 ? (10 - storeDistance) / 10.0 * 10 : 0;
|
|
|
+ double distancePart = distanceScore * 0.2;
|
|
|
+
|
|
|
+ finalScore = scorePart + salesPart + distancePart;
|
|
|
+ } else if (finalSortType == 2) {
|
|
|
+ // 好评优先:综合评分×50% + 近30天好评数×35% + 近7天新评占比×15%
|
|
|
+ double scorePart = baseScore * 0.5;
|
|
|
+
|
|
|
+ // 近30天好评数(需要标准化,假设最大好评数为50)
|
|
|
+ long goodComment30Days = goodComment30DaysMap.getOrDefault(store.getId(), 0L);
|
|
|
+ double goodCommentScore = Math.min(goodComment30Days / 50.0 * 10, 10.0);
|
|
|
+ double goodCommentPart = goodCommentScore * 0.35;
|
|
|
+
|
|
|
+ // 近7天新评占比
|
|
|
+ long comment7Days = comment7DaysMap.getOrDefault(store.getId(), 0L);
|
|
|
+ long totalComment = totalCommentMap.getOrDefault(store.getId(), 1L); // 避免除0
|
|
|
+ double newCommentRatio = (double) comment7Days / totalComment;
|
|
|
+ double newCommentPart = newCommentRatio * 10 * 0.15; // 占比转换为0-10分
|
|
|
+
|
|
|
+ finalScore = scorePart + goodCommentPart + newCommentPart;
|
|
|
+ } else if (finalSortType == 3) {
|
|
|
+ // 距离优先:距离得分 = (10 - 实际距离) × 80% + 基础评分 × 20%(10公里内计算)
|
|
|
+ if (storeDistance <= 10) {
|
|
|
+ double distanceScore = (10 - storeDistance) / 10.0 * 10; // 标准化到0-10
|
|
|
+ double distancePart = distanceScore * 0.8;
|
|
|
+ double scorePart = baseScore * 0.2;
|
|
|
+ finalScore = distancePart + scorePart;
|
|
|
+ } else {
|
|
|
+ finalScore = -1; // 超出范围,不展示
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 设置综合得分(用于排序)
|
|
|
+ store.setDistance(storeDistance);
|
|
|
+ // 使用反射或扩展字段存储finalScore,这里我们使用一个临时字段
|
|
|
+ // 由于StoreInfoVo没有finalScore字段,我们使用distance字段临时存储,排序后再恢复
|
|
|
+ return new Object[] { store, finalScore };
|
|
|
+ })
|
|
|
+ .filter(item -> {
|
|
|
+ // 距离优先模式:过滤掉超出范围的
|
|
|
+ if (finalSortType == 3) {
|
|
|
+ return ((Double) item[1]) >= 0;
|
|
|
+ }
|
|
|
+ return true;
|
|
|
+ })
|
|
|
+ .sorted((a, b) -> Double.compare((Double) b[1], (Double) a[1])) // 按得分降序
|
|
|
+ .map(item -> (StoreInfoVo) item[0])
|
|
|
+ .collect(Collectors.toList());
|
|
|
+
|
|
|
+ // 分页处理
|
|
|
+ long total = sortedList.size();
|
|
|
+ int start = (pageNum - 1) * pageSize;
|
|
|
+ int end = Math.min(start + pageSize, sortedList.size());
|
|
|
+ List<StoreInfoVo> pagedList;
|
|
|
+ if (start < sortedList.size()) {
|
|
|
+ pagedList = sortedList.subList(start, end);
|
|
|
+ } else {
|
|
|
+ pagedList = new ArrayList<>();
|
|
|
+ }
|
|
|
+
|
|
|
+ // 查询每个店铺的最新代金券(只显示一个,最新创建的)
|
|
|
+ Map<Integer, LifeCoupon> latestCouponMap = new HashMap<>();
|
|
|
+ if (!CollectionUtils.isEmpty(pagedList)) {
|
|
|
+ List<Integer> pagedStoreIds = pagedList.stream().map(StoreInfoVo::getId).collect(Collectors.toList());
|
|
|
+ // 查询所有店铺的代金券,按创建时间降序
|
|
|
+ LambdaQueryWrapper<LifeCoupon> couponWrapper = new LambdaQueryWrapper<>();
|
|
|
+ couponWrapper.in(LifeCoupon::getStoreId, pagedStoreIds.stream().map(String::valueOf).collect(Collectors.toList()))
|
|
|
+ .eq(LifeCoupon::getStatus, CouponStatusEnum.ONGOING.getCode())
|
|
|
+ .eq(LifeCoupon::getType, 1) // 代金券类型
|
|
|
+ .eq(LifeCoupon::getDeleteFlag, 0)
|
|
|
+ .orderByDesc(LifeCoupon::getCreatedTime);
|
|
|
+ List<LifeCoupon> allCoupons = lifeCouponMapper.selectList(couponWrapper);
|
|
|
+
|
|
|
+ // 为每个店铺只保留最新创建的一个代金券
|
|
|
+ for (LifeCoupon coupon : allCoupons) {
|
|
|
+ if (coupon.getStoreId() != null) {
|
|
|
+ try {
|
|
|
+ Integer storeId = Integer.parseInt(coupon.getStoreId());
|
|
|
+ // 如果该店铺还没有代金券,或者当前代金券更新,则更新
|
|
|
+ if (!latestCouponMap.containsKey(storeId)) {
|
|
|
+ latestCouponMap.put(storeId, coupon);
|
|
|
+ }
|
|
|
+ } catch (NumberFormatException e) {
|
|
|
+ // 忽略无效的storeId
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 设置评分、平均消费和代金券
|
|
|
+ for (StoreInfoVo store : pagedList) {
|
|
|
+ if (avgScoreMap.containsKey(String.valueOf(store.getId()))) {
|
|
|
+ store.setAvgScore(new BigDecimal(avgScoreMap.get(String.valueOf(store.getId())).get(0).get("avg_score").toString())
|
|
|
+ .setScale(1, RoundingMode.HALF_UP).toString());
|
|
|
+ store.setTotalNum(avgScoreMap.get(String.valueOf(store.getId())).get(0).get("total_num").toString());
|
|
|
+ } else {
|
|
|
+ store.setAvgScore("0");
|
|
|
+ store.setTotalNum("0");
|
|
|
+ }
|
|
|
+
|
|
|
+ if (avgPriceMap.containsKey(String.valueOf(store.getId()))) {
|
|
|
+ store.setAvgPrice(avgPriceMap.get(String.valueOf(store.getId())).get(0).get("avg_price").toString());
|
|
|
+ } else {
|
|
|
+ store.setAvgPrice("0");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 设置最新代金券(只显示一个)
|
|
|
+ if (latestCouponMap.containsKey(store.getId())) {
|
|
|
+ LifeCoupon latestCoupon = latestCouponMap.get(store.getId());
|
|
|
+ LifeCouponVo couponVo = new LifeCouponVo();
|
|
|
+ BeanUtils.copyProperties(latestCoupon, couponVo);
|
|
|
+ // 只设置一个代金券到列表中
|
|
|
+ List<LifeCouponVo> couponList = new ArrayList<>();
|
|
|
+ couponList.add(couponVo);
|
|
|
+ store.setCouponList(couponList);
|
|
|
+ } else {
|
|
|
+ // 如果没有代金券,设置为空列表
|
|
|
+ store.setCouponList(new ArrayList<>());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 创建分页对象
|
|
|
+ IPage<StoreInfoVo> resultPage = new Page<>(pageNum, pageSize);
|
|
|
+ resultPage.setRecords(pagedList);
|
|
|
+ resultPage.setTotal(total);
|
|
|
+ resultPage.setCurrent(pageNum);
|
|
|
+ resultPage.setSize(pageSize);
|
|
|
+ resultPage.setPages((total + pageSize - 1) / pageSize);
|
|
|
+
|
|
|
+ return resultPage;
|
|
|
+ }
|
|
|
+
|
|
|
+ @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);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public List<StoreDictionary> getAllBusinessSection(String businessSection) {
|
|
|
+ // 如果没有传入一级分类参数,返回所有分类
|
|
|
+ if (businessSection == null || businessSection.trim().isEmpty()) {
|
|
|
+ return getAllBusinessSection();
|
|
|
+ }
|
|
|
+
|
|
|
+ // 1. 根据dictId查询一级分类
|
|
|
+ StoreDictionary firstLevelDict = storeDictionaryMapper.selectOne(
|
|
|
+ new LambdaQueryWrapper<StoreDictionary>()
|
|
|
+ .eq(StoreDictionary::getDictId, businessSection.trim())
|
|
|
+ .eq(StoreDictionary::getTypeName, "business_section")
|
|
|
+ .eq(StoreDictionary::getDeleteFlag, 0)
|
|
|
+ );
|
|
|
+
|
|
|
+ if (firstLevelDict == null) {
|
|
|
+ log.warn("未找到一级分类,businessSection={}", businessSection);
|
|
|
+ return new ArrayList<>();
|
|
|
+ }
|
|
|
+
|
|
|
+ // 2. 查询该一级分类下的所有二级分类(business_type)
|
|
|
+ List<StoreDictionary> secondLevelDicts = storeDictionaryMapper.selectList(
|
|
|
+ new LambdaQueryWrapper<StoreDictionary>()
|
|
|
+ .eq(StoreDictionary::getTypeName, "business_type")
|
|
|
+ .eq(StoreDictionary::getParentId, firstLevelDict.getId())
|
|
|
+ .eq(StoreDictionary::getDeleteFlag, 0)
|
|
|
+ .orderByAsc(StoreDictionary::getSortId)
|
|
|
+ );
|
|
|
+
|
|
|
+ if (secondLevelDicts.isEmpty()) {
|
|
|
+ // 如果没有二级分类,只返回一级分类
|
|
|
+ firstLevelDict.setStoreDictionaryList(new ArrayList<>());
|
|
|
+ return Collections.singletonList(firstLevelDict);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 3. 获取所有二级分类的ID
|
|
|
+ List<Integer> secondLevelIds = secondLevelDicts.stream()
|
|
|
+ .map(StoreDictionary::getId)
|
|
|
+ .collect(Collectors.toList());
|
|
|
+
|
|
|
+ // 4. 查询这些二级分类下的所有三级分类(business_classify)
|
|
|
+ List<StoreDictionary> thirdLevelDicts = storeDictionaryMapper.selectList(
|
|
|
+ new LambdaQueryWrapper<StoreDictionary>()
|
|
|
+ .eq(StoreDictionary::getTypeName, "business_classify")
|
|
|
+ .in(StoreDictionary::getParentId, secondLevelIds)
|
|
|
+ .eq(StoreDictionary::getDeleteFlag, 0)
|
|
|
+ .orderByAsc(StoreDictionary::getSortId)
|
|
|
+ );
|
|
|
+
|
|
|
+ // 5. 构建树形结构
|
|
|
+ // 将一级分类、二级分类、三级分类合并
|
|
|
+ List<StoreDictionary> allDicts = new ArrayList<>();
|
|
|
+ allDicts.add(firstLevelDict);
|
|
|
+ allDicts.addAll(secondLevelDicts);
|
|
|
+ allDicts.addAll(thirdLevelDicts);
|
|
|
+
|
|
|
+ // 构建树形结构
|
|
|
+ return buildTreeOptimized(allDicts);
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * web-分页查询店铺信息
|
|
|
+ *
|
|
|
+
|
|
|
+ * @return IPage<StoreInfoVo>
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ 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);
|
|
|
+ //如果查询未过期
|
|
|
+ // 获取当前时刻
|
|
|
+ Date currentDate = new Date();
|
|
|
+ // 获取当前日期和时间
|
|
|
+ Calendar calendar = Calendar.getInstance();
|
|
|
+ // 将时间设置为 0 点
|
|
|
+ calendar.set(Calendar.HOUR_OF_DAY, 0);
|
|
|
+ calendar.set(Calendar.MINUTE, 0);
|
|
|
+ calendar.set(Calendar.SECOND, 0);
|
|
|
+ calendar.set(Calendar.MILLISECOND, 0);
|
|
|
+ // 加上 30 天
|
|
|
+ calendar.add(Calendar.DAY_OF_MONTH, 30);
|
|
|
+ // 如果 expiration_time 为空则不做过期判断;如果不为空则要求大于当前时间
|
|
|
+ queryWrapper.and(w -> w.isNull("a.expiration_time")
|
|
|
+ .or()
|
|
|
+ .gt("a.expiration_time", currentDate));
|
|
|
+
|
|
|
+ // 如果 food_licence_expiration_time 为空则不做过期判断;如果不为空则要求大于当前时间
|
|
|
+ queryWrapper.and(w -> w.isNull("a.food_licence_expiration_time")
|
|
|
+ .or()
|
|
|
+ .gt("a.food_licence_expiration_time", currentDate));
|
|
|
+
|
|
|
+ // 构建一级分类
|
|
|
+ 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();
|
|
|
+ }
|
|
|
+ // 提前查询所有需要的字典数据
|
|
|
+ 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<>();
|
|
|
+
|
|
|
+ // 注意:需要将store_id转换为String类型,与后续containsKey判断保持一致
|
|
|
+ avgScoreMap = storeEvaluationMapper.allStoreAvgScore().stream().collect(Collectors.groupingBy(o -> o.get("store_id").toString()));
|
|
|
+ List<StoreComment> storeComments = storeCommentMapper.selectList(
|
|
|
+ new QueryWrapper<StoreComment>()
|
|
|
+ .eq("business_type", "5")
|
|
|
+ .eq("delete_flag", 0));
|
|
|
+ commentMap = storeComments.stream()
|
|
|
+ .filter(comment -> comment.getStoreId() != null) // 过滤无店铺ID的评论,避免 groupingBy NPE
|
|
|
+ .collect(Collectors.groupingBy(StoreComment::getStoreId));
|
|
|
+
|
|
|
+
|
|
|
+ // 查询入口头图
|
|
|
+ List<Integer> storeIds = storeInfoVoList.stream()
|
|
|
+ .map(StoreInfoVo::getId) // 假设 StoreImg 有 getStoreUrl() 方法
|
|
|
+ .collect(Collectors.toList());
|
|
|
+
|
|
|
+ List<StoreImg> storeImgList = storeImgMapper.selectList(new QueryWrapper<StoreImg>().in("store_id", storeIds).eq("img_type", 1));
|
|
|
+ Map<Integer, List<StoreImg>> storeCollect = storeImgList.stream()
|
|
|
+ .collect(Collectors.groupingBy(StoreImg::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(!CollectionUtils.isEmpty(storeCollect) && storeCollect.containsKey(record.getId())){
|
|
|
+ List<StoreImg> storeImgs = storeCollect.get(record.getId());
|
|
|
+ if(!CollectionUtils.isEmpty(storeImgs)){
|
|
|
+ record.setEntranceImage(storeImgs.get(0).getImgUrl());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ //写经纬度
|
|
|
+ String[] split = record.getStorePosition().split(",");
|
|
|
+ record.setStorePositionLongitude(split[0]);
|
|
|
+ record.setStorePositionLatitude(split[1]);
|
|
|
+ // 格式化距离,移除无意义的小数位
|
|
|
+ if (!StringUtils.isEmpty(record.getDistance3())) {
|
|
|
+ try {
|
|
|
+ BigDecimal distanceValue = new BigDecimal(record.getDistance3());
|
|
|
+ record.setDistance3(distanceValue.stripTrailingZeros().toPlainString());
|
|
|
+ } catch (NumberFormatException ex) {
|
|
|
+ log.warn("店铺距离格式化失败, storeId: {}, distance3: {}", record.getId(), record.getDistance3(), ex);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ //处理一下到期状态
|
|
|
+ Date expirationTime = record.getExpirationTime();
|
|
|
+ if (expirationTime != null) {
|
|
|
+ // 获取当前时间
|
|
|
+ Calendar now = Calendar.getInstance();
|
|
|
+ Date nowCurrentDate = now.getTime();
|
|
|
+ // 计算 30 天后的时间
|
|
|
+ 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();
|
|
|
+ // 将 expirationTime 转换为 LocalDate
|
|
|
+ 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")));
|
|
|
+ } else {
|
|
|
+ record.setAvgScore("0");
|
|
|
+ }
|
|
|
+ // 设置店铺得分,设置店铺人均消费,设置总评论数
|
|
|
+ if (commentMap.containsKey(record.getId())) {
|
|
|
+ record.setTotalNum(String.valueOf(commentMap.get(record.getId()).size()));
|
|
|
+ } else {
|
|
|
+ record.setTotalNum("0");
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ // SQL已经实现了距离过滤和排序,直接返回结果
|
|
|
+ return storeInfoVoList;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public StoreInfoVo getClientStoreDetail(String storeId, String userId, String jingdu, String weidu) {
|
|
|
+ StoreInfoVo result = new StoreInfoVo();
|
|
|
+ StoreInfo storeInfo = storeInfoMapper.selectById(storeId);
|
|
|
+ BeanUtils.copyProperties(storeInfo, result);
|
|
|
+ //将经营板块和种类拆分成集合
|
|
|
+ String businessTypes = storeInfo.getBusinessTypes();
|
|
|
+ if (StringUtils.isNotEmpty(businessTypes)) {
|
|
|
+ String[] split = businessTypes.split(",");
|
|
|
+ List<String> list = Arrays.asList(split);
|
|
|
+ result.setBusinessTypesList(list);
|
|
|
+ }
|
|
|
+ //存入用户账户
|
|
|
+ StoreUser storeUser = storeUserMapper.selectOne(new LambdaQueryWrapper<StoreUser>().eq(StoreUser::getStoreId, storeId));
|
|
|
+ if (storeUser != null) {
|
|
|
+ result.setUserAccount(storeUser.getId().toString());
|
|
|
+ result.setStorePhone(storeUser.getPhone());
|
|
|
+ result.setStoreUserName(storeUser.getName());
|
|
|
+ result.setIdCard(storeUser.getIdCard());
|
|
|
+ }
|
|
|
+// //存入执照图片地址
|
|
|
+// List<StoreImg> storeImgs = storeImgMapper.selectList(new LambdaQueryWrapper<StoreImg>().eq(StoreImg::getStoreId, storeId).eq(StoreImg::getImgType, 14));
|
|
|
+// List<String> storeImgPaths = new ArrayList<>();
|
|
|
+// for (StoreImg storeImg : storeImgs) {
|
|
|
+// storeImgPaths.add(storeImg.getImgUrl());
|
|
|
+// }
|
|
|
+// result.setBusinessLicenseAddress(storeImgPaths);
|
|
|
+// //存入合同图片地址
|
|
|
+// List<StoreImg> storeContractImageImgs = storeImgMapper.selectList(new LambdaQueryWrapper<StoreImg>().eq(StoreImg::getStoreId, storeId).eq(StoreImg::getImgType, 15));
|
|
|
+// List<String> storeContractImagePathImgs = new ArrayList<>();
|
|
|
+// for (StoreImg storeImg : storeContractImageImgs) {
|
|
|
+// storeContractImagePathImgs.add(storeImg.getImgUrl());
|
|
|
+// }
|
|
|
+// result.setContractImageList(storeContractImagePathImgs);
|
|
|
+// //存入续签合同地址
|
|
|
+// List<StoreImg> renewContractImgs = storeImgMapper.selectList(new LambdaQueryWrapper<StoreImg>().eq(StoreImg::getStoreId, storeId).eq(StoreImg::getImgType, 22));
|
|
|
+// List<String> renewContractImagePathImgs = new ArrayList<>();
|
|
|
+// for (StoreImg storeImg : renewContractImgs) {
|
|
|
+// renewContractImagePathImgs.add(storeImg.getImgUrl());
|
|
|
+// }
|
|
|
+// result.setRenewContractImageList(renewContractImagePathImgs);
|
|
|
+// //存入经营许可证通过地址
|
|
|
+// List<StoreImg> foodLicenceImgs = storeImgMapper.selectList(new LambdaQueryWrapper<StoreImg>().eq(StoreImg::getStoreId, storeId).eq(StoreImg::getImgType, 25));
|
|
|
+// List<String> foodLicenceImgsPathImgs = new ArrayList<>();
|
|
|
+// for (StoreImg storeImg : foodLicenceImgs) {
|
|
|
+// foodLicenceImgsPathImgs.add(storeImg.getImgUrl());
|
|
|
+// }
|
|
|
+// result.setFoodLicenceImageList(foodLicenceImgsPathImgs);
|
|
|
+// //存入经营许可证未通过地址
|
|
|
+// List<StoreImg> notPassFoodLicenceImgs = storeImgMapper.selectList(new LambdaQueryWrapper<StoreImg>().eq(StoreImg::getStoreId, storeId).eq(StoreImg::getImgType, 24));
|
|
|
+// List<String> notPassFoodLicenceList = new ArrayList<>();
|
|
|
+// for (StoreImg storeImg : notPassFoodLicenceImgs) {
|
|
|
+// notPassFoodLicenceList.add(storeImg.getImgUrl());
|
|
|
+// }
|
|
|
+// result.setNotPassFoodLicenceImageList(notPassFoodLicenceList);
|
|
|
+ // 存放商家入口图
|
|
|
+ List<StoreImg> storeEntranceImageImgs = storeImgMapper.selectList(new LambdaQueryWrapper<StoreImg>().eq(StoreImg::getStoreId, storeId).eq(StoreImg::getImgType, 1));
|
|
|
+ if (!storeEntranceImageImgs.isEmpty()) {
|
|
|
+ result.setEntranceImage(storeEntranceImageImgs.get(0).getImgUrl());
|
|
|
+ } else {
|
|
|
+ result.setEntranceImage("null");
|
|
|
+ }
|
|
|
+ // 存放商家头像
|
|
|
+ List<StoreImg> storeImgs1 = storeImgMapper.selectList(new LambdaQueryWrapper<StoreImg>().eq(StoreImg::getStoreId, storeId).eq(StoreImg::getImgType, 10));
|
|
|
+ if (!storeImgs1.isEmpty()) {
|
|
|
+ result.setImgUrl(storeImgs1.get(0).getImgUrl());
|
|
|
+ } else {
|
|
|
+ result.setImgUrl("null");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 获取店铺相册
|
|
|
+ List<StoreImg> storeAlbumList = new ArrayList<>();
|
|
|
+ if(storeInfo.getImgMode() != null && storeInfo.getImgMode() == 0){
|
|
|
+ storeAlbumList = storeImgService.getStoreImg(Integer.parseInt(storeId), 20);
|
|
|
+ } else {
|
|
|
+ storeAlbumList = storeImgService.getStoreImg(Integer.parseInt(storeId), 21);
|
|
|
+ }
|
|
|
+
|
|
|
+ if(!CollectionUtils.isEmpty(storeAlbumList)){
|
|
|
+ List<String> storeAlbumUrlList = storeAlbumList.stream().map(StoreImg::getImgUrl) // 假设 StoreImg 有 getStoreUrl() 方法
|
|
|
+ .filter(url -> url != null && !url.trim().isEmpty()) // 过滤空值
|
|
|
+ .collect(Collectors.toList());
|
|
|
+ result.setStoreAlbumUrlList(storeAlbumUrlList);
|
|
|
+ } else {
|
|
|
+ result.setStoreAlbumUrlList(new ArrayList<>());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 设置经纬度
|
|
|
+ result.setStorePositionLongitude(result.getStorePosition().split(",")[0]);
|
|
|
+ result.setStorePositionLatitude(result.getStorePosition().split(",")[1]);
|
|
|
+ // 设置距离
|
|
|
+ if ((jingdu != null && !jingdu.isEmpty()) && (weidu != null && !weidu.isEmpty())) {
|
|
|
+ /*double storeJing = Double.parseDouble(result.getStorePosition().split(",")[0]);
|
|
|
+ double storeWei = Double.parseDouble(result.getStorePosition().split(",")[1]);
|
|
|
+ double storeDistance = DistanceUtil.haversineCalculateDistance(Double.parseDouble(jingdu), Double.parseDouble(weidu), storeJing, storeWei);*/
|
|
|
+
|
|
|
+ Double distance = storeInfoMapper.getStoreDistance(jingdu + "," + weidu,result.getId());
|
|
|
+
|
|
|
+ result.setDistance(distance);
|
|
|
+ }
|
|
|
+ // 计算店铺到最近地铁站的距离
|
|
|
+ JSONObject nearbySubway = gaoDeMapUtil.getNearbySubway(result.getStorePosition().split(",")[0], result.getStorePosition().split(",")[1]);
|
|
|
+ // 地铁名
|
|
|
+ String subWayName = nearbySubway.getString("name");
|
|
|
+ result.setSubwayName(subWayName);
|
|
|
+ // 地铁站经纬度
|
|
|
+ String subWayJing = nearbySubway.getString("location") == null ? null : nearbySubway.getString("location").split(",")[0];
|
|
|
+ String subWayWei = nearbySubway.getString("location") == null ? null : nearbySubway.getString("location").split(",")[1];
|
|
|
+ if ((subWayJing != null && !subWayJing.isEmpty()) && (subWayWei != null && !subWayWei.isEmpty())) {
|
|
|
+ double storeJing = Double.parseDouble(result.getStorePosition().split(",")[0]);
|
|
|
+ double storeWei = Double.parseDouble(result.getStorePosition().split(",")[1]);
|
|
|
+ double storeDistance2 = DistanceUtil.haversineCalculateDistance(Double.parseDouble(subWayJing), Double.parseDouble(subWayWei), storeJing, storeWei);
|
|
|
+ result.setDistance2(storeDistance2);
|
|
|
+ } else {
|
|
|
+ result.setDistance2(0);
|
|
|
+ }
|
|
|
+ // 当前登录用户是否收藏
|
|
|
+ LambdaUpdateWrapper<LifeCollect> shouCangWrapper = new LambdaUpdateWrapper<>();
|
|
|
+ shouCangWrapper.eq(LifeCollect::getUserId, userId).eq(LifeCollect::getStoreId, storeId);
|
|
|
+ List<LifeCollect> shouCangList = lifeCollectMapper.selectList(shouCangWrapper);
|
|
|
+ if (null == shouCangList || shouCangList.isEmpty()) {
|
|
|
+ result.setCollection(0);
|
|
|
+ } else {
|
|
|
+ result.setCollection(1);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 该用户的打卡记录
|
|
|
+ LambdaQueryWrapper<StoreClockIn> clockInWrapper = new LambdaQueryWrapper<>();
|
|
|
+ clockInWrapper.eq(StoreClockIn::getUserId, userId);
|
|
|
+ List<StoreClockIn> clockInList = storeClockInMapper.selectList(clockInWrapper);
|
|
|
+
|
|
|
+ List<StoreClockIn> clockStoreList = clockInList.stream().filter(item -> item.getStoreId() == Integer.parseInt(storeId)).collect(Collectors.toList());
|
|
|
+ // 该用户是否在该店铺打过卡
|
|
|
+ if (!clockStoreList.isEmpty()) {
|
|
|
+ result.setClockInStore(1);
|
|
|
+ } else {
|
|
|
+ result.setClockInStore(0);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 今天在该店铺是否打过卡
|
|
|
+ int today = (int) clockStoreList.stream().filter(item -> item.getCreatedTime().toInstant().atZone(ZoneId.systemDefault()).toLocalDate().equals(LocalDate.now())).count();
|
|
|
+ if (today > 0) {
|
|
|
+ result.setClockInStoreToday(1);
|
|
|
+ } else {
|
|
|
+ result.setClockInStoreToday(0);
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ Map<String, Object> commitCountAndScore = storeCommentService.getCommitCountAndScore(null, 5, Integer.parseInt(storeId), null, null);
|
|
|
+ result.setScore(Double.parseDouble(commitCountAndScore.get("score").toString()));
|
|
|
+ result.setCommitCount(commitCountAndScore.get("commitCount").toString());
|
|
|
+
|
|
|
+
|
|
|
+ // 在该店铺的打卡次数
|
|
|
+ result.setClockInStoreNum(clockStoreList.size());
|
|
|
+
|
|
|
+ // 该用户打卡的所有店铺次数(一个店铺只算一次)
|
|
|
+ int clockInNum = (int) clockInList.stream().map(StoreClockIn::getStoreId).distinct().count();
|
|
|
+ result.setClockInNum(clockInNum);
|
|
|
+
|
|
|
+// // 获取店铺动态列表
|
|
|
+// QueryWrapper<LifeUserDynamics> dynamicsWrapper = new QueryWrapper<>();
|
|
|
+// dynamicsWrapper.eq("phone_id", "store_" + result.getStorePhone()).orderByDesc("lud.created_time");
|
|
|
+// dynamicsWrapper.eq("lud.delete_flag", 0);
|
|
|
+//
|
|
|
+// LambdaQueryWrapper<LifeBlacklist> lambdaQueryWrapper1 = new LambdaQueryWrapper<>();
|
|
|
+// lambdaQueryWrapper1.eq(LifeBlacklist :: getBlockerId, userId);
|
|
|
+// lambdaQueryWrapper1.eq(LifeBlacklist :: getBlockedPhoneId, "store_" + result.getStorePhone());
|
|
|
+// LifeBlacklist blacklist = lifeBlacklistMapper.selectOne(lambdaQueryWrapper1);
|
|
|
+// List<LifeUserDynamicsVo> storeDynamicslist = new ArrayList<>();
|
|
|
+
|
|
|
+// //判断没有拉黑当前门店账户 查出门店动态
|
|
|
+// if(blacklist == null){
|
|
|
+// storeDynamicslist = lifeUserDynamicsMapper.getStoreDynamicslist(userId, "store_" + result.getStorePhone());
|
|
|
+// }
|
|
|
+//
|
|
|
+// List<String> followList = new ArrayList<>();
|
|
|
+// List<String> fansList = new ArrayList<>();
|
|
|
+
|
|
|
+// if (StringUtils.isNotEmpty(userId)) {
|
|
|
+// LifeUser lifeUser = lifeUserMapper.selectById(userId);
|
|
|
+// if (lifeUser != null && StringUtils.isNotEmpty(lifeUser.getUserPhone())) {
|
|
|
+// // 查询我的关注信息,构建关注者ID列表
|
|
|
+// LambdaQueryWrapper<LifeFans> lifeFansWrapper = new LambdaQueryWrapper<>();
|
|
|
+// lifeFansWrapper.eq(LifeFans::getFansId, "user_" + result.getStorePhone());
|
|
|
+// List<LifeFans> lifeFansList = lifeFansMapper.selectList(lifeFansWrapper);
|
|
|
+// if (!CollectionUtils.isEmpty(lifeFansList)) {
|
|
|
+// followList = lifeFansList.stream().map(LifeFans::getFollowedId).collect(Collectors.toList());
|
|
|
+// }
|
|
|
+//
|
|
|
+// // 查询我的粉丝信息,构建粉丝ID列表
|
|
|
+// lifeFansWrapper = new LambdaQueryWrapper<>();
|
|
|
+// lifeFansWrapper.eq(LifeFans::getFollowedId, "user_" + result.getStorePhone());
|
|
|
+// lifeFansList = lifeFansMapper.selectList(lifeFansWrapper);
|
|
|
+// if (!CollectionUtils.isEmpty(lifeFansList)) {
|
|
|
+// fansList = lifeFansList.stream().map(LifeFans::getFansId).collect(Collectors.toList());
|
|
|
+// }
|
|
|
+// }
|
|
|
+// }
|
|
|
+
|
|
|
+// for (LifeUserDynamicsVo vo : storeDynamicslist) {
|
|
|
+// if (followList.contains(vo.getPhoneId())) {
|
|
|
+// vo.setIsFollowThis("1");
|
|
|
+// } else {
|
|
|
+// vo.setIsFollowThis("0");
|
|
|
+// }
|
|
|
+// if (fansList.contains(vo.getPhoneId())) {
|
|
|
+// vo.setIsFollowMe("1");
|
|
|
+// } else {
|
|
|
+// vo.setIsFollowMe("0");
|
|
|
+// }
|
|
|
+// }
|
|
|
+
|
|
|
+// // 返回动态最新的5条
|
|
|
+// List<LifeUserDynamicsVo> storeDynamicslist2 = storeDynamicslist.stream()
|
|
|
+// .limit(5).collect(Collectors.toList());
|
|
|
+// result.setDynamicsList(storeDynamicslist2);
|
|
|
+// //设置动态条数
|
|
|
+// Integer dynamicsNum = storeDynamicslist2.size();
|
|
|
+// result.setDynamicsNum(dynamicsNum);
|
|
|
+//
|
|
|
+// // 获取店铺动态总数
|
|
|
+// result.setTotalDynamicsNum(storeDynamicslist.size());
|
|
|
+
|
|
|
+ //营业时间
|
|
|
+ List<StoreBusinessInfo> storeBusinessInfos = storeBusinessInfoMapper.selectList(new LambdaQueryWrapper<StoreBusinessInfo>().eq(StoreBusinessInfo::getStoreId, storeId).eq(StoreBusinessInfo::getDeleteFlag, 0));
|
|
|
+ if (ObjectUtils.isNotEmpty(storeBusinessInfos)) {
|
|
|
+ result.setStoreBusinessInfo(storeBusinessInfos.get(0));
|
|
|
+ result.setStoreBusinessInfos(storeBusinessInfos);
|
|
|
+ StoreBusinessInfo storeBusinessInfo = result.getStoreBusinessInfos().stream().filter(item -> item.getBusinessType() == 1).findFirst().orElse(null);
|
|
|
+ if (ObjectUtils.isNotEmpty(storeBusinessInfo)) {
|
|
|
+
|
|
|
+ Calendar calendar = Calendar.getInstance(); // 获取Calendar实例
|
|
|
+ int dayOfWeek = calendar.get(Calendar.DAY_OF_WEEK); // 获取星期几,注意Calendar中的DAY_OF_WEEK是从1(代表星期天)开始的
|
|
|
+ String[] days = {"7", "1", "2", "3", "4", "5", "6"};
|
|
|
+ String day = days[dayOfWeek - 1];
|
|
|
+ if (storeBusinessInfo.getBusinessDate().contains(day)) {
|
|
|
+ if (StringUtils.isNotEmpty(storeBusinessInfo.getStartTime()) && StringUtils.isNotEmpty(storeBusinessInfo.getEndTime())) {
|
|
|
+ LocalTime now = LocalTime.now();
|
|
|
+ List<String> startList = Arrays.asList(storeBusinessInfo.getStartTime().split(":"));
|
|
|
+ List<String> endList = Arrays.asList(storeBusinessInfo.getEndTime().split(":"));
|
|
|
+ LocalTime start = LocalTime.of(Integer.parseInt(startList.get(0)), Integer.parseInt(startList.get(1)));
|
|
|
+ LocalTime end = LocalTime.of(Integer.parseInt(endList.get(0)), Integer.parseInt(startList.get(1)));
|
|
|
+ if (now.isAfter(start) && now.isBefore(end)) {
|
|
|
+ result.setYyFlag(1);
|
|
|
+ } else {
|
|
|
+ result.setYyFlag(0);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ result.setYyFlag(0);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ LambdaQueryWrapper<StoreDictionary> storeDictionaryLambdaQueryWrapper = new LambdaQueryWrapper<>();
|
|
|
+ storeDictionaryLambdaQueryWrapper.eq(StoreDictionary::getTypeName, "businessStatus")
|
|
|
+ .eq(StringUtils.isNotEmpty(result.getBusinessStatus().toString()), StoreDictionary::getDictId, result.getBusinessStatus());
|
|
|
+ List<StoreDictionary> storeDictionaries = storeDictionaryMapper.selectList(storeDictionaryLambdaQueryWrapper);
|
|
|
+ if (!storeDictionaries.isEmpty()) {
|
|
|
+ result.setBusinessStatusStr(storeDictionaries.get(0).getDictDetail());
|
|
|
+ }
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public List<LifeCouponVo> getStoreCouponList(String storeId) {
|
|
|
+ // 获取店铺代金券列表
|
|
|
+ LambdaUpdateWrapper<LifeCoupon> quanWrapper = new LambdaUpdateWrapper<>();
|
|
|
+ quanWrapper.eq(LifeCoupon::getStoreId, storeId).eq(LifeCoupon::getStatus, CouponStatusEnum.ONGOING.getCode()).eq(LifeCoupon::getType, 1);
|
|
|
+ List<LifeCoupon> quanList = lifeCouponMapper.selectList(quanWrapper);
|
|
|
+ List<LifeCouponVo> quanVoList = new ArrayList<>();
|
|
|
+ List<String> collect = quanList.stream().map(LifeCoupon::getId).collect(Collectors.toList());
|
|
|
+ // 设置已售数量
|
|
|
+ // 定义需要的订单状态集合
|
|
|
+ Set<Integer> excludeStatuses = new HashSet<>(Arrays.asList(
|
|
|
+ OrderStatusEnum.WAIT_PAY.getStatus(),
|
|
|
+ OrderStatusEnum.WAIT_USE.getStatus(),
|
|
|
+ OrderStatusEnum.USED.getStatus()
|
|
|
+ ));
|
|
|
+ if (!collect.isEmpty()) {
|
|
|
+ List<LifeUserOrderVo> quanCount = lifeUserOrderMapper.getQuanCount(new QueryWrapper<LifeUserOrderVo>()
|
|
|
+ .eq("luo.store_id", storeId)
|
|
|
+ .eq("luo.coupon_type", CouponTypeEnum.COUPON.getCode())
|
|
|
+ .eq("luo.delete_flag", 0)
|
|
|
+ .in("ocm.status", excludeStatuses)
|
|
|
+ .groupBy("ocm.coupon_id"));
|
|
|
+ quanList.forEach(a -> {
|
|
|
+ LifeCouponVo lifeCouponVo = new LifeCouponVo();
|
|
|
+ BeanUtils.copyProperties(a, lifeCouponVo);
|
|
|
+ quanCount.forEach(item -> {
|
|
|
+ if (a.getId().equals(item.getCouponId().toString())) {
|
|
|
+ lifeCouponVo.setCount(item.getCount());
|
|
|
+ }
|
|
|
+ });
|
|
|
+ quanVoList.add(lifeCouponVo);
|
|
|
+ });
|
|
|
+ }
|
|
|
+ return quanVoList;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public StoreInfoVo getNewStoreDetail(String storeId) {
|
|
|
+
|
|
|
+ StoreInfoVo result = new StoreInfoVo();
|
|
|
+ StoreInfo storeInfo = storeInfoMapper.selectById(storeId);
|
|
|
+ BeanUtils.copyProperties(storeInfo, result);
|
|
|
+ //将经营板块和种类拆分成集合
|
|
|
+ String businessTypes = storeInfo.getBusinessTypes();
|
|
|
+ if (StringUtils.isNotEmpty(businessTypes)) {
|
|
|
+ String[] split = businessTypes.split(",");
|
|
|
+ List<String> list = Arrays.asList(split);
|
|
|
+ result.setBusinessTypesList(list);
|
|
|
+ }
|
|
|
+ //存入用户账户
|
|
|
+ StoreUser storeUser = storeUserMapper.selectOne(new LambdaQueryWrapper<StoreUser>().eq(StoreUser::getStoreId, storeId));
|
|
|
+ if (storeUser != null) {
|
|
|
+ result.setUserAccount(storeUser.getId().toString());
|
|
|
+ result.setStorePhone(storeUser.getPhone());
|
|
|
+ result.setStoreUserName(storeUser.getName());
|
|
|
+ result.setIdCard(storeUser.getIdCard());
|
|
|
+ }
|
|
|
+ //存入执照图片地址
|
|
|
+ List<StoreImg> storeImgs = storeImgMapper.selectList(new LambdaQueryWrapper<StoreImg>().eq(StoreImg::getStoreId, storeId).eq(StoreImg::getImgType, 14));
|
|
|
+ List<String> storeImgPaths = new ArrayList<>();
|
|
|
+ for (StoreImg storeImg : storeImgs) {
|
|
|
+ storeImgPaths.add(storeImg.getImgUrl());
|
|
|
+ }
|
|
|
+ result.setBusinessLicenseAddress(storeImgPaths);
|
|
|
+ //存入合同图片地址
|
|
|
+ List<StoreImg> storeContractImageImgs = storeImgMapper.selectList(new LambdaQueryWrapper<StoreImg>().eq(StoreImg::getStoreId, storeId).eq(StoreImg::getImgType, 15));
|
|
|
+ List<String> storeContractImagePathImgs = new ArrayList<>();
|
|
|
+ for (StoreImg storeImg : storeContractImageImgs) {
|
|
|
+ storeContractImagePathImgs.add(storeImg.getImgUrl());
|
|
|
+ }
|
|
|
+ result.setContractImageList(storeContractImagePathImgs);
|
|
|
+ //存入续签合同地址
|
|
|
+ List<StoreImg> renewContractImgs = storeImgMapper.selectList(new LambdaQueryWrapper<StoreImg>().eq(StoreImg::getStoreId, storeId).eq(StoreImg::getImgType, 22));
|
|
|
+ List<String> renewContractImagePathImgs = new ArrayList<>();
|
|
|
+ for (StoreImg storeImg : renewContractImgs) {
|
|
|
+ renewContractImagePathImgs.add(storeImg.getImgUrl());
|
|
|
+ }
|
|
|
+ result.setRenewContractImageList(renewContractImagePathImgs);
|
|
|
+ //存入经营许可证通过地址
|
|
|
+ List<StoreImg> foodLicenceImgs = storeImgMapper.selectList(new LambdaQueryWrapper<StoreImg>().eq(StoreImg::getStoreId, storeId).eq(StoreImg::getImgType, 25));
|
|
|
+ List<String> foodLicenceImgsPathImgs = new ArrayList<>();
|
|
|
+ for (StoreImg storeImg : foodLicenceImgs) {
|
|
|
+ foodLicenceImgsPathImgs.add(storeImg.getImgUrl());
|
|
|
+ }
|
|
|
+ result.setFoodLicenceImageList(foodLicenceImgsPathImgs);
|
|
|
+ //存入经营许可证未通过地址
|
|
|
+ List<StoreImg> notPassFoodLicenceImgs = storeImgMapper.selectList(new LambdaQueryWrapper<StoreImg>().eq(StoreImg::getStoreId, storeId).eq(StoreImg::getImgType, 24));
|
|
|
+ List<String> notPassFoodLicenceList = new ArrayList<>();
|
|
|
+ for (StoreImg storeImg : notPassFoodLicenceImgs) {
|
|
|
+ notPassFoodLicenceList.add(storeImg.getImgUrl());
|
|
|
+ }
|
|
|
+ result.setNotPassFoodLicenceImageList(notPassFoodLicenceList);
|
|
|
+ // 存放商家入口图
|
|
|
+ List<StoreImg> storeEntranceImageImgs = storeImgMapper.selectList(new LambdaQueryWrapper<StoreImg>().eq(StoreImg::getStoreId, storeId).eq(StoreImg::getImgType, 1));
|
|
|
+ if (!storeEntranceImageImgs.isEmpty()) {
|
|
|
+ result.setEntranceImage(storeEntranceImageImgs.get(0).getImgUrl());
|
|
|
+ } else {
|
|
|
+ result.setEntranceImage("null");
|
|
|
+ }
|
|
|
+ // 存放商家头像
|
|
|
+ List<StoreImg> storeImgs1 = storeImgMapper.selectList(new LambdaQueryWrapper<StoreImg>().eq(StoreImg::getStoreId, storeId).eq(StoreImg::getImgType, 10));
|
|
|
+ if (!storeImgs1.isEmpty()) {
|
|
|
+ result.setImgUrl(storeImgs1.get(0).getImgUrl());
|
|
|
+ } else {
|
|
|
+ result.setImgUrl("null");
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ // 店铺平均分
|
|
|
+ /*Map<Object, List<Map<String, Object>>> avgScoreMap = storeEvaluationMapper.allStoreAvgScore().stream().collect(Collectors.groupingBy(o -> o.get("store_id")));
|
|
|
+ if (avgScoreMap.containsKey(String.valueOf(result.getId()))) {
|
|
|
+ result.setScore(Double.parseDouble(avgScoreMap.get(String.valueOf(result.getId())).get(0).get("avg_score").toString()));
|
|
|
+ }*/
|
|
|
+
|
|
|
+// Map<String, Object> commitCountAndScore = storeCommentService.getCommitCountAndScore(null, 5, Integer.parseInt(storeId), null, null);
|
|
|
+// result.setScore(Double.parseDouble(commitCountAndScore.get("score").toString()));
|
|
|
+// result.setCommitCount(commitCountAndScore.get("commitCount").toString());
|
|
|
+//
|
|
|
+
|
|
|
+
|
|
|
+ //营业时间
|
|
|
+ List<StoreBusinessInfo> storeBusinessInfos = storeBusinessInfoMapper.selectList(new LambdaQueryWrapper<StoreBusinessInfo>().eq(StoreBusinessInfo::getStoreId, storeId).eq(StoreBusinessInfo::getDeleteFlag, 0));
|
|
|
+ if (ObjectUtils.isNotEmpty(storeBusinessInfos)) {
|
|
|
+ result.setStoreBusinessInfo(storeBusinessInfos.get(0));
|
|
|
+ result.setStoreBusinessInfos(storeBusinessInfos);
|
|
|
+ //StoreBusinessInfo storeBusinessInfo = result.getStoreBusinessInfo();
|
|
|
+ StoreBusinessInfo storeBusinessInfo = result.getStoreBusinessInfos().stream().filter(item -> item.getBusinessType() == 1).findFirst().orElse(null);
|
|
|
+ if (ObjectUtils.isNotEmpty(storeBusinessInfo)) {
|
|
|
+
|
|
|
+ Calendar calendar = Calendar.getInstance(); // 获取Calendar实例
|
|
|
+ int dayOfWeek = calendar.get(Calendar.DAY_OF_WEEK); // 获取星期几,注意Calendar中的DAY_OF_WEEK是从1(代表星期天)开始的
|
|
|
+ String[] days = {"7", "1", "2", "3", "4", "5", "6"};
|
|
|
+ String day = days[dayOfWeek - 1];
|
|
|
+ if (storeBusinessInfo.getBusinessDate().contains(day)) {
|
|
|
+ if (StringUtils.isNotEmpty(storeBusinessInfo.getStartTime()) && StringUtils.isNotEmpty(storeBusinessInfo.getEndTime())) {
|
|
|
+ LocalTime now = LocalTime.now();
|
|
|
+ List<String> startList = Arrays.asList(storeBusinessInfo.getStartTime().split(":"));
|
|
|
+ List<String> endList = Arrays.asList(storeBusinessInfo.getEndTime().split(":"));
|
|
|
+ LocalTime start = LocalTime.of(Integer.parseInt(startList.get(0)), Integer.parseInt(startList.get(1)));
|
|
|
+ LocalTime end = LocalTime.of(Integer.parseInt(endList.get(0)), Integer.parseInt(startList.get(1)));
|
|
|
+ if (now.isAfter(start) && now.isBefore(end)) {
|
|
|
+ result.setYyFlag(1);
|
|
|
+ } else {
|
|
|
+ result.setYyFlag(0);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ result.setYyFlag(0);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ LambdaQueryWrapper<StoreDictionary> storeDictionaryLambdaQueryWrapper = new LambdaQueryWrapper<>();
|
|
|
+ storeDictionaryLambdaQueryWrapper.eq(StoreDictionary::getTypeName, "businessStatus")
|
|
|
+ .eq(StringUtils.isNotEmpty(result.getBusinessStatus().toString()), StoreDictionary::getDictId, result.getBusinessStatus());
|
|
|
+ List<StoreDictionary> storeDictionaries = storeDictionaryMapper.selectList(storeDictionaryLambdaQueryWrapper);
|
|
|
+ if (!storeDictionaries.isEmpty()) {
|
|
|
+ result.setBusinessStatusStr(storeDictionaries.get(0).getDictDetail());
|
|
|
+ }
|
|
|
+ // TODO 之后修改********** 正常OcrType由前端传存储ocr表要加新字段。传参要由前端传。
|
|
|
+ // 查询并设置各类证件OCR信息
|
|
|
+ result.setJyxkz(convertOcrResultToJson(storeUser.getId(), "BUSINESS_LICENSE", "营业执照", true));
|
|
|
+ result.setIdcardFace(convertOcrResultToJson(storeUser.getId(), "ID_CARD", "face", true));
|
|
|
+ result.setIdcardBack(convertOcrResultToJson(storeUser.getId(), "ID_CARD", "back", true));
|
|
|
+ result.setFoodLicence(convertOcrResultToJson(storeUser.getId(), "FOOD_MANAGE_LICENSE", null, true));
|
|
|
+ result.setEntertainmentLicence(convertOcrResultToJson(storeUser.getId(), "BUSINESS_LICENSE", "娱乐", false));
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public StoreInfoVo editNewStoreInfo(StoreInfoDto storeInfoDto) {
|
|
|
+
|
|
|
+ //获取经营板块id
|
|
|
+ Integer businessSection = storeInfoDto.getBusinessSection();
|
|
|
+ //查询经营板块名称
|
|
|
+ StoreDictionary businessSectionName = storeDictionaryMapper.selectOne(new LambdaQueryWrapper<StoreDictionary>().eq(StoreDictionary::getDictId, businessSection).eq(StoreDictionary::getTypeName, "business_section"));
|
|
|
+ //查询经营种类
|
|
|
+ List<String> businessTypes = storeInfoDto.getBusinessTypes();
|
|
|
+ List<String> businessTypeNames = new ArrayList<>();
|
|
|
+ //获取经营种类名称
|
|
|
+ for (String businessType : businessTypes) {
|
|
|
+ StoreDictionary storeDictionary = storeDictionaryMapper.selectOne(new LambdaQueryWrapper<StoreDictionary>().eq(StoreDictionary::getDictId, businessType).eq(StoreDictionary::getParentId, businessSectionName.getId()));
|
|
|
+ businessTypeNames.add(storeDictionary.getDictDetail());
|
|
|
+ }
|
|
|
+
|
|
|
+ StoreInfoVo result = new StoreInfoVo();
|
|
|
+ StoreInfo storeInfo = new StoreInfo();
|
|
|
+ BeanUtils.copyProperties(storeInfoDto, storeInfo);
|
|
|
+// List<String> storeTypeList = storeInfoDto.getStoreTypeList();
|
|
|
+// String storeType = String.join(",", storeTypeList);
|
|
|
+ //存入营运类型
|
|
|
+// storeInfo.setStoreType(storeType);
|
|
|
+
|
|
|
+ //板块及类型
|
|
|
+ storeInfo.setBusinessSection(businessSection);
|
|
|
+ storeInfo.setBusinessSectionName(businessSectionName.getDictDetail());
|
|
|
+ storeInfo.setBusinessTypes(String.join(",", businessTypes));
|
|
|
+ storeInfo.setBusinessTypesName(String.join(",", businessTypeNames));
|
|
|
+
|
|
|
+ //处理分类信息
|
|
|
+ List<String> businessClassify = storeInfoDto.getBusinessClassify();
|
|
|
+ if (!CollectionUtils.isEmpty(businessClassify)) {
|
|
|
+ List<String> businessClassifyNames = new ArrayList<>();
|
|
|
+ //批量查询分类名称
|
|
|
+ List<StoreDictionary> classifyDicts = storeDictionaryMapper.selectList(
|
|
|
+ new LambdaQueryWrapper<StoreDictionary>()
|
|
|
+ .in(StoreDictionary::getDictId, businessClassify)
|
|
|
+ .eq(StoreDictionary::getTypeName, "business_classify")
|
|
|
+ .eq(StoreDictionary::getDeleteFlag, 0)
|
|
|
+ );
|
|
|
+ //转为Map方便快速获取
|
|
|
+ Map<String, StoreDictionary> classifyDictMap = classifyDicts.stream()
|
|
|
+ .collect(Collectors.toMap(
|
|
|
+ dict -> dict.getDictId().toString(),
|
|
|
+ Function.identity(),
|
|
|
+ (existing, replacement) -> existing
|
|
|
+ ));
|
|
|
+ //提取分类名称
|
|
|
+ for (String classifyId : businessClassify) {
|
|
|
+ StoreDictionary dict = classifyDictMap.get(classifyId);
|
|
|
+ if (Objects.nonNull(dict)) {
|
|
|
+ businessClassifyNames.add(dict.getDictDetail());
|
|
|
+ } else {
|
|
|
+ log.warn("无效的分类id:" + classifyId);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ storeInfo.setBusinessClassify(String.join(",", businessClassify));
|
|
|
+ storeInfo.setBusinessClassifyName(String.join(",", businessClassifyNames));
|
|
|
+ }
|
|
|
+
|
|
|
+ storeInfoMapper.updateById(storeInfo);
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
}
|