Переглянути джерело

Merge branch 'sit' of http://8.152.195.41:3000/alien/alien_cloud into sit

dujian 1 місяць тому
батько
коміт
8976d81fd9
19 змінених файлів з 514 додано та 37 видалено
  1. 4 0
      alien-entity/src/main/java/shop/alien/entity/store/LifeCollect.java
  2. 4 0
      alien-entity/src/main/java/shop/alien/entity/store/LifeDiscountCouponUser.java
  3. 4 0
      alien-entity/src/main/java/shop/alien/entity/store/StoreImg.java
  4. 68 13
      alien-entity/src/main/java/shop/alien/mapper/LifeBrowseRecordMapper.java
  5. 10 0
      alien-entity/src/main/java/shop/alien/mapper/StorePlatformUserRoleMapper.java
  6. 60 13
      alien-store-platform/src/main/java/shop/alien/storeplatform/service/impl/StorePlatformUserRoleServiceImpl.java
  7. 47 1
      alien-store/src/main/java/shop/alien/store/controller/AiChatController.java
  8. 72 10
      alien-store/src/main/java/shop/alien/store/controller/AiSearchController.java
  9. 20 0
      alien-store/src/main/java/shop/alien/store/controller/LifeCollectController.java
  10. 10 0
      alien-store/src/main/java/shop/alien/store/service/LifeDiscountCouponService.java
  11. 3 0
      alien-store/src/main/java/shop/alien/store/service/LifeUserDynamicsService.java
  12. 10 0
      alien-store/src/main/java/shop/alien/store/service/StoreBannerService.java
  13. 150 0
      alien-store/src/main/java/shop/alien/store/service/impl/LifeDiscountCouponServiceImpl.java
  14. 3 0
      alien-store/src/main/java/shop/alien/store/service/impl/LifeDiscountCouponStoreFriendServiceImpl.java
  15. 1 0
      alien-store/src/main/java/shop/alien/store/service/impl/LifeDiscountCouponUserServiceImpl.java
  16. 2 0
      alien-store/src/main/java/shop/alien/store/service/impl/LifeGroupPackageServiceImpl.java
  17. 22 0
      alien-store/src/main/java/shop/alien/store/service/impl/StoreBannerServiceImpl.java
  18. 23 0
      alien-store/src/main/java/shop/alien/store/service/impl/StoreInfoServiceImpl.java
  19. 1 0
      alien-store/src/main/java/shop/alien/store/service/impl/StorePlatformBenefitsServiceImpl.java

+ 4 - 0
alien-entity/src/main/java/shop/alien/entity/store/LifeCollect.java

@@ -53,4 +53,8 @@ public class LifeCollect {
     @ApiModelProperty(value = "修改人ID")
     @TableField("updated_user_id")
     private Integer updatedUserId;
+
+    @ApiModelProperty(value = "优惠券发放标记:0-未发放,1-已发放")
+    @TableField("coupon_issued_flag")
+    private Integer couponIssuedFlag;
 }

+ 4 - 0
alien-entity/src/main/java/shop/alien/entity/store/LifeDiscountCouponUser.java

