소스 검색

feat(store): 添加门店OCR识别功能

- 移动并重命名 AiAuthTokenUtil 类至 store 模块
- 在 LifeUserViolationMapper 中引入 Map 类型
- 新增 /getStoreOcrData 接口用于获取OCR识别数据
- 实现 getStoreOcrData 服务方法以支持OCR识别逻辑
- 配置 RestTemplate 并集成 AI 认证工具类
- 添加第三方标识服务的配置属性 third-party-identification.base-url
- 引入 OcrImageUpload 实体及相关常量类
- 完成OCR识别结果查询与AI接口调用逻辑
- 处理OCR识别响应并提取匹配结果信息
Lhaibo 2 주 전
부모
커밋
dee173e643

+ 26 - 0
alien-entity/src/main/java/shop/alien/mapper/LifeUserViolationMapper.java

@@ -10,6 +10,7 @@ import shop.alien.entity.store.LifeUserViolation;
 import shop.alien.entity.store.vo.LifeUserViolationVo;
 
 import java.util.List;
+import java.util.Map;
 
 /**
  * <p>
@@ -104,4 +105,29 @@ public interface LifeUserViolationMapper extends BaseMapper<LifeUserViolation> {
     List<LifeUserViolationVo> getViolationList(
             @Param(Constants.WRAPPER) QueryWrapper<LifeUserViolationVo> queryWrapper
     );
+
+//    @Select("SELECT \n" +
+//            "    vio.id,\n" +
+//            "    dic.dict_detail as complaint_type,\n" +
+//            "    vio.reporting_user_id as reporter_user_id,\n" +
+//            "    vio.reporting_user_type as reporter_user_type,\n" +
+//            "    vio.reported_user_id,\n" +
+//            "    vio.reported_user_type,\n" +
+//            "    vio.goods_id as product_id,\n" +
+//            "    good.title as product_name,\n" +
+//            "    vio.other_reason_content as complaint_text,\n" +
+//            "    vio.report_evidence_img as evidence_images\n" +
+//            "FROM\n" +
+//            "\tlife_user_violation AS vio\n" +
+//            "\tLEFT JOIN\n" +
+//            "\tstore_dictionary AS dic\n" +
+//            "\tON \n" +
+//            "\t\tvio.dict_id = dic.dict_id AND\n" +
+//            "\t\tvio.dict_type = dic.type_name\n" +
+//            "\tLEFT JOIN\n" +
+//            "\tsecond_goods_record AS good\n" +
+//            "\tON \n" +
+//            "\t\tvio.goods_id = good.goods_id\n" +
+//            "WHERE vio.processing_status = 0 and vio.delete_flag = 0")
+//    List<Map<String, Object>> selectAiCheckList();
 }

+ 22 - 0
alien-store/src/main/java/shop/alien/store/controller/StoreInfoController.java

@@ -754,6 +754,28 @@ public class StoreInfoController {
         return R.data(list);
     }
 
+    @ApiOperation(value = "AI服务-门头识别")
+    @ApiOperationSupport(order = 16)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "storeId", value = "门店id", dataType = "String", paramType = "query", required = true),
+            @ApiImplicitParam(name = "imageUrl", value = "图片URL", dataType = "String", paramType = "query", required = true)
+    })
+    @GetMapping("/getStoreOcrData")
+    public R<Map<String, Object>> getStoreOcrData(@RequestParam("storeId") String storeId,
+                                             @RequestParam("imageUrl") String imageUrl) {
+        log.info("StoreInfoController.getStoreOcrData?storeId={},imageUrl={}", storeId, imageUrl);
+        if (storeId == null || storeId.trim().isEmpty() || imageUrl == null || imageUrl.trim().isEmpty()) {
+            return R.fail("门店ID与图片URL不能为空");
+        }
+        Map<String, Object> ocrData = null;
+        try {
+            ocrData = storeInfoService.getStoreOcrData(storeId, imageUrl);
+        } catch (Exception e) {
+            return R.fail("未查询到OCR识别数据");
+        }
+        return R.data(ocrData);
+    }
+
 
     @ApiOperation(value = "更多推荐(用户端)")
     @GetMapping("/getMoreRecommendedStores")

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

@@ -4,6 +4,7 @@ import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.service.IService;
 import org.springframework.http.ResponseEntity;
 import org.springframework.web.multipart.MultipartRequest;
+import shop.alien.entity.store.OcrImageUpload;
 import shop.alien.entity.store.StoreBusinessInfo;
 import shop.alien.entity.store.StoreImg;
 import shop.alien.entity.store.StoreInfo;
@@ -288,4 +289,13 @@ public interface StoreInfoService extends IService<StoreInfo> {
      */
     List<StoreInfoVo> getMoreRecommendedStores(Double lon , Double lat, int subcategory, int threeLevelClassification);
 
