Browse Source

ai识别优化

cangku
wangfukang 2 years ago
parent
commit
6975878deb
  1. 2
      hiver-modules/hiver-mall/pom.xml
  2. 8
      hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/config/thread/AiPurchaseThread.java
  3. 3
      hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/pojo/vo/CustomerBuyProductLogVo.java
  4. 3
      hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/pojo/vo/SupplierBuyProductLogVo.java
  5. 5
      hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/purchaseocr/service/impl/PurchaseOcrPictureServiceImpl.java
  6. 3
      hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/purchaseocr/vo/PurchaseOciPictureAddVo.java
  7. 14
      hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/purchaseocr/vo/PurchaseOcrExample.java
  8. 6
      hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/serviceimpl/mybatis/PurchaseDetailServiceImpl.java
  9. 433
      hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/utils/AliOcrUtil.java
  10. 3
      hiver-modules/hiver-mall/src/main/resources/mapper/PurchaseMapper.xml
  11. 16
      hiver-modules/hiver-mall/src/main/resources/mapper/SaleMapper.xml

2
hiver-modules/hiver-mall/pom.xml

@ -20,7 +20,7 @@
<dependency> <dependency>
<groupId>com.alibaba</groupId> <groupId>com.alibaba</groupId>
<artifactId>dashscope-sdk-java</artifactId> <artifactId>dashscope-sdk-java</artifactId>
<version>2.12.0</version> <version>2.14.1</version>
<exclusions> <exclusions>
<exclusion> <exclusion>
<groupId>org.slf4j</groupId> <groupId>org.slf4j</groupId>

8
hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/config/thread/AiPurchaseThread.java