@@ -92,6 +92,10 @@ public class LifeDiscountCouponUser extends Model<LifeDiscountCouponUser> {
     @TableField("voucher_id")
     private String voucherId;
 
+    @ApiModelProperty(value = "发放来源:1-手动领取,2-收藏店铺,3-好评送券,4-好友赠送,5-平台发放")
+    @TableField("issue_source")
+    private Integer issueSource;
+
     @Override
     protected Serializable pkVal() {
         return this.id;

+ 4 - 0
alien-entity/src/main/java/shop/alien/entity/store/StoreImg.java

@@ -88,4 +88,8 @@ public class StoreImg extends Model<StoreImg> {
     @ApiModelProperty(value = "活动类型:COMMENT-评论有礼,MARKETING-营销活动")
     @TableField(exist = false)
     private String activityType;
+
+    @ApiModelProperty(value = "图片状态:0-未审核,1-审核通过,2-审核未通过")
+    @TableField("img_status")
+    private Integer imgStatus;
 }

+ 68 - 13
alien-entity/src/main/java/shop/alien/mapper/LifeBrowseRecordMapper.java

@@ -30,18 +30,73 @@ public interface LifeBrowseRecordMapper extends BaseMapper<LifeBrowseRecord> {
             "Order by lbr.liulan_time desc")
     List<Map<String, Object>> getGroupBuyBrowseRecordByUserId(String userId);
 
-    @Select("select lbr.id, lud.id dynamicsId, IFNULL(lud.cover_image, '') coverImage, lbr.liulan_date liulanDate, " +
-            "lud.phone_id phoneId, lud.title, lud.context, lud.image_path imagePath, lud.address, " +
-            "lud.address_context addressContext, lud.address_name addressName, lud.liulan_count liulanCount, " +
-            "lud.dianzan_count dianzanCount, lud.type, lud.draft, lud.created_time createdTime, lud.updated_time updatedTime, " +
-            "lud.address_province addressProvince, lud.top_status topStatus, lud.top_time topTime, " +
-            "lud.enable_status enableStatus, lud.expert_id expertId, lud.business_id businessId, " +
-            "lud.transfer_count transferCount, lud.reality_count realityCount, lud.check_flag checkFlag, " +
-            "lud.ai_task_id aiTaskId, lud.reason, lud.delete_flag deleteFlag\n" +
-            "from life_browse_record lbr \n" +
-            "inner join life_user_dynamics lud on lud.id = lbr.dynamics_id \n" +
-            "and lud.delete_flag = 0 and lbr.delete_flag = 0 \n" +
-            "where lbr.user_id = #{userId} and lbr.dynamics_id is not null\n" +
-            "Order by lbr.liulan_time desc")
+    @Select("SELECT " +
+            "lbr.id, " +
+            "lud.id dynamicsId, " +
+            "IFNULL(lud.cover_image, '') coverImage, " +
+            "lbr.liulan_date liulanDate, " +
+            "lud.phone_id phoneId, " +
+            "lud.title, " +
+            "lud.context, " +
+            "lud.image_path imagePath, " +
+            "lud.address, " +
+            "lud.address_context addressContext, " +
+            "lud.address_name addressName, " +
+            "lud.liulan_count liulanCount, " +
+            "lud.dianzan_count dianzanCount, " +
+            "lud.type, " +
+            "lud.draft, " +
+            "lud.created_time createdTime, " +
+            "lud.updated_time updatedTime, " +
+            "lud.address_province addressProvince, " +
+            "lud.top_status topStatus, " +
+            "lud.top_time topTime, " +
+            "lud.enable_status enableStatus, " +
+            "lud.expert_id expertId, " +
+            "lud.business_id businessId, " +
+            "lud.transfer_count transferCount, " +
+            "lud.reality_count realityCount, " +
+            "lud.check_flag checkFlag, " +
+            "lud.ai_task_id aiTaskId, " +
+            "lud.reason, " +
+            "lud.delete_flag deleteFlag, " +
+            "si1.id storeId, " +
+            "si1.store_name storeName, " +
+            "si1.score_avg scoreAvg, " +
+            "si1.business_section businessSection, " +
+            "si1.business_section_name businessSectionName, " +
+            "ROUND(IFNULL(si1.score_one, 0), 2) scoreOne, " +
+            "ROUND(IFNULL(si1.score_two, 0), 2) scoreTwo, " +
+            "ROUND(IFNULL(si1.score_three, 0), 2) scoreThree, " +
+            "IFNULL(cr.ratingCount, 0) ratingCount " +
+            "FROM " +
+            "life_browse_record lbr " +
+            "INNER JOIN life_user_dynamics lud ON lud.id = lbr.dynamics_id " +
+            "AND lud.delete_flag = 0 " +
+            "AND lbr.delete_flag = 0 " +
+            "LEFT JOIN store_user su ON SUBSTRING_INDEX(lud.phone_id, '_', -1) = su.phone " +
+            "AND su.delete_flag = 0 " +
+            "AND SUBSTRING_INDEX(lud.phone_id, '_', 1) = 'store' " +
+            "LEFT JOIN store_info si1 ON si1.id = su.store_id " +
+            "AND si1.delete_flag = 0 " +
+            "LEFT JOIN ( " +
+            "SELECT " +
+            "business_id, " +
+            "count(*) AS ratingCount " +
+            "FROM " +
+            "common_rating " +
+            "WHERE " +
+            "business_type = 1 " +
+            "AND delete_flag = 0 " +
+            "AND audit_status = 1 " +
+            "AND is_show = 1 " +
+            "GROUP BY " +
+            "business_id " +
+            ") cr ON cr.business_id = si1.id " +
+            "WHERE " +
+            "lbr.user_id = #{userId} " +
+            "AND lbr.dynamics_id IS NOT NULL " +
+            "ORDER BY " +
+            "lbr.liulan_time DESC")
     List<Map<String, Object>> getDynamicsBrowseRecordByUserId(String userId);
 }

+ 10 - 0
alien-entity/src/main/java/shop/alien/mapper/StorePlatformUserRoleMapper.java

@@ -5,6 +5,7 @@ import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import org.apache.ibatis.annotations.Mapper;
 import org.apache.ibatis.annotations.Param;
+import org.apache.ibatis.annotations.Update;
 import shop.alien.entity.store.StorePlatformUserRole;
 import shop.alien.entity.store.vo.SubAccountListVo;
 import shop.alien.entity.store.vo.SubAccountVo;
@@ -47,5 +48,14 @@ public interface StorePlatformUserRoleMapper extends BaseMapper<StorePlatformUse
                                                 @Param("accountId") String accountId,
                                                 @Param("phone") String phone,
                                                 @Param("status") Integer status);
+
+    /**
+     * 回滚删除操作:恢复角色关联记录
+     *
+     * @param id 记录ID
+     * @return 更新影响的行数
+     */
+    @Update("UPDATE store_platform_user_role SET delete_flag = 0 WHERE id = #{id} AND delete_flag = 1")
+    int rollbackDeleteById(@Param("id") Long id);
 }
 

+ 60 - 13
alien-store-platform/src/main/java/shop/alien/storeplatform/service/impl/StorePlatformUserRoleServiceImpl.java

@@ -780,21 +780,68 @@ public class StorePlatformUserRoleServiceImpl extends ServiceImpl<StorePlatformU
     }
 
     @Override
