浏览代码

feat(store): 新增AI门店审核功能

- 新增AiApproveStoreInfo实体类,用于接收AI服务请求参数
- 在StoreInfo实体类和StoreInfoVo视图类中增加mealsFlag字段,标识是否提供餐食
- 在StoreInfoController中新增aiApproveStoreInfo接口,供AI服务调用进行门店审核
- 在StoreInfoService及其实现类中新增aiApproveStoreInfo方法,处理AI审核逻辑
- 配置第三方应用基础路径,支持向AI服务发送请求并解析返回结果
- 根据AI审核结果更新门店申请状态(通过/拒绝)
Lhaibo 1 周之前
父节点
当前提交
531190aec2

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

@@ -244,4 +244,8 @@ public class StoreInfo {
     private String businessClassifyName;
 
 
+
+    @ApiModelProperty(value = "是否提供餐食")
+    @TableField("meals_flag")
+    private Integer  mealsFlag;
 }

+ 3 - 0
alien-entity/src/main/java/shop/alien/entity/store/dto/StoreInfoDto.java

@@ -186,4 +186,7 @@ public class StoreInfoDto {
     @ApiModelProperty(value = "分类id(词典表 键为 business_classify)(多个ID用逗号拼接)")
     @JsonDeserialize(using = StringToListDeserializer.class)
     private List<String> businessClassify;
+
+    @ApiModelProperty(value = "是否提供餐食")
+    private Integer  mealsFlag;
 }

+ 34 - 0
alien-entity/src/main/java/shop/alien/entity/store/vo/AiApproveStoreInfo.java

@@ -0,0 +1,34 @@
+package shop.alien.entity.store.vo;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.util.List;
+
+/**
+ * 店铺审核 - AI 服务请求体
+ */
+@Data
+public class AiApproveStoreInfo {
+
+    @ApiModelProperty(value = "用户id")
+    private String userId;
+
+    @ApiModelProperty(value = "商户名称(公司/门店名称)")
+    private String merchant_name;
+
+    @ApiModelProperty(value = "经营范围 / 业务描述")
+    private String business_scope;
+
+    @ApiModelProperty(value = "联系人姓名")
+    private String contact_name;
+
+    @ApiModelProperty(value = "联系人手机号")
+    private String contact_phone;
+
+    @ApiModelProperty(value = "联系人邮箱")
+    private String contact_email;
+
+    @ApiModelProperty(value = "证照/资质门头照片列表")
+    private List<String> license_images;
+}

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

@@ -1,5 +1,6 @@
 package shop.alien.entity.store.vo;
 
+import com.baomidou.mybatisplus.annotation.TableField;
 import com.fasterxml.jackson.annotation.JsonFormat;
 import com.fasterxml.jackson.annotation.JsonInclude;
 import io.swagger.annotations.ApiModel;
@@ -226,4 +227,7 @@ public class StoreInfoVo extends StoreInfo {
 
     @ApiModelProperty(value = "分类名称")
     private String businessClassifyName;
+
+    @ApiModelProperty(value = "是否提供餐食")
+    private Integer  mealsFlag;
 }

+ 15 - 1
alien-store/src/main/java/shop/alien/store/controller/StoreInfoController.java