@ -3,6 +3,7 @@ package cc.hiver.mall.config.thread;
import cc.hiver.mall.common.constant.PurchaseConstant; import cc.hiver.mall.common.constant.PurchaseConstant;
import cc.hiver.mall.purchaseocr.entity.PurchaseOcrPicture; import cc.hiver.mall.purchaseocr.entity.PurchaseOcrPicture;
import cc.hiver.mall.purchaseocr.service.PurchaseOcrPictureService; import cc.hiver.mall.purchaseocr.service.PurchaseOcrPictureService;
import cc.hiver.mall.purchaseocr.vo.PurchaseOcrExample;
import cc.hiver.mall.service.mybatis.ProductService; import cc.hiver.mall.service.mybatis.ProductService;
import cc.hiver.mall.service.mybatis.PurchaseDetailService; import cc.hiver.mall.service.mybatis.PurchaseDetailService;
import cc.hiver.mall.service.mybatis.PurchaseService; import cc.hiver.mall.service.mybatis.PurchaseService;
@ -29,13 +30,15 @@ public class AiPurchaseThread implements Runnable {
private PurchaseService purchaseService; private PurchaseService purchaseService;
private PurchaseDetailService purchaseDetailService; private PurchaseDetailService purchaseDetailService;
private PurchaseOcrPicture purchaseOcrPicture; private PurchaseOcrPicture purchaseOcrPicture;
private PurchaseOcrExample purchaseOcrExample;
public AiPurchaseThread() { public AiPurchaseThread() {
} }
public AiPurchaseThread(String purchaseId, PurchaseOcrPicture purchaseOcrPicture, PurchaseOcrPictureService purchaseOcrPictureService, ProductService productService, PurchaseDetailService purchaseDetailService, PurchaseService purchaseService) { public AiPurchaseThread(String purchaseId, PurchaseOcrPicture purchaseOcrPicture,PurchaseOcrExample purchaseOcrExample, PurchaseOcrPictureService purchaseOcrPictureService, ProductService productService, PurchaseDetailService purchaseDetailService, PurchaseService purchaseService) {
this.purchaseId = purchaseId; this.purchaseId = purchaseId;
this.purchaseOcrPicture = purchaseOcrPicture; this.purchaseOcrPicture = purchaseOcrPicture;
this.purchaseOcrExample = purchaseOcrExample;
this.purchaseOcrPictureService = purchaseOcrPictureService; this.purchaseOcrPictureService = purchaseOcrPictureService;
this.productService = productService; this.productService = productService;
this.purchaseDetailService = purchaseDetailService; this.purchaseDetailService = purchaseDetailService;
@ -66,7 +69,7 @@ public class AiPurchaseThread implements Runnable {
log.info("当前线程名称:------------>>>>>" + Thread.currentThread().getName()); log.info("当前线程名称:------------>>>>>" + Thread.currentThread().getName());
try { try {
log.info("正在ocr识别:" + purchaseOcrPicture.getOcrPicture() + "<br/>线程池名称:" + Thread.currentThread().getName()); log.info("正在ocr识别:" + purchaseOcrPicture.getOcrPicture() + "<br/>线程池名称:" + Thread.currentThread().getName());
jsonObject = AliOcrUtil.multiRoundConversationCall(purchaseOcrPicture, productService, purchaseDetailService); jsonObject = AliOcrUtil.multiRoundConversationCall(purchaseOcrPicture,purchaseOcrExample, productService, purchaseDetailService);
// 得到处理结果之后,开始新增库存的详细信息 // 得到处理结果之后,开始新增库存的详细信息
purchaseOcrPicture.setOcrMsg(jsonObject.getString("resultContent")); purchaseOcrPicture.setOcrMsg(jsonObject.getString("resultContent"));
purchaseOcrPicture.setOcrStatus(PurchaseConstant.OCR_STATUS[1]); purchaseOcrPicture.setOcrStatus(PurchaseConstant.OCR_STATUS[1]);
@ -78,6 +81,7 @@ public class AiPurchaseThread implements Runnable {
purchaseOcrPictureService.updateStatus(purchaseOcrPicture); purchaseOcrPictureService.updateStatus(purchaseOcrPicture);
jsonObject.put("code", 500); jsonObject.put("code", 500);
jsonObject.put("msg", e.getMessage()); jsonObject.put("msg", e.getMessage());
log.error(e.getMessage(),e);
// throw new RuntimeException(e); // throw new RuntimeException(e);
} }
// 尝试更新主表识别状态 // 尝试更新主表识别状态

3
hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/pojo/vo/CustomerBuyProductLogVo.java

@ -19,6 +19,9 @@ public class CustomerBuyProductLogVo implements Serializable {
@ApiModelProperty(value = "商品名称") @ApiModelProperty(value = "商品名称")
private String productName; private String productName;
@ApiModelProperty(value = "商品货号")
private String productSn;
@ApiModelProperty(value = "历次销售单") @ApiModelProperty(value = "历次销售单")
private List<CustomerBuySaleVo> customerBuySaleVoList; private List<CustomerBuySaleVo> customerBuySaleVoList;
} }

3
hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/pojo/vo/SupplierBuyProductLogVo.java

@ -19,6 +19,9 @@ public class SupplierBuyProductLogVo implements Serializable {
@ApiModelProperty(value = "商品名称") @ApiModelProperty(value = "商品名称")
private String productName; private String productName;
@ApiModelProperty(value = "商品货号")
private String productSn;
@ApiModelProperty(value = "历次采购单") @ApiModelProperty(value = "历次采购单")
private List<SupplierBuyPurchaseVo> supplierBuySaleVos; private List<SupplierBuyPurchaseVo> supplierBuySaleVos;
} }

5
hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/purchaseocr/service/impl/PurchaseOcrPictureServiceImpl.java

@ -12,6 +12,7 @@ import cc.hiver.mall.purchaseocr.mapper.PurchaseOcrPictureMapper;
import cc.hiver.mall.purchaseocr.service.PurchaseOcrPictureService; import cc.hiver.mall.purchaseocr.service.PurchaseOcrPictureService;
import cc.hiver.mall.purchaseocr.vo.PurchaseOciPictureAddVo; import cc.hiver.mall.purchaseocr.vo.PurchaseOciPictureAddVo;
import cc.hiver.mall.purchaseocr.vo.PurchaseOcrCountVo; import cc.hiver.mall.purchaseocr.vo.PurchaseOcrCountVo;
import cc.hiver.mall.purchaseocr.vo.PurchaseOcrExample;
import cc.hiver.mall.service.ShopService; import cc.hiver.mall.service.ShopService;
import cc.hiver.mall.service.mybatis.ProductService; import cc.hiver.mall.service.mybatis.ProductService;
import cc.hiver.mall.service.mybatis.PurchaseDetailService; import cc.hiver.mall.service.mybatis.PurchaseDetailService;
@ -113,9 +114,11 @@ public class PurchaseOcrPictureServiceImpl implements PurchaseOcrPictureService
purchaseOcrPictureMapper.batchSave(purchaseOcrPictureAddList); purchaseOcrPictureMapper.batchSave(purchaseOcrPictureAddList);
// 异步处理ocr识别 // 异步处理ocr识别
try { try {
// 获取参数示例
PurchaseOcrExample purchaseOcrExample = purchaseOciPictureAddVo.getPurchaseOcrExample();
AiPurchaseThread timerThread; AiPurchaseThread timerThread;
for (PurchaseOcrPicture purchaseOcrPicture : purchaseOcrPictureAddList) { for (PurchaseOcrPicture purchaseOcrPicture : purchaseOcrPictureAddList) {
timerThread = new AiPurchaseThread(purchaseId, purchaseOcrPicture, purchaseOcrPictureService, productService, purchaseDetailService, purchaseService); timerThread = new AiPurchaseThread(purchaseId, purchaseOcrPicture,purchaseOcrExample, purchaseOcrPictureService, productService, purchaseDetailService, purchaseService);
// 这里可以使用线程池,也可以使用CompletionService处理,运行任务需要是callable的,需要最终结果。 // 这里可以使用线程池,也可以使用CompletionService处理,运行任务需要是callable的,需要最终结果。
threadPoolConfiguration.threadPoolTaskExecutor().execute(timerThread); threadPoolConfiguration.threadPoolTaskExecutor().execute(timerThread);
} }

3
hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/purchaseocr/vo/PurchaseOciPictureAddVo.java

@ -14,4 +14,7 @@ public class PurchaseOciPictureAddVo {
@ApiModelProperty(value = "Oci图片信息") @ApiModelProperty(value = "Oci图片信息")
private List<PurchaseOcrPicture> purchaseOcrPictureList; private List<PurchaseOcrPicture> purchaseOcrPictureList;
@ApiModelProperty("本次识别示例")
private PurchaseOcrExample purchaseOcrExample;
} }

14
hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/purchaseocr/vo/PurchaseOcrExample.java

@ -0,0 +1,14 @@
package cc.hiver.mall.purchaseocr.vo;
import lombok.Data;
@Data
public class PurchaseOcrExample {
private String productSn;
private String productName;
private String price;
private String productCount;
private String color;
private String size;
}

6
hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/serviceimpl/mybatis/PurchaseDetailServiceImpl.java

@ -34,7 +34,7 @@ public class PurchaseDetailServiceImpl extends ServiceImpl<PurchaseDetailMapper,
@Override @Override
public void batchSavePurchaseList(List<PurchaseDetail> purchaseDetails) { public void batchSavePurchaseList(List<PurchaseDetail> purchaseDetails) {
// 根据货号获取商品的详细信息是否存在 // 根据货号获取商品的详细信息是否存在
final List<PurchaseDetail> addPurchaseDetails = new ArrayList<>(); /*final List<PurchaseDetail> addPurchaseDetails = new ArrayList<>();
final List<PurchaseDetail> updatePurchaseDetails = new ArrayList<>(); final List<PurchaseDetail> updatePurchaseDetails = new ArrayList<>();
purchaseDetails.forEach(purchaseDetail -> { purchaseDetails.forEach(purchaseDetail -> {
final String productSn = purchaseDetail.getProductSn(); final String productSn = purchaseDetail.getProductSn();
@ -61,8 +61,10 @@ public class PurchaseDetailServiceImpl extends ServiceImpl<PurchaseDetailMapper,
} }
if(!updatePurchaseDetails.isEmpty()){ if(!updatePurchaseDetails.isEmpty()){
purchaseDetailMapper.batchUpdatePurchaseList(updatePurchaseDetails); purchaseDetailMapper.batchUpdatePurchaseList(updatePurchaseDetails);
} }*/
// 20240514 识别的时候不进行合并?
purchaseDetailMapper.batchSavePurchaseList(purchaseDetails);
// 批量插入库存日志 // 批量插入库存日志
final List<StockLog> stockLogList = new ArrayList<>(); final List<StockLog> stockLogList = new ArrayList<>();
for (PurchaseDetail purchaseDetail : purchaseDetails) { for (PurchaseDetail purchaseDetail : purchaseDetails) {

433
hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/utils/AliOcrUtil.java

@ -5,6 +5,7 @@ import cc.hiver.mall.config.aliocr.AliOcrConfig;
import cc.hiver.mall.entity.*; import cc.hiver.mall.entity.*;
import cc.hiver.mall.pojo.vo.*; import cc.hiver.mall.pojo.vo.*;
import cc.hiver.mall.purchaseocr.entity.PurchaseOcrPicture; import cc.hiver.mall.purchaseocr.entity.PurchaseOcrPicture;
import cc.hiver.mall.purchaseocr.vo.PurchaseOcrExample;
import cc.hiver.mall.service.mybatis.*; import cc.hiver.mall.service.mybatis.*;
import cn.hutool.core.date.StopWatch; import cn.hutool.core.date.StopWatch;
import com.alibaba.dashscope.aigc.generation.Generation; import com.alibaba.dashscope.aigc.generation.Generation;
@ -13,6 +14,7 @@ import com.alibaba.dashscope.aigc.generation.models.QwenParam;
import com.alibaba.dashscope.aigc.multimodalconversation.*; import com.alibaba.dashscope.aigc.multimodalconversation.*;
import com.alibaba.dashscope.common.Message; import com.alibaba.dashscope.common.Message;
import com.alibaba.dashscope.common.MessageManager; import com.alibaba.dashscope.common.MessageManager;
import com.alibaba.dashscope.common.MultiModalMessage;
import com.alibaba.dashscope.common.Role; import com.alibaba.dashscope.common.Role;
import com.alibaba.dashscope.exception.ApiException; import com.alibaba.dashscope.exception.ApiException;
import com.alibaba.dashscope.exception.InputRequiredException; import com.alibaba.dashscope.exception.InputRequiredException;
@ -22,16 +24,14 @@ import com.alibaba.dashscope.utils.Constants;
import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.JSONObject;
import io.reactivex.Flowable;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import java.util.Arrays; import java.util.*;
import java.util.Collections;
import java.util.Date;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CopyOnWriteArrayList;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@ -49,7 +49,7 @@ public class AliOcrUtil {
private static final Pattern SYCM = Pattern.compile("所有尺码", Pattern.LITERAL); private static final Pattern SYCM = Pattern.compile("所有尺码", Pattern.LITERAL);
/** /**
* AI入库图像识别 * AI入库图像识别-多轮对话
* *
* @param purchaseOcrPicture * @param purchaseOcrPicture
* @param productService * @param productService
@ -58,9 +58,10 @@ public class AliOcrUtil {
* @author 王富康 * @author 王富康
* @date 2024/3/31 * @date 2024/3/31
*/ */
public static JSONObject multiRoundConversationCall(PurchaseOcrPicture purchaseOcrPicture, ProductService productService, PurchaseDetailService purchaseDetailService) throws ApiException, NoApiKeyException, UploadFileException { public static JSONObject multiRoundConversationCall(PurchaseOcrPicture purchaseOcrPicture, PurchaseOcrExample purchaseOcrExample, ProductService productService, PurchaseDetailService purchaseDetailService) throws ApiException, NoApiKeyException, UploadFileException {
final JSONObject jsonObject = new JSONObject(); final JSONObject jsonObject = new JSONObject();
final String picturePath = purchaseOcrPicture.getOcrPicture(); final String picturePath = purchaseOcrPicture.getOcrPicture();
final String parentPicturePath = purchaseOcrPicture.getParentPicturePath();
final Integer count = purchaseOcrPicture.getCount(); final Integer count = purchaseOcrPicture.getCount();
final String orderId = purchaseOcrPicture.getOrderId(); final String orderId = purchaseOcrPicture.getOrderId();
final String shopId = purchaseOcrPicture.getShopId(); final String shopId = purchaseOcrPicture.getShopId();
@ -74,7 +75,16 @@ public class AliOcrUtil {
.role(Role.SYSTEM.getValue()).content(Collections.singletonList(systemText)).build(); .role(Role.SYSTEM.getValue()).content(Collections.singletonList(systemText)).build();
final MultiModalMessageItemImage userImage = new MultiModalMessageItemImage(picturePath); final MultiModalMessageItemImage userImage = new MultiModalMessageItemImage(picturePath);
final String firstQuestionMsg = "将图中内容以JSON格式输出,JSON格式为:{ \"productSn\": \"货号\", \"productName\": \"商品名称\",\"price\": \"单价\", \"productCount\": \"数量\", \"attributeList\": \"{'color':'红色','size':'均码'}\" }, 请帮我返回" + count + "条不同规格的JSON数据"; final String firstQuestionMsg = "请把图片中的内容按照商品的不同规格拆分,返回" + count + "条JSON数据。\n" +
" JSON示例:[{ \"productSn\": \"" + purchaseOcrExample.getProductSn() + "\", \"productName\":\"" + purchaseOcrExample.getProductName() + "\", \"price\": \"" + purchaseOcrExample.getPrice() + "\", \"productCount\": \"" + purchaseOcrExample.getProductCount() + "\", \"attributeList\": \"{'color':'" + purchaseOcrExample.getColor() + "','size':'" + purchaseOcrExample.getSize() + "'}\" }]。" +
"以下是几点要求: " +
"1.JSON中“productSn”字段可能由字母或数字或特殊符号组成,“color”字段如果图片中没有内容,则赋值“均色”,“size”字段如果图片中没有内容,则赋值“均码”。" +
"2.严格按照JSON示例的格式和字段名返回! " +
"3.严格按照图中内容值返回! " +
"4.每个JSON对象必须包含这7个字段。" +
"5.如果图中内容没有productSn和productName信息,则使用JSON示例中的值填充。 " +
"6.请注意返回JSON符号解析格式正确 ,确保java程序能够正确解析。" +
"7.严格返回" + count + "条JSON数据,如果数量有偏差按照实际条数返回,不能自动省略!";
MultiModalMessageItemText userText = new MultiModalMessageItemText(firstQuestionMsg); MultiModalMessageItemText userText = new MultiModalMessageItemText(firstQuestionMsg);
final MultiModalConversationMessage userMessage = final MultiModalConversationMessage userMessage =
MultiModalConversationMessage.builder().role(Role.USER.getValue()) MultiModalConversationMessage.builder().role(Role.USER.getValue())
@ -95,7 +105,8 @@ public class AliOcrUtil {
stopWatch.stop(); stopWatch.stop();
multiModalConversationResults.add(result); multiModalConversationResults.add(result);
// 解析结果 // 解析结果
final String text = result.getOutput().getChoices().get(0).getMessage().getContent().get(0).get("text").toString(); String text = result.getOutput().getChoices().get(0).getMessage().getContent().get(0).get("text").toString();
log.info("一轮对话-的json======"+ text);
// 根据{}截取数据 // 根据{}截取数据
final int startIndex = text.indexOf('{'); final int startIndex = text.indexOf('{');
final int endIndex = text.lastIndexOf('}'); final int endIndex = text.lastIndexOf('}');
@ -103,8 +114,119 @@ public class AliOcrUtil {
// 没有查询到想要的数据,那么就报错返回 // 没有查询到想要的数据,那么就报错返回
throw new RuntimeException("识别数据失败,未找到指定标识符:识别结果为:" + text); throw new RuntimeException("识别数据失败,未找到指定标识符:识别结果为:" + text);
} }
final String jsonStr = '[' + text.substring(startIndex, endIndex + 1) + ']'; // attributeList 有时候解析的不是json格式,特殊处理一下
JSONArray json = JSON.parseArray(jsonStr); String jsonStr = '[' + text.substring(startIndex, endIndex + 1) + ']';
// 有时候返回的是productCounts,改为productCount
jsonStr = fixProductCounts(jsonStr);
JSONArray json = new JSONArray();
try{
json = JSON.parseArray(jsonStr);
}catch (Exception e){
log.error(Thread.currentThread().getName()+"-一轮对话--->json识别报错喽,没关系,我把错误信息输出出来:"+e.getMessage(), e);
// 如果格式化报错,特殊处理下,再次进行转义。
final String message = e.getMessage();
boolean needAgain = true;
// 以下是对目前可能出现的问题进行过滤处理
// attributeList格式不对
if(message.contains("attributeList")){
needAgain = false;
log.error(Thread.currentThread().getName()+"-一轮对话--->json识别报错喽,没关系,attributeList格式有问题,我来替换一下!");
// 如果是attributeList出现问题,那么尝试修复。
jsonStr = fixAttributeList(jsonStr);
try{
json = JSON.parseArray(jsonStr);
}catch (Exception replaceOneException){
log.error(Thread.currentThread().getName()+"-一轮对话--->json识别报错喽,没关系,attributeList替换了也没好使!");
needAgain = true;
}
}
// 识别多了个“}”
if(message.contains("not close json text")){
needAgain = false;
log.error(Thread.currentThread().getName()+"-一轮对话--->json识别报错喽,没关系,可能是多了“}”,我来去掉试一下一下!");
//
jsonStr = jsonStr.substring(0, jsonStr.length()-1);
try{
json = JSON.parseArray(jsonStr);
}catch (Exception replaceOneException){
log.error(Thread.currentThread().getName()+"-一轮对话--->json识别报错喽,没关系,可能是多了“}”,去掉了也没好使!");
needAgain = true;
}
}
if(needAgain){
log.error(Thread.currentThread().getName()+"-一轮对话--->json识别报错喽,没关系,我来重新识别一下!");
//尝试重新识别一次
final String errorJsonQuestionMsg = "返回的json格式有误,请重新返回。";
final MultiModalMessageItemText assistentText = new MultiModalMessageItemText(
text);
final MultiModalConversationMessage assistentMessage = MultiModalConversationMessage.builder()
.role(Role.ASSISTANT.getValue()).content(Collections.singletonList(assistentText)).build();
messages.add(assistentMessage);
userText = new MultiModalMessageItemText(errorJsonQuestionMsg);
messages.add(MultiModalConversationMessage.builder().role(Role.USER.getValue())
.content(Collections.singletonList(userText)).build());
param.setMessages(new CopyOnWriteArrayList<Object>(messages));
stopWatch.start("一轮对话json格式错误重新识别!");
result = conv.call(param);
multiModalConversationResults.add(result);
stopWatch.stop();
// 解析结果
text = result.getOutput().getChoices().get(0).getMessage().getContent().get(0).get("text").toString();
log.info("一轮对话--json格式错误,重新识别的结果======"+ text);
// 根据{}截取数据
final int secondStartIndex = text.indexOf('{');
final int secondEndIndex = text.lastIndexOf('}');
// attributeList 有时候解析的不是json格式,特殊处理一下
String errorJsonJsonStr = '[' + text.substring(secondStartIndex, secondEndIndex + 1) + ']';
// 有时候返回的是productCounts,改为productCount
errorJsonJsonStr = fixProductCounts(errorJsonJsonStr);
try{
//
json = JSON.parseArray(errorJsonJsonStr);
}catch (Exception errorJsonException){
log.error(Thread.currentThread().getName()+"-一轮对话--->json识别报错喽,重新识别又报错了,没关系,我把错误信息输出出来:"+errorJsonException.getMessage(), errorJsonException);
// 如果格式化报错,特殊处理下,再次进行转义。
final String errorJsonExceptionMessage = errorJsonException.getMessage();
boolean firstCheckResult = true;
if(errorJsonExceptionMessage.contains("attributeList")){
firstCheckResult = false;
log.error(Thread.currentThread().getName()+"-一轮对话--->json识别报错喽,重新识别又报错了,没关系,attributeList格式有问题,我来替换一下!");
// 如果是attributeList出现问题,那么尝试修复。
errorJsonJsonStr = fixAttributeList(errorJsonJsonStr);
try{
json = JSON.parseArray(errorJsonJsonStr);
}catch (Exception errorJsonJsonException){
log.error(Thread.currentThread().getName()+"-一轮对话--->json识别报错喽,重新识别又报错了,attributeList替换了也没好使!");
firstCheckResult = true;
}
}
// 识别多了个“}”
if(errorJsonExceptionMessage.contains("not close json text")){
firstCheckResult = false;
log.error(Thread.currentThread().getName()+"-一轮对话--->json识别报错喽,重新识别又报错了,没关系,可能是多了“}”,我来去掉试一下一下!");
//
errorJsonJsonStr = errorJsonJsonStr.substring(0, errorJsonJsonStr.length()-1);
try{
json = JSON.parseArray(errorJsonJsonStr);
}catch (Exception replaceOneException){
log.error(Thread.currentThread().getName()+"-一轮对话--->json识别报错喽,重新识别又报错了,可能是多了“}”,去掉了也没好使!");
firstCheckResult = true;
}
}
if(firstCheckResult){
log.error(Thread.currentThread().getName()+"-一轮对话--->json识别报错喽,,重新识别又报错了,不识别了,直接毁灭吧!");
throw errorJsonException;
}
}
}
}
final int fristResultCount = json.size(); final int fristResultCount = json.size();
// 我会在这里对结果进行解析,然后,判断要不要走第二次回话 // 我会在这里对结果进行解析,然后,判断要不要走第二次回话
final int missingCount = count - fristResultCount; final int missingCount = count - fristResultCount;
@ -124,17 +246,127 @@ public class AliOcrUtil {
multiModalConversationResults.add(result); multiModalConversationResults.add(result);
stopWatch.stop(); stopWatch.stop();
// 解析结果 // 解析结果
final String secondText = result.getOutput().getChoices().get(0).getMessage().getContent().get(0).get("text").toString(); String secondText = result.getOutput().getChoices().get(0).getMessage().getContent().get(0).get("text").toString();
log.info("二轮对话识别结果:"+secondText);
// 根据{}截取数据 // 根据{}截取数据
final int secondStartIndex = secondText.indexOf('{'); final int secondStartIndex = secondText.indexOf('{');
final int secondEndIndex = secondText.lastIndexOf('}'); final int secondEndIndex = secondText.lastIndexOf('}');
final String secondJsonStr = '[' + secondText.substring(secondStartIndex, secondEndIndex + 1) + ']'; if (secondStartIndex == -1 || secondEndIndex == -1) {
// 二轮对话,没有查询到想要的数据,那么就使用一轮的结果
log.info(Thread.currentThread().getName()+"-二轮对话--->识别数据失败,使用第一次的识别结果未找到指定标识符:识别结果为:" + text);
}else{
String secondJsonStr = '[' + secondText.substring(secondStartIndex, secondEndIndex + 1) + ']';
// 有时候返回的是productCounts,改为productCount
secondJsonStr = fixProductCounts(secondJsonStr);
try{
json = JSON.parseArray(secondJsonStr);
}catch (Exception e){
boolean needAgain = true;
log.error(Thread.currentThread().getName()+"-二轮对话--->json识别报错喽,没关系,我把错误信息输出出来:"+e.getMessage(),e);
// 如果格式化报错,特殊处理下,再次进行转义。
final String message = e.getMessage();
if(message.contains("attributeList")){
needAgain = false;
log.error(Thread.currentThread().getName()+"-二轮对话--->json识别报错喽,没关系,attributeList格式有问题,我来替换一下!");
// 如果是attributeList出现问题,那么尝试修复。
secondJsonStr = fixAttributeList(secondJsonStr);
try{
json = JSON.parseArray(secondJsonStr);
}catch (Exception exception){
log.error(Thread.currentThread().getName()+"-二轮对话--->json识别报错喽,attributeList替换了也没好使!");
needAgain = true;
}
}
// 识别多了个“}”
if(message.contains("not close json text")){
needAgain = false;
log.error(Thread.currentThread().getName()+"-二轮对话--->json识别报错喽,没关系,可能是多了“}”,我来去掉试一下一下!");
//
secondJsonStr = secondJsonStr.substring(0, secondJsonStr.length()-1);
try{
json = JSON.parseArray(secondJsonStr);
}catch (Exception replaceOneException){
log.error(Thread.currentThread().getName()+"-二轮对话--->json识别报错喽,可能是多了“}”,去掉了也没好使!");
needAgain = true;
}
}
if(needAgain){
log.error(Thread.currentThread().getName()+"-二轮对话--->json识别报错喽,没关系,我来重新识别一下!");
//尝试重新识别一次
final String errorJsonQuestionMsg = "返回的json格式有误,请重新返回。";
final MultiModalMessageItemText secondJsonErrorAssistentText = new MultiModalMessageItemText(
secondText);
final MultiModalConversationMessage secondJsonErrorAssistentMessage = MultiModalConversationMessage.builder()
.role(Role.ASSISTANT.getValue()).content(Collections.singletonList(secondJsonErrorAssistentText)).build();
messages.add(secondJsonErrorAssistentMessage);
userText = new MultiModalMessageItemText(errorJsonQuestionMsg);
messages.add(MultiModalConversationMessage.builder().role(Role.USER.getValue())
.content(Collections.singletonList(userText)).build());
param.setMessages(new CopyOnWriteArrayList<Object>(messages));
stopWatch.start("二轮会话json格式错误,重新识别!");
result = conv.call(param);
multiModalConversationResults.add(result);
stopWatch.stop();
// 解析结果
secondText = result.getOutput().getChoices().get(0).getMessage().getContent().get(0).get("text").toString();
log.info("二轮对话-json格式错误,重新识别的结果======"+ secondText);
// 根据{}截取数据
final int errorSsecondStartIndex = secondText.indexOf('{');
final int errorSecondEndIndex = secondText.lastIndexOf('}');
// attributeList 有时候解析的不是json格式,特殊处理一下
String errorJsonJsonStr = '[' + secondText.substring(errorSsecondStartIndex, errorSecondEndIndex + 1) + ']';
// 有时候返回的是productCounts,改为productCount
errorJsonJsonStr = fixProductCounts(errorJsonJsonStr);
try{
//
json = JSON.parseArray(errorJsonJsonStr);
}catch (Exception errorJsonException){
boolean secondCheckResult = true;
final String errorJsonExceptionMessage = errorJsonException.getMessage();
log.error(Thread.currentThread().getName()+"-二轮对话--->json识别报错喽,重新识别又报错了,没关系,我把错误信息输出出来:"+ errorJsonExceptionMessage, errorJsonException);
if(errorJsonExceptionMessage.contains("attributeList")){
secondCheckResult = false;
log.error(Thread.currentThread().getName()+"-二轮对话--->json识别报错喽,,重新识别又报错了,没关系,,attributeList格式有问题,我来替换一下!");
// 如果是attributeList出现问题,那么尝试修复。
secondJsonStr = fixAttributeList(secondJsonStr);
try{
json = JSON.parseArray(secondJsonStr); json = JSON.parseArray(secondJsonStr);
}catch (Exception secondCheckException){
log.error(Thread.currentThread().getName()+"-二轮对话--->json识别报错喽,,重新识别又报错了,attributeList替换了也没好使!");
secondCheckResult = true;
}
}
if(errorJsonExceptionMessage.contains("not close json text")){
secondCheckResult = false;
log.error(Thread.currentThread().getName()+"-二轮对话--->json识别报错喽,重新识别又报错了,没关系,可能是多了“}”,我来去掉试一下一下!");
// 如果是attributeList出现问题,那么尝试修复。
secondJsonStr = secondJsonStr.substring(0, secondJsonStr.length()-1);
try{
json = JSON.parseArray(secondJsonStr);
}catch (Exception secondCheckException){
log.error(Thread.currentThread().getName()+"-二轮对话--->json识别报错喽,重新识别又报错了,可能是多了“}”,去掉了也没好使!");
secondCheckResult = true;
}
}
if(secondCheckResult){
log.error(Thread.currentThread().getName()+"-二轮对话--->json识别报错喽,,重新识别又报错了,不识别了,直接毁灭吧!");
throw errorJsonException;
}
}
}
}
}
} }
log.info(JSON.toJSONString(json)); log.info(JSON.toJSONString(json));
stopWatch.start("解析结果=="); stopWatch.start("解析结果==");
// 解析结果 // 解析结果
getPurchaseDetailMap(purchaseDetailService,productService, orderId, shopId, picturePath, json); // 入库的时候子图直接存放主图的路径
/*if (StringUtils.isNotEmpty(parentPicturePath)) {
picturePath = parentPicturePath;
}*/
getPurchaseDetailMap(purchaseDetailService, productService, orderId, shopId, picturePath, json);
stopWatch.stop(); stopWatch.stop();
// 将map 转为list 进行落库操作 // 将map 转为list 进行落库操作
/*stopWatch.start("批量插入=="); /*stopWatch.start("批量插入==");
@ -151,6 +383,153 @@ public class AliOcrUtil {
return jsonObject; return jsonObject;
} }
/**
* 1. attributeList 有时候解析的不是json格式特殊处理一下
* 2. 有时候返回的是productCounts改为productCount
*
* @param attributeList
* @return String
* @author 王富康
* @date 2024/5/12
*/
public static String fixAttributeList(String attributeList) {
// 将字符串转换为正确的json格式
attributeList = attributeList.replace("\"color\":\"", "'color':'");
attributeList = attributeList.replace("\",\"size\":\"", "','size':'");
// 正确的是以\"结尾的,不需要转换
if (!attributeList.contains("\\\"}\"")) {
attributeList = attributeList.replace("\"}\"", "'}\"");
}
attributeList = attributeList.replace("productCounts", "productCount");
return attributeList;
}
/**
* 有时候返回的是productCounts改为productCount
*
* @param str
* @return String
* @author 王富康
* @date 2024/5/12
*/
public static String fixProductCounts(String str) {
str = str.replace("productCounts", "productCount");
return str;
}
/*public static void main(String[] args) throws NoApiKeyException, UploadFileException {
PurchaseOcrPicture purchaseOcrPicture = new PurchaseOcrPicture();
purchaseOcrPicture.setOcrPicture("https://jewel-shop.oss-cn-beijing.aliyuncs.com/f51c446ef147437c9a8200ed2b43ec7f.jpg");
purchaseOcrPicture.setCount(22);
streamCall(purchaseOcrPicture);
}*/
/**
* 图片识别-流式输出
*
* @author 王富康
* @date 2024/5/10
*/
public static JSONObject streamCall(PurchaseOcrPicture purchaseOcrPicture, PurchaseOcrExample purchaseOcrExample, ProductService productService, PurchaseDetailService purchaseDetailService)
throws ApiException, NoApiKeyException, UploadFileException {
final JSONObject jsonObject = new JSONObject();
final String picturePath = purchaseOcrPicture.getOcrPicture();
final String parentPicturePath = purchaseOcrPicture.getParentPicturePath();
final Integer count = purchaseOcrPicture.getCount();
final String orderId = purchaseOcrPicture.getOrderId();
final String shopId = purchaseOcrPicture.getShopId();
final StopWatch stopWatch = new StopWatch("Ai回答计时");
Constants.apiKey = "sk-bcfa4865b89548acb8225f910f13d682";
// final String firstQuestionMsg = "将图中内容以JSON格式输出,JSON格式为:{ \"productSn\": \"货号\", \"productName\": \"商品名称\",\"price\": \"单价\", \"productCount\": \"数量\", \"attributeList\": \"{'color':'红色','size':'均码'}\" }, 请帮我返回" + count + "条不同规格的JSON数据";
final String firstQuestionMsg = "请把图片中的内容按照商品的不同规格拆分,返回" + count + "条JSON数据。\n" +
" JSON示例:[{ \"productSn\": \"" + purchaseOcrExample.getProductSn() + "\", \"productName\":\"" + purchaseOcrExample.getProductName() + "\", \"price\": \"" + purchaseOcrExample.getPrice() + "\", \"productCount\": \"" + purchaseOcrExample.getProductCount() + "\", \"attributeList\": \"{'color':'" + purchaseOcrExample.getColor() + "','size':'" + purchaseOcrExample.getSize() + "'}\" }]。" +
"以下是几点要求: " +
"1.JSON中“productSn”字段可能由字母或数字或特殊符号组成,“color”字段如果图片中没有内容,则赋值“均色”,“size”字段如果图片中没有内容,则赋值“均码”。" +
"2.严格按照JSON示例的格式和字段名返回! " +
"3.严格按照图中内容值返回! " +
"4.每个JSON对象必须包含这7个字段。" +
"5.如果图中内容没有productSn和productName信息,则使用JSON示例中的值填充。 " +
"6. 请注意返回JSON符号解析格式正确 。" +
"7.严格返回" + count + "条JSON数据,如果数量有偏差按照实际条数返回,不能自动省略!";
final MultiModalConversation conv = new MultiModalConversation();
// must create mutable map.
final String finalPicturePath = picturePath;
final MultiModalMessage userMessage = MultiModalMessage.builder().role(Role.USER.getValue())
.content(Arrays.asList(new HashMap<String, Object>() {
private static final long serialVersionUID = 6779588534269073602L;
{
put("image", finalPicturePath);
}
},
new HashMap<String, Object>() {
private static final long serialVersionUID = 6788635831260953019L;
{
put("text", firstQuestionMsg);
}
})).build();
final MultiModalConversationParam param = MultiModalConversationParam.builder()
.model("qwen-vl-max")
.message(userMessage)
/*.stream(true)
.temperature(1F)
// 生成时,核采样方法的概率阈值。例如,取值为0.8时,仅保留累计概率之和大于等于0.8的概率分布中的token,作为随机采样的候选集。取值范围为(0,1.0),取值越大,生成的随机性越高;取值越低,生成的随机性越低。默认值 0.8。注意,取值不要大于等于1
.topP(0.1)
// 生成时,采样候选集的大小。例如,取值为50时,仅将单次生成中得分最高的50个token组成随机采样的候选集。取值越大,生成的随机性越高;取值越小,生成的确定性越高。注意:如果top_k的值大于100,top_k将采用默认值100
.topK(1)
// 生成时,随机数的种子,用于控制模型生成的随机性。如果使用相同的种子,每次运行生成的结果都将相同;当需要复现模型的生成结果时,可以使用相同的种子。seed参数支持无符号64位整数类型。默认值 1234
.seed(500)*/
// 是否使用增量输出。当使用增量输出时每次流式返回的序列仅包含最新生成的增量内容,默认值为false,即输出完整的全量内容
.incrementalOutput(true)
.build();
stopWatch.start("开始识别!");
final Flowable<MultiModalConversationResult> result = conv.streamCall(param);
// 拼接结果集
final StringBuffer resultStr = new StringBuffer();
result.blockingForEach(item -> {
// System.out.println(item);
final List<Map<String, Object>> content = item.getOutput().getChoices().get(0).getMessage().getContent();
if (!content.isEmpty()) {
final String string = content.get(0).get("text").toString();
resultStr.append(string);
}
});
stopWatch.stop();
final String text = resultStr.toString();
log.info(text);
// 根据{}截取数据
final int startIndex = text.indexOf('{');
final int endIndex = text.lastIndexOf('}');
if (startIndex == -1 || endIndex == -1) {
// 没有查询到想要的数据,那么就报错返回
throw new RuntimeException("识别数据失败,未找到指定标识符:识别结果为:" + text);
}
final String jsonStr = '[' + text.substring(startIndex, endIndex + 1) + ']';
final JSONArray json = JSON.parseArray(jsonStr);
log.info(JSON.toJSONString(json));
stopWatch.start("解析结果==");
// 解析结果
// 入库的时候子图直接存放主图的路径
/*if (StringUtils.isNotEmpty(parentPicturePath)) {
picturePath = parentPicturePath;
}*/
getPurchaseDetailMap(purchaseDetailService, productService, orderId, shopId, picturePath, json);
stopWatch.stop();
log.info(stopWatch.prettyPrint());
jsonObject.put("resultContent", json);
/*jsonObject.put("tokenCount", result.getUsage());
jsonObject.put("requestId", result.getRequestId());*/
jsonObject.put("cod", 200);
jsonObject.put("ocrCount", json.size());
return jsonObject;
}
/** /**
* 处理数据 * 处理数据
* *
@ -179,11 +558,32 @@ public class AliOcrUtil {
purchaseOcrVo.setProductCount(jsonObject.getInteger("productCount")); purchaseOcrVo.setProductCount(jsonObject.getInteger("productCount"));
final String attributeListStr = jsonObject.getString("attributeList"); final String attributeListStr = jsonObject.getString("attributeList");
final JSONObject attributeListJson = JSON.parseObject(attributeListStr); final JSONObject attributeListJson = JSON.parseObject(attributeListStr);
final CopyOnWriteArrayList<PurchaseOcrDetailVo> attributeList = new CopyOnWriteArrayList<>();
final String size = attributeListJson.getString("size");
if(size.contains(",")){
final String[] sizeArray = size.split(",");
for (String s : sizeArray) {
final PurchaseOcrDetailVo purchaseOcrDetailVo = new PurchaseOcrDetailVo(); final PurchaseOcrDetailVo purchaseOcrDetailVo = new PurchaseOcrDetailVo();
purchaseOcrDetailVo.setColor(attributeListJson.getString("color")); purchaseOcrDetailVo.setColor(attributeListJson.getString("color"));
purchaseOcrDetailVo.setSize(attributeListJson.getString("size")); purchaseOcrDetailVo.setSize(s);
final CopyOnWriteArrayList<PurchaseOcrDetailVo> attributeList = new CopyOnWriteArrayList<>();
attributeList.add(purchaseOcrDetailVo); attributeList.add(purchaseOcrDetailVo);
}
}else if(size.contains("-")){
final String[] sizeArray = size.split("-");
for (String s : sizeArray) {
final PurchaseOcrDetailVo purchaseOcrDetailVo = new PurchaseOcrDetailVo();
purchaseOcrDetailVo.setColor(attributeListJson.getString("color"));
purchaseOcrDetailVo.setSize(s);
attributeList.add(purchaseOcrDetailVo);
}
}else{
final PurchaseOcrDetailVo purchaseOcrDetailVo = new PurchaseOcrDetailVo();
purchaseOcrDetailVo.setColor(attributeListJson.getString("color"));
purchaseOcrDetailVo.setSize(size);
attributeList.add(purchaseOcrDetailVo);
}
purchaseOcrVo.setAttributeList(attributeList); purchaseOcrVo.setAttributeList(attributeList);
purchaseOcrVos.add(purchaseOcrVo); purchaseOcrVos.add(purchaseOcrVo);
@ -297,7 +697,7 @@ public class AliOcrUtil {
purchaseDetail.setOcrPicturePath(ocrPicturePath); purchaseDetail.setOcrPicturePath(ocrPicturePath);
} else { } else {
final PurchaseDetail purchaseDetail = new PurchaseDetail(); final PurchaseDetail purchaseDetail = new PurchaseDetail();
final StockLog stockLog = new StockLog();
final CopyOnWriteArrayList<StockLog> stockLogList = new CopyOnWriteArrayList<>(); final CopyOnWriteArrayList<StockLog> stockLogList = new CopyOnWriteArrayList<>();
purchaseDetail.setPurchaseId(orderId); purchaseDetail.setPurchaseId(orderId);
purchaseDetail.setCreateTime(new Date()); purchaseDetail.setCreateTime(new Date());
@ -308,6 +708,7 @@ public class AliOcrUtil {
// 采购价 // 采购价
purchaseDetail.setPurchasePrice(purchaseOcrVo.getPrice()); purchaseDetail.setPurchasePrice(purchaseOcrVo.getPrice());
purchaseDetail.setOcrPicturePath(ocrPicturePath); purchaseDetail.setOcrPicturePath(ocrPicturePath);
final StockLog stockLog = new StockLog();
stockLog.setOrderId(orderId); stockLog.setOrderId(orderId);
stockLog.setCreateTime(new Date()); stockLog.setCreateTime(new Date());
stockLog.setDetailId(purchaseDetail.getId()); stockLog.setDetailId(purchaseDetail.getId());

3
hiver-modules/hiver-mall/src/main/resources/mapper/PurchaseMapper.xml

@ -438,7 +438,8 @@
<include refid="Base_Column_List" /> <include refid="Base_Column_List" />
from t_purchase from t_purchase
where del_flag ='0' where del_flag ='0'
and id in (select purchase_id from t_purchase_detail where del_flag ='0' and in_storage_status = 1 and product_id =#{productId}) and in_storage_status = 1
and id in (select purchase_id from t_purchase_detail where del_flag ='0' and product_id =#{productId})
order by create_time desc order by create_time desc
</select> </select>

16
hiver-modules/hiver-mall/src/main/resources/mapper/SaleMapper.xml

@ -769,14 +769,14 @@ trans_company, company_name, product_count, remark, sale_name, company_phone, cr
<select id="getCustomerBuyProductLog" resultType="cc.hiver.mall.pojo.vo.CustomerBuyProductLogVo"> <select id="getCustomerBuyProductLog" resultType="cc.hiver.mall.pojo.vo.CustomerBuyProductLogVo">
SELECT SELECT
product_id,product_name product_id,product_name,product_sn
FROM FROM
t_sale_detail t_sale_detail
WHERE WHERE
shop_id = #{shopId} shop_id = #{shopId}
and sale_id IN ( SELECT id FROM t_sale WHERE user_id = #{userId} and status not in ('2','6','7')) and sale_id IN ( SELECT id FROM t_sale WHERE user_id = #{userId} and status not in ('2','6','7'))
GROUP BY GROUP BY
product_id, product_name product_id, product_name,product_sn
order by product_name desc order by product_name desc
</select> </select>
@ -813,16 +813,15 @@ trans_company, company_name, product_count, remark, sale_name, company_phone, cr
<select id="getSupplierBuyProductLog" resultType="cc.hiver.mall.pojo.vo.SupplierBuyProductLogVo"> <select id="getSupplierBuyProductLog" resultType="cc.hiver.mall.pojo.vo.SupplierBuyProductLogVo">
SELECT SELECT
t.product_id,t.product_name t.product_id,t.product_name,t.product_sn
FROM FROM
t_purchase_detail t t_purchase_detail t
LEFT JOIN t_stock_log s ON t.purchase_id = s.order_id
AND t.product_id = s.product_id
WHERE WHERE
t.shop_id = #{shopId} t.shop_id = #{shopId}
AND purchase_id IN ( SELECT id FROM t_purchase WHERE deflag = 0 and in_storage_status = 1 and supplier_id = #{supplierId} ) and t.del_flag != 1
AND purchase_id IN ( SELECT id FROM t_purchase WHERE del_flag = 0 and in_storage_status = 1 and supplier_id = #{supplierId} )
GROUP BY GROUP BY
product_id, product_name product_id, product_name,product_sn
order by product_name desc order by product_name desc
</select> </select>
@ -835,7 +834,8 @@ trans_company, company_name, product_count, remark, sale_name, company_phone, cr
AND t.product_id = s.product_id AND t.product_id = s.product_id
WHERE WHERE
t.shop_id = #{shopId} t.shop_id = #{shopId}
AND purchase_id IN ( SELECT id FROM t_purchase WHERE deflag = 0 and in_storage_status = 1 and supplier_id = #{supplierId} ) and t.del_flag != 1
AND purchase_id IN ( SELECT id FROM t_purchase WHERE del_flag = 0 and in_storage_status = 1 and supplier_id = #{supplierId} )
and t.product_id in and t.product_id in
<foreach close=")" collection="productIds" item="listItem" open="(" separator=","> <foreach close=")" collection="productIds" item="listItem" open="(" separator=",">
#{listItem} #{listItem}

Loading…
Cancel
Save