+    @Transactional(rollbackFor = Exception.class)
     public boolean updateSubAccountNew(Integer userId, String phone, String accountName, Integer storeId, Long roleId,Integer id) {
-        LambdaUpdateWrapper<StorePlatformUserRole> deleteWrapper = new LambdaUpdateWrapper<>();
-        deleteWrapper.eq(StorePlatformUserRole::getUserId, userId)
-                .eq(StorePlatformUserRole::getId, id)
-                .set(StorePlatformUserRole::getDeleteFlag, 1);
-        int deleteResult = storePlatformUserRoleMapper.update(null, deleteWrapper);
-        if (deleteResult > 0) {
-            log.info("成功删除角色关联记录: userId={}, storeId={}, roleId={}", userId, storeId, roleId);
-           R<String> A =createAccountAndAssignRole(phone, accountName, storeId, roleId);
-           if (A.getCode()==200){
-               return true;
-           }
+        try {
+            // 先查询要删除的记录,保存信息以便回滚
+            StorePlatformUserRole oldUserRole = storePlatformUserRoleMapper.selectById(id);
+            if (oldUserRole == null) {
+                log.error("要删除的角色关联记录不存在: id={}", id);
+                return false;
+            }
+            
+            // 逻辑删除记录
+            LambdaUpdateWrapper<StorePlatformUserRole> deleteWrapper = new LambdaUpdateWrapper<>();
+            deleteWrapper.eq(StorePlatformUserRole::getUserId, userId)
+                    .eq(StorePlatformUserRole::getId, id)
+                    .set(StorePlatformUserRole::getDeleteFlag, 1);
+            int deleteResult = storePlatformUserRoleMapper.update(null, deleteWrapper);
+            
+            if (deleteResult > 0) {
+                log.info("成功删除角色关联记录: userId={}, storeId={}, roleId={}", userId, storeId, roleId);
+                
+                // 尝试创建新账号并分配角色
+                R<String> createResult = createAccountAndAssignRole(phone, accountName, storeId, roleId);
+                
+                if (createResult.getCode() == 200) {
+                    log.info("成功创建新账号并分配角色: phone={}, storeId={}, roleId={}", phone, storeId, roleId);
+                    return true;
+                } else {
+                    // 创建失败,执行回滚:恢复删除的记录
+                    log.warn("创建新账号失败,执行回滚操作: phone={}, storeId={}, roleId={}, error={}", 
+                            phone, storeId, roleId, createResult.getMsg());
+                    
+                    int rollbackResult = storePlatformUserRoleMapper.rollbackDeleteById(Long.valueOf(id));
+                    
+                    if (rollbackResult > 0) {
+                        log.info("成功回滚,恢复角色关联记录: id={}, userId={}, storeId={}, roleId={}", 
+                                id, userId, storeId, roleId);
+                    } else {
+                        log.error("回滚失败,无法恢复角色关联记录: id={}, userId={}, storeId={}, roleId={}", 
+                                id, userId, storeId, roleId);
+                    }
+                    return false;
+                }
+            } else {
+                log.error("删除角色关联记录失败: userId={}, id={}", userId, id);
+                return false;
+            }
+        } catch (Exception e) {
+            log.error("更新子账号失败,尝试回滚: userId={}, phone={}, accountName={}, storeId={}, roleId={}, id={}", 
+                    userId, phone, accountName, storeId, roleId, id, e);
+            
+            // 异常情况下也尝试回滚
+            try {
+                int rollbackResult = storePlatformUserRoleMapper.rollbackDeleteById(Long.valueOf(id));
+                if (rollbackResult > 0) {
+                    log.info("异常情况下成功回滚,恢复角色关联记录: id={}", id);
+                }
+            } catch (Exception rollbackException) {
+                log.error("回滚操作也失败: id={}", id, rollbackException);
+            }
+            
+            return false;
         }
-
-        return false;
     }
 
     @Override

+ 47 - 1
alien-store/src/main/java/shop/alien/store/controller/AiChatController.java

@@ -9,6 +9,7 @@ import org.springframework.beans.factory.annotation.Value;
 import org.springframework.cloud.context.config.annotation.RefreshScope;
 import org.springframework.http.HttpEntity;
 import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpMethod;
 import org.springframework.http.MediaType;
 import org.springframework.http.ResponseEntity;
 import org.springframework.util.StringUtils;
@@ -16,6 +17,8 @@ import org.springframework.web.bind.annotation.CrossOrigin;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RequestParam;
 import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.client.HttpServerErrorException;
+import org.springframework.web.util.UriComponentsBuilder;
 import org.springframework.web.client.RestTemplate;
 import shop.alien.store.util.ai.AiAuthTokenUtil;
 
@@ -35,9 +38,11 @@ public class AiChatController {
 
     private final RestTemplate restTemplate;
 
-    @Value("${third-party-ai-chat.base-url:http://192.168.2.250:9100/ai/life-manager/api/v1/question_classification/classify_and_route}")
+    @Value("${third-party-ai-chat.base-url:http://124.93.18.180:9100/ai/life-manager/api/v1/question_classification/classify_and_route}")
     private String aiChatUrl;
 
+    @Value("${third-party-ai-chat.history-url:http://124.93.18.180:9100/ai/life-manager/api/v1/chat_history}")
+    private String aiHistoryChatUrl;
 
     /**
      * 调用 AI 服务,获取聊天结果
@@ -71,4 +76,45 @@ public class AiChatController {
         }
         return  ResponseEntity.badRequest().body(null);
     }
+
+    /**
+     * 调用 AI 服务,获取聊天结果
+     *
+     * @return 聊天结果
+     */
+    @RequestMapping("/aiChatHistory")
+    public ResponseEntity<String> aiChatHistory(@RequestParam("user_id") String user_id,@RequestParam("page") String page,@RequestParam("rounds_per_page") String rounds_per_page){
+        String accessToken = aiAuthTokenUtil.getAccessToken();
+        JSONObject data = new JSONObject();
+        if (!StringUtils.hasText(accessToken)) {
+            data.put("fail","登录失败");
+            return ResponseEntity.badRequest().body(data.toJSONString());
+        }
+
+        HttpHeaders aiHeaders = new HttpHeaders();
+        aiHeaders.set("Authorization", "Bearer " + accessToken);
+        HttpEntity<Void> request = new HttpEntity<>(aiHeaders);
+
+        String url = UriComponentsBuilder.fromHttpUrl(aiHistoryChatUrl)
+                .queryParam("user_id", user_id)
+                .queryParam("page", page)
+                .queryParam("rounds_per_page", rounds_per_page)
+                .build()
+                .toUriString();
+
+        try {
+            log.info("调用AI聊天历史接口(GET) url={}, user_id={}, page={}, rounds_per_page={}", url, user_id, page, rounds_per_page);
+            ResponseEntity<String> stringResponseEntity = restTemplate.exchange(url, HttpMethod.GET, request, String.class);
+            return stringResponseEntity;
+        } catch (HttpServerErrorException e) {
+            String responseBody = e.getResponseBodyAsString();
+            log.error("调用AI聊天历史接口 5xx异常 url={}, status={}, responseBody={}", url, e.getStatusCode(), responseBody, e);
+            data.put("fail", "AI服务异常: " + e.getStatusCode() + ", " + (StringUtils.hasText(responseBody) ? responseBody : e.getStatusText()));
+            return ResponseEntity.status(e.getStatusCode()).body(data.toJSONString());
+        } catch (Exception e) {
+            log.error("调用AI聊天历史接口 接口异常 url={}", url, e);
+            data.put("fail", "调用AI服务失败: " + e.getMessage());
+            return ResponseEntity.badRequest().body(data.toJSONString());
+        }
+    }
 }

+ 72 - 10
alien-store/src/main/java/shop/alien/store/controller/AiSearchController.java

@@ -20,18 +20,19 @@ import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RestController;
 import org.springframework.web.client.RestTemplate;
 import shop.alien.entity.result.R;
-import shop.alien.entity.store.LifeBlacklist;
-import shop.alien.entity.store.StoreImg;
-import shop.alien.entity.store.StoreUser;
+import shop.alien.entity.store.*;
+import shop.alien.entity.store.vo.StoreBannerVo;
 import shop.alien.entity.store.vo.StoreInfoVo;
-import shop.alien.entity.store.CommonRating;
 import shop.alien.mapper.CommonRatingMapper;
 import shop.alien.mapper.LifeBlacklistMapper;
 import shop.alien.mapper.StoreImgMapper;
 import shop.alien.mapper.StoreUserMapper;
 import shop.alien.store.annotation.TrackEvent;
 import shop.alien.store.service.CommonRatingService;
+import shop.alien.store.service.StoreBannerService;
 import shop.alien.store.service.StoreImgService;
+import shop.alien.store.service.StoreInfoService;
+import shop.alien.entity.store.vo.StoreBusinessStatusVo;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 
 import java.time.Instant;
@@ -63,8 +64,10 @@ public class AiSearchController {
 
     private final RestTemplate restTemplate;
     private final StoreImgService storeImgService;
+    private final StoreBannerService storeBannerService;
     private final CommonRatingService commonRatingService;
     private final CommonRatingMapper commonRatingMapper;
+    private final StoreInfoService storeInfoService;
 
     private final LifeBlacklistMapper lifeBlacklistMapper;
 
@@ -105,9 +108,14 @@ public class AiSearchController {
             List<StoreInfoVo> relatedResult = convertToStoreInfoList(jsonObject.getJSONArray("related_results"),map.get("userId"));
             List<StoreInfoVo> matchedResult = convertToStoreInfoList(jsonObject.getJSONArray("matched_results"),map.get("userId"));
 
-            // 查找图片并设置到result中(图片类型1-入口图)
-            fillStoreImages(relatedResult, 1);
-            fillStoreImages(matchedResult, 1);
+            // 并发处理图片和营业时间,提升性能
+            CompletableFuture<Void> relatedImageFuture = CompletableFuture.runAsync(() -> fillStoreImages(relatedResult, 1));
+            CompletableFuture<Void> matchedImageFuture = CompletableFuture.runAsync(() -> fillStoreImages(matchedResult, 1));
+            CompletableFuture<Void> relatedBusinessHoursFuture = CompletableFuture.runAsync(() -> fillBusinessHours(relatedResult));
+            CompletableFuture<Void> matchedBusinessHoursFuture = CompletableFuture.runAsync(() -> fillBusinessHours(matchedResult));
+            
+            // 等待所有任务完成
+            CompletableFuture.allOf(relatedImageFuture, matchedImageFuture, relatedBusinessHoursFuture, matchedBusinessHoursFuture).join();
 
             // 合并两个列表
 //            List<StoreInfoVo> relatedResults = new ArrayList<>();
@@ -116,6 +124,14 @@ public class AiSearchController {
 //            relatedResults.addAll(relatedResult);
             jsonObject1.put("matchedRecords", matchedResult);
             jsonObject1.put("relatedRecords", relatedResult);
+            // 根据matchedResult中的business_section_name去插入到storeBanners中
+            List<String> sectionNames = matchedResult.stream()
+                    .map(StoreInfoVo::getBusinessSectionName)
+                    .filter(StringUtils::isNotBlank)
+                    .distinct()
+                    .collect(Collectors.toList());
+            List<StoreBannerVo> storeBanners = storeBannerService.getBannerByAISearch(sectionNames);
+            jsonObject1.put("storeBanners", storeBanners);
 
 
             jsonObject1.put("total", jsonObject.get("total"));
@@ -154,12 +170,13 @@ public class AiSearchController {
             // 模糊搜索:从related_results和matched_results字段获取数据
             List<StoreInfoVo> result = convertToStoreInfoList(jsonObject.getJSONArray("results"),map.get("userId"));
 
-            // 并发处理图片和评论数据,提升性能
+            // 并发处理图片、评论数据和营业时间,提升性能
             CompletableFuture<Void> imageFuture = CompletableFuture.runAsync(() -> fillStoreImages(result, 1));
             CompletableFuture<Void> ratingFuture = CompletableFuture.runAsync(() -> fillRatingCountBatch(result));
+            CompletableFuture<Void> businessHoursFuture = CompletableFuture.runAsync(() -> fillBusinessHours(result));
             
-            // 等待两个任务完成
-            CompletableFuture.allOf(imageFuture, ratingFuture).join();
+            // 等待所有任务完成
+            CompletableFuture.allOf(imageFuture, ratingFuture, businessHoursFuture).join();
 
             jsonObject1.put("records", result);
 
@@ -349,6 +366,51 @@ public class AiSearchController {
     }
 
     /**
+     * 批量填充营业时间到StoreInfoVo列表中
+     * 通过调用storeInfoService.getStoreBusinessStatus方法获取营业时间
+     *
+     * @param result StoreInfoVo列表
+     */
+    private void fillBusinessHours(List<StoreInfoVo> result) {
+        if (result == null || result.isEmpty()) {
+            return;
+        }
+
+        // 遍历result集合,为每个门店调用getStoreBusinessStatus方法
+        for (StoreInfoVo storeInfo : result) {
+            if (storeInfo.getId() != null) {
+                try {
+                    // 调用getStoreBusinessStatus方法获取营业时间
+                    StoreBusinessStatusVo businessStatus = storeInfoService.getStoreBusinessStatus(String.valueOf(storeInfo.getId()));
+                    if (businessStatus != null) {
+                        // 设置营业时间信息到StoreInfoVo中
+                        if (businessStatus.getStoreBusinessInfos() != null && !businessStatus.getStoreBusinessInfos().isEmpty()) {
+                            storeInfo.setStoreBusinessInfos(businessStatus.getStoreBusinessInfos());
+                            // 同时设置到openTime字段(格式化为字符串列表)
+                            List<String> openTimeList = businessStatus.getStoreBusinessInfos().stream()
+                                    .map(info -> {
+                                        if (info.getBusinessDate() != null && info.getStartTime() != null && info.getEndTime() != null) {
+                                            return info.getBusinessDate() + " " + info.getStartTime() + "-" + info.getEndTime();
+                                        }
+                                        return null;
+                                    })
+                                    .filter(time -> time != null)
+                                    .collect(Collectors.toList());
+                            storeInfo.setOpenTime(openTimeList);
+                        }
+                        // 设置营业状态
+                        if (businessStatus.getYyFlag() != null) {
+                            storeInfo.setYyFlag(businessStatus.getYyFlag());
+                        }
+                    }
+                } catch (Exception e) {
+                    log.error("获取门店营业时间失败,storeId: {}", storeInfo.getId(), e);
+                }
+            }
+        }
+    }
+
+    /**
      * 将下划线命名转换为驼峰命名
      * 例如: store_tel -> storeTel, created_time -> createdTime
      */

+ 20 - 0
alien-store/src/main/java/shop/alien/store/controller/LifeCollectController.java

@@ -19,6 +19,7 @@ import shop.alien.mapper.*;
 import shop.alien.mapper.second.SecondGoodsMapper;
 import shop.alien.store.annotation.TrackEvent;
 import shop.alien.store.config.GaoDeMapUtil;
+import shop.alien.store.service.LifeDiscountCouponService;
 import shop.alien.util.common.ListToPage;
 
 import java.text.DecimalFormat;
@@ -54,6 +55,8 @@ public class LifeCollectController {
 
     private final LifeGroupBuyMainMapper lifeGroupBuyMainMapper;
 
+    private final LifeDiscountCouponService lifeDiscountCouponService;
+
     @ApiOperation("收藏列表")
     @ApiOperationSupport(order = 1)
     @ApiImplicitParams({@ApiImplicitParam(name = "page", value = "分页页数", dataType = "String", paramType = "query", required = true),
@@ -230,6 +233,23 @@ public class LifeCollectController {
             LambdaQueryWrapper<LifeCollect> queryWrapper = new LambdaQueryWrapper<>();
             queryWrapper.eq(LifeCollect::getStoreId, lifeCollect.getStoreId());
             storeInfoMapper.update(null, new LambdaUpdateWrapper<StoreInfo>().eq(StoreInfo::getId, lifeCollect.getStoreId()).set(StoreInfo::getCollectNum, lifeCollectMapper.selectCount(queryWrapper)));
+            
+            // 收藏店铺时自动发放优惠券(每种类型一张)
+            if (StringUtils.hasText(lifeCollect.getUserId())) {
+                try {
+                    Integer userId = Integer.parseInt(lifeCollect.getUserId());
+                    // 传入收藏记录ID,用于更新发放标记
+                    int issuedCount = lifeDiscountCouponService.issueCouponsForStoreCollect(userId, lifeCollect.getStoreId(), lifeCollect.getId());
+                    if (issuedCount > 0) {
+                        log.info("收藏店铺自动发放优惠券成功,userId={}, storeId={}, collectId={}, issuedCount={}", 
+                                userId, lifeCollect.getStoreId(), lifeCollect.getId(), issuedCount);
+                    }
+                } catch (NumberFormatException e) {
+                    log.warn("收藏店铺发放优惠券失败:用户ID格式错误,userId={}, storeId={}", lifeCollect.getUserId(), lifeCollect.getStoreId());
+                } catch (Exception e) {
+                    log.error("收藏店铺发放优惠券异常,userId={}, storeId={}, error={}", lifeCollect.getUserId(), lifeCollect.getStoreId(), e.getMessage(), e);
+                }
+            }
         }
 
 

+ 10 - 0
alien-store/src/main/java/shop/alien/store/service/LifeDiscountCouponService.java

@@ -168,4 +168,14 @@ public interface LifeDiscountCouponService extends IService<LifeDiscountCoupon>
      * @return List<LifeDiscountCoupon>
      */
     List<LifeDiscountCoupon> getPlatformCoupon(Integer couponId, Integer couponType);
+
+    /**
+     * 收藏店铺时自动发放优惠券(每种类型一张)
+     *
+     * @param userId   用户ID
+     * @param storeId  店铺ID
+     * @param collectId 收藏记录ID(用于更新发放标记)
+     * @return 发放的优惠券数量
+     */
+    int issueCouponsForStoreCollect(Integer userId, String storeId, String collectId);
 }

+ 3 - 0
alien-store/src/main/java/shop/alien/store/service/LifeUserDynamicsService.java

@@ -746,6 +746,9 @@ public class LifeUserDynamicsService extends ServiceImpl<LifeUserDynamicsMapper,
                      StoreInfo storeInfo=storeInfoMapper.getStoreNameByPhone(phoneIdNew);
                      if(storeInfo != null){
                          lifeUserDynamicsVo.setStoreName(storeInfo.getStoreName());
+                         lifeUserDynamicsVo.setBusinessSection(storeInfo.getBusinessSection().toString());
+                         lifeUserDynamicsVo.setBusinessTypeName(storeInfo.getBusinessTypeName());
+                         lifeUserDynamicsVo.setStoreUserId(storeInfo.getId().toString());
                      }
                 } else if (lifeUserDynamicsVo.getType().equals("1")) {
                     String phoneIdNew = lifeUserDynamicsVo.getPhoneId().substring(5);

+ 10 - 0
alien-store/src/main/java/shop/alien/store/service/StoreBannerService.java

@@ -7,6 +7,8 @@ import shop.alien.entity.store.StoreBanner;
 import shop.alien.entity.store.dto.StoreBannerDto;
 import shop.alien.entity.store.vo.StoreBannerVo;
 
+import java.util.List;
+
 /**
  * Banner表 服务类
  *
@@ -34,5 +36,13 @@ public interface StoreBannerService extends IService<StoreBanner> {
      * @return 分页数据
      */
     IPage<StoreBannerVo> pageList(String moduleCode, String moduleName, String status, Integer pageNum, Integer pageSize);
+
+    /**
+     * 根据关键词搜索 Banner
+     *
+     * @param keyword 关键词
+     * @return 搜索结果
+     */
+    List<StoreBannerVo> getBannerByAISearch(List<String> keyword);
 }
 

+ 150 - 0
alien-store/src/main/java/shop/alien/store/service/impl/LifeDiscountCouponServiceImpl.java

@@ -70,6 +70,8 @@ public class LifeDiscountCouponServiceImpl extends ServiceImpl<LifeDiscountCoupo
 
     private final LifeCouponMapper lifeCouponMapper;
 
+    private final LifeCollectMapper lifeCollectMapper;
+
     @Override
     public boolean addDiscountCoupon(LifeDiscountCouponDto lifeDiscountCouponDto) {
         try {
@@ -382,6 +384,20 @@ public class LifeDiscountCouponServiceImpl extends ServiceImpl<LifeDiscountCoupo
         }
         lifeDiscountCouponVo.setAvailableTimeQuantum(availableTimeQuantumList);
         lifeDiscountCouponVo.setCustomizeUnavailableTimeQuantum(customizeUnavailableTimeQuantumList);
+        
+        // 查询店铺名称
+        if (lifeDiscountCoupon != null && !StringUtils.isEmpty(lifeDiscountCoupon.getStoreId())) {
+            try {
+                Integer storeId = Integer.parseInt(lifeDiscountCoupon.getStoreId());
+                StoreInfo storeInfo = storeInfoMapper.selectById(storeId);
+                if (storeInfo != null && !StringUtils.isEmpty(storeInfo.getStoreName())) {
+                    lifeDiscountCouponVo.setStoreName(storeInfo.getStoreName());
+                }
+            } catch (Exception e) {
+                // 查询店铺信息失败,不影响主流程,静默处理
+            }
+        }
+        
         return lifeDiscountCouponVo;
     }
 
@@ -1764,6 +1780,7 @@ public class LifeDiscountCouponServiceImpl extends ServiceImpl<LifeDiscountCoupo
             lifeDiscountCouponUser.setStatus(0);
             lifeDiscountCouponUser.setDeleteFlag(0);
             lifeDiscountCouponUser.setCreatedTime(new Date());
+            lifeDiscountCouponUser.setIssueSource(5); // 5-平台发放
             lifeDiscountCouponUsers.add(lifeDiscountCouponUser);
         }
         return lifeDiscountCouponUserService.saveBatch(lifeDiscountCouponUsers);
@@ -1828,4 +1845,137 @@ public class LifeDiscountCouponServiceImpl extends ServiceImpl<LifeDiscountCoupo
         lifeDiscountCouponLambdaQueryWrapper.eq(LifeDiscountCoupon::getGetStatus, 1);
         return lifeDiscountCouponMapper.selectList(lifeDiscountCouponLambdaQueryWrapper);
     }
+
+    /**
+     * 收藏店铺时自动发放优惠券(每种类型一张)
+     * 
+     * @param userId   用户ID
+     * @param storeId  店铺ID
+     * @param collectId 收藏记录ID(用于更新发放标记)
+     * @return 发放的优惠券数量
+     */
+    @Override
+    public int issueCouponsForStoreCollect(Integer userId, String storeId, String collectId) {
+        if (userId == null || StringUtils.isEmpty(storeId)) {
+            return 0;
+        }
+
+        // 如果提供了收藏记录ID,检查是否已发放过
+        if (!StringUtils.isEmpty(collectId)) {
+            LifeCollect lifeCollect = lifeCollectMapper.selectById(collectId);
+            if (lifeCollect != null && lifeCollect.getCouponIssuedFlag() != null && lifeCollect.getCouponIssuedFlag() == 1) {
+                // 该收藏记录已发放过优惠券,不再重复发放
+                return 0;
+            }
+        }
+
+        try {
+            LocalDate now = LocalDate.now();
+            
+            // 查询该店铺所有开启领取的优惠券(有效期内、有库存、未删除)
+            LambdaQueryWrapper<LifeDiscountCoupon> queryWrapper = new LambdaQueryWrapper<>();
+            queryWrapper.eq(LifeDiscountCoupon::getStoreId, storeId)
+                    .eq(LifeDiscountCoupon::getGetStatus, 1) // 开启领取
+                    .eq(LifeDiscountCoupon::getDeleteFlag, 0) // 未删除
+                    .gt(LifeDiscountCoupon::getSingleQty, 0) // 有库存
+                    .ge(LifeDiscountCoupon::getEndGetDate, now) // 未过期
+                    .eq(LifeDiscountCoupon::getCouponStatus, 1) // 正式状态(非草稿)
+                    .isNotNull(LifeDiscountCoupon::getCouponType) // 必须有优惠券类型
+                    .orderByDesc(LifeDiscountCoupon::getCreatedTime); // 按创建时间降序
+            
+            List<LifeDiscountCoupon> allCoupons = lifeDiscountCouponMapper.selectList(queryWrapper);
+            
+            if (CollectionUtils.isEmpty(allCoupons)) {
+                return 0;
+            }
+
+            // 按 couponType 分组,每种类型只取第一个(最新的)
+            Map<Integer, LifeDiscountCoupon> couponByType = new HashMap<>();
+            for (LifeDiscountCoupon coupon : allCoupons) {
+                Integer couponType = coupon.getCouponType();
+                if (couponType != null && !couponByType.containsKey(couponType)) {
+                    couponByType.put(couponType, coupon);
+                }
+            }
+
+            if (couponByType.isEmpty()) {
+                return 0;
+            }
+
+            // 检查用户已领取的优惠券ID列表(避免重复发放)
+            LambdaQueryWrapper<LifeDiscountCouponUser> userCouponWrapper = new LambdaQueryWrapper<>();
+            userCouponWrapper.eq(LifeDiscountCouponUser::getUserId, userId)
+                    .eq(LifeDiscountCouponUser::getDeleteFlag, 0)
+                    .in(LifeDiscountCouponUser::getCouponId, 
+                        couponByType.values().stream().map(LifeDiscountCoupon::getId).collect(Collectors.toList()));
+            List<LifeDiscountCouponUser> existingCoupons = lifeDiscountCouponUserMapper.selectList(userCouponWrapper);
+            Set<Integer> existingCouponIds = existingCoupons.stream()
+                    .map(LifeDiscountCouponUser::getCouponId)
+                    .filter(Objects::nonNull)
+                    .collect(Collectors.toSet());
+
+            // 发放每种类型的优惠券(每种一张)
+            int issuedCount = 0;
+            for (Map.Entry<Integer, LifeDiscountCoupon> entry : couponByType.entrySet()) {
+                LifeDiscountCoupon coupon = entry.getValue();
+                
+                // 如果用户已领取过该优惠券,跳过
+                if (existingCouponIds.contains(coupon.getId())) {
+                    continue;
+                }
+
+                // 再次检查库存(防止并发问题)
+                if (coupon.getSingleQty() == null || coupon.getSingleQty() <= 0) {
+                    continue;
+                }
+
+                try {
+                    // 创建用户优惠券记录
+                    LifeDiscountCouponUser lifeDiscountCouponUser = new LifeDiscountCouponUser();
+                    lifeDiscountCouponUser.setCouponId(coupon.getId());
+                    lifeDiscountCouponUser.setUserId(userId);
+                    lifeDiscountCouponUser.setReceiveTime(new Date());
+                    
+                    // 设置过期时间:优先使用validDate,如果为null则使用endDate
+                    LocalDate expirationTime = coupon.getValidDate();
+                    if (expirationTime == null && coupon.getEndDate() != null) {
+                        expirationTime = coupon.getEndDate();
+                    }
+                    lifeDiscountCouponUser.setExpirationTime(expirationTime);
+                    lifeDiscountCouponUser.setStatus(Integer.parseInt(DiscountCouponEnum.WAITING_USED.getValue()));
+                    lifeDiscountCouponUser.setDeleteFlag(0);
+                    lifeDiscountCouponUser.setIssueSource(2); // 2-收藏店铺
+                    
+                    // 插入用户优惠券记录
+                    lifeDiscountCouponUserMapper.insert(lifeDiscountCouponUser);
+                    
+                    // 扣减库存
+                    coupon.setSingleQty(coupon.getSingleQty() - 1);
+                    lifeDiscountCouponMapper.updateById(coupon);
+                    
+                    issuedCount++;
+                } catch (Exception e) {
+                    // 单个优惠券发放失败不影响其他优惠券的发放
+                    // 静默处理,不记录日志,避免日志过多
+                }
+            }
+
+            // 如果发放成功且提供了收藏记录ID,更新收藏记录的发放标记
+            if (issuedCount > 0 && !StringUtils.isEmpty(collectId)) {
+                try {
+                    LifeCollect lifeCollect = new LifeCollect();
+                    lifeCollect.setId(collectId);
+                    lifeCollect.setCouponIssuedFlag(1); // 标记为已发放
+                    lifeCollectMapper.updateById(lifeCollect);
+                } catch (Exception e) {
+                    // 更新标记失败不影响主流程
+                }
+            }
+
+            return issuedCount;
+        } catch (Exception e) {
+            // 异常时静默处理,不影响收藏功能
+            return 0;
+        }
+    }
 }

+ 3 - 0
alien-store/src/main/java/shop/alien/store/service/impl/LifeDiscountCouponStoreFriendServiceImpl.java

@@ -390,6 +390,7 @@ public class LifeDiscountCouponStoreFriendServiceImpl extends ServiceImpl<LifeDi
                             lifeDiscountCouponUser.setExpirationTime(lifeDiscountCoupon.getValidDate());
                             // 设置该优惠券的状态为待使用
                             lifeDiscountCouponUser.setStatus(Integer.parseInt(DiscountCouponEnum.WAITING_USED.getValue()));
+                            lifeDiscountCouponUser.setIssueSource(4); // 4-好友赠送
                             // 将该用户优惠券记录插入到数据库中
                             lifeDiscountCouponUserMapper.insert(lifeDiscountCouponUser);
                         }
@@ -966,6 +967,7 @@ public class LifeDiscountCouponStoreFriendServiceImpl extends ServiceImpl<LifeDi
                     lifeDiscountCouponUser.setExpirationTime(expirationTime);
                     lifeDiscountCouponUser.setStatus(Integer.parseInt(DiscountCouponEnum.WAITING_USED.getValue()));
                     lifeDiscountCouponUser.setDeleteFlag(0);
+                    lifeDiscountCouponUser.setIssueSource(3); // 3-好评送券
                     lifeDiscountCouponUserMapper.insert(lifeDiscountCouponUser);
                     coupon.setSingleQty(coupon.getSingleQty() - 1);
                     lifeDiscountCouponMapper.updateById(coupon);
@@ -993,6 +995,7 @@ public class LifeDiscountCouponStoreFriendServiceImpl extends ServiceImpl<LifeDi
                     }
                     lifeDiscountCouponUser.setStatus(Integer.parseInt(DiscountCouponEnum.WAITING_USED.getValue()));
                     lifeDiscountCouponUser.setDeleteFlag(0);
+                    lifeDiscountCouponUser.setIssueSource(3); // 3-好评送券(代金券)
                     lifeDiscountCouponUserMapper.insert(lifeDiscountCouponUser);
                     lifeCoupon.setSingleQty(lifeCoupon.getSingleQty() - 1);
                     lifeCouponMapper.updateById(lifeCoupon);

+ 1 - 0
alien-store/src/main/java/shop/alien/store/service/impl/LifeDiscountCouponUserServiceImpl.java

@@ -108,6 +108,7 @@ public class LifeDiscountCouponUserServiceImpl extends ServiceImpl<LifeDiscountC
 
                 //存入状态  待使用:0
                 lifeDiscountCouponUser.setStatus(Integer.parseInt(DiscountCouponEnum.WAITING_USED.getValue()));
+                lifeDiscountCouponUser.setIssueSource(1); // 1-手动领取
                 lifeDiscountCouponUserMapper.insert(lifeDiscountCouponUser);
             }
             //削减该优惠券库存

