|
|
@@ -21,6 +21,8 @@ import java.util.List;
|
|
|
import java.util.concurrent.*;
|
|
|
import java.util.stream.Collectors;
|
|
|
|
|
|
+import org.springframework.beans.factory.annotation.Qualifier;
|
|
|
+
|
|
|
/**
|
|
|
* 二期-门店图片Controller
|
|
|
*
|
|
|
@@ -39,6 +41,8 @@ public class StoreImgController {
|
|
|
private final StoreInfoService storeInfoService;
|
|
|
private final StoreOfficialAlbumService storeOfficialAlbumService;
|
|
|
private final AiContentModerationUtil aiContentModerationUtil;
|
|
|
+ @Qualifier("imgAuditExecutor")
|
|
|
+ private final ExecutorService imgAuditExecutor;
|
|
|
|
|
|
@ApiOperation("获取图片")
|
|
|
@ApiOperationSupport(order = 1)
|
|
|
@@ -86,37 +90,44 @@ public class StoreImgController {
|
|
|
if(storeImgInfoVo.getStoreImgList().isEmpty()){
|
|
|
return R.fail("图片列表为空,请重新上传图片");
|
|
|
}
|
|
|
- // 审核图片是否违规:有多少张图片就创建多少个线程并行审核
|
|
|
+ Integer imgType = storeImgInfoVo.getImgType();
|
|
|
+ Integer storeId = storeImgInfoVo.getStoreId();
|
|
|
List<String> imageUrls = storeImgList.stream()
|
|
|
.map(StoreImg::getImgUrl)
|
|
|
.collect(Collectors.toList());
|
|
|
- AiContentModerationUtil.AuditResult auditResult = auditImagesInParallel(imageUrls);
|
|
|
+ // 审核与环境相册查询并行,减少总耗时(imgType==4 时相册查询与审核同时进行)
|
|
|
+ CompletableFuture<AiContentModerationUtil.AuditResult> auditFuture = CompletableFuture
|
|
|
+ .supplyAsync(() -> auditImagesInParallel(imageUrls), imgAuditExecutor);
|
|
|
+ CompletableFuture<List<StoreOfficialAlbum>> albumFuture = (imgType != null && imgType == 4)
|
|
|
+ ? CompletableFuture.supplyAsync(() -> storeOfficialAlbumService.lambdaQuery()
|
|
|
+ .eq(StoreOfficialAlbum::getStoreId, storeId)
|
|
|
+ .eq(StoreOfficialAlbum::getAlbumName, "环境")
|
|
|
+ .orderByAsc(StoreOfficialAlbum::getId)
|
|
|
+ .list(), imgAuditExecutor)
|
|
|
+ : CompletableFuture.completedFuture(null);
|
|
|
+ AiContentModerationUtil.AuditResult auditResult = auditFuture.join();
|
|
|
if (!auditResult.isPassed()) {
|
|
|
- return R.fail(auditResult.getFailureReason()); // 文本内容异常(包含敏感词)
|
|
|
+ return R.fail(auditResult.getFailureReason());
|
|
|
}
|
|
|
- // 判断是否是头图(20:单图模式, 21:多图模式)
|
|
|
- Integer imgType = storeImgInfoVo.getImgType();
|
|
|
- boolean isHeadImage = (imgType == 20 || imgType == 21);
|
|
|
- if(imgType==4){
|
|
|
- Integer storeId = storeImgInfoVo.getStoreId();
|
|
|
- // 查询名称为"环境"的相册,因为一个门店可能有多个相册
|
|
|
- List<StoreOfficialAlbum> albumList = storeOfficialAlbumService.lambdaQuery()
|
|
|
- .eq(StoreOfficialAlbum::getStoreId, storeId)
|
|
|
- .eq(StoreOfficialAlbum::getAlbumName, "环境")
|
|
|
- .orderByAsc(StoreOfficialAlbum::getId)
|
|
|
- .list();
|
|
|
+ if (imgType != null && imgType == 4) {
|
|
|
+ List<StoreOfficialAlbum> albumList;
|
|
|
+ try {
|
|
|
+ albumList = albumFuture.get(10, TimeUnit.SECONDS);
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("查询环境相册异常", e);
|
|
|
+ return R.fail("查询环境相册失败");
|
|
|
+ }
|
|
|
if (albumList == null || albumList.isEmpty()) {
|
|
|
return R.fail("没有默认环境相册");
|
|
|
}
|
|
|
- // 如果有多条记录,取第一条(按ID升序)
|
|
|
StoreOfficialAlbum album = albumList.get(0);
|
|
|
storeImgList.forEach(storeImg -> storeImg.setBusinessId(album.getId()));
|
|
|
- // 添加图片时,修改数量(只更新当前使用的相册)
|
|
|
storeOfficialAlbumService.lambdaUpdate()
|
|
|
.eq(StoreOfficialAlbum::getId, album.getId())
|
|
|
.setSql("img_count = img_count + " + storeImgList.size())
|
|
|
.update();
|
|
|
}
|
|
|
+ boolean isHeadImage = imgType != null && (imgType == 20 || imgType == 21);
|
|
|
// 清空storeid,imgType下图片
|
|
|
int deleteCount = storeImgService.saveOrUpdateImg(storeImgInfoVo.getStoreId(),storeImgInfoVo.getImgType());
|
|
|
log.info("StoreImgController.updateStoreImgModeInfo?deleteCount={}", deleteCount);
|
|
|
@@ -140,12 +151,11 @@ public class StoreImgController {
|
|
|
.orElse(null);
|
|
|
}
|
|
|
|
|
|
- // 遍历图片列表,设置图片描述,标记需要异步提取颜色的图片
|
|
|
+ // 图片描述按类型固定,只计算一次
|
|
|
+ String imgDescription = getImgDescriptionByType(imgType);
|
|
|
final StoreImg[] needExtractColorImgRef = {null}; // 需要提取颜色的图片(单图模式或多图模式的第一张)
|
|
|
for (StoreImg storeImg : storeImgList) {
|
|
|
if (storeImg != null && storeImg.getImgUrl() != null) {
|
|
|
- // 根据图片类型设置图片描述
|
|
|
- String imgDescription = getImgDescriptionByType(imgType);
|
|
|
if (imgDescription != null) {
|
|
|
storeImg.setImgDescription(imgDescription);
|
|
|
}
|
|
|
@@ -337,7 +347,7 @@ public class StoreImgController {
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * 有多少张图片就创建多少个线程并行审核,每张图片单独审核
|
|
|
+ * 使用共享线程池并行审核,每张图片单独审核,线程数不超过 IMG_AUDIT_MAX_PARALLEL
|
|
|
*
|
|
|
* @param imageUrls 图片URL列表
|
|
|
* @return 合并后的审核结果,任一张不通过则整体不通过
|
|
|
@@ -346,15 +356,14 @@ public class StoreImgController {
|
|
|
if (imageUrls == null || imageUrls.isEmpty()) {
|
|
|
return new AiContentModerationUtil.AuditResult(true, null);
|
|
|
}
|
|
|
- int threadCount = imageUrls.size();
|
|
|
- ExecutorService executor = Executors.newFixedThreadPool(threadCount);
|
|
|
try {
|
|
|
- List<Future<AiContentModerationUtil.AuditResult>> futures = imageUrls.stream()
|
|
|
- .map(url -> executor.submit(() ->
|
|
|
- aiContentModerationUtil.auditContent(null, Collections.singletonList(url))))
|
|
|
+ List<CompletableFuture<AiContentModerationUtil.AuditResult>> futures = imageUrls.stream()
|
|
|
+ .map(url -> CompletableFuture.supplyAsync(
|
|
|
+ () -> aiContentModerationUtil.auditContent(null, Collections.singletonList(url)),
|
|
|
+ imgAuditExecutor))
|
|
|
.collect(Collectors.toList());
|
|
|
String firstFailureReason = null;
|
|
|
- for (Future<AiContentModerationUtil.AuditResult> future : futures) {
|
|
|
+ for (CompletableFuture<AiContentModerationUtil.AuditResult> future : futures) {
|
|
|
AiContentModerationUtil.AuditResult result = future.get(30, TimeUnit.SECONDS);
|
|
|
if (!result.isPassed() && firstFailureReason == null) {
|
|
|
firstFailureReason = result.getFailureReason();
|
|
|
@@ -373,8 +382,6 @@ public class StoreImgController {
|
|
|
} catch (ExecutionException e) {
|
|
|
log.error("图片审核异常", e);
|
|
|
return new AiContentModerationUtil.AuditResult(false, "审核异常");
|
|
|
- } finally {
|
|
|
- executor.shutdown();
|
|
|
}
|
|
|
}
|
|
|
|