@@ -981,7 +981,7 @@ public class StoreInfoController {
             @RequestParam(value = "businessClassify", required = false) String businessClassify,
             @RequestParam(value = "lon", required = false) Double lon,
             @RequestParam(value = "lat", required = false) Double lat) {
-        log.info("StoreInfoController.getRecommendedStores?businessSection={},businessTypes={},businessClassify={},lon={},lat={}", 
+        log.info("StoreInfoController.getRecommendedStores?businessSection={},businessTypes={},businessClassify={},lon={},lat={}",
                 businessSection, businessTypes, businessClassify, lon, lat);
         try {
             List<StoreInfoVo> result = storeInfoService.getRecommendedStores(businessSection, businessTypes, businessClassify, lon, lat);
@@ -1015,4 +1015,18 @@ public class StoreInfoController {
     }
 
 
+
+    @ApiOperation(value = "AI服务-店铺审核")
+    @ApiOperationSupport(order = 15)
+    @PostMapping("/aiApproveStoreInfo")
+    public R<Boolean> aiApproveStoreInfo(@RequestBody AiApproveStoreInfo aiApproveStoreInfo) {
+        log.info("StoreInfoController.aiApproveStoreInfo");
+        try {
+            storeInfoService.aiApproveStoreInfo(aiApproveStoreInfo);
+            return R.success("店铺审核完成");
+        } catch (Exception e) {
+            log.error("AI服务-店铺审核异常", e);
+            return R.fail("店铺审核失败:" + e.getMessage());
+        }
+    }
 }

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

@@ -370,4 +370,6 @@ public interface StoreInfoService extends IService<StoreInfo> {
      * @return List<StoreDictionaryVo> 三级分类列表
      */
     List<StoreDictionaryVo> getBusinessClassifyData(Integer parentId);
+
+    void aiApproveStoreInfo(AiApproveStoreInfo aiApproveStoreInfo);
 }

+ 77 - 36
alien-store/src/main/java/shop/alien/store/service/impl/StoreInfoServiceImpl.java

@@ -163,6 +163,9 @@ public class StoreInfoServiceImpl extends ServiceImpl<StoreInfoMapper, StoreInfo
     @Value("${third-party-identification.base-url}")
     private String identificationPath;
 
+    @Value("${third-party-applications.base-url}")
+    private String applicationsPath;
+
     /**
      * 懒得查, 留着导出Excel
      */
@@ -643,7 +646,7 @@ public class StoreInfoServiceImpl extends ServiceImpl<StoreInfoMapper, StoreInfo
         storeInfo.setBusinessSectionName(businessSectionName.getDictDetail());
         storeInfo.setBusinessTypes(String.join(",", businessTypes));
         storeInfo.setBusinessTypesName(String.join(",", businessTypeNames));
-        
+
         //处理分类信息
         List<String> businessClassify = storeInfoDto.getBusinessClassify();
         if (!CollectionUtils.isEmpty(businessClassify)) {
@@ -674,7 +677,7 @@ public class StoreInfoServiceImpl extends ServiceImpl<StoreInfoMapper, StoreInfo
             storeInfo.setBusinessClassify(String.join(",", businessClassify));
             storeInfo.setBusinessClassifyName(String.join(",", businessClassifyNames));
         }
-        
+
         //存入审批状态为待审批
         storeInfo.setStoreApplicationStatus(0);
         //处理一下行政区域信息
@@ -880,7 +883,7 @@ public class StoreInfoServiceImpl extends ServiceImpl<StoreInfoMapper, StoreInfo
         storeInfo.setBusinessSectionName(businessSectionName.getDictDetail());
         storeInfo.setBusinessTypes(String.join(",", businessTypes));
         storeInfo.setBusinessTypesName(String.join(",", businessTypeNames));
-        
+
         //处理分类信息
         List<String> businessClassify = storeInfoDto.getBusinessClassify();
         if (!CollectionUtils.isEmpty(businessClassify)) {
@@ -911,7 +914,7 @@ public class StoreInfoServiceImpl extends ServiceImpl<StoreInfoMapper, StoreInfo
             storeInfo.setBusinessClassify(String.join(",", businessClassify));
             storeInfo.setBusinessClassifyName(String.join(",", businessClassifyNames));
         }
-        
+
         //处理一下行政区域信息
         EssentialCityCode essentialCityCode1 = essentialCityCodeMapper.selectOne(new LambdaQueryWrapper<EssentialCityCode>().eq(EssentialCityCode::getAreaCode, storeInfo.getAdministrativeRegionProvinceAdcode()));
         storeInfo.setAdministrativeRegionProvinceName(essentialCityCode1.getAreaName());
@@ -2263,7 +2266,7 @@ public class StoreInfoServiceImpl extends ServiceImpl<StoreInfoMapper, StoreInfo
                 storeInfo.setBusinessTypes(String.join(",", businessTypes));
                 storeInfo.setBusinessTypesName(String.join(",", businessTypeNames));
             }
-            
+
             // 处理分类信息
             List<String> businessClassify = storeInfodto.getBusinessClassify();
             if (!CollectionUtils.isEmpty(businessClassify)) {
@@ -2578,10 +2581,10 @@ public class StoreInfoServiceImpl extends ServiceImpl<StoreInfoMapper, StoreInfo
             if (dict == null || dict.getDeleteFlag() == 1) {
                 throw new IllegalArgumentException("字典id不存在或已删除: " + dictId);
             }
-            
+
             String typeName = dict.getTypeName();
             String dictIdStr =dict.getDictId();
-            
+
             if ("business_section".equals(typeName)) {
                 // 如果是经营板块,直接匹配business_section
                 try {
@@ -2598,7 +2601,7 @@ public class StoreInfoServiceImpl extends ServiceImpl<StoreInfoMapper, StoreInfo
                 if (parentDict == null || !"business_section".equals(parentDict.getTypeName())) {
                     throw new IllegalArgumentException("经营种类的父级不是经营板块,dictId: " + dictId);
                 }
-                
+
                 try {
                     Integer sectionId = Integer.parseInt(parentDict.getDictId());
                     queryWrapper.eq("a.business_section", sectionId);
@@ -2616,12 +2619,12 @@ public class StoreInfoServiceImpl extends ServiceImpl<StoreInfoMapper, StoreInfo
                 if (parentDict == null || !"business_type".equals(parentDict.getTypeName())) {
                     throw new IllegalArgumentException("分类的父级不是经营种类,dictId: " + dictId);
                 }
-                
+
                 StoreDictionary grandParentDict = storeDictionaryMapper.selectById(parentDict.getParentId());
                 if (grandParentDict == null || !"business_section".equals(grandParentDict.getTypeName())) {
                     throw new IllegalArgumentException("经营种类的父级不是经营板块,dictId: " + dictId);
                 }
-                
+
                 try {
                     Integer sectionId = Integer.parseInt(grandParentDict.getDictId());
                     queryWrapper.eq("a.business_section", sectionId);
@@ -3221,18 +3224,18 @@ public class StoreInfoServiceImpl extends ServiceImpl<StoreInfoMapper, StoreInfo
      */
     @Override
     public List<StoreInfoVo> getRecommendedStores(String businessSection, String businessTypes, String businessClassify, Double lon, Double lat) {
-        log.info("StoreInfoServiceImpl.getRecommendedStores?businessSection={},businessTypes={},businessClassify={},lon={},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)
@@ -3251,7 +3254,7 @@ public class StoreInfoServiceImpl extends ServiceImpl<StoreInfoMapper, StoreInfo
                 }
             });
         }
-        
+
         // 2. 然后按二级分类(business_types)筛选
         if (StringUtils.isNotEmpty(businessTypes)) {
             // business_types可能是逗号分隔的字符串,使用FIND_IN_SET处理
@@ -3269,7 +3272,7 @@ public class StoreInfoServiceImpl extends ServiceImpl<StoreInfoMapper, StoreInfo
                 }
             });
         }
-        
+
         // 3. 最后按一级分类(business_section)筛选
         if (StringUtils.isNotEmpty(businessSection)) {
             try {
@@ -3279,7 +3282,7 @@ public class StoreInfoServiceImpl extends ServiceImpl<StoreInfoMapper, StoreInfo
                 log.warn("一级分类参数格式错误: {}", businessSection);
             }
         }
-        
+
         // 查询店铺列表
         List<StoreInfoVo> storeInfoVoList;
         if (lon != null && lat != null) {
@@ -3295,11 +3298,11 @@ public class StoreInfoServiceImpl extends ServiceImpl<StoreInfoMapper, StoreInfo
             queryWrapper.orderByDesc("a.created_time");
             storeInfoVoList = storeInfoMapper.getStoreInfoVoList(queryWrapper);
         }
-        
+
         if (CollectionUtils.isEmpty(storeInfoVoList)) {
             return Collections.emptyList();
         }
-        
+
         // 处理店铺信息,设置评分等
         // 提前查询所有需要的字典数据
         List<StoreInfoVo> collect = storeInfoVoList.stream()
@@ -3309,7 +3312,7 @@ public class StoreInfoServiceImpl extends ServiceImpl<StoreInfoMapper, StoreInfo
                 .map(StoreInfoVo::getStoreType)
                 .flatMap(type -> Arrays.stream(type.split(",")))
                 .collect(Collectors.toSet());
-        
+
         List<StoreDictionary> storeDictionaries = storeDictionaryMapper.selectList(
                 new LambdaQueryWrapper<StoreDictionary>()
                         .eq(StoreDictionary::getTypeName, "storeType")
@@ -3317,11 +3320,11 @@ public class StoreInfoServiceImpl extends ServiceImpl<StoreInfoMapper, StoreInfo
                         .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(
@@ -3330,7 +3333,7 @@ public class StoreInfoServiceImpl extends ServiceImpl<StoreInfoMapper, StoreInfo
                         .eq("delete_flag", 0))
                 .stream()
                 .collect(Collectors.groupingBy(StoreComment::getStoreId));
-        
+
         for (StoreInfoVo record : storeInfoVoList) {
             // 处理类型
             if (StringUtils.isNotEmpty(record.getStoreType())) {
@@ -3342,7 +3345,7 @@ public class StoreInfoServiceImpl extends ServiceImpl<StoreInfoMapper, StoreInfo
                 record.setStoreTypeStr(String.join(",", typeDetails));
                 record.setStoreTypeList(Arrays.asList(types));
             }
-            
+
             // 处理经纬度
             if (StringUtils.isNotEmpty(record.getStorePosition())) {
                 String[] split = record.getStorePosition().split(",");
@@ -3351,7 +3354,7 @@ public class StoreInfoServiceImpl extends ServiceImpl<StoreInfoMapper, StoreInfo
                     record.setStorePositionLatitude(split[1]);
                 }
             }
-            
+
             // 如果提供了经纬度,处理距离信息
             if (lon != null && lat != null && StringUtils.isNotEmpty(record.getStorePosition())) {
                 try {
@@ -3368,7 +3371,7 @@ public class StoreInfoServiceImpl extends ServiceImpl<StoreInfoMapper, StoreInfo
                     log.warn("计算店铺距离失败,storeId={},error={}", record.getId(), e.getMessage());
                 }
             }
-            
+
             // 处理到期状态
             Date expirationTime = record.getExpirationTime();
             if (expirationTime != null) {
@@ -3377,17 +3380,17 @@ public class StoreInfoServiceImpl extends ServiceImpl<StoreInfoMapper, StoreInfo
                 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)) 
+                    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())
@@ -3395,7 +3398,7 @@ public class StoreInfoServiceImpl extends ServiceImpl<StoreInfoMapper, StoreInfo
                 long daysToExpire = ChronoUnit.DAYS.between(nowLocal, expDate);
                 record.setDaysToExpire(daysToExpire);
             }
