Merge branch 'master' of https://gitee.com/liu-jiangs-project/RuoYi-Vue
commit
e2f1b1ec7a
|
|
@ -22,70 +22,9 @@ import java.util.List;
|
|||
* @date 2025-03-26
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/rest/v1/pic-searches")
|
||||
@RequestMapping("/rest/v1/pic-search")
|
||||
public class PictureSearchController extends XktBaseController {
|
||||
@Autowired
|
||||
private IPictureSearchService pictureSearchService;
|
||||
|
||||
/**
|
||||
* 查询以图搜款列表
|
||||
*/
|
||||
// @PreAuthorize("@ss.hasPermi('system:search:list')")
|
||||
@GetMapping("/list")
|
||||
public TableDataInfo list(PictureSearch pictureSearch) {
|
||||
startPage();
|
||||
List<PictureSearch> list = pictureSearchService.selectPictureSearchList(pictureSearch);
|
||||
return getDataTable(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出以图搜款列表
|
||||
*/
|
||||
// @PreAuthorize("@ss.hasPermi('system:search:export')")
|
||||
@Log(title = "以图搜款", businessType = BusinessType.EXPORT)
|
||||
@PostMapping("/export")
|
||||
public void export(HttpServletResponse response, PictureSearch pictureSearch) {
|
||||
List<PictureSearch> list = pictureSearchService.selectPictureSearchList(pictureSearch);
|
||||
ExcelUtil<PictureSearch> util = new ExcelUtil<PictureSearch>(PictureSearch.class);
|
||||
util.exportExcel(response, list, "以图搜款数据");
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取以图搜款详细信息
|
||||
*/
|
||||
// @PreAuthorize("@ss.hasPermi('system:search:query')")
|
||||
@GetMapping(value = "/{picSearchId}")
|
||||
public R getInfo(@PathVariable("picSearchId") Long picSearchId) {
|
||||
return success(pictureSearchService.selectPictureSearchByPicSearchId(picSearchId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增以图搜款
|
||||
*/
|
||||
// @PreAuthorize("@ss.hasPermi('system:search:add')")
|
||||
@Log(title = "以图搜款", businessType = BusinessType.INSERT)
|
||||
@PostMapping
|
||||
public R add(@RequestBody PictureSearch pictureSearch) {
|
||||
return success(pictureSearchService.insertPictureSearch(pictureSearch));
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改以图搜款
|
||||
*/
|
||||
// @PreAuthorize("@ss.hasPermi('system:search:edit')")
|
||||
@Log(title = "以图搜款", businessType = BusinessType.UPDATE)
|
||||
@PutMapping
|
||||
public R edit(@RequestBody PictureSearch pictureSearch) {
|
||||
return success(pictureSearchService.updatePictureSearch(pictureSearch));
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除以图搜款
|
||||
*/
|
||||
// @PreAuthorize("@ss.hasPermi('system:search:remove')")
|
||||
@Log(title = "以图搜款", businessType = BusinessType.DELETE)
|
||||
@DeleteMapping("/{picSearchIds}")
|
||||
public R remove(@PathVariable Long[] picSearchIds) {
|
||||
return success(pictureSearchService.deletePictureSearchByPicSearchIds(picSearchIds));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -143,4 +143,9 @@ public class CacheConstants
|
|||
*/
|
||||
public static final String APP_ADVERT = "app_advert:";
|
||||
|
||||
/**
|
||||
* 图片搜索统计
|
||||
*/
|
||||
public static final String IMG_SEARCH_STATISTICS = "img_search_statistics:";
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -229,5 +229,13 @@ public class Constants
|
|||
* 支付超时最大时间
|
||||
*/
|
||||
public static final Integer PAY_EXPIRE_MAX_HOURS = 24 * 7;
|
||||
/**
|
||||
* 以图搜图图片类目
|
||||
*/
|
||||
public static final int IMG_SEARCH_CATEGORY_ID = 4;
|
||||
/**
|
||||
* 以图搜图匹配分数阈值
|
||||
*/
|
||||
public static final float IMG_SEARCH_MATCH_SCORE_THRESHOLD = (float) 0.5;
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -271,4 +271,8 @@ public class RedisCache
|
|||
public boolean exists(final String key) {
|
||||
return redisTemplate.hasKey(key);
|
||||
}
|
||||
|
||||
public Long valueIncr(final String key) {
|
||||
return redisTemplate.opsForValue().increment(key);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ package com.ruoyi.framework.img;
|
|||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import cn.hutool.core.collection.ListUtil;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.util.BooleanUtil;
|
||||
import com.aliyun.imagesearch20201214.Client;
|
||||
import com.aliyun.imagesearch20201214.models.*;
|
||||
|
|
@ -12,9 +11,7 @@ import com.ruoyi.framework.img.entity.ImgSearchReq;
|
|||
import com.ruoyi.framework.img.entity.ImgSearchResult;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
|
|
@ -40,25 +37,22 @@ public class ImgSearchClientWrapper {
|
|||
* @return
|
||||
*/
|
||||
public boolean addImg(ImgAdd imgAdd) {
|
||||
AddImageAdvanceRequest request = new AddImageAdvanceRequest();
|
||||
request.instanceName = instanceName;
|
||||
request.productId = imgAdd.getProductId();
|
||||
request.picName = imgAdd.getPicName();
|
||||
request.categoryId = imgAdd.getCategoryId();
|
||||
request.customContent = imgAdd.getCustomContent();
|
||||
request.crop = true;
|
||||
Assert.notEmpty(imgAdd.getPicPath());
|
||||
try (InputStream inputStream = BooleanUtil.isTrue(imgAdd.getPicLocalFlag()) ?
|
||||
new FileInputStream(imgAdd.getPicPath()) : new URL(imgAdd.getPicPath()).openStream()) {
|
||||
try (InputStream inputStream = imgAdd.getPicInputStream()) {
|
||||
AddImageAdvanceRequest request = new AddImageAdvanceRequest();
|
||||
request.instanceName = instanceName;
|
||||
request.productId = imgAdd.getProductId();
|
||||
request.picName = imgAdd.getPicName();
|
||||
request.categoryId = imgAdd.getCategoryId();
|
||||
request.customContent = imgAdd.getCustomContent();
|
||||
request.crop = true;
|
||||
request.picContentObject = inputStream;
|
||||
RuntimeOptions runtimeOptions = new RuntimeOptions();
|
||||
AddImageResponse response = client.addImageAdvance(request, runtimeOptions);
|
||||
AddImageResponse response = client.addImageAdvance(request, new RuntimeOptions());
|
||||
if (response.getBody().success) {
|
||||
return true;
|
||||
}
|
||||
log.warn("图片搜索-添加图片失败: {} - {}", imgAdd, response.getBody().toMap());
|
||||
} catch (Exception e) {
|
||||
log.error("图片搜索服务异常", e);
|
||||
log.error("图片搜索服务添加图片异常", e);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
@ -80,7 +74,7 @@ public class ImgSearchClientWrapper {
|
|||
}
|
||||
log.warn("图片搜索-删除图片失败: {} - {}", productId, response.getBody().toMap());
|
||||
} catch (Exception e) {
|
||||
log.error("图片搜索服务异常", e);
|
||||
log.error("图片搜索服务删除图片异常", e);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
@ -92,23 +86,21 @@ public class ImgSearchClientWrapper {
|
|||
* @return
|
||||
*/
|
||||
public List<ImgSearchResult> searchByPic(ImgSearchReq imgSearchReq) {
|
||||
SearchImageByPicAdvanceRequest request = new SearchImageByPicAdvanceRequest();
|
||||
request.instanceName = instanceName;
|
||||
request.categoryId = imgSearchReq.getCategoryId();
|
||||
request.num = imgSearchReq.getNum();
|
||||
request.start = imgSearchReq.getStart();
|
||||
request.crop = true;
|
||||
request.distinctProductId = BooleanUtil.isTrue(imgSearchReq.getDistinctProductId());
|
||||
RuntimeOptions runtimeObject = new RuntimeOptions();
|
||||
try (InputStream inputStream = BooleanUtil.isTrue(imgSearchReq.getPicLocalFlag()) ?
|
||||
new FileInputStream(imgSearchReq.getPicPath()) : new URL(imgSearchReq.getPicPath()).openStream()) {
|
||||
try (InputStream inputStream = imgSearchReq.getPicInputStream()) {
|
||||
SearchImageByPicAdvanceRequest request = new SearchImageByPicAdvanceRequest();
|
||||
request.instanceName = instanceName;
|
||||
request.categoryId = imgSearchReq.getCategoryId();
|
||||
request.num = imgSearchReq.getNum();
|
||||
request.start = imgSearchReq.getStart();
|
||||
request.crop = true;
|
||||
request.distinctProductId = BooleanUtil.isTrue(imgSearchReq.getDistinctProductId());
|
||||
request.picContentObject = inputStream;
|
||||
SearchImageByPicResponse response = client.searchImageByPicAdvance(request, runtimeObject);
|
||||
SearchImageByPicResponse response = client.searchImageByPicAdvance(request, new RuntimeOptions());
|
||||
List<SearchImageByPicResponseBody.SearchImageByPicResponseBodyAuctions> auctions = response.getBody()
|
||||
.getAuctions();
|
||||
return BeanUtil.copyToList(auctions, ImgSearchResult.class);
|
||||
} catch (Exception e) {
|
||||
log.error("图片搜索服务异常", e);
|
||||
log.error("图片搜索服务搜索异常", e);
|
||||
}
|
||||
return ListUtil.empty();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@ package com.ruoyi.framework.img.entity;
|
|||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
* @author liangyq
|
||||
* @date 2025-04-25 20:08
|
||||
|
|
@ -32,15 +34,8 @@ public class ImgAdd {
|
|||
*/
|
||||
private String customContent;
|
||||
/**
|
||||
* 图片路径
|
||||
* <p>
|
||||
* 本地图片:E:/test/123.jpg
|
||||
* URL:https://www.example.com/123.jpg
|
||||
* 图片输入流
|
||||
*/
|
||||
private String picPath;
|
||||
/**
|
||||
* 是否本地图片
|
||||
*/
|
||||
private Boolean picLocalFlag;
|
||||
private InputStream picInputStream;
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@ package com.ruoyi.framework.img.entity;
|
|||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
* @author liangyq
|
||||
* @date 2025-04-25 20:50
|
||||
|
|
@ -9,16 +11,9 @@ import lombok.Data;
|
|||
@Data
|
||||
public class ImgSearchReq {
|
||||
/**
|
||||
* 图片路径
|
||||
* <p>
|
||||
* 本地图片:E:/test/123.jpg
|
||||
* URL:https://www.example.com/123.jpg
|
||||
* 图片输入流
|
||||
*/
|
||||
private String picPath;
|
||||
/**
|
||||
* 是否本地图片
|
||||
*/
|
||||
private Boolean picLocalFlag;
|
||||
private InputStream picInputStream;
|
||||
/**
|
||||
* 选填,图片类目
|
||||
* <p>
|
||||
|
|
|
|||
|
|
@ -242,7 +242,7 @@ public class OSSClientWrapper {
|
|||
return "image/jpeg";
|
||||
}
|
||||
|
||||
public InputStream getObject(String key) throws Exception {
|
||||
public InputStream getObject(String key) {
|
||||
OSSObject ossObject = client.getObject(configuration.getBucketName(), key);
|
||||
return ossObject.getObjectContent();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,22 @@
|
|||
package com.ruoyi.xkt.dto.picture;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author liangyq
|
||||
* @date 2025-05-20
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class PicSyncResultDTO {
|
||||
|
||||
private String productPicKey;
|
||||
|
||||
private Boolean success;
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
package com.ruoyi.xkt.dto.picture;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* @author liangyq
|
||||
* @date 2025-05-21
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class ProductMatchDTO {
|
||||
|
||||
private Long storeProductId;
|
||||
|
||||
private Float score;
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
package com.ruoyi.xkt.dto.picture;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author liangyq
|
||||
* @date 2025-05-20
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class ProductPicSyncDTO {
|
||||
|
||||
private Long storeProductId;
|
||||
|
||||
private List<String> productPicKeys;
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
package com.ruoyi.xkt.dto.picture;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author liangyq
|
||||
* @date 2025-05-20
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class ProductPicSyncResultDTO {
|
||||
|
||||
private Boolean success;
|
||||
|
||||
private List<PicSyncResultDTO> picSyncResults;
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
package com.ruoyi.xkt.dto.picture;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* @author liangyq
|
||||
* @date 2025-05-21
|
||||
*/
|
||||
@Data
|
||||
public class SearchRequestDTO {
|
||||
|
||||
private String picKey;
|
||||
|
||||
private BigDecimal picSize;
|
||||
|
||||
private Long userId;
|
||||
|
||||
private Integer num;
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@ package com.ruoyi.xkt.mapper;
|
|||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.ruoyi.xkt.domain.PictureSearch;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
|
@ -11,52 +12,6 @@ import java.util.List;
|
|||
* @author ruoyi
|
||||
* @date 2025-03-26
|
||||
*/
|
||||
@Repository
|
||||
public interface PictureSearchMapper extends BaseMapper<PictureSearch> {
|
||||
/**
|
||||
* 查询以图搜款
|
||||
*
|
||||
* @param id 以图搜款主键
|
||||
* @return 以图搜款
|
||||
*/
|
||||
public PictureSearch selectPictureSearchByPicSearchId(Long id);
|
||||
|
||||
/**
|
||||
* 查询以图搜款列表
|
||||
*
|
||||
* @param pictureSearch 以图搜款
|
||||
* @return 以图搜款集合
|
||||
*/
|
||||
public List<PictureSearch> selectPictureSearchList(PictureSearch pictureSearch);
|
||||
|
||||
/**
|
||||
* 新增以图搜款
|
||||
*
|
||||
* @param pictureSearch 以图搜款
|
||||
* @return 结果
|
||||
*/
|
||||
public int insertPictureSearch(PictureSearch pictureSearch);
|
||||
|
||||
/**
|
||||
* 修改以图搜款
|
||||
*
|
||||
* @param pictureSearch 以图搜款
|
||||
* @return 结果
|
||||
*/
|
||||
public int updatePictureSearch(PictureSearch pictureSearch);
|
||||
|
||||
/**
|
||||
* 删除以图搜款
|
||||
*
|
||||
* @param id 以图搜款主键
|
||||
* @return 结果
|
||||
*/
|
||||
public int deletePictureSearchByPicSearchId(Long id);
|
||||
|
||||
/**
|
||||
* 批量删除以图搜款
|
||||
*
|
||||
* @param picSearchIds 需要删除的数据主键集合
|
||||
* @return 结果
|
||||
*/
|
||||
public int deletePictureSearchByPicSearchIds(Long[] picSearchIds);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package com.ruoyi.xkt.mapper;
|
|||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.ruoyi.xkt.domain.SysFile;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
|
@ -11,6 +12,7 @@ import java.util.List;
|
|||
* @author ruoyi
|
||||
* @date 2025-03-26
|
||||
*/
|
||||
@Repository
|
||||
public interface SysFileMapper extends BaseMapper<SysFile> {
|
||||
/**
|
||||
* 查询file
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package com.ruoyi.xkt.service;
|
||||
|
||||
import com.ruoyi.xkt.domain.PictureSearch;
|
||||
import com.ruoyi.xkt.dto.picture.ProductMatchDTO;
|
||||
import com.ruoyi.xkt.dto.picture.SearchRequestDTO;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
|
@ -12,50 +13,11 @@ import java.util.List;
|
|||
*/
|
||||
public interface IPictureSearchService {
|
||||
/**
|
||||
* 查询以图搜款
|
||||
* 图片搜索商品
|
||||
*
|
||||
* @param picSearchId 以图搜款主键
|
||||
* @return 以图搜款
|
||||
* @param requestDTO
|
||||
* @return
|
||||
*/
|
||||
public PictureSearch selectPictureSearchByPicSearchId(Long picSearchId);
|
||||
List<ProductMatchDTO> searchProductByPic(SearchRequestDTO requestDTO);
|
||||
|
||||
/**
|
||||
* 查询以图搜款列表
|
||||
*
|
||||
* @param pictureSearch 以图搜款
|
||||
* @return 以图搜款集合
|
||||
*/
|
||||
public List<PictureSearch> selectPictureSearchList(PictureSearch pictureSearch);
|
||||
|
||||
/**
|
||||
* 新增以图搜款
|
||||
*
|
||||
* @param pictureSearch 以图搜款
|
||||
* @return 结果
|
||||
*/
|
||||
public int insertPictureSearch(PictureSearch pictureSearch);
|
||||
|
||||
/**
|
||||
* 修改以图搜款
|
||||
*
|
||||
* @param pictureSearch 以图搜款
|
||||
* @return 结果
|
||||
*/
|
||||
public int updatePictureSearch(PictureSearch pictureSearch);
|
||||
|
||||
/**
|
||||
* 批量删除以图搜款
|
||||
*
|
||||
* @param picSearchIds 需要删除的以图搜款主键集合
|
||||
* @return 结果
|
||||
*/
|
||||
public int deletePictureSearchByPicSearchIds(Long[] picSearchIds);
|
||||
|
||||
/**
|
||||
* 删除以图搜款信息
|
||||
*
|
||||
* @param picSearchId 以图搜款主键
|
||||
* @return 结果
|
||||
*/
|
||||
public int deletePictureSearchByPicSearchId(Long picSearchId);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,11 @@
|
|||
package com.ruoyi.xkt.service;
|
||||
|
||||
import com.ruoyi.xkt.dto.picture.PicZipDTO;
|
||||
import com.ruoyi.xkt.dto.picture.ProductMatchDTO;
|
||||
import com.ruoyi.xkt.dto.picture.ProductPicSyncDTO;
|
||||
import com.ruoyi.xkt.dto.picture.ProductPicSyncResultDTO;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author liangyq
|
||||
|
|
@ -16,4 +21,21 @@ public interface IPictureService {
|
|||
*/
|
||||
PicZipDTO processPicZip(String key);
|
||||
|
||||
/**
|
||||
* 同步图片到搜图服务器
|
||||
*
|
||||
* @param productPicSyncDTO
|
||||
* @return
|
||||
*/
|
||||
ProductPicSyncResultDTO sync2ImgSearchServer(ProductPicSyncDTO productPicSyncDTO);
|
||||
|
||||
/**
|
||||
* 以图搜商品
|
||||
*
|
||||
* @param picKey
|
||||
* @param num
|
||||
* @return
|
||||
*/
|
||||
List<ProductMatchDTO> searchProductByPicKey(String picKey, Integer num);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,14 +1,25 @@
|
|||
package com.ruoyi.xkt.service.impl;
|
||||
|
||||
import com.ruoyi.common.utils.DateUtils;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import com.ruoyi.common.constant.CacheConstants;
|
||||
import com.ruoyi.common.constant.Constants;
|
||||
import com.ruoyi.common.core.redis.RedisCache;
|
||||
import com.ruoyi.xkt.domain.PictureSearch;
|
||||
import com.ruoyi.xkt.domain.SysFile;
|
||||
import com.ruoyi.xkt.dto.picture.ProductMatchDTO;
|
||||
import com.ruoyi.xkt.dto.picture.SearchRequestDTO;
|
||||
import com.ruoyi.xkt.mapper.PictureSearchMapper;
|
||||
import com.ruoyi.xkt.mapper.SysFileMapper;
|
||||
import com.ruoyi.xkt.service.IPictureSearchService;
|
||||
import com.ruoyi.xkt.service.IPictureService;
|
||||
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.Date;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* 以图搜款Service业务层处理
|
||||
|
|
@ -16,82 +27,44 @@ import java.util.List;
|
|||
* @author ruoyi
|
||||
* @date 2025-03-26
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class PictureSearchServiceImpl implements IPictureSearchService {
|
||||
@Autowired
|
||||
private PictureSearchMapper pictureSearchMapper;
|
||||
@Autowired
|
||||
private SysFileMapper sysFileMapper;
|
||||
@Autowired
|
||||
private IPictureService pictureService;
|
||||
@Autowired
|
||||
private RedisCache redisCache;
|
||||
|
||||
/**
|
||||
* 查询以图搜款
|
||||
*
|
||||
* @param picSearchId 以图搜款主键
|
||||
* @return 以图搜款
|
||||
*/
|
||||
@Override
|
||||
@Transactional(readOnly = true)
|
||||
public PictureSearch selectPictureSearchByPicSearchId(Long picSearchId) {
|
||||
return pictureSearchMapper.selectPictureSearchByPicSearchId(picSearchId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询以图搜款列表
|
||||
*
|
||||
* @param pictureSearch 以图搜款
|
||||
* @return 以图搜款
|
||||
*/
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@Override
|
||||
@Transactional(readOnly = true)
|
||||
public List<PictureSearch> selectPictureSearchList(PictureSearch pictureSearch) {
|
||||
return pictureSearchMapper.selectPictureSearchList(pictureSearch);
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增以图搜款
|
||||
*
|
||||
* @param pictureSearch 以图搜款
|
||||
* @return 结果
|
||||
*/
|
||||
@Override
|
||||
@Transactional
|
||||
public int insertPictureSearch(PictureSearch pictureSearch) {
|
||||
pictureSearch.setCreateTime(DateUtils.getNowDate());
|
||||
return pictureSearchMapper.insertPictureSearch(pictureSearch);
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改以图搜款
|
||||
*
|
||||
* @param pictureSearch 以图搜款
|
||||
* @return 结果
|
||||
*/
|
||||
@Override
|
||||
@Transactional
|
||||
public int updatePictureSearch(PictureSearch pictureSearch) {
|
||||
pictureSearch.setUpdateTime(DateUtils.getNowDate());
|
||||
return pictureSearchMapper.updatePictureSearch(pictureSearch);
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量删除以图搜款
|
||||
*
|
||||
* @param picSearchIds 需要删除的以图搜款主键
|
||||
* @return 结果
|
||||
*/
|
||||
@Override
|
||||
@Transactional
|
||||
public int deletePictureSearchByPicSearchIds(Long[] picSearchIds) {
|
||||
return pictureSearchMapper.deletePictureSearchByPicSearchIds(picSearchIds);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除以图搜款信息
|
||||
*
|
||||
* @param picSearchId 以图搜款主键
|
||||
* @return 结果
|
||||
*/
|
||||
@Override
|
||||
@Transactional
|
||||
public int deletePictureSearchByPicSearchId(Long picSearchId) {
|
||||
return pictureSearchMapper.deletePictureSearchByPicSearchId(picSearchId);
|
||||
public List<ProductMatchDTO> searchProductByPic(SearchRequestDTO requestDTO) {
|
||||
Assert.notEmpty(requestDTO.getPicKey());
|
||||
SysFile sysFile = new SysFile();
|
||||
sysFile.setFileUrl(requestDTO.getPicKey());
|
||||
sysFile.setFileSize(requestDTO.getPicSize());
|
||||
sysFile.setVersion(0);
|
||||
sysFile.setDelFlag(Constants.UNDELETED);
|
||||
sysFileMapper.insert(sysFile);
|
||||
PictureSearch pictureSearch = new PictureSearch();
|
||||
pictureSearch.setSearchFileId(sysFile.getId());
|
||||
pictureSearch.setUserId(requestDTO.getUserId());
|
||||
pictureSearch.setVoucherDate(new Date());
|
||||
pictureSearch.setVersion(0);
|
||||
pictureSearch.setDelFlag(Constants.UNDELETED);
|
||||
pictureSearchMapper.insert(pictureSearch);
|
||||
//搜索
|
||||
List<ProductMatchDTO> results = pictureService.searchProductByPicKey(requestDTO.getPicKey(),
|
||||
//默认30条
|
||||
Optional.ofNullable(requestDTO.getNum()).orElse(30));
|
||||
for (ProductMatchDTO result : results) {
|
||||
//匹配次数+1
|
||||
redisCache.valueIncr(CacheConstants.IMG_SEARCH_STATISTICS + result.getStoreProductId());
|
||||
}
|
||||
return results;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
package com.ruoyi.xkt.service.impl;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.collection.ListUtil;
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import cn.hutool.core.io.file.FileNameUtil;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
|
|
@ -7,13 +9,16 @@ import cn.hutool.core.util.IdUtil;
|
|||
import cn.hutool.core.util.NumberUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.core.util.ZipUtil;
|
||||
import com.ruoyi.common.constant.Constants;
|
||||
import com.ruoyi.common.core.text.CharsetKit;
|
||||
import com.ruoyi.common.exception.ServiceException;
|
||||
import com.ruoyi.common.utils.ImgExtUtil;
|
||||
import com.ruoyi.framework.config.properties.OSSProperties;
|
||||
import com.ruoyi.framework.img.ImgSearchClientWrapper;
|
||||
import com.ruoyi.framework.img.entity.ImgAdd;
|
||||
import com.ruoyi.framework.img.entity.ImgSearchReq;
|
||||
import com.ruoyi.framework.oss.OSSClientWrapper;
|
||||
import com.ruoyi.xkt.dto.picture.DownloadResultDTO;
|
||||
import com.ruoyi.xkt.dto.picture.PicZipDTO;
|
||||
import com.ruoyi.xkt.dto.picture.*;
|
||||
import com.ruoyi.xkt.service.IPictureService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
|
@ -25,7 +30,9 @@ import java.io.File;
|
|||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @author liangyq
|
||||
|
|
@ -39,6 +46,8 @@ public class PictureServiceImpl implements IPictureService {
|
|||
private OSSClientWrapper ossClient;
|
||||
@Autowired
|
||||
private OSSProperties ossProperties;
|
||||
@Autowired
|
||||
private ImgSearchClientWrapper imgSearchClient;
|
||||
|
||||
@Override
|
||||
public PicZipDTO processPicZip(String key) {
|
||||
|
|
@ -113,6 +122,59 @@ public class PictureServiceImpl implements IPictureService {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProductPicSyncResultDTO sync2ImgSearchServer(ProductPicSyncDTO productPicSyncDTO) {
|
||||
Assert.notNull(productPicSyncDTO);
|
||||
String productId = productPicSyncDTO.getStoreProductId().toString();
|
||||
//删除商品图片
|
||||
boolean allSuccess = imgSearchClient.deleteImg(productId);
|
||||
List<String> picKeys = CollUtil.emptyIfNull(productPicSyncDTO.getProductPicKeys());
|
||||
List<PicSyncResultDTO> picSyncResultList = new ArrayList<>(picKeys.size());
|
||||
for (String picKey : picKeys) {
|
||||
ImgAdd imgAdd = new ImgAdd();
|
||||
imgAdd.setProductId(productId);
|
||||
imgAdd.setPicName(FileUtil.getName(picKey));
|
||||
imgAdd.setCategoryId(Constants.IMG_SEARCH_CATEGORY_ID);
|
||||
try {
|
||||
imgAdd.setPicInputStream(ossClient.getObject(picKey));
|
||||
} catch (Exception e) {
|
||||
log.error("获取图片流异常: " + picKey, e);
|
||||
allSuccess = false;
|
||||
picSyncResultList.add(new PicSyncResultDTO(picKey, false));
|
||||
continue;
|
||||
}
|
||||
//添加商品图片
|
||||
boolean success = imgSearchClient.addImg(imgAdd);
|
||||
if (!success) {
|
||||
allSuccess = false;
|
||||
}
|
||||
picSyncResultList.add(new PicSyncResultDTO(picKey, success));
|
||||
}
|
||||
return new ProductPicSyncResultDTO(allSuccess, picSyncResultList);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ProductMatchDTO> searchProductByPicKey(String picKey, Integer num) {
|
||||
Assert.notEmpty(picKey);
|
||||
ImgSearchReq imgSearchReq = new ImgSearchReq();
|
||||
imgSearchReq.setCategoryId(Constants.IMG_SEARCH_CATEGORY_ID);
|
||||
imgSearchReq.setNum(num);
|
||||
imgSearchReq.setStart(0);
|
||||
imgSearchReq.setDistinctProductId(true);
|
||||
try {
|
||||
imgSearchReq.setPicInputStream(ossClient.getObject(picKey));
|
||||
} catch (Exception e) {
|
||||
log.error("获取图片流异常: " + picKey, e);
|
||||
return ListUtil.empty();
|
||||
}
|
||||
return imgSearchClient.searchByPic(imgSearchReq)
|
||||
.stream()
|
||||
//过滤搜索评分0.5以下的商品
|
||||
.filter(o -> o.getScore() >= Constants.IMG_SEARCH_MATCH_SCORE_THRESHOLD)
|
||||
.map(o -> new ProductMatchDTO(Long.parseLong(o.getProductId()), o.getScore()))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* 解压文件
|
||||
* TODO 仅支持zip
|
||||
|
|
|
|||
|
|
@ -1,6 +1,10 @@
|
|||
package com.ruoyi.xkt.service.impl;
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.collection.ListUtil;
|
||||
import cn.hutool.core.thread.ThreadUtil;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import co.elastic.clients.elasticsearch.core.BulkResponse;
|
||||
import co.elastic.clients.elasticsearch.core.CreateResponse;
|
||||
import co.elastic.clients.elasticsearch.core.UpdateResponse;
|
||||
|
|
@ -19,6 +23,8 @@ import com.ruoyi.system.domain.dto.productCategory.ProdCateDTO;
|
|||
import com.ruoyi.system.mapper.SysProductCategoryMapper;
|
||||
import com.ruoyi.xkt.domain.*;
|
||||
import com.ruoyi.xkt.dto.es.ESProductDTO;
|
||||
import com.ruoyi.xkt.dto.picture.ProductPicSyncDTO;
|
||||
import com.ruoyi.xkt.dto.picture.ProductPicSyncResultDTO;
|
||||
import com.ruoyi.xkt.dto.storeColor.StoreColorDTO;
|
||||
import com.ruoyi.xkt.dto.storeProdCateAttr.StoreProdCateAttrDTO;
|
||||
import com.ruoyi.xkt.dto.storeProdColor.StoreProdColorDTO;
|
||||
|
|
@ -34,8 +40,10 @@ import com.ruoyi.xkt.enums.EProductStatus;
|
|||
import com.ruoyi.xkt.enums.FileType;
|
||||
import com.ruoyi.xkt.enums.ProductSizeStatus;
|
||||
import com.ruoyi.xkt.mapper.*;
|
||||
import com.ruoyi.xkt.service.IPictureService;
|
||||
import com.ruoyi.xkt.service.IStoreProductService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
|
@ -57,6 +65,7 @@ import static com.ruoyi.common.constant.Constants.*;
|
|||
* @author ruoyi
|
||||
* @date 2025-03-26
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class StoreProductServiceImpl implements IStoreProductService {
|
||||
|
|
@ -80,6 +89,7 @@ public class StoreProductServiceImpl implements IStoreProductService {
|
|||
final StoreProductStockMapper prodStockMapper;
|
||||
final StoreCustomerMapper storeCusMapper;
|
||||
final StoreCustomerProductDiscountMapper storeCusProdDiscMapper;
|
||||
final IPictureService pictureService;
|
||||
|
||||
|
||||
/**
|
||||
|
|
@ -210,6 +220,8 @@ public class StoreProductServiceImpl implements IStoreProductService {
|
|||
this.handleStoreProdProperties(storeProd, storeProdDTO);
|
||||
// 向ES索引: product_info 创建文档
|
||||
this.createESDoc(storeProd, storeProdDTO);
|
||||
// 搜图服务同步
|
||||
sync2ImgSearchServer(storeProd.getId(), storeProdDTO.getFileList());
|
||||
return count;
|
||||
}
|
||||
|
||||
|
|
@ -260,6 +272,8 @@ public class StoreProductServiceImpl implements IStoreProductService {
|
|||
this.handleStoreProdColorSizeList(storeProdDTO.getSizeList(), storeProdId, Boolean.FALSE);
|
||||
// 更新索引: product_info 的文档
|
||||
this.updateESDoc(storeProd, storeProdDTO);
|
||||
// 搜图服务同步
|
||||
sync2ImgSearchServer(storeProd.getId(), storeProdDTO.getFileList());
|
||||
return count;
|
||||
}
|
||||
|
||||
|
|
@ -421,10 +435,20 @@ public class StoreProductServiceImpl implements IStoreProductService {
|
|||
// 筛选商品状态为已下架,则删除ES文档
|
||||
if (Objects.equals(prodStatusDTO.getProdStatus(), EProductStatus.OFF_SALE.getValue())) {
|
||||
this.deleteESDoc(prodStatusDTO.getStoreProdIdList());
|
||||
// 搜图服务同步
|
||||
prodStatusDTO.getStoreProdIdList().forEach(spId -> sync2ImgSearchServer(spId, ListUtil.empty()));
|
||||
}
|
||||
// 已下架的商品重新上架
|
||||
if (CollectionUtils.isNotEmpty(reSaleList)) {
|
||||
this.reSaleCreateESDoc(reSaleList);
|
||||
// 搜图服务同步
|
||||
Map<Long, List<String>> picKeyGroupMap = storeProdFileMapper.selectMainPicByStoreProdIdList(
|
||||
reSaleList.stream().map(StoreProduct::getId).collect(Collectors.toList()),
|
||||
FileType.MAIN_PIC.getValue(), null
|
||||
).stream().collect(Collectors.groupingBy(StoreProdMainPicDTO::getStoreProdId,
|
||||
Collectors.mapping(StoreProdMainPicDTO::getFileUrl, Collectors.toList())));
|
||||
prodStatusDTO.getStoreProdIdList()
|
||||
.forEach(spId -> sync2ImgSearchServer(spId, picKeyGroupMap.get(spId), true));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -778,4 +802,34 @@ public class StoreProductServiceImpl implements IStoreProductService {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 搜图服务同步商品
|
||||
*
|
||||
* @param storeProductId
|
||||
* @param storeProdFiles
|
||||
*/
|
||||
private void sync2ImgSearchServer(Long storeProductId, List<StoreProdFileDTO> storeProdFiles) {
|
||||
List<String> picKeys = CollUtil.emptyIfNull(storeProdFiles)
|
||||
.stream()
|
||||
.filter(o -> FileType.MAIN_PIC.getValue().equals(o.getFileType()))
|
||||
.map(StoreProdFileDTO::getFileUrl)
|
||||
.collect(Collectors.toList());
|
||||
sync2ImgSearchServer(storeProductId, picKeys, true);
|
||||
}
|
||||
|
||||
private void sync2ImgSearchServer(Long storeProductId, List<String> picKeys, boolean async) {
|
||||
if (async) {
|
||||
ThreadUtil.execAsync(() -> {
|
||||
ProductPicSyncResultDTO r =
|
||||
pictureService.sync2ImgSearchServer(new ProductPicSyncDTO(storeProductId, picKeys));
|
||||
log.info("商品图片同步至搜图服务器: id: {}, result: {}", storeProductId, JSONUtil.toJsonStr(r));
|
||||
}
|
||||
);
|
||||
} else {
|
||||
ProductPicSyncResultDTO r =
|
||||
pictureService.sync2ImgSearchServer(new ProductPicSyncDTO(storeProductId, picKeys));
|
||||
log.info("商品图片同步至搜图服务器: id: {}, result: {}", storeProductId, JSONUtil.toJsonStr(r));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,88 +4,4 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.ruoyi.xkt.mapper.PictureSearchMapper">
|
||||
|
||||
<resultMap type="PictureSearch" id="PictureSearchResult">
|
||||
<result property="id" column="id" />
|
||||
<result property="searchFileId" column="search_file_id" />
|
||||
<result property="userId" column="user_id" />
|
||||
<result property="voucherDate" column="voucher_date" />
|
||||
<result property="version" column="version" />
|
||||
<result property="delFlag" column="del_flag" />
|
||||
<result property="createBy" column="create_by" />
|
||||
<result property="createTime" column="create_time" />
|
||||
<result property="updateBy" column="update_by" />
|
||||
<result property="updateTime" column="update_time" />
|
||||
</resultMap>
|
||||
|
||||
<sql id="selectPictureSearchVo">
|
||||
select id, search_file_id, user_id, voucher_date, version, del_flag, create_by, create_time, update_by, update_time from picture_search
|
||||
</sql>
|
||||
|
||||
<select id="selectPictureSearchList" parameterType="PictureSearch" resultMap="PictureSearchResult">
|
||||
<include refid="selectPictureSearchVo"/>
|
||||
<where>
|
||||
<if test="searchFileId != null "> and search_file_id = #{searchFileId}</if>
|
||||
<if test="userId != null "> and user_id = #{userId}</if>
|
||||
<if test="voucherDate != null "> and voucher_date = #{voucherDate}</if>
|
||||
<if test="version != null "> and version = #{version}</if>
|
||||
</where>
|
||||
</select>
|
||||
|
||||
<select id="selectPictureSearchByPicSearchId" parameterType="Long" resultMap="PictureSearchResult">
|
||||
<include refid="selectPictureSearchVo"/>
|
||||
where id = #{id}
|
||||
</select>
|
||||
|
||||
<insert id="insertPictureSearch" parameterType="PictureSearch" useGeneratedKeys="true" keyProperty="picSearchId">
|
||||
insert into picture_search
|
||||
<trim prefix="(" suffix=")" suffixOverrides=",">
|
||||
<if test="searchFileId != null">search_file_id,</if>
|
||||
<if test="userId != null">user_id,</if>
|
||||
<if test="voucherDate != null">voucher_date,</if>
|
||||
<if test="version != null">version,</if>
|
||||
<if test="delFlag != null">del_flag,</if>
|
||||
<if test="createBy != null">create_by,</if>
|
||||
<if test="createTime != null">create_time,</if>
|
||||
<if test="updateBy != null">update_by,</if>
|
||||
<if test="updateTime != null">update_time,</if>
|
||||
</trim>
|
||||
<trim prefix="values (" suffix=")" suffixOverrides=",">
|
||||
<if test="searchFileId != null">#{searchFileId},</if>
|
||||
<if test="userId != null">#{userId},</if>
|
||||
<if test="voucherDate != null">#{voucherDate},</if>
|
||||
<if test="version != null">#{version},</if>
|
||||
<if test="delFlag != null">#{delFlag},</if>
|
||||
<if test="createBy != null">#{createBy},</if>
|
||||
<if test="createTime != null">#{createTime},</if>
|
||||
<if test="updateBy != null">#{updateBy},</if>
|
||||
<if test="updateTime != null">#{updateTime},</if>
|
||||
</trim>
|
||||
</insert>
|
||||
|
||||
<update id="updatePictureSearch" parameterType="PictureSearch">
|
||||
update picture_search
|
||||
<trim prefix="SET" suffixOverrides=",">
|
||||
<if test="searchFileId != null">search_file_id = #{searchFileId},</if>
|
||||
<if test="userId != null">user_id = #{userId},</if>
|
||||
<if test="voucherDate != null">voucher_date = #{voucherDate},</if>
|
||||
<if test="version != null">version = #{version},</if>
|
||||
<if test="delFlag != null">del_flag = #{delFlag},</if>
|
||||
<if test="createBy != null">create_by = #{createBy},</if>
|
||||
<if test="createTime != null">create_time = #{createTime},</if>
|
||||
<if test="updateBy != null">update_by = #{updateBy},</if>
|
||||
<if test="updateTime != null">update_time = #{updateTime},</if>
|
||||
</trim>
|
||||
where id = #{id}
|
||||
</update>
|
||||
|
||||
<delete id="deletePictureSearchByPicSearchId" parameterType="Long">
|
||||
delete from picture_search where id = #{id}
|
||||
</delete>
|
||||
|
||||
<delete id="deletePictureSearchByPicSearchIds" parameterType="String">
|
||||
delete from picture_search where id in
|
||||
<foreach item="id" collection="array" open="(" separator="," close=")">
|
||||
#{id}
|
||||
</foreach>
|
||||
</delete>
|
||||
</mapper>
|
||||
|
|
@ -135,8 +135,12 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||
#{storeProdId}
|
||||
</foreach>
|
||||
</if>
|
||||
AND spf.file_type = #{fileType}
|
||||
AND spf.order_num = #{orderNum}
|
||||
<if test="fileType != null">
|
||||
AND spf.file_type = #{fileType}
|
||||
</if>
|
||||
<if test="orderNum != null">
|
||||
AND spf.order_num = #{orderNum}
|
||||
</if>
|
||||
</select>
|
||||
|
||||
<select id="selectPicSpaceList" >
|
||||
|
|
|
|||
Loading…
Reference in New Issue