+
+    /**
+     * 根据门店及图片地址查询最新OCR识别数据
+     *
+     * @param storeId  门店ID
+     * @param imageUrl 图片URL
+     * @return OCR识别记录
+     */
+    Map<String, Object> getStoreOcrData(String storeId, String imageUrl);
 }

+ 74 - 7
alien-store/src/main/java/shop/alien/store/service/impl/StoreInfoServiceImpl.java

@@ -17,12 +17,12 @@ import org.springframework.beans.BeanUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.data.geo.Point;
-import org.springframework.http.HttpHeaders;
-import org.springframework.http.HttpStatus;
-import org.springframework.http.ResponseEntity;
+import org.springframework.http.*;
+import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 import org.springframework.util.CollectionUtils;
+import org.springframework.web.client.RestTemplate;
 import org.springframework.web.multipart.MultipartFile;
 import org.springframework.web.multipart.MultipartRequest;
 import shop.alien.entity.store.*;
@@ -41,14 +41,12 @@ import shop.alien.store.service.*;
 import shop.alien.store.util.CommonConstant;
 import shop.alien.store.util.FileUploadUtil;
 import shop.alien.store.util.GroupConstant;
+import shop.alien.store.util.ai.AiAuthTokenUtil;
 import shop.alien.store.util.ali.AliApi;
 import shop.alien.util.ali.AliOSSUtil;
 import shop.alien.util.common.DistanceUtil;
 import shop.alien.util.common.UniqueRandomNumGenerator;
-import shop.alien.util.common.constant.CouponStatusEnum;
-import shop.alien.util.common.constant.CouponTypeEnum;
-import shop.alien.util.common.constant.DiscountCouponEnum;
-import shop.alien.util.common.constant.OrderStatusEnum;
+import shop.alien.util.common.constant.*;
 
 import javax.annotation.Resource;
 import java.io.File;
@@ -143,6 +141,12 @@ public class StoreInfoServiceImpl extends ServiceImpl<StoreInfoMapper, StoreInfo
 
     private final LifeBlacklistMapper lifeBlacklistMapper;
 
+    private final OcrImageUploadMapper ocrImageUploadMapper;
+
+    private final AiAuthTokenUtil aiAuthTokenUtil;
+
+    private final RestTemplate restTemplate;
+
     @Resource
     private StoreIncomeDetailsRecordService storeIncomeDetailsRecordService;
 
@@ -153,6 +157,9 @@ public class StoreInfoServiceImpl extends ServiceImpl<StoreInfoMapper, StoreInfo
     @Value("${spring.web.resources.excel-generate-path}")
     private String excelGeneratePath;
 
+    @Value("${third-party-identification.base-url}")
+    private String identificationPath;
+
     /**
      * 懒得查, 留着导出Excel
      */
@@ -2604,4 +2611,64 @@ public class StoreInfoServiceImpl extends ServiceImpl<StoreInfoMapper, StoreInfo
         }
         return filteredList;
     }