-            
+
             // 设置店铺得分和总评论数
             if (avgScoreMap.containsKey(String.valueOf(record.getId()))) {
                 record.setAvgScore(String.valueOf(
@@ -3405,7 +3408,7 @@ public class StoreInfoServiceImpl extends ServiceImpl<StoreInfoMapper, StoreInfo
                 record.setTotalNum(String.valueOf(commentMap.get(record.getId()).size()));
             }
         }
-        
+
         return storeInfoVoList;
     }
 
@@ -3975,7 +3978,7 @@ public class StoreInfoServiceImpl extends ServiceImpl<StoreInfoMapper, StoreInfo
             );
             if (firstLevelDict != null) {
                 allDicts.add(firstLevelDict);
-                log.debug("查询到一级分类: id={}, dictId={}, dictDetail={}", 
+                log.debug("查询到一级分类: id={}, dictId={}, dictDetail={}",
                         firstLevelDict.getId(), firstLevelDict.getDictId(), firstLevelDict.getDictDetail());
             } else {
                 log.warn("未查询到一级分类,businessSection={}", businessSection);
@@ -4009,7 +4012,7 @@ public class StoreInfoServiceImpl extends ServiceImpl<StoreInfoMapper, StoreInfo
                 allDicts.addAll(secondLevelDicts);
                 log.debug("查询到二级分类数量: {}, dictIds={}", secondLevelDicts.size(), typeDictIdList);
             } else {
-                log.warn("无法查询二级分类: typeDictIdList为空或一级分类不存在, typeDictIdList={}, firstLevelDict={}", 
+                log.warn("无法查询二级分类: typeDictIdList为空或一级分类不存在, typeDictIdList={}, firstLevelDict={}",
                         typeDictIdList, firstLevelDict != null ? firstLevelDict.getId() : null);
             }
         }
@@ -4043,10 +4046,10 @@ public class StoreInfoServiceImpl extends ServiceImpl<StoreInfoMapper, StoreInfo
 //                                .orderByAsc(StoreDictionary::getSortId)
                 );
                 allDicts.addAll(thirdLevelDicts);