+ 2 - 0
alien-store/src/main/java/shop/alien/store/service/impl/LifeGroupPackageServiceImpl.java

@@ -153,6 +153,7 @@ public class LifeGroupPackageServiceImpl extends ServiceImpl<LifeGroupPackageMap
                                     lifeDiscountCouponUser.setExpirationTime(insertedCoupon.getEndDate());
                                     // 设置该优惠券的状态为待使用
                                     lifeDiscountCouponUser.setStatus(Integer.parseInt(DiscountCouponEnum.WAITING_USED.getValue()));
+                                    lifeDiscountCouponUser.setIssueSource(5); // 5-平台发放(团购套餐补偿)
                                     // 将该用户优惠券记录插入到数据库中
                                     if (lifeDiscountCouponUserMapper.insert(lifeDiscountCouponUser) > 0) {
                                         // 发送通知
@@ -283,6 +284,7 @@ public class LifeGroupPackageServiceImpl extends ServiceImpl<LifeGroupPackageMap
                                     lifeDiscountCouponUser.setExpirationTime(insertedCoupon.getEndDate());
                                     // 设置该优惠券的状态为待使用
                                     lifeDiscountCouponUser.setStatus(Integer.parseInt(DiscountCouponEnum.WAITING_USED.getValue()));
+                                    lifeDiscountCouponUser.setIssueSource(5); // 5-平台发放(团购套餐补偿)
                                     // 将该用户优惠券记录插入到数据库中
                                     if (lifeDiscountCouponUserMapper.insert(lifeDiscountCouponUser) > 0) {
                                         // 发送通知

+ 22 - 0
alien-store/src/main/java/shop/alien/store/service/impl/StoreBannerServiceImpl.java

@@ -15,6 +15,10 @@ import shop.alien.entity.store.vo.StoreBannerVo;
 import shop.alien.mapper.StoreBannerMapper;
 import shop.alien.store.service.StoreBannerService;
 
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.Collectors;
+
 /**
  * Banner表 服务实现类
  *
@@ -78,6 +82,24 @@ public class StoreBannerServiceImpl extends ServiceImpl<StoreBannerMapper, Store
         return this.page(page, wrapper).convert(this::toVo);
     }
 
+    @Override
+    public List<StoreBannerVo> getBannerByAISearch(List<String> keyword) {
+        // 去重:过滤空白并按关键词去重
+        List<String> distinctKeywords = (keyword == null || keyword.isEmpty())
+                ? Collections.emptyList()
+                : keyword.stream().filter(StringUtils::isNotBlank).distinct().collect(Collectors.toList());
+
+        LambdaQueryWrapper<StoreBanner> wrapper = new LambdaQueryWrapper<>();
+        wrapper.eq(StoreBanner::getStatus, "1")
+                .orderByAsc(StoreBanner::getSort)
+                .orderByDesc(StoreBanner::getCreatedTime);
+        // 无关键词时查全部 banner,有关键词时按 module_name 过滤
+        if (distinctKeywords != null && !distinctKeywords.isEmpty()) {
+            wrapper.in(StoreBanner::getModuleName, distinctKeywords);
+        }
+        return this.list(wrapper).stream().map(this::toVo).collect(Collectors.toList());
+    }
+
     private StoreBannerVo toVo(StoreBanner banner) {
         StoreBannerVo vo = new StoreBannerVo();
         BeanUtils.copyProperties(banner, vo);

+ 23 - 0
alien-store/src/main/java/shop/alien/store/service/impl/StoreInfoServiceImpl.java

@@ -2946,6 +2946,24 @@ public class StoreInfoServiceImpl extends ServiceImpl<StoreInfoMapper, StoreInfo
                 );
             }
 
+            // 5.5. 判断门店名称是否改变,如果改变则清空类型为20或21的图片
+            StoreInfo oldStoreInfo = storeInfoMapper.selectById(storeInfodto.getId());
+            if (Objects.nonNull(oldStoreInfo) && StringUtils.isNotEmpty(storeInfodto.getStoreName())) {
+                String oldStoreName = oldStoreInfo.getStoreName();
+                String newStoreName = storeInfodto.getStoreName();
+                // 如果门店名称发生改变
+                if (!Objects.equals(oldStoreName, newStoreName)) {
+                    log.info("门店名称发生改变,清空类型为20或21的图片。门店ID:{},旧名称:{},新名称:{}", 
+                            storeInfodto.getId(), oldStoreName, newStoreName);
+                    // 删除该门店类型为20或21的图片
+                    LambdaUpdateWrapper<StoreImg> imgUpdateWrapper = new LambdaUpdateWrapper<>();
+                    imgUpdateWrapper.eq(StoreImg::getStoreId, storeInfodto.getId())
+                            .in(StoreImg::getImgType, Arrays.asList(20, 21))
+                            .set(StoreImg::getDeleteFlag, 1); // 逻辑删除
+                    storeImgMapper.update(null, imgUpdateWrapper);
+                }
+            }
+
             // 6. 执行更新
             int updateNum = storeInfoMapper.updateById(storeInfo);
 
@@ -3391,6 +3409,11 @@ public class StoreInfoServiceImpl extends ServiceImpl<StoreInfoMapper, StoreInfo
                     map.put("overall_match", dataMap.get("overall_match"));
                     if (Objects.nonNull(matchResults) && !matchResults.isEmpty()) {
                         map.put("match_reason", matchResults.get(0).get("match_reason"));
+
+                        //根据url查询表中图片信息
+//                        LambdaQueryWrapper<StoreImg> eq = new LambdaQueryWrapper<StoreImg>().eq(StoreImg::getImgType, 20).eq(StoreImg::getImgUrl, imageUrl);
+//                        StoreImg storeImg = storeImgMapper.selectOne(eq);
+//                        storeImg.setImgStatus("true".equals(matchResults.get(0).get("overall_match"))?1:2);
                     }
                 }
             } else {

+ 1 - 0
alien-store/src/main/java/shop/alien/store/service/impl/StorePlatformBenefitsServiceImpl.java

@@ -264,6 +264,7 @@ public class StorePlatformBenefitsServiceImpl extends ServiceImpl<StorePlatformB
         // 设置该优惠券的状态为待使用
         lifeDiscountCouponUser.setStatus(Integer.parseInt(DiscountCouponEnum.WAITING_USED.getValue()));
         lifeDiscountCouponUser.setWelfareId(benefitsId);
+        lifeDiscountCouponUser.setIssueSource(5); // 5-平台发放(平台福利)
         lifeDiscountCouponUserMapper.insert(lifeDiscountCouponUser);
 
         if (updateResult > 0) {