Browse Source

Merge branch 'release_20260123' into release_buried-point_ph

lutong 2 months ago
parent
commit
7dcec93367

+ 297 - 0
alien-store/doc/埋点实现代码清单.md

@@ -0,0 +1,297 @@
+# 埋点实现代码清单
+
+## 一、已创建的文件
+
+### 1. 实体类(Entity)
+- ✅ `alien-entity/src/main/java/shop/alien/entity/store/StoreTrackEvent.java` - 埋点事件实体类
+- ✅ `alien-entity/src/main/java/shop/alien/entity/store/StoreTrackStatistics.java` - 埋点统计数据实体类
+
+### 2. 注解(Annotation)
+- ✅ `alien-store/src/main/java/shop/alien/store/annotation/TrackEvent.java` - 埋点注解
+
+### 3. 服务接口(Service Interface)
+- ✅ `alien-store/src/main/java/shop/alien/store/service/TrackEventService.java` - 埋点事件服务接口
+
+### 4. Controller
+- ✅ `alien-store/src/main/java/shop/alien/store/controller/TrackEventController.java` - 埋点上报接口
+
+### 5. 文档
+- ✅ `alien-store/doc/埋点需求完整方案.md` - 完整方案文档
+
+## 二、待创建的文件(核心代码)
+
+### 1. AOP切面(重要)
+**文件路径**: `alien-store/src/main/java/shop/alien/store/aspect/TrackEventAspect.java`
+
+**功能**: 拦截标注了`@TrackEvent`注解的方法,自动收集埋点数据并写入Redis List
+
+**关键代码要点**:
+- 使用`@Around`环绕通知
+- 解析SpEL表达式获取`storeId`、`userId`等参数
+- 调用`BaseRedisService.setListRight()`写入Redis List
+- 使用`@Order`注解设置切面执行顺序
+
+### 2. Service实现类(重要)
+**文件路径**: `alien-store/src/main/java/shop/alien/store/service/impl/TrackEventServiceImpl.java`
+
+**功能**: 
+- 实现`saveTrackEvent()`方法,将埋点数据写入Redis List
+- 实现`batchSaveTrackEvents()`方法,批量保存到数据库
+- 实现`getBusinessData()`方法,统计查询经营数据
+- 实现`compareBusinessData()`方法,对比数据
+- 实现`calculateAndSaveStatistics()`方法,计算统计数据
+- 实现`getPriceRankingData()`方法,获取价目表排名
+
+### 3. 数据消费服务(重要)
+**文件路径**: `alien-store/src/main/java/shop/alien/store/service/TrackEventConsumer.java`
+
+**功能**: 定时任务,从Redis List批量消费数据并写入数据库
+
+**关键代码要点**:
+- 使用`@Scheduled(cron = "0/10 * * * * ?")`每10秒执行一次
+- 使用分布式锁防止多实例重复消费
+- 每次从Redis List取出100条数据
+- 批量保存到数据库
+
+### 4. Mapper接口
+**文件路径**: `alien-store/src/main/java/shop/alien/mapper/StoreTrackEventMapper.java`
+**文件路径**: `alien-store/src/main/java/shop/alien/mapper/StoreTrackStatisticsMapper.java`
+
+### 5. 统计查询Controller
+**文件路径**: `alien-store/src/main/java/shop/alien/store/controller/BusinessDataController.java`
+
+**接口列表**:
+- `GET /business/data` - 查询经营数据
+- `GET /business/data/compare` - 数据对比
+- `GET /business/data/history` - 历史数据查询
+
+### 6. AI推荐Controller
+**文件路径**: `alien-store/src/main/java/shop/alien/store/controller/AIRecoveryController.java`
+
+**接口列表**:
+- `GET /business/ai/recommendation` - 获取AI推荐
+
+## 三、关键代码示例
+
+### 3.1 AOP切面核心代码
+
+```java
+@Slf4j
+@Aspect
+@Component
+@Order(2)
+@RequiredArgsConstructor
+public class TrackEventAspect {
+
+    private final BaseRedisService baseRedisService;
+    private final SpelExpressionParser spelExpressionParser = new SpelExpressionParser();
+    private final DefaultParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();
+    private static final String REDIS_QUEUE_KEY = "track:event:queue";
+
+    @Around("@annotation(trackEvent)")
+    public Object around(ProceedingJoinPoint joinPoint, TrackEvent trackEvent) throws Throwable {
+        Object result = joinPoint.proceed();
+        
+        // 构建埋点事件对象
+        StoreTrackEvent event = buildTrackEvent(joinPoint, trackEvent);
+        
+        // 异步写入Redis List
+        if (trackEvent.async()) {
+            baseRedisService.setListRight(REDIS_QUEUE_KEY, JSON.toJSONString(event));
+        } else {
+            // 同步写入
+            trackEventService.batchSaveTrackEvents(Collections.singletonList(event));
+        }
+        
+        return result;
+    }
+    
+    private StoreTrackEvent buildTrackEvent(ProceedingJoinPoint joinPoint, TrackEvent annotation) {
+        // 解析SpEL表达式获取参数值
+        // 获取用户ID、店铺ID等
+        // 设置IP、User-Agent等
+        // ...
+    }
+}
+```
+
+### 3.2 Redis List消费核心代码
+
+```java
+@Component
+@RequiredArgsConstructor
+@Slf4j
+public class TrackEventConsumer {
+
+    private final BaseRedisService baseRedisService;
+    private final TrackEventService trackEventService;
+    private static final String REDIS_QUEUE_KEY = "track:event:queue";
+    private static final String CONSUMER_LOCK_KEY = "track:event:consumer:lock";
+    private static final int BATCH_SIZE = 100;
+
+    @Scheduled(cron = "0/10 * * * * ?") // 每10秒执行一次
+    public void consumeTrackEvents() {
+        // 获取分布式锁
+        String lockId = baseRedisService.lock(CONSUMER_LOCK_KEY, 5000, 1000);
+        if (lockId == null) {
+            log.debug("获取消费锁失败,跳过本次消费");
+            return;
+        }
+
+        try {
+            // 批量从Redis List取出数据
+            List<String> eventList = baseRedisService.popBatchFromList(REDIS_QUEUE_KEY);
+            
+            if (eventList == null || eventList.isEmpty()) {
+                return;
+            }
+
+            // 转换为实体对象
+            List<StoreTrackEvent> events = eventList.stream()
+                    .map(json -> JSON.parseObject(json, StoreTrackEvent.class))
+                    .collect(Collectors.toList());
+
+            // 批量保存到数据库
+            trackEventService.batchSaveTrackEvents(events);
+            
+            log.info("成功消费{}条埋点数据", events.size());
+        } catch (Exception e) {
+            log.error("消费埋点数据失败", e);
+        } finally {
+            // 释放锁
+            baseRedisService.unlock(CONSUMER_LOCK_KEY, lockId);
+        }
+    }
+}
+```
+
+### 3.3 统计数据查询核心代码
+
+```java
+@Override
+public Map<String, Object> getBusinessData(Integer storeId, Date startDate, Date endDate, String category) {
+    Map<String, Object> result = new HashMap<>();
+    
+    if (category == null || category.equals("TRAFFIC")) {
+        // 流量数据统计
+        Map<String, Object> trafficData = new HashMap<>();
+        // 查询浏览量、访客数等
+        result.put("trafficData", trafficData);
+    }
+    
+    if (category == null || category.equals("INTERACTION")) {
+        // 互动数据统计
+        Map<String, Object> interactionData = new HashMap<>();
+        // 查询收藏、分享等
+        result.put("interactionData", interactionData);
+    }
+    
+    // ... 其他分类数据
+    
+    return result;
+}
+```
+
+## 四、数据库表SQL
+
+### 4.1 埋点事件表
+
+```sql
+CREATE TABLE `store_track_event` (
+  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
+  `event_type` varchar(50) NOT NULL COMMENT '事件类型',
+  `event_category` varchar(50) NOT NULL COMMENT '事件分类',
+  `user_id` int(11) DEFAULT NULL COMMENT '用户ID',
+  `store_id` int(11) DEFAULT NULL COMMENT '店铺ID',
+  `target_id` int(11) DEFAULT NULL COMMENT '目标对象ID',
+  `target_type` varchar(50) DEFAULT NULL COMMENT '目标对象类型',
+  `event_data` text COMMENT '事件附加数据(JSON格式)',
+  `amount` decimal(10,2) DEFAULT NULL COMMENT '金额',
+  `duration` bigint(20) DEFAULT NULL COMMENT '时长(毫秒)',
+  `ip_address` varchar(50) DEFAULT NULL COMMENT 'IP地址',
+  `user_agent` varchar(500) DEFAULT NULL COMMENT '用户代理',
+  `device_type` varchar(20) DEFAULT NULL COMMENT '设备类型',
+  `app_version` varchar(20) DEFAULT NULL COMMENT 'APP版本号',
+  `event_time` datetime NOT NULL COMMENT '事件发生时间',
+  `created_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+  `delete_flag` int(1) NOT NULL DEFAULT '0' COMMENT '删除标记',
+  PRIMARY KEY (`id`),
+  KEY `idx_store_id` (`store_id`),
+  KEY `idx_user_id` (`user_id`),
+  KEY `idx_event_type` (`event_type`),
+  KEY `idx_event_category` (`event_category`),
+  KEY `idx_event_time` (`event_time`),
+  KEY `idx_store_event_time` (`store_id`,`event_time`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='埋点事件表';
+```
+
+### 4.2 埋点统计表
+
+```sql
+CREATE TABLE `store_track_statistics` (
+  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
+  `store_id` int(11) NOT NULL COMMENT '店铺ID',
+  `stat_date` date NOT NULL COMMENT '统计日期',
+  `stat_type` varchar(50) NOT NULL COMMENT '统计类型',
+  `traffic_data` text COMMENT '流量数据(JSON格式)',
+  `interaction_data` text COMMENT '互动数据(JSON格式)',
+  `coupon_data` text COMMENT '优惠券数据(JSON格式)',
+  `voucher_data` text COMMENT '代金券数据(JSON格式)',
+  `service_data` text COMMENT '服务质量数据(JSON格式)',
+  `price_ranking_data` text COMMENT '价目表排名数据(JSON格式)',
+  `created_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+  `updated_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
+  PRIMARY KEY (`id`),
+  UNIQUE KEY `uk_store_date_type` (`store_id`,`stat_date`,`stat_type`),
+  KEY `idx_store_id` (`store_id`),
+  KEY `idx_stat_date` (`stat_date`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='埋点统计表';
+```
+
+## 五、前端调用示例
+
+### 5.1 上报埋点事件
+
+```javascript
+// 上报浏览事件
+axios.post('/track/event', {
+  eventType: 'VIEW',
+  eventCategory: 'TRAFFIC',
+  storeId: 1001,
+  targetType: 'STORE',
+  duration: 3000
+});
+```
+
+### 5.2 查询经营数据
+
+```javascript
+axios.get('/business/data', {
+  params: {
+    storeId: 1001,
+    startDate: '2026-01-08',
+    endDate: '2026-01-14'
+  }
+});
+```
+
+## 六、后续开发步骤
+
+1. ✅ 创建实体类和注解(已完成)
+2. ⬜ 实现AOP切面(TrackEventAspect)
+3. ⬜ 实现Service实现类(TrackEventServiceImpl)
+4. ⬜ 实现数据消费服务(TrackEventConsumer)
+5. ⬜ 创建Mapper接口
+6. ⬜ 实现统计查询Controller(BusinessDataController)
+7. ⬜ 实现AI推荐Controller(AIRecoveryController)
+8. ⬜ 执行数据库建表SQL
+9. ⬜ 编写单元测试
+10. ⬜ 联调测试
+
+## 七、注意事项
+
+1. Redis List需要设置最大长度,防止内存溢出
+2. 消费服务需要异常处理和重试机制
+3. 统计数据建议使用定时任务预计算
+4. AI推荐接口需要缓存,避免频繁调用AI服务
+5. 埋点数据量大,需要定期清理历史数据

+ 0 - 4
alien-store/src/main/java/shop/alien/store/service/impl/BarPerformanceServiceImpl.java

@@ -251,10 +251,6 @@ public class BarPerformanceServiceImpl implements BarPerformanceService {
             if (barPerformance.getDeleteFlag() == null) {
                 barPerformance.setDeleteFlag(0);
             }
-            // 设置默认状态为禁用(0)
-            if (barPerformance.getStatus() == null) {
-                barPerformance.setStatus(0);
-            }
             // 审核状态已在AI审核结果后设置(1-审核通过 或 2-审核拒绝)
             // 如果AI审核通过,reviewStatus已设置为1,并且onlineStatus已设置为1(上线)
             // 如果AI审核失败,reviewStatus已设置为2,onlineStatus保持为null或0(下线)

+ 47 - 33
alien-store/src/main/java/shop/alien/store/service/impl/StoreInfoServiceImpl.java

@@ -5636,20 +5636,18 @@ public class StoreInfoServiceImpl extends ServiceImpl<StoreInfoMapper, StoreInfo
         Integer totalCount = 0;
         double storeScore;
         Object ratingObj =  commonRatingService.getRatingCount(storeInfo.getId(), 1);
-        if (ratingObj != null && ratingObj instanceof Map) {
+        if (ratingObj != null) {
             Map<String, Object> ratingMap = (Map<String, Object>) ratingObj;
-
-            // 校验外层状态(success、code)
-            Boolean isSuccess = (Boolean) ratingMap.get("success");
-            Integer code = (Integer) ratingMap.get("code");
-            if (isSuccess != null && isSuccess && code != null && code == 200) {
-                // 提取核心数据层data
-                Map<String, Object> dataMap = (Map<String, Object>) ratingMap.get("data");
-                if (dataMap != null) {
-                    // 解析各字段(非空校验+类型转换)
-                    storeScore = dataMap.get("storeScore") != null ? Double.parseDouble(dataMap.get("storeScore").toString()) : 0.0;
-                    totalCount = dataMap.get("totalCount") != null ? Integer.parseInt(dataMap.get("totalCount").toString()) : 0;
+            Object totalCountObj = ratingMap.get("totalCount");
+            if (totalCountObj != null) {
+                // 安全转换为整数
+                try {
+                    totalCount = Integer.parseInt(totalCountObj.toString().trim());
+                } catch (NumberFormatException e) {
+                    totalCount = 0; // 转换失败时默认值
                 }
+            } else {
+                totalCount = 0;
             }
         }
 
@@ -5863,19 +5861,27 @@ public class StoreInfoServiceImpl extends ServiceImpl<StoreInfoMapper, StoreInfo
                             );
 
                             boolean isInBusiness;
-                            // 处理跨天营业
-                            if (start.isBefore(end) || start.equals(end)) {
-                                // 同一天营业:包含边界值
-                                isInBusiness = (currentTime.isAfter(start) || currentTime.equals(start))
-                                        && (currentTime.isBefore(end) || currentTime.equals(end));
+                            // 判断是否是全天营业(00:00到00:00)
+                            LocalTime midnight = LocalTime.of(0, 0);
+                            if (start.equals(midnight) && end.equals(midnight)) {
+                                // 全天营业
+                                isInBusiness = true;
+                                log.info("特殊日期营业时间判断 - 全天营业(00:00-00:00),当前时间: {},是否在营业时间内: true", currentTime);
                             } else {
-                                // 跨天营业:包含边界值
-                                isInBusiness = (currentTime.isAfter(start) || currentTime.equals(start))
-                                        || (currentTime.isBefore(end) || currentTime.equals(end));
+                                // 处理跨天营业
+                                if (start.isBefore(end) || start.equals(end)) {
+                                    // 同一天营业:包含边界值
+                                    isInBusiness = (currentTime.isAfter(start) || currentTime.equals(start))
+                                            && (currentTime.isBefore(end) || currentTime.equals(end));
+                                } else {
+                                    // 跨天营业:包含边界值
+                                    isInBusiness = (currentTime.isAfter(start) || currentTime.equals(start))
+                                            || (currentTime.isBefore(end) || currentTime.equals(end));
+                                }
+                                log.info("特殊日期营业时间判断 - 开始时间: {}, 结束时间: {}, 当前时间: {}, 是否在营业时间内: {}",
+                                        start, end, currentTime, isInBusiness);
                             }
 
-                            log.info("特殊日期营业时间判断 - 开始时间: {}, 结束时间: {}, 当前时间: {}, 是否在营业时间内: {}",
-                                    start, end, currentTime, isInBusiness);
                             result.setYyFlag(isInBusiness ? 1 : 0);
                             isCurrentSpecialBusinessTime = isInBusiness;
                         } else {
@@ -5942,19 +5948,27 @@ public class StoreInfoServiceImpl extends ServiceImpl<StoreInfoMapper, StoreInfo
                                     );
 
                                     boolean isInBusiness;
-                                    // 处理跨天营业
-                                    if (start.isBefore(end) || start.equals(end)) {
-                                        // 同一天营业:包含边界值
-                                        isInBusiness = (currentTime.isAfter(start) || currentTime.equals(start))
-                                                && (currentTime.isBefore(end) || currentTime.equals(end));
+                                    // 判断是否是全天营业(00:00到00:00)
+                                    LocalTime midnight = LocalTime.of(0, 0);
+                                    if (start.equals(midnight) && end.equals(midnight)) {
+                                        // 全天营业
+                                        isInBusiness = true;
+                                        log.info("正常营业时间判断 - 全天营业(00:00-00:00),当前时间: {},是否在营业时间内: true", currentTime);
                                     } else {
-                                        // 跨天营业:包含边界值
-                                        isInBusiness = (currentTime.isAfter(start) || currentTime.equals(start))
-                                                || (currentTime.isBefore(end) || currentTime.equals(end));
+                                        // 处理跨天营业
+                                        if (start.isBefore(end) || start.equals(end)) {
+                                            // 同一天营业:包含边界值
+                                            isInBusiness = (currentTime.isAfter(start) || currentTime.equals(start))
+                                                    && (currentTime.isBefore(end) || currentTime.equals(end));
+                                        } else {
+                                            // 跨天营业:包含边界值
+                                            isInBusiness = (currentTime.isAfter(start) || currentTime.equals(start))
+                                                    || (currentTime.isBefore(end) || currentTime.equals(end));
+                                        }
+                                        log.info("正常营业时间判断 - 开始时间: {}, 结束时间: {}, 当前时间: {}, 是否在营业时间内: {}",
+                                                start, end, currentTime, isInBusiness);
                                     }
 
-                                    log.info("正常营业时间判断 - 开始时间: {}, 结束时间: {}, 当前时间: {}, 是否在营业时间内: {}",
-                                            start, end, currentTime, isInBusiness);
                                     result.setYyFlag(isInBusiness ? 1 : 0);
                                 } else {
                                     log.warn("正常营业时间格式错误 - 开始时间: {}, 结束时间: {}",
@@ -6402,7 +6416,7 @@ public class StoreInfoServiceImpl extends ServiceImpl<StoreInfoMapper, StoreInfo
         storeInfo.setId(storeImg.getStoreId());
         storeInfo.setBusinessLicenseStatus(2); // 2-待审核
         storeInfo.setUpdateBusinessLicenseTime(new Date());
-
+        
         storeInfoMapper.updateById(storeInfo);
         
         // 异步调用证照审核AI接口并处理审核结果

+ 14 - 20
alien-store/src/main/java/shop/alien/store/service/impl/StoreRenovationBrowseRecordServiceImpl.java

@@ -8,7 +8,6 @@ import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 import org.springframework.util.StringUtils;
-import shop.alien.entity.store.StoreInfo;
 import shop.alien.entity.store.StoreRenovationBrowseRecord;
 import shop.alien.entity.store.StoreRenovationRequirement;
 import shop.alien.entity.store.StoreUser;
@@ -20,11 +19,7 @@ import shop.alien.store.service.StoreRenovationBrowseRecordService;
 import shop.alien.store.service.StoreRenovationRequirementService;
 import shop.alien.util.common.JwtUtil;
 
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Date;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
 import java.util.stream.Collectors;
 
 /**
@@ -200,24 +195,23 @@ public class StoreRenovationBrowseRecordServiceImpl extends ServiceImpl<StoreRen
     public List<StoreRenovationBrowseRequirementDto> getBrowsedRequirementsByStoreTel(String renovationStoreTel, String normalStoreTel) {
         log.info("StoreRenovationBrowseRecordServiceImpl.getBrowsedRequirementsByStoreTel?renovationStoreTel={}, normalStoreTel={}", renovationStoreTel, normalStoreTel);
         try {
-            // 0. 通过门店电话查询门店ID
-            LambdaQueryWrapper<StoreInfo> renovationStoreWrapper = new LambdaQueryWrapper<>();
-            renovationStoreWrapper.eq(StoreInfo::getStoreTel, renovationStoreTel);
-            renovationStoreWrapper.eq(StoreInfo::getDeleteFlag, 0);
-            StoreInfo renovationStoreInfo = storeInfoMapper.selectOne(renovationStoreWrapper);
-            if (renovationStoreInfo == null) {
-                throw new RuntimeException("装修商铺不存在,门店电话: " + renovationStoreTel);
+            // 0. 通过手机号查询门店ID
+            LambdaQueryWrapper<StoreUser> renovationStoreUserWrapper = new LambdaQueryWrapper<>();
+            renovationStoreUserWrapper.eq(StoreUser::getPhone, renovationStoreTel);
+            StoreUser renovationStoreUser = storeUserMapper.selectOne(renovationStoreUserWrapper);
+            if (renovationStoreUser == null || renovationStoreUser.getStoreId() == null) {
+                throw new RuntimeException("装修商铺不存在,手机号: " + renovationStoreTel);
             }
-            Integer renovationStoreId = renovationStoreInfo.getId();
+            Integer renovationStoreId = renovationStoreUser.getStoreId();
 
-            LambdaQueryWrapper<StoreInfo> normalStoreWrapper = new LambdaQueryWrapper<>();
-            normalStoreWrapper.eq(StoreInfo::getStoreTel, normalStoreTel);
-            normalStoreWrapper.eq(StoreInfo::getDeleteFlag, 0);
-            StoreInfo normalStoreInfo = storeInfoMapper.selectOne(normalStoreWrapper);
-            if (normalStoreInfo == null) {
+            // 1. 通过手机号查询门店ID
+            LambdaQueryWrapper<StoreUser> normalStoreUserWrapper = new LambdaQueryWrapper<>();
+            normalStoreUserWrapper.eq(StoreUser::getPhone, normalStoreTel);
+            StoreUser normalStoreUser = storeUserMapper.selectOne(normalStoreUserWrapper);
+            if (normalStoreUser == null || normalStoreUser.getStoreId() == null) {
                 throw new RuntimeException("普通商铺不存在,门店电话: " + normalStoreTel);
             }
-            Integer normalStoreId = normalStoreInfo.getId();
+            Integer normalStoreId = normalStoreUser.getStoreId();
 
             // 1. 查询普通商铺发布的所有装修需求ID
             LambdaQueryWrapper<StoreRenovationRequirement> requirementWrapper = new LambdaQueryWrapper<>();

+ 58 - 14
alien-store/src/main/java/shop/alien/store/service/impl/StoreRenovationRequirementServiceImpl.java

@@ -12,16 +12,10 @@ import org.springframework.beans.BeanUtils;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 import org.springframework.util.StringUtils;
-import shop.alien.entity.store.LifeNotice;
-import shop.alien.entity.store.StoreInfo;
-import shop.alien.entity.store.StoreRenovationRequirement;
-import shop.alien.entity.store.StoreUser;
+import shop.alien.entity.store.*;
 import shop.alien.entity.store.dto.StoreRenovationRequirementDto;
 import shop.alien.entity.store.vo.WebSocketVo;
-import shop.alien.mapper.LifeNoticeMapper;
-import shop.alien.mapper.StoreInfoMapper;
-import shop.alien.mapper.StoreRenovationRequirementMapper;
-import shop.alien.mapper.StoreUserMapper;
+import shop.alien.mapper.*;
 import shop.alien.store.config.WebSocketProcess;
 import shop.alien.store.service.StoreRenovationRequirementService;
 import shop.alien.store.util.ai.AiContentModerationUtil;
@@ -52,6 +46,7 @@ public class StoreRenovationRequirementServiceImpl extends ServiceImpl<StoreReno
 
     private final StoreInfoMapper storeInfoMapper;
 
+    private final LifeMessageMapper lifeMessageMapper;
 
     private final LifeNoticeMapper lifeNoticeMapper;
 
@@ -363,18 +358,18 @@ public class StoreRenovationRequirementServiceImpl extends ServiceImpl<StoreReno
                 if ("video".equals(auditType)) {
                     message = "在" + commonDate + ",您发布的装修动态视频审核已通过。";
                 } else {
-                    message = "在" + commonDate + ",您发布的装修动态审核已通过。";
+                    message = "您发布的装修需求已通过审核,平台已将此需求发布,有意向的装修公司会与您联系";
                 }
             } else {
                 // 审核不通过
                 title = "审核通知";
-                String reasonText = auditReason != null && !auditReason.trim().isEmpty() 
-                    ? ",原因:" + auditReason 
-                    : "";
+//                String reasonText = auditReason != null && !auditReason.trim().isEmpty()
+//                    ? ",原因:" + auditReason
+//                    : "";
                 if ("video".equals(auditType)) {
-                    message = "在" + commonDate + ",您发布的装修动态视频审核未通过" + reasonText + ",请修改后重新提交。";
+                    message = "在" + commonDate + ",您发布的装修动态视频审核未通过" + auditReason + ",请修改后重新提交。";
                 } else {
-                    message = "在" + commonDate + ",您发布的装修动态审核未通过" + reasonText + ",请修改后重新提交。";
+                    message = "您发布的装修需求未通过审核.驳回原因:"+auditReason+"请您重新发布.";
                 }
             }
 
@@ -519,6 +514,55 @@ public class StoreRenovationRequirementServiceImpl extends ServiceImpl<StoreReno
                     }
                 });
             }
+
+            // 检查是否已与发布商铺发生沟通
+            String currentUserPhone = null;
+            try {
+                com.alibaba.fastjson.JSONObject userInfo = JwtUtil.getCurrentUserInfo();
+                if (userInfo != null) {
+                    currentUserPhone = userInfo.getString("phone");
+                }
+            } catch (Exception e) {
+                log.debug("获取当前用户手机号失败(用户可能未登录): {}", e.getMessage());
+            }
+
+            if (currentUserPhone != null && StringUtils.hasText(currentUserPhone)) {
+                String currentUserPhoneId = "store_" + currentUserPhone;
+
+                // 批量查询沟通状态(优化:使用Map缓存结果,避免重复查询)
+                Map<String, Boolean> communicationStatusMap = new HashMap<>();
+
+                dtoPage.getRecords().forEach(dto -> {
+                    String storeTel = dto.getStoreTel();
+                    if (storeTel != null && StringUtils.hasText(storeTel)) {
+                        String publishingShopPhoneId = "store_" + storeTel;
+
+                        // 检查是否已查询过该商铺的沟通状态
+                        String communicationKey = currentUserPhoneId + "_" + publishingShopPhoneId;
+                        Boolean hasCommunicated = communicationStatusMap.get(communicationKey);
+
+                        if (hasCommunicated == null) {
+                            // 查询是否有消息记录(检查双向消息)
+                            LambdaQueryWrapper<LifeMessage> messageWrapper = new LambdaQueryWrapper<>();
+                            messageWrapper.eq(LifeMessage::getDeleteFlag, 0);
+                            messageWrapper.and(w -> w.and(w1 -> w1.eq(LifeMessage::getSenderId, currentUserPhoneId)
+                                            .eq(LifeMessage::getReceiverId, publishingShopPhoneId))
+                                    .or(w2 -> w2.eq(LifeMessage::getSenderId, publishingShopPhoneId)
+                                            .eq(LifeMessage::getReceiverId, currentUserPhoneId)));
+                            Integer messageCount = lifeMessageMapper.selectCount(messageWrapper);
+                            hasCommunicated = messageCount != null && messageCount > 0;
+                            communicationStatusMap.put(communicationKey, hasCommunicated);
+                        }
+
+                        dto.setHasCommunicated(hasCommunicated);
+                    } else {
+                        dto.setHasCommunicated(false);
+                    }
+                });
+            } else {
+                // 用户未登录,所有记录都设为未沟通
+                dtoPage.getRecords().forEach(dto -> dto.setHasCommunicated(false));
+            }
         }
 
         return dtoPage;

+ 2 - 2
alien-store/src/main/resources/bootstrap-dev.yml

@@ -7,7 +7,7 @@ spring:
       #注册中心
       discovery:
         server-addr: 120.26.186.130:8848
-        username: nacos
+        username: dev
         password: Alien123456
 
       #配置中心
@@ -15,7 +15,7 @@ spring:
         enabled: true
         refresh-enabled: true
         server-addr: 120.26.186.130:8848
-        username: nacos
+        username: dev
         password: Alien123456
         group: DEFAULT_GROUP
         file-extension: yml

+ 2 - 2
alien-store/src/main/resources/bootstrap-test.yml

@@ -7,7 +7,7 @@ spring:
       #注册中心
       discovery:
         server-addr: 120.26.186.130:8848
-        username: nacos
+        username: dev
         password: Alien123456
         namespace: acd615de-5b62-4d92-996b-740183cd7f15
 
@@ -16,7 +16,7 @@ spring:
         enabled: true
         refresh-enabled: true
         server-addr: 120.26.186.130:8848
-        username: nacos
+        username: dev
         password: Alien123456
         group: DEFAULT_GROUP
         file-extension: yml