-                log.debug("查询到三级分类数量: {}, dictIds={}, parentIds={}", 
+                log.debug("查询到三级分类数量: {}, dictIds={}, parentIds={}",
                         thirdLevelDicts.size(), classifyDictIdList, secondLevelIds);
             } else {
-                log.warn("无法查询三级分类: classifyDictIdList为空或二级分类为空, classifyDictIdList={}, secondLevelDicts.size={}", 
+                log.warn("无法查询三级分类: classifyDictIdList为空或二级分类为空, classifyDictIdList={}, secondLevelDicts.size={}",
                         classifyDictIdList, secondLevelDicts.size());
             }
         }
@@ -4059,8 +4062,46 @@ public class StoreInfoServiceImpl extends ServiceImpl<StoreInfoMapper, StoreInfo
         vo.setStoreId(storeInfo.getId());
         vo.setStoreName(storeInfo.getStoreName());
         vo.setThreeLevelStructure(treeStructure);
-        
+
         return vo;
     }
 
+
+    @Override
+    public void aiApproveStoreInfo(AiApproveStoreInfo aiApproveStoreInfo) {
+        HttpHeaders aiHeaders = new HttpHeaders();
+        aiHeaders.setContentType(MediaType.APPLICATION_JSON);
+        aiHeaders.set("Authorization", "Bearer " + aiAuthTokenUtil.getAccessToken());
+
+        HttpEntity<AiApproveStoreInfo> request = new HttpEntity<>(aiApproveStoreInfo, aiHeaders);
+        ResponseEntity<String> response = null;
+        try {
+            response = restTemplate.postForEntity(applicationsPath, request, String.class);
+            if (response.getStatusCodeValue() != 200) {
+                throw new RuntimeException("AI门店审核接口调用失败 http状态:" + response.getStatusCode());
+            }
+            if (StringUtils.isNotEmpty(response.getBody())) {
+                JSONObject jsonObject = JSONObject.parseObject(response.getBody());
+                if (jsonObject.getInteger("code") == 200) {
+                    JSONObject data = jsonObject.getJSONObject("data");
+                    List<StoreInfo> storeInfos = storeInfoMapper.selectList(new LambdaQueryWrapper<StoreInfo>()
+                            .eq(StoreInfo::getCreatedUserId, aiApproveStoreInfo.getUserId()).eq(StoreInfo::getStoreApplicationStatus, 0).eq(StoreInfo::getDeleteFlag, 0));
+                    for (StoreInfo storeInfo : storeInfos) {
+                        if ("approved".equals(data.getString("status"))) {
+                            storeInfo.setStoreApplicationStatus(1);
+                        } else if ("rejected".equals(data.getString("status"))) {
+                            storeInfo.setStoreApplicationStatus(2);
+                        } else {
+                            System.out.println("未知状态");
+                        }
+                        storeInfoMapper.updateById(storeInfo);
+                    }
+                } else {
+                    throw new RuntimeException("AI门店审核接口调用失败 code:" + jsonObject.getInteger("code"));
+                }
+            }
+        } catch (Exception e){
+            throw new RuntimeException("调用门店审核接口异常", e);
+        }
+    }
 }