|
@@ -3,21 +3,27 @@ package shop.alien.store.service.impl;
|
|
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
|
|
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
|
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
|
|
|
|
+import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
|
|
|
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
|
|
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
|
|
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
|
|
import lombok.RequiredArgsConstructor;
|
|
import lombok.RequiredArgsConstructor;
|
|
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
|
|
+import org.springframework.scheduling.annotation.Async;
|
|
|
import org.springframework.stereotype.Service;
|
|
import org.springframework.stereotype.Service;
|
|
|
import org.springframework.transaction.annotation.Transactional;
|
|
import org.springframework.transaction.annotation.Transactional;
|
|
|
import shop.alien.entity.result.R;
|
|
import shop.alien.entity.result.R;
|
|
|
-import shop.alien.entity.store.LifeClassManage;
|
|
|
|
|
import shop.alien.entity.store.LifeUser;
|
|
import shop.alien.entity.store.LifeUser;
|
|
|
import shop.alien.entity.store.StoreImg;
|
|
import shop.alien.entity.store.StoreImg;
|
|
|
import shop.alien.entity.store.StoreOfficialAlbum;
|
|
import shop.alien.entity.store.StoreOfficialAlbum;
|
|
|
|
|
+import shop.alien.entity.store.vo.StoreImgInfoVo;
|
|
|
import shop.alien.entity.store.vo.StoreImgTypeVo;
|
|
import shop.alien.entity.store.vo.StoreImgTypeVo;
|
|
|
import shop.alien.mapper.LifeUserMapper;
|
|
import shop.alien.mapper.LifeUserMapper;
|
|
|
import shop.alien.mapper.StoreImgMapper;
|
|
import shop.alien.mapper.StoreImgMapper;
|
|
|
import shop.alien.mapper.StoreOfficialAlbumMapper;
|
|
import shop.alien.mapper.StoreOfficialAlbumMapper;
|
|
|
import shop.alien.store.service.StoreImgService;
|
|
import shop.alien.store.service.StoreImgService;
|
|
|
|
|
+import shop.alien.store.service.StoreInfoService;
|
|
|
|
|
+import shop.alien.store.util.GroupConstant;
|
|
|
|
|
+import shop.alien.store.util.ai.AiImageColorExtractUtil;
|
|
|
|
|
|
|
|
import java.util.Comparator;
|
|
import java.util.Comparator;
|
|
|
import java.util.List;
|
|
import java.util.List;
|
|
@@ -30,6 +36,7 @@ import java.util.stream.Collectors;
|
|
|
* @author ssk
|
|
* @author ssk
|
|
|
* @since 2024-12-05
|
|
* @since 2024-12-05
|
|
|
*/
|
|
*/
|
|
|
|
|
+@Slf4j
|
|
|
@Transactional
|
|
@Transactional
|
|
|
@Service
|
|
@Service
|
|
|
@RequiredArgsConstructor
|
|
@RequiredArgsConstructor
|
|
@@ -38,6 +45,8 @@ public class StoreImgServiceImpl extends ServiceImpl<StoreImgMapper, StoreImg> i
|
|
|
private final StoreImgMapper storeImgMapper;
|
|
private final StoreImgMapper storeImgMapper;
|
|
|
private final StoreOfficialAlbumMapper storeOfficialAlbumMapper;
|
|
private final StoreOfficialAlbumMapper storeOfficialAlbumMapper;
|
|
|
private final LifeUserMapper lifeUserMapper;
|
|
private final LifeUserMapper lifeUserMapper;
|
|
|
|
|
+ private final AiImageColorExtractUtil aiImageColorExtractUtil;
|
|
|
|
|
+ private final StoreInfoService storeInfoService;
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
* 获取门店图片
|
|
* 获取门店图片
|
|
@@ -174,4 +183,206 @@ public class StoreImgServiceImpl extends ServiceImpl<StoreImgMapper, StoreImg> i
|
|
|
}
|
|
}
|
|
|
return result;
|
|
return result;
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 异步提取图片颜色(不阻塞主流程)
|
|
|
|
|
+ * 该方法在后台异步执行,提取成功后更新数据库中的颜色码
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param storeImgId 图片ID
|
|
|
|
|
+ * @param imgUrl 图片URL
|
|
|
|
|
+ */
|
|
|
|
|
+ @Override
|
|
|
|
|
+ @Async("taskExecutor")
|
|
|
|
|
+ public void asyncExtractColorForImg(Integer storeImgId, String imgUrl) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ log.info("开始异步提取图片颜色,storeImgId: {}, imgUrl: {}", storeImgId, imgUrl);
|
|
|
|
|
+ String colorCode = aiImageColorExtractUtil.extractCoverColor(imgUrl);
|
|
|
|
|
+ if (colorCode != null) {
|
|
|
|
|
+ // 更新数据库中的颜色码
|
|
|
|
|
+ StoreImg updateImg = new StoreImg();
|
|
|
|
|
+ updateImg.setId(storeImgId);
|
|
|
|
|
+ updateImg.setColorCode(colorCode);
|
|
|
|
|
+ updateImg.setIsExtract(1); // 提取成功,设置为已提取
|
|
|
|
|
+ this.updateById(updateImg);
|
|
|
|
|
+ log.info("异步提取图片颜色成功,storeImgId: {}, colorCode: {}", storeImgId, colorCode);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ log.warn("异步提取图片颜色失败,storeImgId: {}, imgUrl: {}", storeImgId, imgUrl);
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ log.error("异步提取图片颜色异常,storeImgId: {}, imgUrl: {}", storeImgId, imgUrl, e);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 新增/修改/删除图片(包含完整的业务逻辑处理)
|
|
|
|
|
+ * 包括:删除旧图片、更新图片模式、设置图片描述、异步提取颜色、更新审核状态等
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param storeImgInfoVo 图片信息VO
|
|
|
|
|
+ * @return 操作结果
|
|
|
|
|
+ */
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public R<String> saveOrUpdateImgWithInfo(StoreImgInfoVo storeImgInfoVo) {
|
|
|
|
|
+ log.info("StoreImgServiceImpl.saveOrUpdateImgWithInfo?storeImgInfoVo={}", storeImgInfoVo);
|
|
|
|
|
+ List<StoreImg> storeImgList = storeImgInfoVo.getStoreImgList();
|
|
|
|
|
+
|
|
|
|
|
+ // 参数校验
|
|
|
|
|
+ if (storeImgInfoVo.getStoreId() == null || storeImgInfoVo.getImgType() == null) {
|
|
|
|
|
+ return R.fail("失败");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 判断是否是头图(20:单图模式, 21:多图模式)
|
|
|
|
|
+ Integer imgType = storeImgInfoVo.getImgType();
|
|
|
|
|
+ boolean isHeadImage = (imgType == 20 || imgType == 21);
|
|
|
|
|
+
|
|
|
|
|
+ // 清空storeId,imgType下图片
|
|
|
|
|
+ int deleteCount = this.saveOrUpdateImg(storeImgInfoVo.getStoreId(), storeImgInfoVo.getImgType());
|
|
|
|
|
+ log.info("StoreImgServiceImpl.saveOrUpdateImgWithInfo?deleteCount={}", deleteCount);
|
|
|
|
|
+
|
|
|
|
|
+ // 更新图片模式信息
|
|
|
|
|
+ int result = storeInfoService.updateStoreImgModeInfo(storeImgInfoVo);
|
|
|
|
|
+ log.info("StoreImgServiceImpl.saveOrUpdateImgWithInfo?result={}", result);
|
|
|
|
|
+
|
|
|
|
|
+ if (!CollectionUtils.isEmpty(storeImgList)) {
|
|
|
|
|
+ // 判断是否为多图模式
|
|
|
|
|
+ boolean isMultiMode = imgType != null && imgType.equals(GroupConstant.IMG_TYPE_MULTI_MODE);
|
|
|
|
|
+
|
|
|
|
|
+ // 如果是多图模式,需要找到第一张图片(imgSort最小的)
|
|
|
|
|
+ StoreImg firstImg = null;
|
|
|
|
|
+ if (isMultiMode) {
|
|
|
|
|
+ firstImg = storeImgList.stream()
|
|
|
|
|
+ .filter(img -> img != null && img.getImgUrl() != null)
|
|
|
|
|
+ .min((img1, img2) -> {
|
|
|
|
|
+ Integer sort1 = img1.getImgSort() != null ? img1.getImgSort() : Integer.MAX_VALUE;
|
|
|
|
|
+ Integer sort2 = img2.getImgSort() != null ? img2.getImgSort() : Integer.MAX_VALUE;
|
|
|
|
|
+ return sort1.compareTo(sort2);
|
|
|
|
|
+ })
|
|
|
|
|
+ .orElse(null);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 遍历图片列表,设置图片描述,标记需要异步提取颜色的图片
|
|
|
|
|
+ final StoreImg[] needExtractColorImgRef = {null}; // 需要提取颜色的图片(单图模式或多图模式的第一张)
|
|
|
|
|
+ for (StoreImg storeImg : storeImgList) {
|
|
|
|
|
+ if (storeImg != null && storeImg.getImgUrl() != null) {
|
|
|
|
|
+ // 根据图片类型设置图片描述
|
|
|
|
|
+ String imgDescription = getImgDescriptionByType(imgType);
|
|
|
|
|
+ if (imgDescription != null) {
|
|
|
|
|
+ storeImg.setImgDescription(imgDescription);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 只有当 is_extract=1 时才标记需要提取颜色
|
|
|
|
|
+ Integer isExtract = storeImg.getIsExtract();
|
|
|
|
|
+ if (isExtract != null && isExtract == 1) {
|
|
|
|
|
+ // 如果是多图模式,只对第一张图片提取颜色
|
|
|
|
|
+ if (isMultiMode) {
|
|
|
|
|
+ // 判断是否为第一张图片(通过比较imgSort,如果imgSort相同则认为是同一张图片)
|
|
|
|
|
+ boolean isFirstImg = firstImg != null &&
|
|
|
|
|
+ firstImg.getImgSort() != null &&
|
|
|
|
|
+ storeImg.getImgSort() != null &&
|
|
|
|
|
+ firstImg.getImgSort().equals(storeImg.getImgSort());
|
|
|
|
|
+
|
|
|
|
|
+ if (isFirstImg) {
|
|
|
|
|
+ log.info("多图模式,标记第一张图片需要异步提取颜色,imgUrl: {}, imgSort: {}, isExtract: {}",
|
|
|
|
|
+ storeImg.getImgUrl(), storeImg.getImgSort(), isExtract);
|
|
|
|
|
+ needExtractColorImgRef[0] = storeImg;
|
|
|
|
|
+ } else {
|
|
|
|
|
+ log.info("多图模式,跳过非第一张图片的颜色提取,imgUrl: {}, imgSort: {}",
|
|
|
|
|
+ storeImg.getImgUrl(), storeImg.getImgSort());
|
|
|
|
|
+ // 非第一张图片,保持 is_extract 为 1,但不提取颜色
|
|
|
|
|
+ }
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // 单图模式,标记需要提取颜色
|
|
|
|
|
+ log.info("单图模式,标记图片需要异步提取颜色,imgUrl: {}, isExtract: {}", storeImg.getImgUrl(), isExtract);
|
|
|
|
|
+ needExtractColorImgRef[0] = storeImg;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ Integer id = storeImgList.get(0).getId();
|
|
|
|
|
+ if (this.saveOrUpdateBatch(storeImgList)) {
|
|
|
|
|
+ // 异步提取颜色,不阻塞主流程
|
|
|
|
|
+ // 保存后,MyBatis-Plus会自动填充ID,需要从保存后的列表中获取
|
|
|
|
|
+ StoreImg needExtractColorImg = needExtractColorImgRef[0];
|
|
|
|
|
+ if (needExtractColorImg != null) {
|
|
|
|
|
+ // 如果是新增的图片,保存后ID会被自动填充,需要重新查找
|
|
|
|
|
+ Integer storeImgId = needExtractColorImg.getId();
|
|
|
|
|
+ if (storeImgId == null) {
|
|
|
|
|
+ // 新增图片,保存后从列表中查找对应的图片(通过imgUrl匹配)
|
|
|
|
|
+ String targetImgUrl = needExtractColorImg.getImgUrl();
|
|
|
|
|
+ StoreImg savedImg = storeImgList.stream()
|
|
|
|
|
+ .filter(img -> img.getImgUrl() != null &&
|
|
|
|
|
+ img.getImgUrl().equals(targetImgUrl))
|
|
|
|
|
+ .findFirst()
|
|
|
|
|
+ .orElse(null);
|
|
|
|
|
+ if (savedImg != null) {
|
|
|
|
|
+ storeImgId = savedImg.getId();
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ if (storeImgId != null) {
|
|
|
|
|
+ // 调用异步提取颜色方法
|
|
|
|
|
+ this.asyncExtractColorForImg(storeImgId, needExtractColorImg.getImgUrl());
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 如果是头图,设置审核状态为通过(1),前端已完成门头图识别
|
|
|
|
|
+ if (isHeadImage) {
|
|
|
|
|
+ // 有头图,设置为审核通过状态(1)
|
|
|
|
|
+ try {
|
|
|
|
|
+ storeInfoService.updateHeadImgStatus(storeImgInfoVo.getStoreId(), 1);
|
|
|
|
|
+ log.info("头图保存成功,设置审核状态为通过,storeId={}", storeImgInfoVo.getStoreId());
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ log.error("更新头图审核状态失败,storeId={}, error={}",
|
|
|
|
|
+ storeImgInfoVo.getStoreId(), e.getMessage(), e);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ if (null != id) {
|
|
|
|
|
+ return R.success("修改成功");
|
|
|
|
|
+ }
|
|
|
|
|
+ return R.success("新增成功");
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // 图片保存失败,如果是头图,设置审核状态为不通过
|
|
|
|
|
+ if (isHeadImage) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ storeInfoService.updateHeadImgStatus(storeImgInfoVo.getStoreId(), 2);
|
|
|
|
|
+ log.info("图片保存失败,更新头图审核状态为不通过,storeId={}", storeImgInfoVo.getStoreId());
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ log.error("更新头图审核状态失败,storeId={}, error={}",
|
|
|
|
|
+ storeImgInfoVo.getStoreId(), e.getMessage(), e);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // 如果是头图且没有图片,设置审核状态为不通过
|
|
|
|
|
+ if (isHeadImage) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ storeInfoService.updateHeadImgStatus(storeImgInfoVo.getStoreId(), 2);
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ log.error("更新头图审核状态失败,storeId={}, error={}",
|
|
|
|
|
+ storeImgInfoVo.getStoreId(), e.getMessage(), e);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ return R.success("保存成功");
|
|
|
|
|
+ }
|
|
|
|
|
+ return R.fail("失败");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 根据图片类型获取图片描述
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param imgType 图片类型
|
|
|
|
|
+ * @return 图片描述
|
|
|
|
|
+ */
|
|
|
|
|
+ private String getImgDescriptionByType(Integer imgType) {
|
|
|
|
|
+ if (imgType == null) {
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
|
|
+ switch (imgType) {
|
|
|
|
|
+ case 20:
|
|
|
|
|
+ return "头图单图模式";
|
|
|
|
|
+ case 21:
|
|
|
|
|
+ return "头图多图模式";
|
|
|
|
|
+ default:
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|