|
@@ -12,6 +12,7 @@ import com.baomidou.mybatisplus.core.toolkit.StringUtils;
|
|
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
|
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
|
|
import lombok.RequiredArgsConstructor;
|
|
import lombok.RequiredArgsConstructor;
|
|
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
import org.springframework.beans.BeanUtils;
|
|
import org.springframework.beans.BeanUtils;
|
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
|
import org.springframework.beans.factory.annotation.Value;
|
|
import org.springframework.beans.factory.annotation.Value;
|
|
@@ -59,6 +60,7 @@ import java.text.SimpleDateFormat;
|
|
|
import java.time.*;
|
|
import java.time.*;
|
|
|
import java.time.format.DateTimeFormatter;
|
|
import java.time.format.DateTimeFormatter;
|
|
|
import java.time.format.DateTimeFormatterBuilder;
|
|
import java.time.format.DateTimeFormatterBuilder;
|
|
|
|
|
+import java.time.format.SignStyle;
|
|
|
import java.time.temporal.ChronoField;
|
|
import java.time.temporal.ChronoField;
|
|
|
import java.time.temporal.ChronoUnit;
|
|
import java.time.temporal.ChronoUnit;
|
|
|
import java.util.*;
|
|
import java.util.*;
|
|
@@ -71,6 +73,7 @@ import java.util.stream.Collectors;
|
|
|
* @author ssk
|
|
* @author ssk
|
|
|
* @since 2024-12-05
|
|
* @since 2024-12-05
|
|
|
*/
|
|
*/
|
|
|
|
|
+@Slf4j
|
|
|
@Service
|
|
@Service
|
|
|
@RequiredArgsConstructor
|
|
@RequiredArgsConstructor
|
|
|
@Transactional
|
|
@Transactional
|
|
@@ -1550,7 +1553,7 @@ public class StoreInfoServiceImpl extends ServiceImpl<StoreInfoMapper, StoreInfo
|
|
|
List<String> storeIds = Arrays.asList(storeId.split(","));
|
|
List<String> storeIds = Arrays.asList(storeId.split(","));
|
|
|
if (StringUtils.isNotEmpty(startTime) && StringUtils.isNotEmpty(endTime)) {
|
|
if (StringUtils.isNotEmpty(startTime) && StringUtils.isNotEmpty(endTime)) {
|
|
|
List<StoreBusinessInfo> storeBusinessInfos = storeBusinessInfoMapper.selectList(new LambdaQueryWrapper<StoreBusinessInfo>().in(StoreBusinessInfo::getStoreId, storeIds));
|
|
List<StoreBusinessInfo> storeBusinessInfos = storeBusinessInfoMapper.selectList(new LambdaQueryWrapper<StoreBusinessInfo>().in(StoreBusinessInfo::getStoreId, storeIds));
|
|
|
- DateTimeFormatter formatter = new DateTimeFormatterBuilder().appendValue(ChronoField.HOUR_OF_DAY, 1, 2, java.time.format.SignStyle.NOT_NEGATIVE).appendLiteral(':').appendValue(ChronoField.MINUTE_OF_HOUR, 2).toFormatter();
|
|
|
|
|
|
|
+ DateTimeFormatter formatter = new DateTimeFormatterBuilder().appendValue(ChronoField.HOUR_OF_DAY, 1, 2, SignStyle.NOT_NEGATIVE).appendLiteral(':').appendValue(ChronoField.MINUTE_OF_HOUR, 2).toFormatter();
|
|
|
List<StoreBusinessInfo> list = storeBusinessInfos.stream().filter(item -> {
|
|
List<StoreBusinessInfo> list = storeBusinessInfos.stream().filter(item -> {
|
|
|
// 商家开门时间
|
|
// 商家开门时间
|
|
|
LocalTime timeStart = LocalTime.parse(item.getEndTime(), formatter);
|
|
LocalTime timeStart = LocalTime.parse(item.getEndTime(), formatter);
|
|
@@ -1630,7 +1633,7 @@ public class StoreInfoServiceImpl extends ServiceImpl<StoreInfoMapper, StoreInfo
|
|
|
List<String> storeIds = Arrays.asList(storeId.split(","));
|
|
List<String> storeIds = Arrays.asList(storeId.split(","));
|
|
|
if (StringUtils.isNotEmpty(startTime) && StringUtils.isNotEmpty(endTime)) {
|
|
if (StringUtils.isNotEmpty(startTime) && StringUtils.isNotEmpty(endTime)) {
|
|
|
List<StoreBusinessInfo> storeBusinessInfos = storeBusinessInfoMapper.selectList(new LambdaQueryWrapper<StoreBusinessInfo>().in(StoreBusinessInfo::getStoreId, storeIds));
|
|
List<StoreBusinessInfo> storeBusinessInfos = storeBusinessInfoMapper.selectList(new LambdaQueryWrapper<StoreBusinessInfo>().in(StoreBusinessInfo::getStoreId, storeIds));
|
|
|
- DateTimeFormatter formatter = new DateTimeFormatterBuilder().appendValue(ChronoField.HOUR_OF_DAY, 1, 2, java.time.format.SignStyle.NOT_NEGATIVE).appendLiteral(':').appendValue(ChronoField.MINUTE_OF_HOUR, 2).toFormatter();
|
|
|
|
|
|
|
+ DateTimeFormatter formatter = new DateTimeFormatterBuilder().appendValue(ChronoField.HOUR_OF_DAY, 1, 2, SignStyle.NOT_NEGATIVE).appendLiteral(':').appendValue(ChronoField.MINUTE_OF_HOUR, 2).toFormatter();
|
|
|
List<StoreBusinessInfo> list = storeBusinessInfos.stream().filter(item -> {
|
|
List<StoreBusinessInfo> list = storeBusinessInfos.stream().filter(item -> {
|
|
|
// 商家开门时间
|
|
// 商家开门时间
|
|
|
LocalTime timeStart = LocalTime.parse(item.getEndTime(), formatter);
|
|
LocalTime timeStart = LocalTime.parse(item.getEndTime(), formatter);
|
|
@@ -2460,4 +2463,145 @@ public class StoreInfoServiceImpl extends ServiceImpl<StoreInfoMapper, StoreInfo
|
|
|
}
|
|
}
|
|
|
return result;
|
|
return result;
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * web-分页查询店铺信息
|
|
|
|
|
+ *
|
|
|
|
|
+
|
|
|
|
|
+ * @return IPage<StoreInfoVo>
|
|
|
|
|
+ */
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public List<StoreInfoVo> getMoreRecommendedStores(Double lon , Double lat, int subcategory, int threeLevelClassification) {
|
|
|
|
|
+ QueryWrapper<StoreInfoVo> queryWrapper = new QueryWrapper<>();
|
|
|
|
|
+ queryWrapper.eq("a.delete_flag", 0).eq("b.delete_flag", 0).orderByDesc("a.created_time");
|
|
|
|
|
+ //如果查询未过期
|
|
|
|
|
+ // 获取当前时刻
|
|
|
|
|
+ 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);
|
|
|
|
|
+
|
|
|
|
|
+ // 距离选择(一千米)
|
|
|
|
|
+ int distance = 5000;
|
|
|
|
|
+
|
|
|
|
|
+ List<StoreInfoVo> storeInfoVoList = storeInfoMapper.getMoreRecommendedStores(queryWrapper);
|
|
|
|
|
+ if (!storeInfoVoList.isEmpty()) {
|
|
|
|
|
+ // 提前查询所有需要的字典数据
|
|
|
|
|
+ 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<Object, List<Map<String, Object>>> avgScoreMap = new HashMap<>();
|
|
|
|
|
+ Map<Integer, List<StoreComment>> commentMap = new HashMap<>();
|
|
|
|
|
+
|
|
|
|
|
+ // 如果获取美食列表进行以下前置操作
|
|
|
|
|
+ List<Integer> storeIds = storeInfoVoList.stream().map(StoreInfoVo::getId).collect(Collectors.toList());
|
|
|
|
|
+ LambdaUpdateWrapper<LifeCoupon> quanWrapper = new LambdaUpdateWrapper<>();
|
|
|
|
|
+ quanWrapper.in(LifeCoupon::getStoreId, storeIds).eq(LifeCoupon::getStatus, 1).orderByDesc(LifeCoupon::getCreatedTime);
|
|
|
|
|
+
|
|
|
|
|
+ avgScoreMap = storeEvaluationMapper.allStoreAvgScore().stream().collect(Collectors.groupingBy(o -> o.get("store_id")));
|
|
|
|
|
+ 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));
|
|
|
|
|
+ }
|
|
|
|
|
+ //写经纬度
|
|
|
|
|
+ String[] split = record.getStorePosition().split(",");
|
|
|
|
|
+ record.setStorePositionLongitude(split[0]);
|
|
|
|
|
+ record.setStorePositionLatitude(split[1]);
|
|
|
|
|
+ //处理一下到期状态
|
|
|
|
|
+ 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")));
|
|
|
|
|
+ }
|
|
|
|
|
+ // 设置店铺得分,设置店铺人均消费,设置总评论数
|
|
|
|
|
+ if (commentMap.containsKey(record.getId())) {
|
|
|
|
|
+ record.setTotalNum(String.valueOf(commentMap.get(record.getId()).size()));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ List<StoreInfoVo> filteredList = new ArrayList<>();
|
|
|
|
|
+ // 根据距离筛选店铺列表
|
|
|
|
|
+ if (lon != null && lat != null) {
|
|
|
|
|
+ filteredList = storeInfoVoList.stream()
|
|
|
|
|
+ .filter(store -> {
|
|
|
|
|
+ // 检查店铺坐标是否存在
|
|
|
|
|
+ if (StringUtils.isEmpty(store.getStorePosition())) {
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ try {
|
|
|
|
|
+ // 解析店铺坐标(格式:经度,纬度)
|
|
|
|
|
+ String[] positionArray = store.getStorePosition().split(",");
|
|
|
|
|
+ if (positionArray.length != 2) {
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ double storeLon = Double.parseDouble(positionArray[0].trim());
|
|
|
|
|
+ double storeLat = Double.parseDouble(positionArray[1].trim());
|
|
|
|
|
+
|
|
|
|
|
+ // 计算距离(单位:千米)
|
|
|
|
|
+ double calculatedDistance = DistanceUtil.haversineCalculateDistance(lon, lat, storeLon, storeLat);
|
|
|
|
|
+
|
|
|
|
|
+ // 将距离转换为米进行比较(distance 单位是米)
|
|
|
|
|
+ double distanceInMeters = calculatedDistance * 1000;
|
|
|
|
|
+
|
|
|
|
|
+ // 筛选距离范围内的店铺
|
|
|
|
|
+ return distanceInMeters <= distance;
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ // 计算距离失败时,记录日志并过滤掉该店铺
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+ .collect(Collectors.toList());
|
|
|
|
|
+
|
|
|
|
|
+ // 记录距离筛选结果
|
|
|
|
|
+ if (log.isInfoEnabled()) {
|
|
|
|
|
+ log.info("距离筛选完成,原始店铺数量:" + storeInfoVoList.size() + ",筛选后店铺数量:" + filteredList.size() + ",筛选距离:" + distance + "米");
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ return filteredList;
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|