90 changed files with 2113 additions and 229 deletions
@ -0,0 +1,14 @@ |
|||
package cc.hiver.core.common.constant; |
|||
|
|||
/** |
|||
* 订单常量类 |
|||
* @author 王富康 |
|||
* @date 2023/10/21 |
|||
*/ |
|||
public interface ProductConstant { |
|||
/** |
|||
商品状态:0:下架;1:上架:2删除 |
|||
*/ |
|||
String[] DEL_FLAG = {"0", "1", "2"}; |
|||
|
|||
} |
|||
@ -1,26 +1,67 @@ |
|||
package cc.hiver.mall.checkstock.controller; |
|||
|
|||
import cc.hiver.core.common.utils.ResultUtil; |
|||
import cc.hiver.core.common.vo.Result; |
|||
import cc.hiver.mall.checkstock.service.CheckStockDetailService; |
|||
import cc.hiver.mall.checkstock.vo.CheckStockVo; |
|||
import cc.hiver.mall.checkstock.vo.CheckStockDetailVo; |
|||
import io.swagger.annotations.Api; |
|||
import io.swagger.annotations.ApiOperation; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.apache.commons.lang3.StringUtils; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.transaction.annotation.Transactional; |
|||
import org.springframework.web.bind.annotation.RequestBody; |
|||
import org.springframework.web.bind.annotation.RequestMapping; |
|||
import org.springframework.web.bind.annotation.RequestMethod; |
|||
import org.springframework.web.bind.annotation.RestController; |
|||
|
|||
import java.util.List; |
|||
|
|||
/** |
|||
* 盘点商品明细 |
|||
* @author 王富康 |
|||
* @date 2024/3/22 |
|||
*/ |
|||
@Slf4j |
|||
@RestController |
|||
@Api(tags = "盘点商品明细接口") |
|||
@RequestMapping("/hiver/app/checkStock/") |
|||
@RequestMapping("/hiver/app/checkStockDetail/") |
|||
@Transactional |
|||
public class CheckStockDetailController { |
|||
|
|||
@Autowired |
|||
private CheckStockDetailService checkStockDetailService; |
|||
|
|||
public Result addCheckStockDetail(CheckStockVo checkStockVo){ |
|||
return null; |
|||
/** |
|||
* 增加盘点明细 |
|||
* |
|||
* @param checkStockDetailVos 商品明细 |
|||
* @return Result |
|||
* @author 王富康 |
|||
* @date 2024/3/17 |
|||
*/ |
|||
@RequestMapping(value = "/addCheckStockDetail", method = RequestMethod.POST) |
|||
@ApiOperation("盘点-新增商品详细信息") |
|||
public Result addCheckStockDetail(@RequestBody List<CheckStockDetailVo> checkStockDetailVos) { |
|||
checkStockDetailService.addCheckStockDetail(checkStockDetailVos); |
|||
return ResultUtil.success("新增商品信息成功!"); |
|||
} |
|||
|
|||
/** |
|||
* 删除盘点明细 |
|||
* |
|||
* @param id 盘点id |
|||
* @return Result |
|||
* @author 王富康 |
|||
* @date 2024/3/17 |
|||
*/ |
|||
@RequestMapping(value = "/deleteCheckStockDetail", method = RequestMethod.POST) |
|||
@ApiOperation("盘点-删除商品详细信息") |
|||
public Result deleteCheckStockDetail(String id) { |
|||
if (StringUtils.isEmpty(id)) { |
|||
ResultUtil.error("盘点明细id不能为空!"); |
|||
} |
|||
checkStockDetailService.deleteCheckStockDetail(id); |
|||
return ResultUtil.success("删除成功!"); |
|||
} |
|||
} |
|||
|
|||
@ -1,9 +1,13 @@ |
|||
package cc.hiver.mall.checkstock.mapper; |
|||
|
|||
import cc.hiver.mall.checkstock.entity.CheckStock; |
|||
import cc.hiver.mall.checkstock.pojo.CheckStockPageQuery; |
|||
import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
|||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
|||
import org.apache.ibatis.annotations.Param; |
|||
import org.springframework.stereotype.Repository; |
|||
|
|||
@Repository |
|||
public interface CheckStockMapper extends BaseMapper<CheckStock> { |
|||
Page<CheckStock> getCheckStockList(Page<CheckStock> page,@Param("deductLogPageQuery") CheckStockPageQuery checkStockPageQuery); |
|||
} |
|||
|
|||
@ -0,0 +1,30 @@ |
|||
package cc.hiver.mall.checkstock.pojo; |
|||
|
|||
import cc.hiver.core.base.HiverBasePageQuery; |
|||
import io.swagger.annotations.ApiModelProperty; |
|||
import lombok.Data; |
|||
|
|||
import java.util.List; |
|||
|
|||
@Data |
|||
public class CheckStockPageQuery extends HiverBasePageQuery { |
|||
|
|||
private static final long serialVersionUID = 1L; |
|||
|
|||
@ApiModelProperty(value = "店铺id") |
|||
private String shopId; |
|||
|
|||
@ApiModelProperty(value = "盘点id") |
|||
private String checkStockId; |
|||
|
|||
@ApiModelProperty(value = "开始时间") |
|||
private String startDate; |
|||
|
|||
@ApiModelProperty(value = "结束时间") |
|||
private String endDate; |
|||
|
|||
@ApiModelProperty(value = "商品id") |
|||
private List<String> productIdList; |
|||
|
|||
|
|||
} |
|||
@ -1,7 +1,20 @@ |
|||
package cc.hiver.mall.checkstock.service; |
|||
|
|||
import cc.hiver.mall.checkstock.entity.CheckStockDetail; |
|||
import cc.hiver.mall.checkstock.pojo.CheckStockPageQuery; |
|||
import cc.hiver.mall.checkstock.vo.CheckStockDetailVo; |
|||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
|||
import com.baomidou.mybatisplus.extension.service.IService; |
|||
|
|||
import java.util.List; |
|||
|
|||
public interface CheckStockDetailService extends IService<CheckStockDetail> { |
|||
|
|||
void addCheckStockDetail(List<CheckStockDetailVo> checkStockDetailVos); |
|||
|
|||
void deleteCheckStockDetail(String id); |
|||
|
|||
List<CheckStockDetailVo> getByCheckStockDetailByCheckId(String checkStockId); |
|||
|
|||
Page<CheckStockDetailVo> getByCheckStockDetailByCheckIdOfPage(CheckStockPageQuery checkStockPageQuery); |
|||
} |
|||
|
|||
@ -1,11 +1,20 @@ |
|||
package cc.hiver.mall.checkstock.service; |
|||
|
|||
import cc.hiver.mall.checkstock.entity.CheckStock; |
|||
import cc.hiver.mall.checkstock.vo.CheckStockDetailVo; |
|||
import cc.hiver.mall.checkstock.pojo.CheckStockPageQuery; |
|||
import cc.hiver.mall.checkstock.vo.CheckStockPageVo; |
|||
import cc.hiver.mall.checkstock.vo.CheckStockVo; |
|||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
|||
import com.baomidou.mybatisplus.extension.service.IService; |
|||
|
|||
import java.util.List; |
|||
|
|||
public interface CheckStockService extends IService<CheckStock> { |
|||
void addCheckStock(List<CheckStockDetailVo> checkStockDetailVos); |
|||
CheckStock addCheckStock(CheckStockVo checkStockVo); |
|||
|
|||
CheckStockPageVo getCheckStock(CheckStockPageQuery checkStockPageQuery); |
|||
|
|||
void submitToStock(String id); |
|||
|
|||
Page<CheckStock> getCheckStockList( CheckStockPageQuery checkStockPageQuery); |
|||
|
|||
void deleteCheckStock(String id); |
|||
} |
|||
|
|||
@ -1,11 +1,214 @@ |
|||
package cc.hiver.mall.checkstock.service.impl; |
|||
|
|||
import cc.hiver.core.common.utils.SecurityUtil; |
|||
import cc.hiver.mall.checkstock.entity.CheckStockDetail; |
|||
import cc.hiver.mall.checkstock.mapper.CheckStockDetailMapper; |
|||
import cc.hiver.mall.checkstock.pojo.CheckStockPageQuery; |
|||
import cc.hiver.mall.checkstock.service.CheckStockDetailService; |
|||
import cc.hiver.mall.checkstock.vo.CheckStockAttributeVo; |
|||
import cc.hiver.mall.checkstock.vo.CheckStockDetailVo; |
|||
import cc.hiver.mall.deductlog.vo.WorkerDeductLogVo; |
|||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
|||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.stereotype.Service; |
|||
|
|||
import java.util.ArrayList; |
|||
import java.util.HashMap; |
|||
import java.util.List; |
|||
import java.util.Map; |
|||
import java.util.stream.Collectors; |
|||
|
|||
/** |
|||
* 盘点商品实现类 |
|||
* |
|||
* @author 王富康 |
|||
* @date 2024/3/22 |
|||
*/ |
|||
@Service |
|||
public class CheckStockDetailServiceImpl extends ServiceImpl<CheckStockDetailMapper, CheckStockDetail> implements CheckStockDetailService { |
|||
|
|||
@Autowired |
|||
private SecurityUtil securityUtil; |
|||
|
|||
@Autowired |
|||
private CheckStockDetailMapper checkStockDetailMapper; |
|||
|
|||
/** |
|||
* 添加盘点详情信息 |
|||
* |
|||
* @param checkStockDetailVos 盘点详情视图对象列表,包含需要添加的盘点细节 |
|||
*/ |
|||
@Override |
|||
public void addCheckStockDetail(List<CheckStockDetailVo> checkStockDetailVos) { |
|||
if (checkStockDetailVos == null || checkStockDetailVos.isEmpty()) { |
|||
return; // 处理边界条件,输入参数为空的情况
|
|||
} |
|||
|
|||
// 获取当前店铺ID
|
|||
final String shopId = securityUtil.getShopId(); |
|||
|
|||
// 初始化盘点详情列表
|
|||
final List<CheckStockDetail> checkStockDetailList = new ArrayList<>(); |
|||
for (CheckStockDetailVo checkStockDetailVo : checkStockDetailVos) { |
|||
// 获取商品ID和盘点ID
|
|||
final String productId = checkStockDetailVo.getProductId(); |
|||
final String checkStockId = checkStockDetailVo.getCheckStockId(); |
|||
try { |
|||
// 先根据商品ID删除原有的盘点明细
|
|||
checkStockDetailMapper.deleteByProductId(shopId, checkStockId, productId); |
|||
|
|||
// 遍历并新增盘点的商品明细
|
|||
final List<CheckStockAttributeVo> checkStockAttributeVos = checkStockDetailVo.getCheckStockAttributeVos(); |
|||
for (CheckStockAttributeVo checkStockAttributeVo : checkStockAttributeVos) { |
|||
checkStockDetailList.addAll(buildCheckStockDetailList(checkStockDetailVo, checkStockAttributeVo, shopId, checkStockId, productId)); |
|||
} |
|||
} catch (Exception e) { |
|||
// 日志记录异常,可以进行相应的异常处理逻辑
|
|||
log.error("添加盘点详情信息失败", e); |
|||
} |
|||
} |
|||
|
|||
// 批量插入盘点详情信息
|
|||
try { |
|||
checkStockDetailMapper.batchInsertCheckStock(checkStockDetailList); |
|||
} catch (Exception e) { |
|||
// 日志记录异常,可以进行相应的异常处理逻辑
|
|||
log.error("批量插入盘点详情信息失败", e); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 构建盘点详情信息列表 |
|||
*/ |
|||
private static List<CheckStockDetail> buildCheckStockDetailList(CheckStockDetailVo checkStockDetailVo, |
|||
CheckStockAttributeVo checkStockAttributeVo, |
|||
String shopId, |
|||
String checkStockId, |
|||
String productId) { |
|||
final List<CheckStockDetail> details = new ArrayList<>(); |
|||
final String attributeList = checkStockAttributeVo.getAttributeList(); |
|||
final Integer pdNum = checkStockAttributeVo.getPdNum(); |
|||
final Integer stockCount = checkStockAttributeVo.getStockCount(); |
|||
|
|||
// 可为负数
|
|||
final int changeCount = pdNum - stockCount; |
|||
|
|||
final CheckStockDetail checkStockDetail = new CheckStockDetail(); |
|||
checkStockDetail.setCheckStockId(checkStockId); |
|||
checkStockDetail.setShopId(shopId); |
|||
checkStockDetail.setProductId(productId); |
|||
checkStockDetail.setProductName(checkStockDetailVo.getProductName()); |
|||
checkStockDetail.setProductSn(checkStockDetailVo.getProductSn()); |
|||
checkStockDetail.setProductPicture(checkStockDetailVo.getProductPicture()); |
|||
checkStockDetail.setPdNum(pdNum); |
|||
checkStockDetail.setChangeCount(changeCount); |
|||
checkStockDetail.setStockCount(stockCount); |
|||
checkStockDetail.setAttributeList(attributeList); |
|||
|
|||
details.add(checkStockDetail); |
|||
return details; |
|||
} |
|||
|
|||
|
|||
@Override |
|||
public void deleteCheckStockDetail(String id) { |
|||
// 删除明细
|
|||
checkStockDetailMapper.deleteById(id); |
|||
} |
|||
|
|||
@Override |
|||
public List<CheckStockDetailVo> getByCheckStockDetailByCheckId(String checkStockId) { |
|||
|
|||
// 查询明细
|
|||
final List<CheckStockDetail> checkStockDetailList = checkStockDetailMapper.getByCheckStockDetailByCheckId(checkStockId); |
|||
|
|||
// 封装至返回结果中
|
|||
final Map<String, CheckStockDetailVo> checkStockDetailVoMap = new HashMap<>(); |
|||
for (CheckStockDetail checkStockDetail : checkStockDetailList) { |
|||
final String productId = checkStockDetail.getProductId(); |
|||
if (checkStockDetailVoMap.containsKey(productId)) { |
|||
// 直接放规格即可
|
|||
final CheckStockAttributeVo checkStockAttributeVo = new CheckStockAttributeVo(); |
|||
checkStockAttributeVo.setAttributeList(checkStockDetail.getAttributeList()); |
|||
checkStockAttributeVo.setStockCount(checkStockDetail.getStockCount()); |
|||
checkStockAttributeVo.setPdNum(checkStockDetail.getPdNum()); |
|||
checkStockDetailVoMap.get(productId).getCheckStockAttributeVos().add(checkStockAttributeVo); |
|||
} else { |
|||
// 新增对象
|
|||
final CheckStockDetailVo checkStockDetailVo = new CheckStockDetailVo(); |
|||
checkStockDetailVo.setCheckStockId(checkStockDetail.getCheckStockId()); |
|||
checkStockDetailVo.setProductId(productId); |
|||
checkStockDetailVo.setProductName(checkStockDetail.getProductName()); |
|||
checkStockDetailVo.setProductPicture(checkStockDetail.getProductPicture()); |
|||
checkStockDetailVo.setProductSn(checkStockDetail.getProductSn()); |
|||
final List<CheckStockAttributeVo> checkStockAttributeVos = new ArrayList<>(); |
|||
final CheckStockAttributeVo checkStockAttributeVo = new CheckStockAttributeVo(); |
|||
checkStockAttributeVo.setAttributeList(checkStockDetail.getAttributeList()); |
|||
checkStockAttributeVo.setStockCount(checkStockDetail.getStockCount()); |
|||
checkStockAttributeVo.setPdNum(checkStockDetail.getPdNum()); |
|||
checkStockAttributeVos.add(checkStockAttributeVo); |
|||
checkStockDetailVo.setCheckStockAttributeVos(checkStockAttributeVos); |
|||
checkStockDetailVoMap.put(productId, checkStockDetailVo); |
|||
} |
|||
} |
|||
return new ArrayList<>(checkStockDetailVoMap.values()); |
|||
} |
|||
|
|||
@Override |
|||
public Page<CheckStockDetailVo> getByCheckStockDetailByCheckIdOfPage(CheckStockPageQuery checkStockPageQuery) { |
|||
// 校验分页参数
|
|||
if (checkStockPageQuery.getPageNum() <= 0 || checkStockPageQuery.getPageSize() <= 0) { |
|||
throw new IllegalArgumentException("分页参数异常!"); |
|||
} |
|||
|
|||
final Page<WorkerDeductLogVo> page = new Page<>(checkStockPageQuery.getPageNum(), checkStockPageQuery.getPageSize()); |
|||
final Page<CheckStockDetailVo> returnData = new Page<>(); |
|||
try { |
|||
// 查询商品id集合
|
|||
// 查询明细,这里假设底层已经做了SQL注入等安全防护
|
|||
final Page<String> checkStockDetailListOfProduct = checkStockDetailMapper.getByCheckStockProductByCheckId(page, checkStockPageQuery); |
|||
if (checkStockDetailListOfProduct.getRecords() == null) { |
|||
// 避免空指针异常
|
|||
checkStockDetailListOfProduct.setRecords(new ArrayList<>()); |
|||
} |
|||
checkStockPageQuery.setProductIdList(checkStockDetailListOfProduct.getRecords()); |
|||
|
|||
|
|||
if (!checkStockPageQuery.getProductIdList().isEmpty()) { |
|||
final List<CheckStockDetail> checkStockDetailList = checkStockDetailMapper.getByCheckStockDetailByCheckIdOfPage(checkStockPageQuery); |
|||
|
|||
// 封装至返回结果中,使用Map和computeIfAbsent简化逻辑
|
|||
final Map<String, CheckStockDetailVo> checkStockDetailVoMap = checkStockDetailList.stream() |
|||
.collect(Collectors.toMap(CheckStockDetail::getProductId, detail -> { |
|||
final CheckStockDetailVo vo = new CheckStockDetailVo(); |
|||
vo.setCheckStockId(detail.getCheckStockId()); |
|||
vo.setProductId(detail.getProductId()); |
|||
vo.setProductName(detail.getProductName()); |
|||
vo.setProductPicture(detail.getProductPicture()); |
|||
vo.setProductSn(detail.getProductSn()); |
|||
vo.setCheckStockAttributeVos(new ArrayList<>()); |
|||
final CheckStockAttributeVo attributeVo = new CheckStockAttributeVo(); |
|||
attributeVo.setAttributeList(detail.getAttributeList()); |
|||
attributeVo.setStockCount(detail.getStockCount()); |
|||
attributeVo.setPdNum(detail.getPdNum()); |
|||
vo.getCheckStockAttributeVos().add(attributeVo); |
|||
return vo; |
|||
}, (vo1, vo2) -> { |
|||
vo2.getCheckStockAttributeVos().addAll(vo1.getCheckStockAttributeVos()); |
|||
return vo2; |
|||
})); |
|||
returnData.setCountId(checkStockDetailListOfProduct.getCountId()); |
|||
returnData.setCurrent(checkStockDetailListOfProduct.getCurrent()); |
|||
returnData.setTotal(checkStockDetailListOfProduct.getTotal()); |
|||
returnData.setRecords(new ArrayList<>(checkStockDetailVoMap.values())); |
|||
|
|||
} |
|||
return returnData; |
|||
} catch (Exception e) { |
|||
// 异常处理,建议加上更详细的日志记录
|
|||
throw new RuntimeException("获取盘点信息出错!", e); |
|||
} |
|||
} |
|||
|
|||
} |
|||
|
|||
@ -1,11 +1,17 @@ |
|||
package cc.hiver.mall.checkstock.vo; |
|||
|
|||
import io.swagger.annotations.ApiModelProperty; |
|||
import lombok.Data; |
|||
|
|||
@Data |
|||
public class CheckStockAttributeVo { |
|||
|
|||
@ApiModelProperty(value = "规格") |
|||
private String attributeList; |
|||
|
|||
@ApiModelProperty(value = "盘点的数量") |
|||
private Integer pdNum; |
|||
|
|||
@ApiModelProperty(value = "盘点前库存数量") |
|||
private Integer stockCount; |
|||
} |
|||
|
|||
@ -0,0 +1,27 @@ |
|||
package cc.hiver.mall.checkstock.vo; |
|||
|
|||
import cc.hiver.core.base.HiverBaseEntity; |
|||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
|||
import io.swagger.annotations.ApiModelProperty; |
|||
import lombok.Data; |
|||
|
|||
@Data |
|||
public class CheckStockPageVo extends HiverBaseEntity { |
|||
|
|||
private static final long serialVersionUID = 1L; |
|||
|
|||
@ApiModelProperty(value = "盘点人姓名") |
|||
private String createByName; |
|||
|
|||
@ApiModelProperty(value = "店铺id") |
|||
private String shopId; |
|||
|
|||
@ApiModelProperty(value = "店铺名称") |
|||
private String shopName; |
|||
|
|||
@ApiModelProperty(value = "备注") |
|||
private String remark; |
|||
|
|||
private Page<CheckStockDetailVo> checkStockDetailVoList; |
|||
|
|||
} |
|||
@ -0,0 +1,20 @@ |
|||
package cc.hiver.mall.common.constant; |
|||
|
|||
/** |
|||
* 店铺常量 |
|||
* |
|||
* @author cc |
|||
*/ |
|||
public interface PurchaseConstant { |
|||
|
|||
/** |
|||
* 入库状态:0:待入库(未维护对应的采购价信息);1:已入库;2:ocr入库(未识别)3:ocr入库(已识别); |
|||
*/ |
|||
int[] IN_STORAGE_STATUS = {0, 1, 2, 3}; |
|||
|
|||
/** |
|||
* ocr识别状态 0:(未识别)1:(已识别); |
|||
*/ |
|||
int[] OCR_STATUS = {0, 1}; |
|||
|
|||
} |
|||
@ -0,0 +1,12 @@ |
|||
package cc.hiver.mall.config.aliocr; |
|||
|
|||
import lombok.Data; |
|||
import org.springframework.boot.context.properties.ConfigurationProperties; |
|||
import org.springframework.context.annotation.Configuration; |
|||
|
|||
@Data |
|||
@Configuration |
|||
@ConfigurationProperties(prefix = "aliyun.openapi") |
|||
public class AliOcrConfig { |
|||
private String apiKey; |
|||
} |
|||
@ -0,0 +1,22 @@ |
|||
package cc.hiver.mall.config.thread; |
|||
|
|||
import lombok.Data; |
|||
import org.springframework.beans.factory.annotation.Value; |
|||
import org.springframework.stereotype.Component; |
|||
|
|||
@Data |
|||
@Component |
|||
public class ThreadConfig { |
|||
|
|||
@Value("${spring.task.execution.pool.core-size}") |
|||
private int corePoolSize; |
|||
|
|||
@Value("${spring.task.execution.pool.max-size}") |
|||
private int maxPoolSize; |
|||
|
|||
@Value("${spring.task.execution.pool.queue-capacity}") |
|||
private int queueCapacity; |
|||
|
|||
@Value("${spring.task.execution.pool.keep-alive}") |
|||
private int keepAliveSeconds; |
|||
} |
|||
@ -0,0 +1,51 @@ |
|||
package cc.hiver.mall.config.thread; |
|||
|
|||
import org.springframework.context.annotation.Bean; |
|||
import org.springframework.context.annotation.Configuration; |
|||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; |
|||
import org.springframework.stereotype.Component; |
|||
|
|||
import java.util.concurrent.ThreadPoolExecutor; |
|||
|
|||
/** |
|||
* 线程池bean |
|||
* |
|||
* @author 王富康 |
|||
* @date 2023/3/24 10:03 |
|||
**/ |
|||
@Configuration |
|||
@Component |
|||
public class ThreadPoolConfiguration { |
|||
|
|||
/** |
|||
* 核心线程池大小-32 |
|||
**/ |
|||
private int corePoolSize = 10; |
|||
/** |
|||
* 最大可创建的线程数-50 |
|||
**/ |
|||
private int maxPoolSize = 50; |
|||
/** |
|||
* 线程池维护线程所允许的空闲时间-60 |
|||
**/ |
|||
private int keepAliveTime = 60; |
|||
/** |
|||
* 队列最大长度-100 |
|||
**/ |
|||
private int queueCapacity = 100; |
|||
|
|||
@Bean(name = "threadPoolTaskExecutor") |
|||
public ThreadPoolTaskExecutor threadPoolTaskExecutor() { |
|||
final ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); |
|||
executor.setMaxPoolSize(maxPoolSize); |
|||
executor.setCorePoolSize(corePoolSize); |
|||
executor.setQueueCapacity(queueCapacity); |
|||
executor.setKeepAliveSeconds(keepAliveTime); |
|||
//配置线程池中的线程的名称前缀
|
|||
executor.setThreadNamePrefix("ocr-"); |
|||
// 线程池对拒绝任务(无线程可用)的处理策略
|
|||
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); |
|||
executor.initialize(); |
|||
return executor; |
|||
} |
|||
} |
|||
@ -0,0 +1,65 @@ |
|||
package cc.hiver.mall.config.thread; |
|||
|
|||
import cc.hiver.core.common.utils.ResultUtil; |
|||
import cc.hiver.core.common.vo.Result; |
|||
import cc.hiver.mall.common.constant.PurchaseConstant; |
|||
import cc.hiver.mall.purchaseocr.entity.PurchaseOcrPicture; |
|||
import cc.hiver.mall.purchaseocr.service.PurchaseOcrPictureService; |
|||
import cc.hiver.mall.utils.AliOcrUtil; |
|||
import cn.hutool.json.JSONObject; |
|||
import com.alibaba.dashscope.exception.NoApiKeyException; |
|||
import com.alibaba.dashscope.exception.UploadFileException; |
|||
import lombok.Data; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
|
|||
import java.util.List; |
|||
import java.util.concurrent.Callable; |
|||
|
|||
/** |
|||
* 请详细描述方法 |
|||
* @author 王富康 |
|||
* @date 2024/3/24 |
|||
*/ |
|||
@Data |
|||
@Slf4j |
|||
public class TimerThread implements Callable { |
|||
|
|||
private PurchaseOcrPictureService purchaseOcrPictureService; |
|||
private List<PurchaseOcrPicture> purchaseOcrPictureAddList; |
|||
|
|||
public TimerThread() { |
|||
} |
|||
|
|||
public TimerThread(List<PurchaseOcrPicture> purchaseOcrPictureAddList,PurchaseOcrPictureService purchaseOcrPictureService) { |
|||
this.purchaseOcrPictureAddList = purchaseOcrPictureAddList; |
|||
this.purchaseOcrPictureService = purchaseOcrPictureService; |
|||
} |
|||
/** |
|||
* 每个线程拿到数据后如何去处理 |
|||
* |
|||
* @return |
|||
*/ |
|||
public Result call() { |
|||
try { |
|||
log.info("当前线程名称:------------>>>>>"+Thread.currentThread().getName()); |
|||
for (PurchaseOcrPicture purchaseOcrPicture : purchaseOcrPictureAddList) { |
|||
try { |
|||
log.info("正在ocr识别:" + purchaseOcrPicture.getOcrPicture() + "<br/>线程池名称:"+Thread.currentThread().getName()); |
|||
final JSONObject jsonObject = AliOcrUtil.simpleMultiModalConversationCall(purchaseOcrPicture.getOcrPicture()); |
|||
// 得到处理结果之后,开始新增库存的详细信息
|
|||
log.info("识别结果:" + jsonObject); |
|||
purchaseOcrPicture.setOcrMsg(jsonObject.get("msg").toString()); |
|||
purchaseOcrPicture.setOcrStatus(PurchaseConstant.OCR_STATUS[1]); |
|||
} catch (NoApiKeyException e) { |
|||
throw new RuntimeException(e); |
|||
} catch (UploadFileException e) { |
|||
throw new RuntimeException(e); |
|||
} |
|||
} |
|||
purchaseOcrPictureService.batchUpdate(purchaseOcrPictureAddList); |
|||
} catch (Exception e) { |
|||
log.error("timerThread出错,线程------>" + Thread.currentThread().getName(), e); |
|||
} |
|||
return ResultUtil.success(); |
|||
} |
|||
} |
|||
@ -0,0 +1,24 @@ |
|||
package cc.hiver.mall.pojo.vo; |
|||
|
|||
import cc.hiver.core.base.HiverBaseEntity; |
|||
import io.swagger.annotations.ApiModel; |
|||
import io.swagger.annotations.ApiModelProperty; |
|||
import lombok.Data; |
|||
|
|||
import java.util.List; |
|||
|
|||
@Data |
|||
@ApiModel(value = "商品规格属性") |
|||
public class ProductAttributeOfAddVo extends HiverBaseEntity { |
|||
|
|||
private static final long serialVersionUID = 1L; |
|||
|
|||
@ApiModelProperty(value = "商品类别ID") |
|||
private String categoryId; |
|||
|
|||
@ApiModelProperty(value = "属性名称") |
|||
private String attributeName; |
|||
|
|||
private List<ProductAttributeValueVo> productAttributeValueVoList; |
|||
|
|||
} |
|||
@ -0,0 +1,20 @@ |
|||
package cc.hiver.mall.pojo.vo; |
|||
|
|||
import cc.hiver.core.base.HiverBaseEntity; |
|||
import io.swagger.annotations.ApiModel; |
|||
import io.swagger.annotations.ApiModelProperty; |
|||
import lombok.Data; |
|||
|
|||
@Data |
|||
@ApiModel(value = "商品属性值表") |
|||
public class ProductAttributeValueVo extends HiverBaseEntity { |
|||
|
|||
private static final long serialVersionUID = 1L; |
|||
|
|||
@ApiModelProperty(value = "商品属性ID") |
|||
private String attributeId; |
|||
|
|||
@ApiModelProperty(value = "属性值") |
|||
private String value; |
|||
|
|||
} |
|||
@ -0,0 +1,26 @@ |
|||
package cc.hiver.mall.pojo.vo; |
|||
|
|||
import io.swagger.annotations.ApiModel; |
|||
import io.swagger.annotations.ApiModelProperty; |
|||
import lombok.Data; |
|||
|
|||
import java.io.Serializable; |
|||
import java.util.List; |
|||
|
|||
@Data |
|||
@ApiModel(value = "类别Vo") |
|||
public class ProductCategoryVo implements Serializable { |
|||
|
|||
private static final long serialVersionUID = 1L; |
|||
|
|||
@ApiModelProperty(value = "商品类别名称") |
|||
private String categoryName; |
|||
|
|||
@ApiModelProperty(value = "店铺id") |
|||
private String shopId; |
|||
|
|||
@ApiModelProperty(value = "分类列表") |
|||
private List<ProductAttributeOfAddVo> productAttributeOfAddVos; |
|||
|
|||
|
|||
} |
|||
@ -0,0 +1,15 @@ |
|||
package cc.hiver.mall.pojo.vo; |
|||
|
|||
import io.swagger.annotations.ApiModelProperty; |
|||
import lombok.Data; |
|||
|
|||
@Data |
|||
public class StockAttributeVo { |
|||
private static final long serialVersionUID = 1L; |
|||
|
|||
@ApiModelProperty(value = "商品规格") |
|||
private String attributeList; |
|||
|
|||
@ApiModelProperty(value = "库存数") |
|||
private Integer stockCount; |
|||
} |
|||
@ -0,0 +1,32 @@ |
|||
package cc.hiver.mall.pojo.vo; |
|||
|
|||
import io.swagger.annotations.ApiModelProperty; |
|||
import lombok.Data; |
|||
|
|||
import java.util.List; |
|||
|
|||
@Data |
|||
public class StockProductVo { |
|||
private static final long serialVersionUID = 1L; |
|||
|
|||
@ApiModelProperty(value = "商品名称") |
|||
private String productName; |
|||
|
|||
@ApiModelProperty(value = "货号") |
|||
private String productSn; |
|||
|
|||
@ApiModelProperty(value = "货品图片") |
|||
private String productPicture; |
|||
|
|||
@ApiModelProperty(value = "条码") |
|||
private String barcode; |
|||
|
|||
@ApiModelProperty(value = "尾货预警") |
|||
private Integer tailWarn; |
|||
|
|||
@ApiModelProperty(value = "商品总库存(某一规格库存为负数的按0计算总数)") |
|||
private Integer stockCount; |
|||
|
|||
@ApiModelProperty(value = "商品规格") |
|||
private List<StockAttributeVo> stockAttributeVoList; |
|||
} |
|||
@ -0,0 +1,54 @@ |
|||
package cc.hiver.mall.purchaseocr.controller; |
|||
|
|||
import cc.hiver.core.common.utils.ResultUtil; |
|||
import cc.hiver.core.common.vo.Result; |
|||
import cc.hiver.mall.purchaseocr.service.PurchaseOcrPictureService; |
|||
import cc.hiver.mall.purchaseocr.vo.PurchaseOciPictureAddVo; |
|||
import cn.hutool.json.JSONObject; |
|||
import com.alibaba.dashscope.aigc.multimodalconversation.MultiModalConversationResult; |
|||
import com.alibaba.dashscope.exception.InputRequiredException; |
|||
import com.alibaba.dashscope.exception.NoApiKeyException; |
|||
import com.alibaba.dashscope.exception.UploadFileException; |
|||
import io.swagger.annotations.Api; |
|||
import io.swagger.annotations.ApiOperation; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.transaction.annotation.Transactional; |
|||
import org.springframework.web.bind.annotation.RequestBody; |
|||
import org.springframework.web.bind.annotation.RequestMapping; |
|||
import org.springframework.web.bind.annotation.RequestMethod; |
|||
import org.springframework.web.bind.annotation.RestController; |
|||
|
|||
import java.util.List; |
|||
|
|||
@Slf4j |
|||
@RestController |
|||
@Api(tags = "问题反馈接口") |
|||
@RequestMapping("/hiver/purchaseOcrPicture/") |
|||
@Transactional |
|||
public class PurchaseOcrPictureController { |
|||
|
|||
@Autowired |
|||
private PurchaseOcrPictureService purchaseOcrPictureService; |
|||
|
|||
@RequestMapping(value = "/batchSave", method = RequestMethod.POST) |
|||
@ApiOperation("新增或编辑OCR识别信息") |
|||
public Result batchSave(@RequestBody PurchaseOciPictureAddVo purchaseOciPictureAddVo) { |
|||
JSONObject jsonObject = purchaseOcrPictureService.batchSave(purchaseOciPictureAddVo); |
|||
return new ResultUtil<JSONObject>().setData(jsonObject); |
|||
} |
|||
|
|||
@RequestMapping(value = "/invoicingAi", method = RequestMethod.POST) |
|||
@ApiOperation("AI开单") |
|||
public Result invoicingAi(String questionMsg) throws NoApiKeyException, InputRequiredException { |
|||
JSONObject jsonObject = purchaseOcrPictureService.invoicingAi(questionMsg); |
|||
return new ResultUtil<JSONObject>().setData(jsonObject); |
|||
} |
|||
|
|||
@RequestMapping(value = "/multiRoundConversationCall", method = RequestMethod.POST) |
|||
@ApiOperation("图片识别-多轮对话") |
|||
public List<MultiModalConversationResult> multiRoundConversationCall(String picturePath, String firstQuestionMsg, String secondQuestionMsg) throws NoApiKeyException, InputRequiredException, UploadFileException { |
|||
List<MultiModalConversationResult> multiModalConversationResults = purchaseOcrPictureService.multiRoundConversationCall(picturePath,firstQuestionMsg, secondQuestionMsg); |
|||
return multiModalConversationResults; |
|||
} |
|||
} |
|||
@ -0,0 +1,42 @@ |
|||
package cc.hiver.mall.purchaseocr.entity; |
|||
|
|||
import cc.hiver.core.base.HiverBaseEntity; |
|||
import com.baomidou.mybatisplus.annotation.TableName; |
|||
import io.swagger.annotations.ApiModel; |
|||
import io.swagger.annotations.ApiModelProperty; |
|||
import lombok.Data; |
|||
|
|||
/** |
|||
* ai图片表 |
|||
* @author 王富康 |
|||
* @date 2024/3/23 |
|||
*/ |
|||
@Data |
|||
@ApiModel(value = "ai图片表") |
|||
@TableName(value = "t_purchase_ocr_picture", autoResultMap = true) |
|||
public class PurchaseOcrPicture extends HiverBaseEntity { |
|||
|
|||
@ApiModelProperty(value = "创建人名称") |
|||
private String createByName; |
|||
|
|||
@ApiModelProperty(value = "采购单id") |
|||
private String orderId; |
|||
|
|||
@ApiModelProperty(value = "图片路径") |
|||
private String ocrPicture; |
|||
|
|||
@ApiModelProperty(value = "图片顺序") |
|||
private String ocrPictureOrder; |
|||
|
|||
@ApiModelProperty(value = "识别标识:0:未识别;1:识别成功;2:识别失败") |
|||
private Integer ocrStatus; |
|||
|
|||
@ApiModelProperty(value = "识别结果") |
|||
private String ocrMsg; |
|||
|
|||
@ApiModelProperty(value = "店铺ID") |
|||
private String shopId; |
|||
|
|||
@ApiModelProperty(value = "店铺名称") |
|||
private String shopName; |
|||
} |
|||
@ -0,0 +1,13 @@ |
|||
package cc.hiver.mall.purchaseocr.mapper; |
|||
|
|||
import cc.hiver.mall.purchaseocr.entity.PurchaseOcrPicture; |
|||
import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
|||
import org.apache.ibatis.annotations.Param; |
|||
|
|||
import java.util.List; |
|||
|
|||
public interface PurchaseOcrPictureMapper extends BaseMapper<PurchaseOcrPicture> { |
|||
void batchSave(@Param("purchaseOcrPictureList") List<PurchaseOcrPicture> purchaseOcrPictureList); |
|||
|
|||
void batchUpdate(@Param("purchaseOcrPictureList") List<PurchaseOcrPicture> purchaseOcrPictureAddList); |
|||
} |
|||
@ -0,0 +1,20 @@ |
|||
package cc.hiver.mall.purchaseocr.service; |
|||
|
|||
import cc.hiver.mall.purchaseocr.entity.PurchaseOcrPicture; |
|||
import cc.hiver.mall.purchaseocr.vo.PurchaseOciPictureAddVo; |
|||
import cn.hutool.json.JSONObject; |
|||
import com.alibaba.dashscope.aigc.multimodalconversation.MultiModalConversationResult; |
|||
import com.alibaba.dashscope.exception.InputRequiredException; |
|||
import com.alibaba.dashscope.exception.NoApiKeyException; |
|||
import com.alibaba.dashscope.exception.UploadFileException; |
|||
|
|||
import java.util.List; |
|||
|
|||
public interface PurchaseOcrPictureService { |
|||
JSONObject batchSave(PurchaseOciPictureAddVo purchaseOciPictureAddVo); |
|||
void batchUpdate(List<PurchaseOcrPicture> purchaseOcrPictureList); |
|||
|
|||
JSONObject invoicingAi(String questionMsg) throws NoApiKeyException, InputRequiredException; |
|||
|
|||
List<MultiModalConversationResult> multiRoundConversationCall(String picturePath,String firstQuestionMsg, String secondQuestionMsg) throws NoApiKeyException, InputRequiredException, UploadFileException; |
|||
} |
|||
@ -0,0 +1,111 @@ |
|||
package cc.hiver.mall.purchaseocr.service.impl; |
|||
|
|||
import cc.hiver.core.common.utils.SecurityUtil; |
|||
import cc.hiver.core.common.vo.Result; |
|||
import cc.hiver.mall.common.constant.PurchaseConstant; |
|||
import cc.hiver.mall.config.thread.TimerThread; |
|||
import cc.hiver.mall.entity.Purchase; |
|||
import cc.hiver.mall.purchaseocr.entity.PurchaseOcrPicture; |
|||
import cc.hiver.mall.purchaseocr.mapper.PurchaseOcrPictureMapper; |
|||
import cc.hiver.mall.purchaseocr.service.PurchaseOcrPictureService; |
|||
import cc.hiver.mall.purchaseocr.vo.PurchaseOciPictureAddVo; |
|||
import cc.hiver.mall.service.mybatis.PurchaseService; |
|||
import cc.hiver.mall.utils.AliOcrUtil; |
|||
import cn.hutool.json.JSONObject; |
|||
import com.alibaba.dashscope.aigc.multimodalconversation.MultiModalConversationResult; |
|||
import com.alibaba.dashscope.exception.InputRequiredException; |
|||
import com.alibaba.dashscope.exception.NoApiKeyException; |
|||
import com.alibaba.dashscope.exception.UploadFileException; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.apache.commons.lang3.StringUtils; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; |
|||
import org.springframework.stereotype.Service; |
|||
|
|||
import java.util.List; |
|||
import java.util.concurrent.CompletionService; |
|||
import java.util.concurrent.CopyOnWriteArrayList; |
|||
import java.util.concurrent.ExecutorCompletionService; |
|||
import java.util.concurrent.Future; |
|||
|
|||
@Slf4j |
|||
@Service |
|||
public class PurchaseOcrPictureServiceImpl implements PurchaseOcrPictureService { |
|||
|
|||
@Autowired |
|||
private PurchaseOcrPictureMapper purchaseOcrPictureMapper; |
|||
|
|||
@Autowired |
|||
private SecurityUtil securityUtil; |
|||
|
|||
@Autowired |
|||
private PurchaseService purchaseService; |
|||
|
|||
@Autowired |
|||
private PurchaseOcrPictureService purchaseOcrPictureService; |
|||
|
|||
@Autowired |
|||
ThreadPoolTaskExecutor threadPoolTaskExecutor; |
|||
|
|||
@Override |
|||
public JSONObject batchSave(PurchaseOciPictureAddVo purchaseOciPictureAddVo) { |
|||
final JSONObject jsonObject = new JSONObject(); |
|||
// shopId从缓存中设置
|
|||
final String shopId = "wfkde"; |
|||
// 如果采购单位空,那么就需要新增采购单信息
|
|||
if (StringUtils.isEmpty(purchaseOciPictureAddVo.getOrderId())) { |
|||
final Purchase purchase = new Purchase(); |
|||
// 新增的对象会有id,将新增的id放到参数中取
|
|||
purchaseOciPictureAddVo.setOrderId(purchase.getId()); |
|||
purchase.setShopId(shopId); |
|||
// 设置为ocr识别(未识别)状态
|
|||
purchase.setInStorageStatus(PurchaseConstant.IN_STORAGE_STATUS[2]); |
|||
final boolean save = purchaseService.save(purchase); |
|||
} |
|||
// 然后批量新增图片
|
|||
final List<PurchaseOcrPicture> purchaseOcrPictureList = purchaseOciPictureAddVo.getPurchaseOcrPictureList(); |
|||
final CopyOnWriteArrayList<PurchaseOcrPicture> purchaseOcrPictureAddList = new CopyOnWriteArrayList<>(); |
|||
|
|||
for (PurchaseOcrPicture purchaseOcrPicture : purchaseOcrPictureList) { |
|||
// todo 如果前台传了id,后台会覆盖么
|
|||
final String id = purchaseOcrPicture.getId(); |
|||
final PurchaseOcrPicture oldPurchaseOcrPicture = purchaseOcrPictureMapper.selectById(id); |
|||
if(oldPurchaseOcrPicture ==null ){ |
|||
final PurchaseOcrPicture addPurchaseOcrPicture = new PurchaseOcrPicture(); |
|||
// 如果是修改新增的图片,需要增加图片信息
|
|||
addPurchaseOcrPicture.setShopId(shopId); |
|||
addPurchaseOcrPicture.setOcrPicture(purchaseOcrPicture.getOcrPicture()); |
|||
addPurchaseOcrPicture.setOrderId(purchaseOciPictureAddVo.getOrderId()); |
|||
addPurchaseOcrPicture.setOcrStatus(PurchaseConstant.OCR_STATUS[0]); |
|||
purchaseOcrPictureAddList.add(addPurchaseOcrPicture); |
|||
} |
|||
} |
|||
purchaseOcrPictureMapper.batchSave(purchaseOcrPictureAddList); |
|||
// 异步处理ocr识别
|
|||
try { |
|||
CompletionService<CopyOnWriteArrayList<PurchaseOcrPicture>> service = new ExecutorCompletionService<>(threadPoolTaskExecutor); |
|||
TimerThread timerThread = new TimerThread(purchaseOcrPictureAddList,purchaseOcrPictureService); |
|||
// 这里可以使用线程池,也可以使用CompletionService处理,运行任务需要是callable的,需要最终结果。
|
|||
final Future<Result> submit = service.submit(timerThread); |
|||
} catch (Exception e) { |
|||
log.error("异步处理ocr识别失败", e); |
|||
} |
|||
jsonObject.set("msg", "success"); |
|||
return jsonObject; |
|||
} |
|||
|
|||
@Override |
|||
public void batchUpdate(List<PurchaseOcrPicture> purchaseOcrPictureList) { |
|||
purchaseOcrPictureMapper.batchUpdate(purchaseOcrPictureList); |
|||
} |
|||
|
|||
@Override |
|||
public JSONObject invoicingAi(String questionMsg) throws NoApiKeyException, InputRequiredException { |
|||
return AliOcrUtil.callWithMessage(questionMsg); |
|||
} |
|||
|
|||
@Override |
|||
public List<MultiModalConversationResult> multiRoundConversationCall(String picturePath,String firstQuestionMsg, String secondQuestionMsg) throws NoApiKeyException, InputRequiredException, UploadFileException { |
|||
return AliOcrUtil.multiRoundConversationCall(picturePath,firstQuestionMsg,secondQuestionMsg); |
|||
} |
|||
} |
|||
@ -0,0 +1,17 @@ |
|||
package cc.hiver.mall.purchaseocr.vo; |
|||
|
|||
import cc.hiver.mall.purchaseocr.entity.PurchaseOcrPicture; |
|||
import io.swagger.annotations.ApiModelProperty; |
|||
import lombok.Data; |
|||
|
|||
import java.util.List; |
|||
|
|||
@Data |
|||
public class PurchaseOciPictureAddVo { |
|||
|
|||
@ApiModelProperty(value = "采购单id") |
|||
private String orderId; |
|||
|
|||
@ApiModelProperty(value = "Oci图片信息") |
|||
private List<PurchaseOcrPicture> purchaseOcrPictureList; |
|||
} |
|||
@ -1,7 +1,9 @@ |
|||
package cc.hiver.mall.service.mybatis; |
|||
|
|||
import cc.hiver.mall.entity.ProductCategory; |
|||
import cc.hiver.mall.pojo.vo.ProductCategoryVo; |
|||
import com.baomidou.mybatisplus.extension.service.IService; |
|||
|
|||
public interface ProductCategoryService extends IService<ProductCategory> { |
|||
boolean batchSaveCategoryAndAttribute(ProductCategoryVo productCategoryVo); |
|||
} |
|||
|
|||
@ -1,11 +1,84 @@ |
|||
package cc.hiver.mall.serviceimpl.mybatis; |
|||
|
|||
import cc.hiver.core.common.utils.SecurityUtil; |
|||
import cc.hiver.mall.dao.mapper.ProductCategoryMapper; |
|||
import cc.hiver.mall.entity.ProductAttribute; |
|||
import cc.hiver.mall.entity.ProductAttributeValue; |
|||
import cc.hiver.mall.entity.ProductCategory; |
|||
import cc.hiver.mall.pojo.vo.ProductAttributeOfAddVo; |
|||
import cc.hiver.mall.pojo.vo.ProductAttributeValueVo; |
|||
import cc.hiver.mall.pojo.vo.ProductCategoryVo; |
|||
import cc.hiver.mall.service.mybatis.ProductAttributeService; |
|||
import cc.hiver.mall.service.mybatis.ProductAttributeValueService; |
|||
import cc.hiver.mall.service.mybatis.ProductCategoryService; |
|||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.stereotype.Service; |
|||
import org.springframework.transaction.annotation.Transactional; |
|||
|
|||
import java.util.ArrayList; |
|||
import java.util.List; |
|||
|
|||
@Slf4j |
|||
@Service |
|||
public class ProductCategoryServiceImpl extends ServiceImpl<ProductCategoryMapper, ProductCategory> implements ProductCategoryService { |
|||
|
|||
@Autowired |
|||
private SecurityUtil securityUtil; |
|||
|
|||
@Autowired |
|||
private ProductCategoryMapper productCategoryMapper; |
|||
|
|||
@Autowired |
|||
private ProductAttributeService productAttributeService; |
|||
|
|||
@Autowired |
|||
private ProductAttributeValueService productAttributeValueService; |
|||
|
|||
@Transactional(rollbackFor = Exception.class) |
|||
@Override |
|||
public boolean batchSaveCategoryAndAttribute(ProductCategoryVo productCategoryVo) { |
|||
|
|||
try { |
|||
// 新增类别信息
|
|||
final ProductCategory productCategory = new ProductCategory(); |
|||
final String shopId = securityUtil.getShopId(); |
|||
productCategory.setShopId(shopId); |
|||
productCategory.setCategoryName(productCategoryVo.getCategoryName()); |
|||
productCategoryMapper.insert(productCategory); |
|||
|
|||
// 新增类别
|
|||
final List<ProductAttributeOfAddVo> productAttributeOfAddVos = productCategoryVo.getProductAttributeOfAddVos(); |
|||
// 需要新增的类别集合
|
|||
final List<ProductAttribute> productAttributes = new ArrayList<>(); |
|||
// 需要新增的属性值集合
|
|||
final List<ProductAttributeValue> productAttributeValues = new ArrayList<>(); |
|||
for (ProductAttributeOfAddVo productAttributeOfAddVo : productAttributeOfAddVos) { |
|||
final ProductAttribute productAttribute = new ProductAttribute(); |
|||
productAttribute.setCategoryId(productCategory.getId()); |
|||
productAttribute.setAttributeName(productAttributeOfAddVo.getAttributeName()); |
|||
productAttributes.add(productAttribute); |
|||
final List<ProductAttributeValueVo> productAttributeValueVoList = productAttributeOfAddVo.getProductAttributeValueVoList(); |
|||
for (ProductAttributeValueVo productAttributeValueVo : productAttributeValueVoList) { |
|||
final ProductAttributeValue productAttributeValue = new ProductAttributeValue(); |
|||
productAttributeValue.setAttributeId(productAttribute.getId()); |
|||
productAttributeValue.setValue(productAttributeValueVo.getValue()); |
|||
productAttributeValues.add(productAttributeValue); |
|||
} |
|||
} |
|||
// 批量插入分类
|
|||
if (!productAttributes.isEmpty()) { |
|||
productAttributeService.saveBatch(productAttributes, productAttributes.size()); |
|||
} |
|||
//批量插入属性值
|
|||
if (!productAttributeValues.isEmpty()) { |
|||
productAttributeValueService.saveBatch(productAttributeValues, productAttributeValues.size()); |
|||
} |
|||
return true; |
|||
} catch (Exception e) { |
|||
log.error(e.getMessage(), e); |
|||
return false; |
|||
} |
|||
} |
|||
} |
|||
|
|||
@ -0,0 +1,208 @@ |
|||
package cc.hiver.mall.utils;// Copyright (c) Alibaba, Inc. and its affiliates.
|
|||
|
|||
import cc.hiver.mall.config.aliocr.AliOcrConfig; |
|||
import cn.hutool.core.date.StopWatch; |
|||
import cn.hutool.json.JSONObject; |
|||
import com.alibaba.dashscope.aigc.generation.Generation; |
|||
import com.alibaba.dashscope.aigc.generation.GenerationParam; |
|||
import com.alibaba.dashscope.aigc.generation.GenerationResult; |
|||
import com.alibaba.dashscope.aigc.generation.models.QwenParam; |
|||
import com.alibaba.dashscope.aigc.multimodalconversation.*; |
|||
import com.alibaba.dashscope.common.Message; |
|||
import com.alibaba.dashscope.common.MessageManager; |
|||
import com.alibaba.dashscope.common.MultiModalMessage; |
|||
import com.alibaba.dashscope.common.Role; |
|||
import com.alibaba.dashscope.exception.ApiException; |
|||
import com.alibaba.dashscope.exception.InputRequiredException; |
|||
import com.alibaba.dashscope.exception.NoApiKeyException; |
|||
import com.alibaba.dashscope.exception.UploadFileException; |
|||
import com.alibaba.dashscope.utils.Constants; |
|||
import com.alibaba.dashscope.utils.JsonUtils; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.apache.commons.lang3.StringUtils; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.stereotype.Component; |
|||
|
|||
import java.util.*; |
|||
|
|||
@Slf4j |
|||
@Component |
|||
public class AliOcrUtil { |
|||
|
|||
@Autowired |
|||
private static AliOcrConfig aliOcrConfig; |
|||
|
|||
/** |
|||
* 图片识别 |
|||
* @author 王富康 |
|||
* @date 2024/3/24 |
|||
* @param picturePath |
|||
* @return JSONObject |
|||
*/ |
|||
public static JSONObject simpleMultiModalConversationCall(String picturePath) |
|||
throws ApiException, NoApiKeyException, UploadFileException { |
|||
final JSONObject jsonObject = new JSONObject(); |
|||
final StopWatch stopWatch = new StopWatch("Ai回答计时"); |
|||
Constants.apiKey = "sk-bcfa4865b89548acb8225f910f13d682"; |
|||
final MultiModalConversation conv = new MultiModalConversation(); |
|||
final MultiModalMessage userMessage = MultiModalMessage.builder().role(Role.USER.getValue()) |
|||
.content(Arrays.asList(Collections.singletonMap("image", picturePath), |
|||
Collections.singletonMap("text", "这是一个入库小票,请帮我把图片中所有内容封装为json,必须一次性列出所有json数据,json格式为:{ \"productSn\": \"货号\", \"productName\": \"商品名称\",\"price\": \"单价\", \"productCount\": \"数量\", \"attributeList\": \"{\"颜色\":\"红色\",\"尺码\":\"均码\"}\" }"))).build(); |
|||
stopWatch.start("开始回答:"); |
|||
final MultiModalConversationParam param = MultiModalConversationParam.builder() |
|||
.model("qwen-vl-max") |
|||
.message(userMessage) |
|||
.temperature(1F) |
|||
.topP(0.1) |
|||
.topK(1) |
|||
.seed(500) |
|||
.build(); |
|||
final MultiModalConversationResult result = conv.call(param); |
|||
stopWatch.stop(); |
|||
log.info(String.valueOf(result)); |
|||
log.info(stopWatch.prettyPrint()); |
|||
final Map<String, Object> stringObjectMap = result.getOutput().getChoices().get(0).getMessage().getContent().get(0); |
|||
jsonObject.set("msg", stringObjectMap); |
|||
return jsonObject; |
|||
} |
|||
|
|||
public static List<MultiModalConversationResult> multiRoundConversationCall(String picturePath,String firstQuestionMsg,String secondQuestionMsg) throws ApiException, NoApiKeyException, UploadFileException { |
|||
final StopWatch stopWatch = new StopWatch("Ai回答计时"); |
|||
Constants.apiKey = "sk-bcfa4865b89548acb8225f910f13d682"; |
|||
List<MultiModalConversationResult> multiModalConversationResults = new ArrayList<>(); |
|||
final MultiModalConversation conv = new MultiModalConversation(); |
|||
final MultiModalMessageItemText systemText = new MultiModalMessageItemText("You are a helpful assistant."); |
|||
final MultiModalConversationMessage systemMessage = MultiModalConversationMessage.builder() |
|||
.role(Role.SYSTEM.getValue()).content(Collections.singletonList(systemText)).build(); |
|||
final MultiModalMessageItemImage userImage = new MultiModalMessageItemImage(picturePath); |
|||
// "这是一个入库小票,请帮我把图片中所有内容封装为json,必须一次性列出所有json数据,json格式为:{ \"productSn\": \"货号\", \"productName\": \"商品名称\",\"price\": \"单价\", \"productCount\": \"数量\", \"attributeList\": \"{\"颜色\":\"红色\",\"尺码\":\"均码\"}\" }"
|
|||
MultiModalMessageItemText userText = new MultiModalMessageItemText(firstQuestionMsg); |
|||
final MultiModalConversationMessage userMessage = |
|||
MultiModalConversationMessage.builder().role(Role.USER.getValue()) |
|||
.content(Arrays.asList(userImage, userText)).build(); |
|||
final List<MultiModalConversationMessage> messages = new ArrayList<>(); |
|||
messages.add(systemMessage); |
|||
messages.add(userMessage); |
|||
final MultiModalConversationParam param = MultiModalConversationParam.builder() |
|||
.model("qwen-vl-max") |
|||
.messages(messages) |
|||
.temperature(1F) |
|||
.topP(0.1) |
|||
.topK(1) |
|||
.seed(500) |
|||
.build(); |
|||
stopWatch.start("一轮会话"); |
|||
MultiModalConversationResult result = conv.call(param); |
|||
stopWatch.stop(); |
|||
multiModalConversationResults.add(result); |
|||
System.out.println(result); |
|||
// 我会在这里对结果进行解析,然后,判断要不要走第二次回话
|
|||
if(StringUtils.isNotEmpty(secondQuestionMsg)){ |
|||
|
|||
final MultiModalMessageItemText assistentText = new MultiModalMessageItemText( |
|||
result.getOutput().getChoices().get(0).getMessage().getContent().get(0).get("text").toString()); |
|||
final MultiModalConversationMessage assistentMessage = MultiModalConversationMessage.builder() |
|||
.role(Role.ASSISTANT.getValue()).content(Collections.singletonList(assistentText)).build(); |
|||
messages.add(assistentMessage); |
|||
userText = new MultiModalMessageItemText(secondQuestionMsg); |
|||
messages.add(MultiModalConversationMessage.builder().role(Role.USER.getValue()) |
|||
.content(Collections.singletonList(userText)).build()); |
|||
param.setMessages(new ArrayList<Object>(messages)); |
|||
stopWatch.start("二轮会话"); |
|||
result = conv.call(param); |
|||
multiModalConversationResults.add(result); |
|||
stopWatch.stop(); |
|||
System.out.print(result); |
|||
log.info(stopWatch.prettyPrint()); |
|||
} |
|||
return multiModalConversationResults; |
|||
} |
|||
|
|||
public static JSONObject callWithMessage(String questionMsg) |
|||
throws NoApiKeyException, ApiException, InputRequiredException { |
|||
final JSONObject jsonObject = new JSONObject(); |
|||
Constants.apiKey = "sk-bcfa4865b89548acb8225f910f13d682"; |
|||
final StopWatch stopWatch = new StopWatch("Ai回答计时"); |
|||
final Generation gen = new Generation(); |
|||
final MessageManager msgManager = new MessageManager(10); |
|||
final Message systemMsg = |
|||
Message.builder().role(Role.SYSTEM.getValue()).content("You are a helpful assistant.").build(); |
|||
final Message userMsg = Message.builder().role(Role.USER.getValue()).content(questionMsg).build(); |
|||
msgManager.add(systemMsg); |
|||
msgManager.add(userMsg); |
|||
stopWatch.start("开始回答"); |
|||
final QwenParam param = |
|||
QwenParam.builder().model(Generation.Models.QWEN_PLUS).messages(msgManager.get()) |
|||
.resultFormat(QwenParam.ResultFormat.MESSAGE) |
|||
.temperature(1F) |
|||
.topP(0.1) |
|||
.topK(1) |
|||
.seed(500) |
|||
.build(); |
|||
final GenerationResult result = gen.call(param); |
|||
stopWatch.stop(); |
|||
log.info(stopWatch.prettyPrint()); |
|||
System.out.println(result); |
|||
jsonObject.set("msg", result.getOutput().getChoices().get(0).getMessage().getContent()); |
|||
return jsonObject; |
|||
} |
|||
|
|||
public static void callWithMessageOfMany(String questionMsg,int count) |
|||
throws NoApiKeyException, ApiException, InputRequiredException { |
|||
final Generation gen = new Generation(); |
|||
Constants.apiKey = "sk-bcfa4865b89548acb8225f910f13d682"; |
|||
final Message systemMsg = |
|||
Message.builder().role(Role.SYSTEM.getValue()).content("You are a helpful assistant.").build(); |
|||
Message userMsg = Message.builder().role(Role.USER.getValue()).content(questionMsg).build(); |
|||
final List<Message> messages = new ArrayList<>(); |
|||
messages.add(systemMsg); |
|||
messages.add(userMsg); |
|||
final GenerationParam param = |
|||
GenerationParam.builder().model(Generation.Models.QWEN_PLUS).messages(messages) |
|||
.resultFormat(GenerationParam.ResultFormat.MESSAGE) |
|||
.topP(0.8) |
|||
.build(); |
|||
GenerationResult result = gen.call(param); |
|||
System.out.println(result); |
|||
// 添加assistant返回到messages列表,user/assistant消息必须交替出现
|
|||
messages.add(result.getOutput().getChoices().get(0).getMessage()); |
|||
// new message
|
|||
userMsg = Message.builder().role(Role.USER.getValue()).content("请全部返回").build(); |
|||
messages.add(userMsg); |
|||
result = gen.call(param); |
|||
System.out.println(result); |
|||
System.out.println(JsonUtils.toJson(result)); |
|||
} |
|||
|
|||
public static void main(String[] args) { |
|||
try { |
|||
/*final String questionMsg = "货号87654321,S码黑色30件,M码儿,白色50件儿,XL码儿,天蓝色50件儿。\n" + |
|||
"货号87654322,S码粉红色40件,M码儿,玫瑰色50件儿,XL码儿,天蓝色50件儿。\n" + |
|||
"货号87654323,S码奶奶灰50件,M码儿,绿色50件儿,XL码儿,天蓝色50件儿。\n" + |
|||
"货号87654324,S码卡其色60件,M码儿,王浩伟色50件儿,XL码儿,天蓝色50件儿。\n" + |
|||
"货号87654325,S码天空蓝70件,M码儿,白色50件儿,XL码儿,天蓝色50件儿。\n" + |
|||
"货号87654326,S码所有颜色80件,M码儿,白色50件儿,XL码儿,天蓝色50件儿。\n" + |
|||
"货号87654327,S码黑色30件,M码儿,白色50件儿,XL码儿,天蓝色50件儿。\n" + |
|||
"货号87654328,S码粉红色40件,M码儿,玫瑰色50件儿,XL码儿,天蓝色50件儿。\n" + |
|||
"货号87654329,S码奶奶灰50件,M码儿,绿色50件儿,XL码儿,天蓝色50件儿。\n" + |
|||
"货号87654330,S码卡其色60件,M码儿,王浩伟色50件儿,XL码儿,天蓝色50件儿。\n" + |
|||
"货号87654331,S码天空蓝70件,M码儿,白色50件儿,XL码儿,天蓝色50件儿。\n" + |
|||
"货号87654332,S码所有颜色80件,M码儿,白色50件儿,XL码儿,天蓝色50件儿。\n" + |
|||
"货号87654333,S码黑色30件,M码儿,白色50件儿,XL码儿,天蓝色50件儿。\n" + |
|||
"货号87654334,S码粉红色40件,M码儿,玫瑰色50件儿,XL码儿,天蓝色50件儿。\n" + |
|||
"货号87654335,S码奶奶灰50件,M码儿,绿色50件儿,XL码儿,天蓝色50件儿。\n" + |
|||
"货号87654336,S码卡其色60件,M码儿,王浩伟色50件儿,XL码儿,天蓝色50件儿。\n" + |
|||
"货号87654337,S码天空蓝70件,M码儿,白色50件儿,XL码儿,天蓝色50件儿。\n" + |
|||
"货号87654338,S码天空蓝70件,M码儿,白色50件儿,XL码儿,天蓝色50件儿。\n" + |
|||
"货号87654339,S码天空蓝70件,M码儿,白色50件儿,XL码儿,天蓝色50件儿。\n" + |
|||
"货号87654340,S码天空蓝70件,M码儿,白色50件儿,XL码儿,天蓝色50件儿。 \n" + |
|||
"这是客户要货信息,请帮我把所有内容封装为json,必须一次性列出所有json数据,json格式为:[{ \"productSn\": \"货号\", \"attributeList\": \"[{\"颜色\":\"红色\",\"尺码\":\"均码\",\"productCount\": \"数量\"}]\" }]"; |
|||
callWithMessage(questionMsg);*/ |
|||
|
|||
// multiRoundConversationCall("https://jewel-shop.oss-cn-beijing.aliyuncs.com/abcbafd10e1b40c082bc1fb271b08da1.jpg");
|
|||
}catch (Exception e){ |
|||
log.error(e.getMessage()); |
|||
} |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,106 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> |
|||
<mapper namespace="cc.hiver.mall.purchaseocr.mapper.PurchaseOcrPictureMapper"> |
|||
<resultMap id="BaseResultMap" type="cc.hiver.mall.purchaseocr.entity.PurchaseOcrPicture"> |
|||
<id column="id" jdbcType="VARCHAR" property="id" /> |
|||
<result column="create_by" jdbcType="VARCHAR" property="createBy" /> |
|||
<result column="create_by_name" jdbcType="VARCHAR" property="createByName" /> |
|||
<result column="create_time" jdbcType="TIMESTAMP" property="createTime" /> |
|||
<result column="del_flag" jdbcType="INTEGER" property="delFlag" /> |
|||
<result column="update_by" jdbcType="VARCHAR" property="updateBy" /> |
|||
<result column="update_time" jdbcType="TIMESTAMP" property="updateTime" /> |
|||
<result column="order_id" jdbcType="VARCHAR" property="orderId" /> |
|||
<result column="ocr_picture" jdbcType="VARCHAR" property="ocrPicture" /> |
|||
<result column="ocr_picture_order" jdbcType="INTEGER" property="ocrPictureOrder" /> |
|||
<result column="ocr_status" jdbcType="INTEGER" property="ocrstatus" /> |
|||
<result column="ocr_msg" jdbcType="VARCHAR" property="ocrMsg" /> |
|||
<result column="shop_id" jdbcType="VARCHAR" property="shopId" /> |
|||
<result column="shop_name" jdbcType="VARCHAR" property="shopName" /> |
|||
</resultMap> |
|||
<sql id="Example_Where_Clause"> |
|||
<where> |
|||
<foreach collection="oredCriteria" item="criteria" separator="or"> |
|||
<if test="criteria.valid"> |
|||
<trim prefix="(" prefixOverrides="and" suffix=")"> |
|||
<foreach collection="criteria.criteria" item="criterion"> |
|||
<choose> |
|||
<when test="criterion.noValue"> |
|||
and ${criterion.condition} |
|||
</when> |
|||
<when test="criterion.singleValue"> |
|||
and ${criterion.condition} #{criterion.value} |
|||
</when> |
|||
<when test="criterion.betweenValue"> |
|||
and ${criterion.condition} #{criterion.value} and #{criterion.secondValue} |
|||
</when> |
|||
<when test="criterion.listValue"> |
|||
and ${criterion.condition} |
|||
<foreach close=")" collection="criterion.value" item="listItem" open="(" |
|||
separator=","> |
|||
#{listItem} |
|||
</foreach> |
|||
</when> |
|||
</choose> |
|||
</foreach> |
|||
</trim> |
|||
</if> |
|||
</foreach> |
|||
</where> |
|||
</sql> |
|||
<sql id="Update_By_Example_Where_Clause"> |
|||
<where> |
|||
<foreach collection="example.oredCriteria" item="criteria" separator="or"> |
|||
<if test="criteria.valid"> |
|||
<trim prefix="(" prefixOverrides="and" suffix=")"> |
|||
<foreach collection="criteria.criteria" item="criterion"> |
|||
<choose> |
|||
<when test="criterion.noValue"> |
|||
and ${criterion.condition} |
|||
</when> |
|||
<when test="criterion.singleValue"> |
|||
and ${criterion.condition} #{criterion.value} |
|||
</when> |
|||
<when test="criterion.betweenValue"> |
|||
and ${criterion.condition} #{criterion.value} and #{criterion.secondValue} |
|||
</when> |
|||
<when test="criterion.listValue"> |
|||
and ${criterion.condition} |
|||
<foreach close=")" collection="criterion.value" item="listItem" open="(" |
|||
separator=","> |
|||
#{listItem} |
|||
</foreach> |
|||
</when> |
|||
</choose> |
|||
</foreach> |
|||
</trim> |
|||
</if> |
|||
</foreach> |
|||
</where> |
|||
</sql> |
|||
<sql id="Base_Column_List"> |
|||
id, create_by, create_by_name, create_time, del_flag, update_by, update_time, order_id, ocr_picture, |
|||
ocr_picture_order, ocr_status, ocr_msg, shop_id, shop_name |
|||
|
|||
</sql> |
|||
|
|||
<insert id="batchSave" parameterType="java.util.List"> |
|||
insert into t_purchase_ocr_picture (id, create_by, create_by_name, create_time, del_flag, update_by, update_time, order_id, ocr_picture, |
|||
ocr_picture_order, ocr_status, ocr_msg, shop_id, shop_name) values |
|||
<foreach item="purchaseOcrPicture" collection="purchaseOcrPictureList" index="index" separator=","> |
|||
(#{purchaseOcrPicture.id,jdbcType=VARCHAR},#{purchaseOcrPicture.createBy,jdbcType=VARCHAR}, |
|||
#{purchaseOcrPicture.createByName,jdbcType=VARCHAR},#{purchaseOcrPicture.createTime,jdbcType=TIMESTAMP}, |
|||
#{purchaseOcrPicture.delFlag,jdbcType=INTEGER},#{purchaseOcrPicture.updateBy,jdbcType=VARCHAR}, |
|||
#{purchaseOcrPicture.updateTime,jdbcType=TIMESTAMP},#{purchaseOcrPicture.orderId,jdbcType=VARCHAR}, |
|||
#{purchaseOcrPicture.ocrPicture,jdbcType=VARCHAR},#{purchaseOcrPicture.ocrPictureOrder,jdbcType=INTEGER}, |
|||
#{purchaseOcrPicture.ocrStatus,jdbcType=INTEGER},#{purchaseOcrPicture.ocrMsg,jdbcType=VARCHAR}, |
|||
#{purchaseOcrPicture.shopId,jdbcType=VARCHAR},#{purchaseOcrPicture.shopName,jdbcType=VARCHAR}) |
|||
</foreach> |
|||
|
|||
</insert> |
|||
|
|||
<update id="batchUpdate"> |
|||
<foreach collection="purchaseOcrPictureList" item="item" separator=";" open="" close=""> |
|||
UPDATE t_purchase_ocr_picture SET ocr_msg = #{item.ocrMsg} , ocr_status = #{item.ocrStatus} WHERE id = #{item.id} |
|||
</foreach> |
|||
</update> |
|||
</mapper> |
|||
Loading…
Reference in new issue