ocr识别修改

This commit is contained in:
张松
2025-11-26 14:07:49 +08:00
parent 1dae19fb27
commit 8cd2bbf3d8
4 changed files with 401 additions and 323 deletions

View File

@@ -21,6 +21,15 @@ import java.math.BigDecimal;
@NoArgsConstructor @NoArgsConstructor
@AllArgsConstructor @AllArgsConstructor
public class OrderInfoAddDTO implements Serializable { public class OrderInfoAddDTO implements Serializable {
/**
* 上菜方式
* 待起菜 PENDING_PREP
* 待出菜 READY_TO_SERVE
* 已出菜 SENT_OUT
* 已上菜 DELIVERED
* 已超时 EXPIRED
*/
private String subStatus;
//限时折扣部分 //限时折扣部分
private LimitRateDTO limitRate; private LimitRateDTO limitRate;

View File

@@ -8,13 +8,17 @@ import com.alibaba.dashscope.app.ApplicationParam;
import com.alibaba.dashscope.app.ApplicationResult; import com.alibaba.dashscope.app.ApplicationResult;
import com.alibaba.dashscope.app.RagOptions; import com.alibaba.dashscope.app.RagOptions;
import com.aliyun.bailian20231229.Client; import com.aliyun.bailian20231229.Client;
import com.aliyun.bailian20231229.models.AddFileResponse;
import com.aliyun.bailian20231229.models.ApplyFileUploadLeaseResponse; import com.aliyun.bailian20231229.models.ApplyFileUploadLeaseResponse;
import com.aliyun.bailian20231229.models.ApplyFileUploadLeaseResponseBody; import com.aliyun.bailian20231229.models.ApplyFileUploadLeaseResponseBody;
import com.aliyun.bailian20231229.models.DescribeFileResponse;
import com.czg.exception.CzgException; import com.czg.exception.CzgException;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import java.io.InputStream; import java.io.DataOutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@@ -53,13 +57,14 @@ public class AliOcrUtil {
public static ApplyFileUploadLeaseResponseBody.ApplyFileUploadLeaseResponseBodyData applyFileUpload(InputStream stream, String fileName) { public static ApplyFileUploadLeaseResponseBody.ApplyFileUploadLeaseResponseBodyData applyFileUpload(byte[] bytes, String fileName) {
String md5 = DigestUtil.md5Hex(stream);
String md5 = DigestUtil.md5Hex(bytes);
com.aliyun.bailian20231229.Client client = createClient(); com.aliyun.bailian20231229.Client client = createClient();
com.aliyun.bailian20231229.models.ApplyFileUploadLeaseRequest applyFileUploadLeaseRequest = new com.aliyun.bailian20231229.models.ApplyFileUploadLeaseRequest() com.aliyun.bailian20231229.models.ApplyFileUploadLeaseRequest applyFileUploadLeaseRequest = new com.aliyun.bailian20231229.models.ApplyFileUploadLeaseRequest()
.setFileName(fileName) .setFileName(fileName)
.setMd5(md5) .setMd5(md5)
.setSizeInBytes("100000") .setSizeInBytes(String.valueOf(bytes.length))
.setCategoryType("SESSION_FILE") .setCategoryType("SESSION_FILE")
.setUseInternalEndpoint(false); .setUseInternalEndpoint(false);
com.aliyun.teautil.models.RuntimeOptions runtime = new com.aliyun.teautil.models.RuntimeOptions(); com.aliyun.teautil.models.RuntimeOptions runtime = new com.aliyun.teautil.models.RuntimeOptions();
@@ -76,40 +81,94 @@ public class AliOcrUtil {
} }
} }
public static String uploadFile(InputStream stream, String fileName) throws Exception { public static String getSessionId(byte[] bytes, String fileName) {
String leaseId = null;
try {
leaseId = uploadFile(bytes, fileName);
} catch (Exception e) {
throw new RuntimeException(e);
}
com.aliyun.bailian20231229.Client client = createClient();
com.aliyun.bailian20231229.models.AddFileRequest addFileRequest = new com.aliyun.bailian20231229.models.AddFileRequest()
.setLeaseId(leaseId)
.setParser("DASHSCOPE_DOCMIND")
.setCategoryId("default")
.setCategoryType("SESSION_FILE");
com.aliyun.teautil.models.RuntimeOptions runtime = new com.aliyun.teautil.models.RuntimeOptions();
java.util.Map<String, String> headers = new java.util.HashMap<>();
try {
// 复制代码运行请自行打印 API 的返回值
AddFileResponse addFileResponse = client.addFileWithOptions("llm-9zg04s7wlbvi32tq", addFileRequest, headers, runtime);
String fileId = addFileResponse.getBody().getData().getFileId();
while (!getFileStatus(fileId)) {
Thread.sleep(100);
}
return fileId;
} catch (Exception error) {
throw new RuntimeException(error);
}
}
ApplyFileUploadLeaseResponseBody.ApplyFileUploadLeaseResponseBodyData applyInfo = applyFileUpload(stream, fileName); public static boolean getFileStatus(String fileId) {
com.aliyun.bailian20231229.Client client = createClient();
com.aliyun.teautil.models.RuntimeOptions runtime = new com.aliyun.teautil.models.RuntimeOptions();
java.util.Map<String, String> headers = new java.util.HashMap<>();
try {
// 复制代码运行请自行打印 API 的返回值
DescribeFileResponse describeFileResponse = client.describeFileWithOptions("llm-9zg04s7wlbvi32tq", fileId, headers, runtime);
log.info("file status: {}", describeFileResponse.getBody());
return describeFileResponse.getBody().getData() != null && "FILE_IS_READY".equals(describeFileResponse.getBody().getData().getStatus());
} catch (Exception error) {
throw new RuntimeException(error);
}
}
public static String uploadFile(byte[] bytes, String fileName) throws Exception {
ApplyFileUploadLeaseResponseBody.ApplyFileUploadLeaseResponseBodyData applyInfo = applyFileUpload(bytes, fileName);
// 获取预签名要求的所有头
Map<String, String> headers = (Map<String, String>) applyInfo.getParam().getHeaders(); Map<String, String> headers = (Map<String, String>) applyInfo.getParam().getHeaders();
// 补充必须的 Content-Type
if (!headers.containsKey("Content-Type")) {
headers.put("Content-Type", "application/octet-stream");
}
HttpRequest request = HttpRequest.put(applyInfo.getParam().getUrl()); HttpRequest request = HttpRequest.put(applyInfo.getParam().getUrl());
// 必须一模一样地设置所有 headers否则签名失效 // 设置所有 header
for (Map.Entry<String, String> entry : headers.entrySet()) { for (Map.Entry<String, String> entry : headers.entrySet()) {
request.header(entry.getKey(), entry.getValue()); request.header(entry.getKey(), entry.getValue());
} }
// 设置文件内容 // 设置文件内容
request.body(stream.readAllBytes()); request.body(bytes);
HttpResponse resp = request.execute(); HttpResponse resp = request.execute();
log.info(resp.body());
log.info("upload status: {}", resp.getStatus());
if (resp.getStatus() != 200) {
throw new CzgException("OSS 上传失败: " + resp.body());
}
return applyInfo.getFileUploadLeaseId(); return applyInfo.getFileUploadLeaseId();
} }
public static String appCall(InputStream stream, String fileName) {
public static String appCall(byte[] bytes, String fileName) {
String id = null; String id = null;
try { try {
id = uploadFile(stream, fileName); id = getSessionId(bytes, fileName);
} catch (Exception e) { } catch (Exception e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
ApplicationParam param = ApplicationParam.builder() ApplicationParam param = ApplicationParam.builder()
.apiKey("sk-2343af4413834ad1ab43b036e3a903de") .apiKey("sk-2343af4413834ad1ab43b036e3a903de")
.appId("3493340ef5e146c487364395fbca7bf3") .appId("cd612ac509a4499f8ac68a656532d4ae")
.prompt("开始处理") .prompt("你是一名票据OCR结构化专家请从我提供的票据图片中智能提取信息并只输出JSON不得添加解释、不补充不存在内容、不得返回空字符串、字段缺失填null、字段不可省略、数字一律用字符串使用以下固定JSON结构{\"documentType\":\"\",\"orderNumber\":\"\",\"date\":\"\",\"customerName\":\"\",\"operator\":\"\",\"items\":[{\"conName\":\"\",\"spec\":\"\",\"unitName\":\"\",\"inOutNumber\":\"\",\"purchasePrice\":\"\",\"subTotal\":\"\"}],\"totalAmount\":\"\",\"remark\":\"\"}。字段映射规则documentType对应单据类型/销售单/采购单/出货单orderNumber对应单号/编号/Nodate对应日期/开单日期customerName对应客户名称/收货单位/供应商operator对应业务员/经办人/制单人/操作员items.conName对应品名/名称items.spec对应规格/型号items.unitName对应单位items.inOutNumber对应数量items.purchasePrice对应单价items.subTotal对应金额/小计totalAmount对应总金额/合计金额remark对应备注。严禁生成图片中不存在的字段内容看不清或未出现的字段必须为null不允许推测或补全不得生成多余字段items只能根据识别到的行生成不得虚构。必须能识别旋转、倾斜、模糊、撕裂、光照差异、列顺序混乱、无表格线等情况并尽量恢复信息。最终输出必须是纯JSON不得包含任何非JSON字符。")
.ragOptions(RagOptions.builder() .ragOptions(RagOptions.builder()
.sessionFileIds(List.of(id)) .sessionFileIds(List.of(id))
.build()) .build())

View File

@@ -293,6 +293,7 @@ public class OrderInfoServiceImpl extends ServiceImpl<OrderInfoMapper, OrderInfo
} }
} }
List<OrderDetail> orderDetails = cartService.getCartByTableCode(shopInfo.getId(), param.getTableCode(), param.getPlaceNum()); List<OrderDetail> orderDetails = cartService.getCartByTableCode(shopInfo.getId(), param.getTableCode(), param.getPlaceNum());
// if (param.)
AssertUtil.isListEmpty(orderDetails, "下单失败 购物车为空"); AssertUtil.isListEmpty(orderDetails, "下单失败 购物车为空");
processOrderDetails(orderDetails, param.getLimitRate()); processOrderDetails(orderDetails, param.getLimitRate());
//生成订单 //生成订单

View File

@@ -1,340 +1,349 @@
package com.czg.service.product.service.impl; package com.czg.service.product.service.impl;
import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions; import cn.hutool.core.bean.copier.CopyOptions;
import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.thread.ThreadUtil; import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.NumberUtil; import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.digest.DigestUtil; import cn.hutool.crypto.digest.DigestUtil;
import com.alibaba.fastjson2.JSON; import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject; import com.alibaba.fastjson2.JSONObject;
import com.czg.exception.CzgException; import com.czg.exception.CzgException;
import com.czg.market.entity.MkOcr; import com.czg.market.entity.MkOcr;
import com.czg.product.entity.MkOcrService; import com.czg.product.entity.MkOcrService;
import com.czg.product.dto.ConsStockFlowDTO; import com.czg.product.dto.ConsStockFlowDTO;
import com.czg.product.dto.SaleOrderDTO; import com.czg.product.dto.SaleOrderDTO;
import com.czg.product.entity.ConsInfo; import com.czg.product.entity.ConsInfo;
import com.czg.product.entity.ConsStockFlow; import com.czg.product.entity.ConsStockFlow;
import com.czg.product.enums.InOutItemEnum; import com.czg.product.enums.InOutItemEnum;
import com.czg.product.enums.InOutTypeEnum; import com.czg.product.enums.InOutTypeEnum;
import com.czg.product.param.*; import com.czg.product.param.*;
import com.czg.product.service.ConsStockFlowService; import com.czg.product.service.ConsStockFlowService;
import com.czg.product.vo.ConsCheckStockRecordVo; import com.czg.product.vo.ConsCheckStockRecordVo;
import com.czg.sa.StpKit; import com.czg.sa.StpKit;
import com.czg.service.product.mapper.ConsInfoMapper; import com.czg.service.product.mapper.ConsInfoMapper;
import com.czg.service.product.mapper.ConsStockFlowMapper; import com.czg.service.product.mapper.ConsStockFlowMapper;
import com.czg.service.product.mapper.ProductMapper; import com.czg.service.product.mapper.ProductMapper;
import com.czg.service.product.util.WxAccountUtil; import com.czg.service.product.util.WxAccountUtil;
import com.czg.utils.AliOcrUtil; import com.czg.utils.AliOcrUtil;
import com.czg.utils.PageUtil; import com.czg.utils.PageUtil;
import com.github.pagehelper.PageHelper; import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo; import com.github.pagehelper.PageInfo;
import com.mybatisflex.core.paginate.Page; import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.core.query.QueryWrapper; import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.spring.service.impl.ServiceImpl; import com.mybatisflex.spring.service.impl.ServiceImpl;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import java.io.InputStream; import java.io.ByteArrayInputStream;
import java.math.BigDecimal; import java.io.IOException;
import java.time.LocalDate; import java.io.InputStream;
import java.util.*; import java.math.BigDecimal;
import java.util.stream.Collectors; import java.time.LocalDate;
import java.util.*;
import java.util.stream.Collectors;
/** /**
* 耗材库存变动记录 * 耗材库存变动记录
* *
* @author Tankaikai tankaikai@aliyun.com * @author Tankaikai tankaikai@aliyun.com
* @since 1.0 2025-02-21 * @since 1.0 2025-02-21
*/ */
@AllArgsConstructor @AllArgsConstructor
@Service @Service
@Slf4j @Slf4j
public class ConsStockFlowServiceImpl extends ServiceImpl<ConsStockFlowMapper, ConsStockFlow> implements ConsStockFlowService { public class ConsStockFlowServiceImpl extends ServiceImpl<ConsStockFlowMapper, ConsStockFlow> implements ConsStockFlowService {
private final ConsInfoMapper consInfoMapper; private final ConsInfoMapper consInfoMapper;
private final ProductMapper productMapper; private final ProductMapper productMapper;
@Resource @Resource
private WxAccountUtil wxAccountUtil; private WxAccountUtil wxAccountUtil;
@Resource @Resource
private MkOcrService ocrService; private MkOcrService ocrService;
private QueryWrapper buildQueryWrapper(ConsStockFlowDTO param) { private QueryWrapper buildQueryWrapper(ConsStockFlowDTO param) {
QueryWrapper queryWrapper = PageUtil.buildSortQueryWrapper(); QueryWrapper queryWrapper = PageUtil.buildSortQueryWrapper();
/*if (StrUtil.isNotEmpty(param.getName())) { /*if (StrUtil.isNotEmpty(param.getName())) {
queryWrapper.like(ConsStockFlow::getName, param.getName()); queryWrapper.like(ConsStockFlow::getName, param.getName());
}*/ }*/
Long shopId = StpKit.USER.getShopId(0L); Long shopId = StpKit.USER.getShopId(0L);
queryWrapper.eq(ConsStockFlow::getShopId, shopId); queryWrapper.eq(ConsStockFlow::getShopId, shopId);
queryWrapper.orderBy(ConsStockFlow::getId, false); queryWrapper.orderBy(ConsStockFlow::getId, false);
return queryWrapper; return queryWrapper;
} }
@Override @Override
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public void inStock(ConsInOutStockHeadParam param) { public void inStock(ConsInOutStockHeadParam param) {
Long shopId = StpKit.USER.getShopId(0L); Long shopId = StpKit.USER.getShopId(0L);
Long createUserId = StpKit.USER.getLoginIdAsLong(); Long createUserId = StpKit.USER.getLoginIdAsLong();
String createUserName = StpKit.USER.getAccount(); String createUserName = StpKit.USER.getAccount();
ConsStockFlow head = BeanUtil.copyProperties(param, ConsStockFlow.class); ConsStockFlow head = BeanUtil.copyProperties(param, ConsStockFlow.class);
List<ConsStockFlow> entityList = BeanUtil.copyToList(param.getBodyList(), ConsStockFlow.class); List<ConsStockFlow> entityList = BeanUtil.copyToList(param.getBodyList(), ConsStockFlow.class);
List<ConsStockFlow> insertList = new ArrayList<>(); List<ConsStockFlow> insertList = new ArrayList<>();
List<ConsInfo> updateStockList = new ArrayList<>(); List<ConsInfo> updateStockList = new ArrayList<>();
for (ConsStockFlow entity : entityList) { for (ConsStockFlow entity : entityList) {
BeanUtil.copyProperties(head, entity, CopyOptions.create().ignoreNullValue()); BeanUtil.copyProperties(head, entity, CopyOptions.create().ignoreNullValue());
entity.setShopId(shopId); entity.setShopId(shopId);
entity.setInOutType(InOutTypeEnum.IN.value()); entity.setInOutType(InOutTypeEnum.IN.value());
entity.setInOutItem(InOutItemEnum.MANUAL_IN.value()); entity.setInOutItem(InOutItemEnum.MANUAL_IN.value());
entity.setCreateUserId(createUserId);
entity.setCreateUserName(createUserName);
Long conId = entity.getConId();
ConsInfo consInfo = consInfoMapper.selectOneById(conId);
if (consInfo == null) {
throw new CzgException(StrUtil.format("耗材{}不存在", entity.getConName()));
}
entity.setBeforeNumber(consInfo.getStockNumber());
entity.setAfterNumber(NumberUtil.add(entity.getBeforeNumber(), entity.getInOutNumber()));
insertList.add(entity);
consInfo.setStockNumber(entity.getAfterNumber());
updateStockList.add(consInfo);
}
super.saveBatch(insertList);
for (ConsInfo consInfo : updateStockList) {
consInfoMapper.update(consInfo);
}
}
@Override
@Transactional(rollbackFor = Exception.class)
public void outStock(ConsInOutStockHeadParam param) {
Long shopId = StpKit.USER.getShopId(0L);
Long createUserId = StpKit.USER.getLoginIdAsLong();
String createUserName = StpKit.USER.getAccount();
ConsStockFlow head = BeanUtil.copyProperties(param, ConsStockFlow.class);
List<ConsStockFlow> entityList = BeanUtil.copyToList(param.getBodyList(), ConsStockFlow.class);
List<ConsStockFlow> insertList = new ArrayList<>();
List<ConsInfo> updateStockList = new ArrayList<>();
for (ConsStockFlow entity : entityList) {
BeanUtil.copyProperties(head, entity, CopyOptions.create().ignoreNullValue());
entity.setInOutNumber(NumberUtil.sub(BigDecimal.ZERO, entity.getInOutNumber()));
entity.setShopId(shopId);
entity.setInOutType(InOutTypeEnum.OUT.value());
entity.setInOutItem(InOutItemEnum.MANUAL_OUT.value());
entity.setCreateUserId(createUserId);
entity.setCreateUserName(createUserName);
Long conId = entity.getConId();
ConsInfo consInfo = consInfoMapper.selectOneById(conId);
if (consInfo == null) {
throw new CzgException(StrUtil.format("耗材{}不存在", entity.getConName()));
}
entity.setBeforeNumber(consInfo.getStockNumber());
entity.setAfterNumber(NumberUtil.add(entity.getBeforeNumber(), entity.getInOutNumber()));
insertList.add(entity);
consInfo.setStockNumber(entity.getAfterNumber());
updateStockList.add(consInfo);
}
super.saveBatch(insertList);
for (ConsInfo consInfo : updateStockList) {
consInfoMapper.update(consInfo);
}
}
@Override
@Transactional(rollbackFor = Exception.class)
public void checkStock(ConsCheckStockParam param) {
Long shopId = StpKit.USER.getShopId(0L);
Long createUserId = StpKit.USER.getLoginIdAsLong();
String createUserName = StpKit.USER.getAccount();
ConsStockFlow entity = new ConsStockFlow();
entity.setCreateUserId(createUserId); entity.setCreateUserId(createUserId);
entity.setCreateUserName(createUserName); entity.setCreateUserName(createUserName);
Long conId = entity.getConId(); entity.setShopId(shopId);
ConsInfo consInfo = consInfoMapper.selectOneById(conId); entity.setConId(param.getConId());
entity.setConName(param.getConName());
entity.setPurchasePrice(param.getPrice());
ConsInfo consInfo = consInfoMapper.selectOneById(param.getConId());
if (consInfo == null) { if (consInfo == null) {
throw new CzgException(StrUtil.format("耗材{}不存在", entity.getConName())); throw new CzgException(StrUtil.format("耗材{}不存在", entity.getConName()));
} }
BigDecimal winLossNumber = NumberUtil.sub(param.getActualNumber(), param.getStockNumber());
if (!NumberUtil.equals(winLossNumber, param.getWinLossNumber())) {
throw new CzgException(StrUtil.format("耗材{}库存在发生变动,请刷新后重试", entity.getConName()));
}
entity.setBeforeNumber(consInfo.getStockNumber()); entity.setBeforeNumber(consInfo.getStockNumber());
entity.setInOutNumber(winLossNumber);
entity.setAfterNumber(NumberUtil.add(entity.getBeforeNumber(), entity.getInOutNumber())); entity.setAfterNumber(NumberUtil.add(entity.getBeforeNumber(), entity.getInOutNumber()));
insertList.add(entity); if (NumberUtil.isLess(winLossNumber, BigDecimal.ZERO)) {
entity.setInOutType(InOutTypeEnum.OUT.value());
entity.setInOutItem(InOutItemEnum.LOSS_OUT.value());
} else {
entity.setInOutType(InOutTypeEnum.IN.value());
entity.setInOutItem(InOutItemEnum.WIN_IN.value());
}
entity.setSubTotal(NumberUtil.mul(winLossNumber, param.getPrice()));
entity.setRemark(param.getRemark());
saveFlow(entity);
consInfo.setStockNumber(entity.getAfterNumber()); consInfo.setStockNumber(entity.getAfterNumber());
updateStockList.add(consInfo);
}
super.saveBatch(insertList);
for (ConsInfo consInfo : updateStockList) {
consInfoMapper.update(consInfo); consInfoMapper.update(consInfo);
} }
}
@Override @Override
@Transactional(rollbackFor = Exception.class) public Page<ConsCheckStockRecordVo> getCheckStockRecordPage(Long conId) {
public void outStock(ConsInOutStockHeadParam param) { Long shopId = StpKit.USER.getShopId(0L);
Long shopId = StpKit.USER.getShopId(0L); return super.pageAs(PageUtil.buildPage(), query().eq(ConsStockFlow::getShopId, shopId).eq(ConsStockFlow::getConId, conId).orderBy(ConsStockFlow::getId, false), ConsCheckStockRecordVo.class);
Long createUserId = StpKit.USER.getLoginIdAsLong(); }
String createUserName = StpKit.USER.getAccount();
ConsStockFlow head = BeanUtil.copyProperties(param, ConsStockFlow.class); @Override
List<ConsStockFlow> entityList = BeanUtil.copyToList(param.getBodyList(), ConsStockFlow.class); public List<ConsCheckStockRecordVo> getCheckStockRecordList(Long conId) {
List<ConsStockFlow> insertList = new ArrayList<>(); Long shopId = StpKit.USER.getShopId(0L);
List<ConsInfo> updateStockList = new ArrayList<>(); return super.mapper.selectListByQueryAs(query().eq(ConsStockFlow::getShopId, shopId).eq(ConsStockFlow::getConId, conId).orderBy(ConsStockFlow::getId, false), ConsCheckStockRecordVo.class);
for (ConsStockFlow entity : entityList) { }
BeanUtil.copyProperties(head, entity, CopyOptions.create().ignoreNullValue());
entity.setInOutNumber(NumberUtil.sub(BigDecimal.ZERO, entity.getInOutNumber())); @Override
entity.setShopId(shopId); public void reportDamage(ConsReportDamageParam param) {
entity.setInOutType(InOutTypeEnum.OUT.value()); Long shopId = StpKit.USER.getShopId(0L);
entity.setInOutItem(InOutItemEnum.MANUAL_OUT.value()); Long createUserId = StpKit.USER.getLoginIdAsLong();
String createUserName = StpKit.USER.getAccount();
ConsInfo consInfo = consInfoMapper.selectOneById(param.getConId());
if (consInfo == null) {
throw new CzgException("耗材不存在");
}
ConsStockFlow entity = new ConsStockFlow();
entity.setCreateUserId(createUserId); entity.setCreateUserId(createUserId);
entity.setCreateUserName(createUserName); entity.setCreateUserName(createUserName);
Long conId = entity.getConId(); entity.setShopId(shopId);
ConsInfo consInfo = consInfoMapper.selectOneById(conId); entity.setConId(param.getConId());
if (consInfo == null) { entity.setConName(consInfo.getConName());
throw new CzgException(StrUtil.format("耗材{}不存在", entity.getConName())); entity.setPurchasePrice(consInfo.getPrice());
BigDecimal balance = NumberUtil.sub(consInfo.getStockNumber(), param.getNumber());
if (NumberUtil.isLess(balance, BigDecimal.ZERO)) {
throw new CzgException(StrUtil.format("耗材{}报损数量不能大于当前库存{}", entity.getConName(), consInfo.getStockNumber()));
} }
entity.setBeforeNumber(consInfo.getStockNumber()); entity.setBeforeNumber(consInfo.getStockNumber());
entity.setAfterNumber(NumberUtil.add(entity.getBeforeNumber(), entity.getInOutNumber())); entity.setInOutNumber(NumberUtil.sub(BigDecimal.ZERO, param.getNumber()));
insertList.add(entity); entity.setAfterNumber(balance);
entity.setInOutType(InOutTypeEnum.OUT.value());
entity.setInOutItem(InOutItemEnum.DAMAGE_OUT.value());
entity.setSubTotal(NumberUtil.mul(param.getNumber(), consInfo.getPrice()));
entity.setImgUrls(JSON.toJSONString(param.getImgUrls()));
saveFlow(entity);
consInfo.setStockNumber(entity.getAfterNumber()); consInfo.setStockNumber(entity.getAfterNumber());
updateStockList.add(consInfo);
}
super.saveBatch(insertList);
for (ConsInfo consInfo : updateStockList) {
consInfoMapper.update(consInfo); consInfoMapper.update(consInfo);
} }
}
@Override @Override
@Transactional(rollbackFor = Exception.class) public Page<ConsStockFlowDTO> findConsStockFlowPage(ConsStockFlowParam param) {
public void checkStock(ConsCheckStockParam param) { Long shopId = StpKit.USER.getShopId(0L);
Long shopId = StpKit.USER.getShopId(0L); PageHelper.startPage(PageUtil.buildPageHelp());
Long createUserId = StpKit.USER.getLoginIdAsLong(); return PageUtil.convert(new PageInfo<>(mapper.findConsStockFlowPage(shopId, param.getInOutType(), param.getInOutItem(), param.getConId())));
String createUserName = StpKit.USER.getAccount();
ConsStockFlow entity = new ConsStockFlow();
entity.setCreateUserId(createUserId);
entity.setCreateUserName(createUserName);
entity.setShopId(shopId);
entity.setConId(param.getConId());
entity.setConName(param.getConName());
entity.setPurchasePrice(param.getPrice());
ConsInfo consInfo = consInfoMapper.selectOneById(param.getConId());
if (consInfo == null) {
throw new CzgException(StrUtil.format("耗材{}不存在", entity.getConName()));
} }
BigDecimal winLossNumber = NumberUtil.sub(param.getActualNumber(), param.getStockNumber());
if (!NumberUtil.equals(winLossNumber, param.getWinLossNumber())) {
throw new CzgException(StrUtil.format("耗材{}库存在发生变动,请刷新后重试", entity.getConName()));
}
entity.setBeforeNumber(consInfo.getStockNumber());
entity.setInOutNumber(winLossNumber);
entity.setAfterNumber(NumberUtil.add(entity.getBeforeNumber(), entity.getInOutNumber()));
if (NumberUtil.isLess(winLossNumber, BigDecimal.ZERO)) {
entity.setInOutType(InOutTypeEnum.OUT.value());
entity.setInOutItem(InOutItemEnum.LOSS_OUT.value());
} else {
entity.setInOutType(InOutTypeEnum.IN.value());
entity.setInOutItem(InOutItemEnum.WIN_IN.value());
}
entity.setSubTotal(NumberUtil.mul(winLossNumber, param.getPrice()));
entity.setRemark(param.getRemark());
saveFlow(entity);
consInfo.setStockNumber(entity.getAfterNumber());
consInfoMapper.update(consInfo);
}
@Override @Override
public Page<ConsCheckStockRecordVo> getCheckStockRecordPage(Long conId) { public void saveFlow(ConsStockFlow entity) {
Long shopId = StpKit.USER.getShopId(0L); super.save(entity);
return super.pageAs(PageUtil.buildPage(), query().eq(ConsStockFlow::getShopId, shopId).eq(ConsStockFlow::getConId, conId).orderBy(ConsStockFlow::getId, false), ConsCheckStockRecordVo.class); Long shopId = entity.getShopId();
} BigDecimal afterNumber = entity.getAfterNumber();
ConsInfo consInfo = consInfoMapper.selectOneById(entity.getConId());
@Override String shopName = "";
public List<ConsCheckStockRecordVo> getCheckStockRecordList(Long conId) {
Long shopId = StpKit.USER.getShopId(0L);
return super.mapper.selectListByQueryAs(query().eq(ConsStockFlow::getShopId, shopId).eq(ConsStockFlow::getConId, conId).orderBy(ConsStockFlow::getId, false), ConsCheckStockRecordVo.class);
}
@Override
public void reportDamage(ConsReportDamageParam param) {
Long shopId = StpKit.USER.getShopId(0L);
Long createUserId = StpKit.USER.getLoginIdAsLong();
String createUserName = StpKit.USER.getAccount();
ConsInfo consInfo = consInfoMapper.selectOneById(param.getConId());
if (consInfo == null) {
throw new CzgException("耗材不存在");
}
ConsStockFlow entity = new ConsStockFlow();
entity.setCreateUserId(createUserId);
entity.setCreateUserName(createUserName);
entity.setShopId(shopId);
entity.setConId(param.getConId());
entity.setConName(consInfo.getConName());
entity.setPurchasePrice(consInfo.getPrice());
BigDecimal balance = NumberUtil.sub(consInfo.getStockNumber(), param.getNumber());
if (NumberUtil.isLess(balance, BigDecimal.ZERO)) {
throw new CzgException(StrUtil.format("耗材{}报损数量不能大于当前库存{}", entity.getConName(), consInfo.getStockNumber()));
}
entity.setBeforeNumber(consInfo.getStockNumber());
entity.setInOutNumber(NumberUtil.sub(BigDecimal.ZERO, param.getNumber()));
entity.setAfterNumber(balance);
entity.setInOutType(InOutTypeEnum.OUT.value());
entity.setInOutItem(InOutItemEnum.DAMAGE_OUT.value());
entity.setSubTotal(NumberUtil.mul(param.getNumber(), consInfo.getPrice()));
entity.setImgUrls(JSON.toJSONString(param.getImgUrls()));
saveFlow(entity);
consInfo.setStockNumber(entity.getAfterNumber());
consInfoMapper.update(consInfo);
}
@Override
public Page<ConsStockFlowDTO> findConsStockFlowPage(ConsStockFlowParam param) {
Long shopId = StpKit.USER.getShopId(0L);
PageHelper.startPage(PageUtil.buildPageHelp());
return PageUtil.convert(new PageInfo<>(mapper.findConsStockFlowPage(shopId, param.getInOutType(), param.getInOutItem(), param.getConId())));
}
@Override
public void saveFlow(ConsStockFlow entity) {
super.save(entity);
Long shopId = entity.getShopId();
BigDecimal afterNumber = entity.getAfterNumber();
ConsInfo consInfo = consInfoMapper.selectOneById(entity.getConId());
String shopName = "";
try {
shopName = StpKit.USER.getShopName();
} catch (Exception e) {
log.error("获取店铺名称失败");
}
if (StrUtil.isEmpty(shopName)) {
shopName = productMapper.getShopName(shopId);
}
BigDecimal conWarning = consInfo.getConWarning();
// 库存小于警告值,发送消息提醒
if (NumberUtil.isLess(afterNumber, conWarning)) {
List<String> openIdList = consInfoMapper.findOpenIdList(shopId, "con");
if (CollUtil.isEmpty(openIdList)) {
return;
}
String conName = StrUtil.format("{}数量<预警值{}", consInfo.getConName(), conWarning);
String finalShopName = shopName;
ThreadUtil.execAsync(() -> {
openIdList.parallelStream().forEach(openId -> {
wxAccountUtil.sendStockMsg(finalShopName, conName, afterNumber, openId);
});
});
}
}
@Override
public Integer ocr(String originalFilename, InputStream inputStream) {
Long shopId = StpKit.USER.getShopId();
String md5 = DigestUtil.md5Hex(inputStream);
MkOcr ocr = ocrService.getOne(new QueryWrapper().eq(MkOcr::getShopId, shopId).eq(MkOcr::getMd5, md5));
if (ocr != null) {
return ocr.getId();
}
MkOcr mkOcr = new MkOcr();
mkOcr.setShopId(shopId);
mkOcr.setMd5(md5);
ocrService.save(mkOcr);
ThreadUtil.execAsync(() -> {
try { try {
String infoStr = AliOcrUtil.appCall(inputStream, originalFilename); shopName = StpKit.USER.getShopName();
SaleOrderDTO saleOrderDTO = JSONObject.parseObject(infoStr, SaleOrderDTO.class); } catch (Exception e) {
log.error("获取店铺名称失败");
ArrayList<ConsInOutStockBodyParam> bodyList = new ArrayList<>(); }
Set<String> nameList = saleOrderDTO.getItems().stream().map(SaleOrderDTO.Item::getConName).collect(Collectors.toSet()); if (StrUtil.isEmpty(shopName)) {
Map<String, ConsInfo> consInfoMap = new HashMap<>(); shopName = productMapper.getShopName(shopId);
if (!nameList.isEmpty()) { }
consInfoMap = consInfoMapper.selectListByQuery(new QueryWrapper().in(ConsInfo::getConName, nameList).eq(ConsInfo::getShopId, shopId)) BigDecimal conWarning = consInfo.getConWarning();
.stream().collect(Collectors.toMap(ConsInfo::getConName, consInfo -> consInfo)); // 库存小于警告值,发送消息提醒
} if (NumberUtil.isLess(afterNumber, conWarning)) {
ArrayList<SaleOrderDTO.Item> unInCons = new ArrayList<>(); List<String> openIdList = consInfoMapper.findOpenIdList(shopId, "con");
for (SaleOrderDTO.Item item : saleOrderDTO.getItems()) { if (CollUtil.isEmpty(openIdList)) {
ConsInfo consInfo = consInfoMap.get(item.getConName()); return;
if (consInfo == null) { }
unInCons.add(item); String conName = StrUtil.format("{}数量<预警值{}", consInfo.getConName(), conWarning);
continue; String finalShopName = shopName;
} ThreadUtil.execAsync(() -> {
bodyList.add(new ConsInOutStockBodyParam() openIdList.parallelStream().forEach(openId -> {
.setConId(consInfo.getId().toString()) wxAccountUtil.sendStockMsg(finalShopName, conName, afterNumber, openId);
.setConName(consInfo.getConName()) });
.setPurchasePrice(new BigDecimal(item.getPurchasePrice())) });
.setUnitName(item.getUnitName())
.setSubTotal(new BigDecimal(item.getSubTotal()))
.setInOutNumber(new BigDecimal(item.getInOutNumber())));
}
ConsInOutStockHeadParam headParam = new ConsInOutStockHeadParam();
headParam.setBatchNo(saleOrderDTO.getOrderNumber())
.setInOutDate(LocalDate.parse(saleOrderDTO.getDate()))
.setAmountPayable(new BigDecimal(saleOrderDTO.getTotalAmount()))
.setBodyList(bodyList)
.setUnInCons(unInCons)
.setOcrSaleOrder(saleOrderDTO);
mkOcr.setStatus("SUCCESS");
mkOcr.setResp(JSON.toJSONString(headParam));
}catch (Exception e) {
mkOcr.setErr(e.getMessage());
mkOcr.setStatus("FAILED");
log.warn("ocr失败: {}", e.getMessage());
}finally {
ocrService.updateById(mkOcr);
} }
});
return mkOcr.getId();
}
@Override
public ConsInOutStockHeadParam ocrDetail(Long id) {
MkOcr mkOcr = ocrService.getOne(new QueryWrapper().eq(MkOcr::getShopId, StpKit.USER.getShopId()).eq(MkOcr::getId, id));
if (StrUtil.isNotBlank(mkOcr.getResp())) {
return JSONObject.parseObject(mkOcr.getResp(), ConsInOutStockHeadParam.class);
} }
return null;
@Override
public Integer ocr(String originalFilename, InputStream inputStream) {
Long shopId = StpKit.USER.getShopId();
byte[] readAllBytes = null;
try {
readAllBytes = inputStream.readAllBytes();
} catch (IOException e) {
throw new RuntimeException(e);
}
String md5 = DigestUtil.md5Hex(readAllBytes);
MkOcr ocr = ocrService.getOne(new QueryWrapper().eq(MkOcr::getShopId, shopId).eq(MkOcr::getMd5, md5));
if (ocr != null) {
// return ocr.getId();
}
MkOcr mkOcr = new MkOcr();
mkOcr.setShopId(shopId);
mkOcr.setMd5(md5);
ocrService.save(mkOcr);
byte[] finalReadAllBytes1 = readAllBytes;
ThreadUtil.execAsync(() -> {
try {
String infoStr = AliOcrUtil.appCall(finalReadAllBytes1, originalFilename);
SaleOrderDTO saleOrderDTO = JSONObject.parseObject(infoStr, SaleOrderDTO.class);
ArrayList<ConsInOutStockBodyParam> bodyList = new ArrayList<>();
Set<String> nameList = saleOrderDTO.getItems().stream().map(SaleOrderDTO.Item::getConName).collect(Collectors.toSet());
Map<String, ConsInfo> consInfoMap = new HashMap<>();
if (!nameList.isEmpty()) {
consInfoMap = consInfoMapper.selectListByQuery(new QueryWrapper().in(ConsInfo::getConName, nameList).eq(ConsInfo::getShopId, shopId))
.stream().collect(Collectors.toMap(ConsInfo::getConName, consInfo -> consInfo));
}
ArrayList<SaleOrderDTO.Item> unInCons = new ArrayList<>();
for (SaleOrderDTO.Item item : saleOrderDTO.getItems()) {
ConsInfo consInfo = consInfoMap.get(item.getConName());
if (consInfo == null) {
unInCons.add(item);
continue;
}
bodyList.add(new ConsInOutStockBodyParam()
.setConId(consInfo.getId().toString())
.setConName(consInfo.getConName())
.setPurchasePrice(new BigDecimal(item.getPurchasePrice()))
.setUnitName(item.getUnitName())
.setSubTotal(new BigDecimal(item.getSubTotal()))
.setInOutNumber(new BigDecimal(item.getInOutNumber())));
}
ConsInOutStockHeadParam headParam = new ConsInOutStockHeadParam();
headParam.setBatchNo(saleOrderDTO.getOrderNumber())
.setInOutDate(LocalDate.parse(saleOrderDTO.getDate()))
.setAmountPayable(new BigDecimal(saleOrderDTO.getTotalAmount()))
.setBodyList(bodyList)
.setUnInCons(unInCons)
.setOcrSaleOrder(saleOrderDTO);
mkOcr.setStatus("SUCCESS");
mkOcr.setResp(JSON.toJSONString(headParam));
}catch (Exception e) {
mkOcr.setErr(e.getMessage());
mkOcr.setStatus("FAILED");
log.error("ocr失败:", e);
}finally {
ocrService.updateById(mkOcr);
}
});
return mkOcr.getId();
}
@Override
public ConsInOutStockHeadParam ocrDetail(Long id) {
MkOcr mkOcr = ocrService.getOne(new QueryWrapper().eq(MkOcr::getShopId, StpKit.USER.getShopId()).eq(MkOcr::getId, id));
if (StrUtil.isNotBlank(mkOcr.getResp())) {
return JSONObject.parseObject(mkOcr.getResp(), ConsInOutStockHeadParam.class);
}
return null;
}
} }
}