+
+    @Override
+    public Map<String, Object> getStoreOcrData(String storeId, String imageUrl) {
+        Map<String, Object> map = new HashMap<>();
+        LambdaQueryWrapper<OcrImageUpload> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(OcrImageUpload::getStoreId, storeId)
+                .eq(OcrImageUpload::getOcrType, OcrTypeEnum.BUSINESS_LICENSE.getCode());
+        OcrImageUpload ocrImageUploads = ocrImageUploadMapper.selectOne(queryWrapper);
+        if(ocrImageUploads== null){
+            throw new RuntimeException("未找到OCI识别数据!");
+        }
+        String accessToken = aiAuthTokenUtil.getAccessToken();
+
+        HttpHeaders aiHeaders = new HttpHeaders();
+        aiHeaders.setContentType(MediaType.APPLICATION_JSON);
+        aiHeaders.set("Authorization", "Bearer " + accessToken);
+        Map<String, Object> jsonBody = new HashMap<>();
+        List<String> imageUrls = new ArrayList<>();
+        imageUrls.add(imageUrl);
+        jsonBody.put("image_urls", imageUrls);
+        String ocrResult = ocrImageUploads.getOcrResult();
+        JSONObject jsonObject = JSONObject.parseObject(ocrResult);
+        jsonBody.put("merchant_name", jsonObject.get("companyName"));
+
+        HttpEntity<Map<String, Object>> request = new HttpEntity<>(jsonBody, aiHeaders);
+        ResponseEntity<String> response = null;
+        HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory();
+        factory.setConnectTimeout(5000);
+        factory.setReadTimeout(60000);
+        restTemplate.setRequestFactory(factory);
+        try {
+            response = restTemplate.postForEntity(identificationPath, request, String.class);
+            if (response.getStatusCodeValue() != 200) {
+                throw new RuntimeException("AI内容审核接口调用失败 http状态:" + response.getStatusCode());
+            }
+
+        } catch (Exception e) {
+            throw new RuntimeException("AI接口调用失败");
+        }
+        com.alibaba.fastjson2.JSONObject responseNode = com.alibaba.fastjson2.JSONObject.parseObject(response.getBody());
+        if (responseNode == null) {
+            throw new RuntimeException("AI接口调用失败,响应内容为空");
+        } else {
+            Integer code = responseNode.getInteger("code");
+            if (code == 200) {
+                Map<String, Object> dataMap = (Map<String, Object>) responseNode.get("data");
+                if (Objects.nonNull(dataMap)) {
+                    // 获取match_results(JSON中是数组,反序列化为List<Map>)
+                    List<Map<String, Object>> matchResults = (List<Map<String, Object>>) dataMap.get("match_results");
+                    map.put("overall_match", dataMap.get("overall_match"));
+                    if (Objects.nonNull(matchResults) && !matchResults.isEmpty()) {
+                        map.put("match_reason", matchResults.get(0).get("match_reason"));
+                    }
+                }
+            } else {
+                throw new RuntimeException("AI接口调用失败,错误码: " + code);
+            }
+        }
+        return map;
+    }
 }

+ 4 - 4
alien-util/src/main/java/shop/alien/util/ai/AiAuthTokenUtil.java → alien-store/src/main/java/shop/alien/store/util/ai/AiAuthTokenUtil.java

@@ -1,4 +1,4 @@
-package shop.alien.util.ai;
+package shop.alien.store.util.ai;
 
 import com.alibaba.fastjson2.JSONObject;
 import lombok.RequiredArgsConstructor;
@@ -21,13 +21,13 @@ public class AiAuthTokenUtil {
 
     private final RestTemplate restTemplate;
 
-    @Value("${third-party-login.base-url:http://192.168.2.250:9000/ai/user-auth-core/api/v1/auth/login}")
+    @Value("${third-party-login.base-url}")
     private String loginUrl;
 
-    @Value("${third-party-user-name.base-url:UdUser}")
+    @Value("${third-party-user-name.base-url}")
     private String userName;
 
-    @Value("${third-party-pass-word.base-url:123456}")
+    @Value("${third-party-pass-word.base-url}")
     private String passWord;
 
     /**