ruoyi-admin/src/main/java/org/ruoyi/RuoYiAIApplication.java
@@ -3,6 +3,7 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.context.metrics.buffering.BufferingApplicationStartup; import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.scheduling.annotation.EnableScheduling; /** @@ -12,6 +13,7 @@ */ @SpringBootApplication @EnableScheduling @EnableAsync public class RuoYiAIApplication { public static void main(String[] args) { ruoyi-modules-api/ruoyi-knowledge-api/pom.xml
@@ -114,6 +114,10 @@ <artifactId>commons-io</artifactId> <version>2.17.0</version> </dependency> <dependency> <groupId>org.ruoyi</groupId> <artifactId>ruoyi-system-api</artifactId> </dependency> </dependencies> ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/constant/DealStatus.java
@@ -11,6 +11,8 @@ public static final Integer STATUS_20 = 20; //å·²ç»æ public static final Integer STATUS_30 = 30; //å¤ç失败 public static final Integer STATUS_40 = 40; } ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/domain/KnowledgeAttachPic.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,81 @@ package org.ruoyi.domain; import com.baomidou.mybatisplus.annotation.*; import lombok.Data; import lombok.EqualsAndHashCode; import java.io.Serial; import org.ruoyi.core.domain.BaseEntity; /** * ç¥è¯åºéä»¶å¾çå对象 knowledge_attach_pic * * @author Albert * @date 2025-05-15 */ @Data @EqualsAndHashCode(callSuper = true) @TableName("knowledge_attach_pic") public class KnowledgeAttachPic extends BaseEntity { @Serial private static final long serialVersionUID = 1L; /** * ä¸»é® */ @TableId(value = "id") private Long id; /** * ç¥è¯åºid */ private String kid; /** * éä»¶id */ private String aid; /** * ææ¡£åç§° */ private String docName; /** * ææ¡£ç±»å */ private String docType; /** * ææ¡£å 容 */ private String content; /** * æå¨é¡µæ° */ private Integer pageNum; /** * æå¨é¡µindex */ private Integer indexNum; /** * åæå¾çç¶æ10æªå¼å§ï¼20è¿è¡ä¸ï¼30已宿 */ private Integer picAnysStatus; /** * 对象åå¨ä¸»é® */ private Long ossId; /** * 夿³¨ */ private String remark; } ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/domain/bo/KnowledgeAttachPicBo.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,90 @@ package org.ruoyi.domain.bo; import org.ruoyi.common.core.validate.AddGroup; import org.ruoyi.common.core.validate.EditGroup; import org.ruoyi.core.domain.BaseEntity; import io.github.linpeilie.annotations.AutoMapper; import lombok.Data; import lombok.EqualsAndHashCode; import jakarta.validation.constraints.*; import org.ruoyi.domain.KnowledgeAttachPic; /** * ç¥è¯åºéä»¶å¾çåä¸å¡å¯¹è±¡ knowledge_attach_pic * * @author Albert * @date 2025-05-15 */ @Data @EqualsAndHashCode(callSuper = true) @AutoMapper(target = KnowledgeAttachPic.class, reverseConvertGenerate = false) public class KnowledgeAttachPicBo extends BaseEntity { /** * ä¸»é® */ @NotNull(message = "主é®ä¸è½ä¸ºç©º", groups = {EditGroup.class}) private Long id; /** * ç¥è¯åºid */ @NotBlank(message = "ç¥è¯åºidä¸è½ä¸ºç©º", groups = {AddGroup.class, EditGroup.class}) private String kid; /** * éä»¶id */ @NotBlank(message = "éä»¶idä¸è½ä¸ºç©º", groups = {AddGroup.class, EditGroup.class}) private String aid; /** * ææ¡£åç§° */ @NotBlank(message = "ææ¡£åç§°ä¸è½ä¸ºç©º", groups = {AddGroup.class, EditGroup.class}) private String docName; /** * ææ¡£ç±»å */ @NotBlank(message = "ææ¡£ç±»åä¸è½ä¸ºç©º", groups = {AddGroup.class, EditGroup.class}) private String docType; /** * ææ¡£å 容 */ @NotBlank(message = "ææ¡£å 容ä¸è½ä¸ºç©º", groups = {AddGroup.class, EditGroup.class}) private String content; /** * æå¨é¡µæ° */ @NotNull(message = "æå¨é¡µæ°ä¸è½ä¸ºç©º", groups = {AddGroup.class, EditGroup.class}) private Integer pageNum; /** * æå¨é¡µindex */ @NotNull(message = "æå¨é¡µindexä¸è½ä¸ºç©º", groups = {AddGroup.class, EditGroup.class}) private Integer indexNum; /** * åæå¾çç¶æ10æªå¼å§ï¼20è¿è¡ä¸ï¼30已宿 */ @NotNull(message = "åæå¾çç¶æ10æªå¼å§ï¼20è¿è¡ä¸ï¼30已宿ä¸è½ä¸ºç©º", groups = {AddGroup.class, EditGroup.class}) private Integer picAnysStatus; /** * 对象åå¨ä¸»é® */ @NotNull(message = "对象åå¨ä¸»é®ä¸è½ä¸ºç©º", groups = {AddGroup.class, EditGroup.class}) private Long ossId; /** * 夿³¨ */ @NotBlank(message = "夿³¨ä¸è½ä¸ºç©º", groups = {AddGroup.class, EditGroup.class}) private String remark; } ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/domain/vo/KnowledgeAttachPicVo.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,92 @@ package org.ruoyi.domain.vo; import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; import com.alibaba.excel.annotation.ExcelProperty; import io.github.linpeilie.annotations.AutoMapper; import lombok.Data; import java.io.Serial; import java.io.Serializable; import org.ruoyi.domain.KnowledgeAttachPic; /** * ç¥è¯åºéä»¶å¾çåè§å¾å¯¹è±¡ knowledge_attach_pic * * @author Albert * @date 2025-05-15 */ @Data @ExcelIgnoreUnannotated @AutoMapper(target = KnowledgeAttachPic.class) public class KnowledgeAttachPicVo implements Serializable { @Serial private static final long serialVersionUID = 1L; /** * ä¸»é® */ @ExcelProperty(value = "主é®") private Long id; /** * ç¥è¯åºid */ @ExcelProperty(value = "ç¥è¯åºid") private String kid; /** * éä»¶id */ @ExcelProperty(value = "éä»¶id") private String aid; /** * ææ¡£åç§° */ @ExcelProperty(value = "ææ¡£åç§°") private String docName; /** * ææ¡£ç±»å */ @ExcelProperty(value = "ææ¡£ç±»å") private String docType; /** * ææ¡£å 容 */ @ExcelProperty(value = "ææ¡£å 容") private String content; /** * æå¨é¡µæ° */ @ExcelProperty(value = "æå¨é¡µæ°") private Integer pageNum; /** * æå¨é¡µindex */ @ExcelProperty(value = "æå¨é¡µindex") private Integer indexNum; /** * åæå¾çç¶æ10æªå¼å§ï¼20è¿è¡ä¸ï¼30已宿 */ @ExcelProperty(value = "åæå¾çç¶æ10æªå¼å§ï¼20è¿è¡ä¸ï¼30已宿") private Integer picAnysStatus; /** * 对象åå¨ä¸»é® */ @ExcelProperty(value = "对象åå¨ä¸»é®") private Long ossId; /** * 夿³¨ */ @ExcelProperty(value = "夿³¨") private String remark; } ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/mapper/KnowledgeAttachPicMapper.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,15 @@ package org.ruoyi.mapper; import org.ruoyi.core.mapper.BaseMapperPlus; import org.ruoyi.domain.KnowledgeAttachPic; import org.ruoyi.domain.vo.KnowledgeAttachPicVo; /** * ç¥è¯åºéä»¶å¾çåMapperæ¥å£ * * @author Albert * @date 2025-05-15 */ public interface KnowledgeAttachPicMapper extends BaseMapperPlus<KnowledgeAttachPic, KnowledgeAttachPicVo> { } ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/service/IKnowledgeAttachPicService.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,47 @@ package org.ruoyi.service; import java.util.Collection; import java.util.List; import org.ruoyi.core.page.PageQuery; import org.ruoyi.core.page.TableDataInfo; import org.ruoyi.domain.bo.KnowledgeAttachPicBo; import org.ruoyi.domain.vo.KnowledgeAttachPicVo; /** * ç¥è¯åºéä»¶å¾çåServiceæ¥å£ * * @author Albert * @date 2025-05-15 */ public interface IKnowledgeAttachPicService { /** * æ¥è¯¢ç¥è¯åºéä»¶å¾çå */ KnowledgeAttachPicVo queryById(Long id); /** * æ¥è¯¢ç¥è¯åºéä»¶å¾çåå表 */ TableDataInfo<KnowledgeAttachPicVo> queryPageList(KnowledgeAttachPicBo bo, PageQuery pageQuery); /** * æ¥è¯¢ç¥è¯åºéä»¶å¾çåå表 */ List<KnowledgeAttachPicVo> queryList(KnowledgeAttachPicBo bo); /** * æ°å¢ç¥è¯åºéä»¶å¾çå */ Boolean insertByBo(KnowledgeAttachPicBo bo); /** * ä¿®æ¹ç¥è¯åºéä»¶å¾çå */ Boolean updateByBo(KnowledgeAttachPicBo bo); /** * æ ¡éªå¹¶æ¹éå é¤ç¥è¯åºéä»¶å¾çåä¿¡æ¯ */ Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid); } ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/service/impl/KnowledgeAttachPicServiceImpl.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,123 @@ package org.ruoyi.service.impl; import org.ruoyi.common.core.utils.MapstructUtils; import org.ruoyi.common.core.utils.StringUtils; import org.ruoyi.core.page.TableDataInfo; import org.ruoyi.core.page.PageQuery; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import lombok.RequiredArgsConstructor; import org.ruoyi.domain.KnowledgeAttachPic; import org.ruoyi.domain.bo.KnowledgeAttachPicBo; import org.ruoyi.domain.vo.KnowledgeAttachPicVo; import org.ruoyi.mapper.KnowledgeAttachPicMapper; import org.ruoyi.service.IKnowledgeAttachPicService; import org.springframework.stereotype.Service; import java.util.List; import java.util.Map; import java.util.Collection; /** * ç¥è¯åºéä»¶å¾çåServiceä¸å¡å±å¤ç * * @author ageerle * @date 2025-05-15 */ @RequiredArgsConstructor @Service public class KnowledgeAttachPicServiceImpl implements IKnowledgeAttachPicService { private final KnowledgeAttachPicMapper baseMapper; /** * æ¥è¯¢ç¥è¯åºéä»¶å¾çå */ @Override public KnowledgeAttachPicVo queryById(Long id) { return baseMapper.selectVoById(id); } /** * æ¥è¯¢ç¥è¯åºéä»¶å¾çåå表 */ @Override public TableDataInfo<KnowledgeAttachPicVo> queryPageList(KnowledgeAttachPicBo bo, PageQuery pageQuery) { LambdaQueryWrapper<KnowledgeAttachPic> lqw = buildQueryWrapper(bo); Page<KnowledgeAttachPicVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw); return TableDataInfo.build(result); } /** * æ¥è¯¢ç¥è¯åºéä»¶å¾çåå表 */ @Override public List<KnowledgeAttachPicVo> queryList(KnowledgeAttachPicBo bo) { LambdaQueryWrapper<KnowledgeAttachPic> lqw = buildQueryWrapper(bo); return baseMapper.selectVoList(lqw); } private LambdaQueryWrapper<KnowledgeAttachPic> buildQueryWrapper(KnowledgeAttachPicBo bo) { Map<String, Object> params = bo.getParams(); LambdaQueryWrapper<KnowledgeAttachPic> lqw = Wrappers.lambdaQuery(); lqw.eq(StringUtils.isNotBlank(bo.getKid()), KnowledgeAttachPic::getKid, bo.getKid()); lqw.eq(StringUtils.isNotBlank(bo.getAid()), KnowledgeAttachPic::getAid, bo.getAid()); lqw.like(StringUtils.isNotBlank(bo.getDocName()), KnowledgeAttachPic::getDocName, bo.getDocName()); lqw.eq(StringUtils.isNotBlank(bo.getDocType()), KnowledgeAttachPic::getDocType, bo.getDocType()); lqw.eq(StringUtils.isNotBlank(bo.getContent()), KnowledgeAttachPic::getContent, bo.getContent()); lqw.eq(bo.getPageNum() != null, KnowledgeAttachPic::getPageNum, bo.getPageNum()); lqw.eq(bo.getIndexNum() != null, KnowledgeAttachPic::getIndexNum, bo.getIndexNum()); lqw.eq(bo.getPicAnysStatus() != null, KnowledgeAttachPic::getPicAnysStatus, bo.getPicAnysStatus()); lqw.eq(bo.getOssId() != null, KnowledgeAttachPic::getOssId, bo.getOssId()); return lqw; } /** * æ°å¢ç¥è¯åºéä»¶å¾çå */ @Override public Boolean insertByBo(KnowledgeAttachPicBo bo) { KnowledgeAttachPic add = MapstructUtils.convert(bo, KnowledgeAttachPic.class); validEntityBeforeSave(add); boolean flag = baseMapper.insert(add) > 0; if (flag) { bo.setId(add.getId()); } return flag; } /** * ä¿®æ¹ç¥è¯åºéä»¶å¾çå */ @Override public Boolean updateByBo(KnowledgeAttachPicBo bo) { KnowledgeAttachPic update = MapstructUtils.convert(bo, KnowledgeAttachPic.class); validEntityBeforeSave(update); return baseMapper.updateById(update) > 0; } /** * ä¿ååçæ°æ®æ ¡éª */ private void validEntityBeforeSave(KnowledgeAttachPic entity) { //TODO åä¸äºæ°æ®æ ¡éª,å¦å¯ä¸çº¦æ } /** * æ¹éå é¤ç¥è¯åºéä»¶å¾çå */ @Override public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) { if (isValid) { //TODO åä¸äºä¸å¡ä¸çæ ¡éª,夿æ¯å¦éè¦æ ¡éª } return baseMapper.deleteBatchIds(ids) > 0; } } ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/service/impl/KnowledgeAttachServiceImpl.java
@@ -1,5 +1,7 @@ package org.ruoyi.service.impl; import cn.hutool.core.util.ObjectUtil; import java.util.stream.Collectors; import org.ruoyi.common.core.utils.MapstructUtils; import org.ruoyi.common.core.utils.StringUtils; import org.ruoyi.core.page.TableDataInfo; @@ -8,8 +10,11 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import lombok.RequiredArgsConstructor; import org.ruoyi.domain.KnowledgeAttachPic; import org.ruoyi.domain.vo.KnowledgeAttachVo; import org.ruoyi.mapper.KnowledgeAttachPicMapper; import org.ruoyi.mapper.KnowledgeFragmentMapper; import org.ruoyi.system.service.ISysOssService; import org.springframework.stereotype.Service; import org.ruoyi.domain.bo.KnowledgeAttachBo; @@ -33,99 +38,130 @@ @Service public class KnowledgeAttachServiceImpl implements IKnowledgeAttachService { private final KnowledgeAttachMapper baseMapper; private final KnowledgeFragmentMapper fragmentMapper; private final KnowledgeAttachMapper baseMapper; private final KnowledgeFragmentMapper fragmentMapper; /** * æ¥è¯¢ç¥è¯åºéä»¶ */ @Override public KnowledgeAttachVo queryById(Long id){ return baseMapper.selectVoById(id); private final ISysOssService ossService; private final KnowledgeAttachPicMapper picMapper; /** * æ¥è¯¢ç¥è¯åºéä»¶ */ @Override public KnowledgeAttachVo queryById(Long id) { return baseMapper.selectVoById(id); } /** * æ¥è¯¢ç¥è¯åºéä»¶å表 */ @Override public TableDataInfo<KnowledgeAttachVo> queryPageList(KnowledgeAttachBo bo, PageQuery pageQuery) { LambdaQueryWrapper<KnowledgeAttach> lqw = buildQueryWrapper(bo); Page<KnowledgeAttachVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw); return TableDataInfo.build(result); } /** * æ¥è¯¢ç¥è¯åºéä»¶å表 */ @Override public List<KnowledgeAttachVo> queryList(KnowledgeAttachBo bo) { LambdaQueryWrapper<KnowledgeAttach> lqw = buildQueryWrapper(bo); return baseMapper.selectVoList(lqw); } private LambdaQueryWrapper<KnowledgeAttach> buildQueryWrapper(KnowledgeAttachBo bo) { Map<String, Object> params = bo.getParams(); LambdaQueryWrapper<KnowledgeAttach> lqw = Wrappers.lambdaQuery(); lqw.eq(StringUtils.isNotBlank(bo.getKid()), KnowledgeAttach::getKid, bo.getKid()); lqw.eq(StringUtils.isNotBlank(bo.getDocId()), KnowledgeAttach::getDocId, bo.getDocId()); lqw.like(StringUtils.isNotBlank(bo.getDocName()), KnowledgeAttach::getDocName, bo.getDocName()); lqw.eq(StringUtils.isNotBlank(bo.getDocType()), KnowledgeAttach::getDocType, bo.getDocType()); lqw.eq(StringUtils.isNotBlank(bo.getContent()), KnowledgeAttach::getContent, bo.getContent()); return lqw; } /** * æ°å¢ç¥è¯åºéä»¶ */ @Override public Boolean insertByBo(KnowledgeAttachBo bo) { KnowledgeAttach add = MapstructUtils.convert(bo, KnowledgeAttach.class); validEntityBeforeSave(add); boolean flag = baseMapper.insert(add) > 0; if (flag) { bo.setId(add.getId()); } return flag; } /** * æ¥è¯¢ç¥è¯åºéä»¶å表 */ @Override public TableDataInfo<KnowledgeAttachVo> queryPageList(KnowledgeAttachBo bo, PageQuery pageQuery) { LambdaQueryWrapper<KnowledgeAttach> lqw = buildQueryWrapper(bo); Page<KnowledgeAttachVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw); return TableDataInfo.build(result); /** * ä¿®æ¹ç¥è¯åºéä»¶ */ @Override public Boolean updateByBo(KnowledgeAttachBo bo) { KnowledgeAttach update = MapstructUtils.convert(bo, KnowledgeAttach.class); validEntityBeforeSave(update); return baseMapper.updateById(update) > 0; } /** * ä¿ååçæ°æ®æ ¡éª */ private void validEntityBeforeSave(KnowledgeAttach entity) { //TODO åä¸äºæ°æ®æ ¡éª,å¦å¯ä¸çº¦æ } /** * æ¹éå é¤ç¥è¯åºéä»¶ */ @Override public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) { if (isValid) { //TODO åä¸äºä¸å¡ä¸çæ ¡éª,夿æ¯å¦éè¦æ ¡éª } return baseMapper.deleteBatchIds(ids) > 0; } /** * æ¥è¯¢ç¥è¯åºéä»¶å表 */ @Override public List<KnowledgeAttachVo> queryList(KnowledgeAttachBo bo) { LambdaQueryWrapper<KnowledgeAttach> lqw = buildQueryWrapper(bo); return baseMapper.selectVoList(lqw); @Override public void removeKnowledgeAttach(String docId) { Map<String, Object> map = new HashMap<>(); map.put("doc_id", docId); List<KnowledgeAttachVo> knowledgeAttachVos = baseMapper.selectVoByMap(map); if (ObjectUtil.isNotEmpty(knowledgeAttachVos)) { Collection<Long> ossIds = knowledgeAttachVos.stream() .map(KnowledgeAttachVo::getOssId) .collect(Collectors.toList()); //å é¤oss ossService.deleteWithValidByIds(ossIds, false); //å é¤å¾çoss List<KnowledgeAttachPic> knowledgeAttachPics = picMapper.selectList( new LambdaQueryWrapper<KnowledgeAttachPic>() .in(KnowledgeAttachPic::getKid, knowledgeAttachVos.stream().map(KnowledgeAttachVo::getKid) .collect(Collectors.toList())) .in(KnowledgeAttachPic::getAid, knowledgeAttachVos.stream().map(KnowledgeAttachVo::getId) .collect(Collectors.toList())) ); if (ObjectUtil.isNotEmpty(knowledgeAttachPics)) { Collection<Long> tossIds = knowledgeAttachPics.stream() .map(KnowledgeAttachPic::getOssId) .collect(Collectors.toList()); ossService.deleteWithValidByIds(tossIds, false); List<Long> collect = knowledgeAttachPics.stream().map(KnowledgeAttachPic::getId) .collect(Collectors.toList()); picMapper.deleteByIds(collect); } } baseMapper.deleteByMap(map); fragmentMapper.deleteByMap(map); } private LambdaQueryWrapper<KnowledgeAttach> buildQueryWrapper(KnowledgeAttachBo bo) { Map<String, Object> params = bo.getParams(); LambdaQueryWrapper<KnowledgeAttach> lqw = Wrappers.lambdaQuery(); lqw.eq(StringUtils.isNotBlank(bo.getKid()), KnowledgeAttach::getKid, bo.getKid()); lqw.eq(StringUtils.isNotBlank(bo.getDocId()), KnowledgeAttach::getDocId, bo.getDocId()); lqw.like(StringUtils.isNotBlank(bo.getDocName()), KnowledgeAttach::getDocName, bo.getDocName()); lqw.eq(StringUtils.isNotBlank(bo.getDocType()), KnowledgeAttach::getDocType, bo.getDocType()); lqw.eq(StringUtils.isNotBlank(bo.getContent()), KnowledgeAttach::getContent, bo.getContent()); return lqw; } /** * æ°å¢ç¥è¯åºéä»¶ */ @Override public Boolean insertByBo(KnowledgeAttachBo bo) { KnowledgeAttach add = MapstructUtils.convert(bo, KnowledgeAttach.class); validEntityBeforeSave(add); boolean flag = baseMapper.insert(add) > 0; if (flag) { bo.setId(add.getId()); } return flag; } /** * ä¿®æ¹ç¥è¯åºéä»¶ */ @Override public Boolean updateByBo(KnowledgeAttachBo bo) { KnowledgeAttach update = MapstructUtils.convert(bo, KnowledgeAttach.class); validEntityBeforeSave(update); return baseMapper.updateById(update) > 0; } /** * ä¿ååçæ°æ®æ ¡éª */ private void validEntityBeforeSave(KnowledgeAttach entity){ //TODO åä¸äºæ°æ®æ ¡éª,å¦å¯ä¸çº¦æ } /** * æ¹éå é¤ç¥è¯åºéä»¶ */ @Override public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) { if(isValid){ //TODO åä¸äºä¸å¡ä¸çæ ¡éª,夿æ¯å¦éè¦æ ¡éª } return baseMapper.deleteBatchIds(ids) > 0; } @Override public void removeKnowledgeAttach(String docId) { Map<String,Object> map = new HashMap<>(); map.put("doc_id",docId); baseMapper.deleteByMap(map); fragmentMapper.deleteByMap(map); } @Override public String translationByFile(MultipartFile file, String targetLanguage) { @Override public String translationByFile(MultipartFile file, String targetLanguage) { /*String fileName = file.getOriginalFilename(); String docType = fileName.substring(fileName.lastIndexOf(".")+1); String content = ""; @@ -173,6 +209,6 @@ throw new BaseException("è°ç¨å¤§æ¨¡å失败ï¼è¯·æ£æ¥å¯é¥æ¯å¦æ£ç¡®ï¼"); } return chatCompletionResponse.getChoices().get(0).getMessage().getContent().toString();*/ return "æ¥å£å¼åä¸!"; } return "æ¥å£å¼åä¸!"; } } ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/service/impl/PdfImageExtractServiceImpl.java
@@ -4,6 +4,8 @@ import java.util.ArrayList; import java.util.List; import java.util.concurrent.TimeUnit; import lombok.AllArgsConstructor; import lombok.Data; import lombok.extern.slf4j.Slf4j; import okhttp3.MediaType; import okhttp3.MultipartBody; @@ -23,16 +25,19 @@ /** * PDFå¾çæåæå¡å®ç°ç±» */ @Service //@Service @Slf4j public class PdfImageExtractServiceImpl implements PdfImageExtractService { @Data @AllArgsConstructor //public class PdfImageExtractServiceImpl implements PdfImageExtractService { public class PdfImageExtractServiceImpl { @Value("${pdf.extract.service.url}") // @Value("${pdf.extract.service.url}") private String serviceUrl; @Value("${pdf.extract.ai-api.url}") // @Value("${pdf.extract.ai-api.url}") private String aiApiUrl; @Value("${pdf.extract.ai-api.key}") private String aiApiKey ; // @Value("${pdf.extract.ai-api.key}") private String aiApiKey; private final OkHttpClient client = new Builder() .connectTimeout(100, TimeUnit.SECONDS) @@ -43,7 +48,7 @@ private static final MediaType JSON = MediaType.parse("application/json; charset=utf-8"); @Override // @Override public byte[] extractImages(MultipartFile pdfFile, String imageFormat, boolean allowDuplicates) throws IOException { // æå»ºmultipartè¯·æ± @@ -77,7 +82,7 @@ * @return æä»¶å å®¹ç»æå表 * @throws IOException 妿APIè°ç¨è¿ç¨ä¸åçé误 */ @Override // @Override public List<PdfFileContentResult> dealFileContent(String[] unzip) throws IOException { List<PdfFileContentResult> results = new ArrayList<>(); int i = 0; @@ -110,6 +115,7 @@ // æ§è¡è¯·æ± try { log.info("=============call=" + ++i); Response response = client.newCall(request).execute(); log.info("=============response=" + response); if (!response.isSuccessful()) { @@ -126,11 +132,10 @@ throw new RuntimeException(e); } } return results; } @Override // @Override public List<PdfFileContentResult> extractImages(MultipartFile file) throws IOException { String format = "png"; boolean allowDuplicates = true; ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/service/impl/VectorStoreServiceImpl.java
@@ -97,7 +97,7 @@ for (int i = 0; i < chunkList.size(); i++) { Map<String, Object> dataSchema = new HashMap<>(); dataSchema.put("kid", storeEmbeddingBo.getKid()); dataSchema.put("docId", storeEmbeddingBo.getKid()); dataSchema.put("docId", storeEmbeddingBo.getDocId()); dataSchema.put("fid", storeEmbeddingBo.getFids().get(i)); Embedding embedding = embeddingModel.embed(chunkList.get(i)).content(); TextSegment segment = TextSegment.from(chunkList.get(i)); ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/utils/ZipUtils.java
@@ -11,6 +11,8 @@ import java.util.List; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; import org.springframework.mock.web.MockMultipartFile; import org.springframework.web.multipart.MultipartFile; /** * ZIPæä»¶å¤çå·¥å ·ç±» @@ -92,4 +94,90 @@ } return base64Contents.toArray(new String[0]); } /** * è§£åZIPæä»¶å¹¶è¿åMultipartFileæ°ç» * * @param zipData ZIPæä»¶çåèæ°ç» * @return MultipartFileæ°ç» * @throws IOException å¦æè§£åè¿ç¨ä¸åçé误 */ public static MultipartFile[] unzipToMultipartFiles(byte[] zipData) throws IOException { List<MultipartFile> multipartFiles = new ArrayList<>(); try (ByteArrayInputStream bis = new ByteArrayInputStream(zipData); ZipInputStream zis = new ZipInputStream(bis)) { ZipEntry zipEntry; while ((zipEntry = zis.getNextEntry()) != null) { if (!zipEntry.isDirectory()) { // 读åæä»¶å 容å°å å ByteArrayOutputStream baos = new ByteArrayOutputStream(); byte[] buffer = new byte[4096]; int read; while ((read = zis.read(buffer)) != -1) { baos.write(buffer, 0, read); } // å建MultipartFile对象 String fileName = zipEntry.getName(); byte[] content = baos.toByteArray(); String contentType = determineContentType(fileName); MultipartFile multipartFile = new MockMultipartFile( fileName, // æä»¶å fileName, // åå§æä»¶å contentType, // å 容类å content // æä»¶å 容 ); multipartFiles.add(multipartFile); } zis.closeEntry(); } } return multipartFiles.toArray(new MultipartFile[0]); } /** * æ ¹æ®æä»¶åç¡®å®å 容类å * * @param fileName æä»¶å * @return å 容类å */ private static String determineContentType(String fileName) { String extension = ""; int i = fileName.lastIndexOf('.'); if (i > 0) { extension = fileName.substring(i + 1).toLowerCase(); } switch (extension) { case "txt": return "text/plain"; case "html": case "htm": return "text/html"; case "pdf": return "application/pdf"; case "jpg": case "jpeg": return "image/jpeg"; case "png": return "image/png"; case "gif": return "image/gif"; case "doc": case "docx": return "application/msword"; case "xls": case "xlsx": return "application/vnd.ms-excel"; case "xml": return "application/xml"; case "json": return "application/json"; default: return "application/octet-stream"; } } } ruoyi-modules-api/ruoyi-system-api/pom.xml
@@ -17,5 +17,11 @@ <maven.compiler.target>17</maven.compiler.target> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> </dependency> </dependencies> </project> ruoyi-modules-api/ruoyi-system-api/src/main/java/org/ruoyi/system/service/ISysLogininforService.java
@@ -1,5 +1,6 @@ package org.ruoyi.system.service; import org.ruoyi.common.log.event.LogininforEvent; import org.ruoyi.core.page.PageQuery; import org.ruoyi.core.page.TableDataInfo; import org.ruoyi.system.domain.bo.SysLogininforBo; @@ -44,4 +45,6 @@ * æ¸ ç©ºç³»ç»ç»å½æ¥å¿ */ void cleanLogininfor(); void recordLogininfor(LogininforEvent logininforEvent); } ruoyi-modules-api/ruoyi-system-api/src/main/java/org/ruoyi/system/service/ISysOperLogService.java
@@ -1,5 +1,6 @@ package org.ruoyi.system.service; import org.ruoyi.common.log.event.OperLogEvent; import org.ruoyi.core.page.PageQuery; import org.ruoyi.core.page.TableDataInfo; import org.ruoyi.system.domain.bo.SysOperLogBo; @@ -51,4 +52,5 @@ * æ¸ ç©ºæä½æ¥å¿ */ void cleanOperLog(); void recordOper(OperLogEvent operLogEvent); } ruoyi-modules-api/ruoyi-system-api/src/main/java/org/ruoyi/system/service/ISysOssService.java
@@ -18,16 +18,20 @@ */ public interface ISysOssService { TableDataInfo<SysOssVo> queryPageList(SysOssBo sysOss, PageQuery pageQuery); TableDataInfo<SysOssVo> queryPageList(SysOssBo sysOss, PageQuery pageQuery); List<SysOssVo> listByIds(Collection<Long> ossIds); List<SysOssVo> listByIds(Collection<Long> ossIds); SysOssVo getById(Long ossId); SysOssVo getById(Long ossId); SysOssVo upload(MultipartFile file); SysOssVo upload(MultipartFile file); void download(Long ossId, HttpServletResponse response) throws IOException; void download(Long ossId, HttpServletResponse response) throws IOException; Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid); MultipartFile downloadByFile(Long ossId) throws IOException; String downloadByByte(Long ossId) throws IOException; Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid); } ruoyi-modules-api/ruoyi-system-api/src/main/java/org/ruoyi/system/service/impl/SysLogininforServiceImpl.java
@@ -48,6 +48,7 @@ */ @Async @EventListener @Override public void recordLogininfor(LogininforEvent logininforEvent) { HttpServletRequest request = logininforEvent.getRequest(); final UserAgent userAgent = UserAgentUtil.parse(request.getHeader("User-Agent")); ruoyi-modules-api/ruoyi-system-api/src/main/java/org/ruoyi/system/service/impl/SysOperLogServiceImpl.java
@@ -42,6 +42,7 @@ */ @Async @EventListener @Override public void recordOper(OperLogEvent operLogEvent) { SysOperLogBo operLog = MapstructUtils.convert(operLogEvent, SysOperLogBo.class); // è¿ç¨æ¥è¯¢æä½å°ç¹ ruoyi-modules-api/ruoyi-system-api/src/main/java/org/ruoyi/system/service/impl/SysOssServiceImpl.java
@@ -7,6 +7,7 @@ import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import jakarta.servlet.http.HttpServletResponse; import java.util.Base64; import lombok.RequiredArgsConstructor; import org.ruoyi.common.core.constant.CacheNames; import org.ruoyi.common.core.exception.ServiceException; @@ -29,6 +30,7 @@ import org.ruoyi.system.service.ISysOssService; import org.springframework.cache.annotation.Cacheable; import org.springframework.http.MediaType; import org.springframework.mock.web.MockMultipartFile; import org.springframework.stereotype.Service; import org.springframework.web.multipart.MultipartFile; @@ -48,127 +50,167 @@ @Service public class SysOssServiceImpl implements ISysOssService, OssService { private final SysOssMapper baseMapper; private final SysOssMapper baseMapper; @Override public TableDataInfo<SysOssVo> queryPageList(SysOssBo bo, PageQuery pageQuery) { LambdaQueryWrapper<SysOss> lqw = buildQueryWrapper(bo); Page<SysOssVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw); List<SysOssVo> filterResult = StreamUtils.toList(result.getRecords(), this::matchingUrl); result.setRecords(filterResult); return TableDataInfo.build(result); @Override public TableDataInfo<SysOssVo> queryPageList(SysOssBo bo, PageQuery pageQuery) { LambdaQueryWrapper<SysOss> lqw = buildQueryWrapper(bo); Page<SysOssVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw); List<SysOssVo> filterResult = StreamUtils.toList(result.getRecords(), this::matchingUrl); result.setRecords(filterResult); return TableDataInfo.build(result); } @Override public List<SysOssVo> listByIds(Collection<Long> ossIds) { List<SysOssVo> list = new ArrayList<>(); for (Long id : ossIds) { SysOssVo vo = SpringUtils.getAopProxy(this).getById(id); if (ObjectUtil.isNotNull(vo)) { list.add(this.matchingUrl(vo)); } } return list; } @Override public String selectUrlByIds(String ossIds) { List<String> list = new ArrayList<>(); for (Long id : StringUtils.splitTo(ossIds, Convert::toLong)) { SysOssVo vo = SpringUtils.getAopProxy(this).getById(id); if (ObjectUtil.isNotNull(vo)) { list.add(this.matchingUrl(vo).getUrl()); } } return String.join(StringUtils.SEPARATOR, list); } private LambdaQueryWrapper<SysOss> buildQueryWrapper(SysOssBo bo) { Map<String, Object> params = bo.getParams(); LambdaQueryWrapper<SysOss> lqw = Wrappers.lambdaQuery(); lqw.like(StringUtils.isNotBlank(bo.getFileName()), SysOss::getFileName, bo.getFileName()); lqw.like(StringUtils.isNotBlank(bo.getOriginalName()), SysOss::getOriginalName, bo.getOriginalName()); lqw.eq(StringUtils.isNotBlank(bo.getFileSuffix()), SysOss::getFileSuffix, bo.getFileSuffix()); lqw.eq(StringUtils.isNotBlank(bo.getUrl()), SysOss::getUrl, bo.getUrl()); lqw.between(params.get("beginCreateTime") != null && params.get("endCreateTime") != null, SysOss::getCreateTime, params.get("beginCreateTime"), params.get("endCreateTime")); lqw.eq(ObjectUtil.isNotNull(bo.getCreateBy()), SysOss::getCreateBy, bo.getCreateBy()); lqw.eq(StringUtils.isNotBlank(bo.getService()), SysOss::getService, bo.getService()); return lqw; } @Cacheable(cacheNames = CacheNames.SYS_OSS, key = "#ossId") @Override public SysOssVo getById(Long ossId) { return baseMapper.selectVoById(ossId); } @Override public void download(Long ossId, HttpServletResponse response) throws IOException { SysOssVo sysOss = SpringUtils.getAopProxy(this).getById(ossId); if (ObjectUtil.isNull(sysOss)) { throw new ServiceException("æä»¶æ°æ®ä¸åå¨!"); } FileUtils.setAttachmentResponseHeader(response, sysOss.getOriginalName()); response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE + "; charset=UTF-8"); OssClient storage = OssFactory.instance(); try (InputStream inputStream = storage.getObjectContent(sysOss.getUrl())) { int available = inputStream.available(); IoUtil.copy(inputStream, response.getOutputStream(), available); response.setContentLength(available); } catch (Exception e) { throw new ServiceException(e.getMessage()); } } @Override public String downloadByByte(Long ossId) throws IOException { SysOssVo sysOss = SpringUtils.getAopProxy(this).getById(ossId); if (ObjectUtil.isNull(sysOss)) { throw new ServiceException("æä»¶æ°æ®ä¸åå¨!"); } @Override public List<SysOssVo> listByIds(Collection<Long> ossIds) { List<SysOssVo> list = new ArrayList<>(); for (Long id : ossIds) { SysOssVo vo = SpringUtils.getAopProxy(this).getById(id); if (ObjectUtil.isNotNull(vo)) { list.add(this.matchingUrl(vo)); } } return list; OssClient storage = OssFactory.instance(); try (InputStream inputStream = storage.getObjectContent(sysOss.getUrl())) { // 读åè¾å ¥æµä¸çææåè byte[] bytes = IoUtil.readBytes(inputStream); // å°åèæ°ç»è½¬æ¢ä¸ºBase64ç¼ç çå符串 return Base64.getEncoder().encodeToString(bytes); } catch (Exception e) { throw new ServiceException(e.getMessage()); } } @Override public MultipartFile downloadByFile(Long ossId) throws IOException { SysOssVo sysOss = SpringUtils.getAopProxy(this).getById(ossId); if (ObjectUtil.isNull(sysOss)) { throw new ServiceException("æä»¶æ°æ®ä¸åå¨!"); } @Override public String selectUrlByIds(String ossIds) { List<String> list = new ArrayList<>(); for (Long id : StringUtils.splitTo(ossIds, Convert::toLong)) { SysOssVo vo = SpringUtils.getAopProxy(this).getById(id); if (ObjectUtil.isNotNull(vo)) { list.add(this.matchingUrl(vo).getUrl()); } } return String.join(StringUtils.SEPARATOR, list); OssClient storage = OssFactory.instance(); try (InputStream inputStream = storage.getObjectContent(sysOss.getUrl())) { byte[] content = IoUtil.readBytes(inputStream); return new MockMultipartFile( sysOss.getFileName(), sysOss.getOriginalName(), MediaType.APPLICATION_OCTET_STREAM_VALUE, content ); } catch (Exception e) { throw new ServiceException(e.getMessage()); } } private LambdaQueryWrapper<SysOss> buildQueryWrapper(SysOssBo bo) { Map<String, Object> params = bo.getParams(); LambdaQueryWrapper<SysOss> lqw = Wrappers.lambdaQuery(); lqw.like(StringUtils.isNotBlank(bo.getFileName()), SysOss::getFileName, bo.getFileName()); lqw.like(StringUtils.isNotBlank(bo.getOriginalName()), SysOss::getOriginalName, bo.getOriginalName()); lqw.eq(StringUtils.isNotBlank(bo.getFileSuffix()), SysOss::getFileSuffix, bo.getFileSuffix()); lqw.eq(StringUtils.isNotBlank(bo.getUrl()), SysOss::getUrl, bo.getUrl()); lqw.between(params.get("beginCreateTime") != null && params.get("endCreateTime") != null, SysOss::getCreateTime, params.get("beginCreateTime"), params.get("endCreateTime")); lqw.eq(ObjectUtil.isNotNull(bo.getCreateBy()), SysOss::getCreateBy, bo.getCreateBy()); lqw.eq(StringUtils.isNotBlank(bo.getService()), SysOss::getService, bo.getService()); return lqw; @Override public SysOssVo upload(MultipartFile file) { String originalfileName = file.getOriginalFilename(); String suffix = StringUtils.substring(originalfileName, originalfileName.lastIndexOf("."), originalfileName.length()); OssClient storage = OssFactory.instance(); UploadResult uploadResult; try { uploadResult = storage.uploadSuffix(file.getBytes(), suffix, file.getContentType()); } catch (IOException e) { throw new ServiceException(e.getMessage()); } // ä¿åæä»¶ä¿¡æ¯ SysOss oss = new SysOss(); oss.setUrl(uploadResult.getUrl()); oss.setFileSuffix(suffix); oss.setFileName(uploadResult.getFilename()); oss.setOriginalName(originalfileName); oss.setService(storage.getConfigKey()); baseMapper.insert(oss); SysOssVo sysOssVo = MapstructUtils.convert(oss, SysOssVo.class); return this.matchingUrl(sysOssVo); } @Cacheable(cacheNames = CacheNames.SYS_OSS, key = "#ossId") @Override public SysOssVo getById(Long ossId) { return baseMapper.selectVoById(ossId); @Override public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) { if (isValid) { // åä¸äºä¸å¡ä¸çæ ¡éª,夿æ¯å¦éè¦æ ¡éª } List<SysOss> list = baseMapper.selectBatchIds(ids); for (SysOss sysOss : list) { OssClient storage = OssFactory.instance(sysOss.getService()); storage.delete(sysOss.getUrl()); } return baseMapper.deleteBatchIds(ids) > 0; } @Override public void download(Long ossId, HttpServletResponse response) throws IOException { SysOssVo sysOss = SpringUtils.getAopProxy(this).getById(ossId); if (ObjectUtil.isNull(sysOss)) { throw new ServiceException("æä»¶æ°æ®ä¸åå¨!"); } FileUtils.setAttachmentResponseHeader(response, sysOss.getOriginalName()); response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE + "; charset=UTF-8"); OssClient storage = OssFactory.instance(); try(InputStream inputStream = storage.getObjectContent(sysOss.getUrl())) { int available = inputStream.available(); IoUtil.copy(inputStream, response.getOutputStream(), available); response.setContentLength(available); } catch (Exception e) { throw new ServiceException(e.getMessage()); } /** * å¹é Url * * @param oss OSS对象 * @return oss å¹é UrlçOSS对象 */ private SysOssVo matchingUrl(SysOssVo oss) { OssClient storage = OssFactory.instance(oss.getService()); // ä» ä¿®æ¹æ¡¶ç±»å为 private çURLï¼ä¸´æ¶URLæ¶é¿ä¸º120s if (AccessPolicyType.PRIVATE == storage.getAccessPolicy()) { oss.setUrl(storage.getPrivateUrl(oss.getFileName(), 120)); } @Override public SysOssVo upload(MultipartFile file) { String originalfileName = file.getOriginalFilename(); String suffix = StringUtils.substring(originalfileName, originalfileName.lastIndexOf("."), originalfileName.length()); OssClient storage = OssFactory.instance(); UploadResult uploadResult; try { uploadResult = storage.uploadSuffix(file.getBytes(), suffix, file.getContentType()); } catch (IOException e) { throw new ServiceException(e.getMessage()); } // ä¿åæä»¶ä¿¡æ¯ SysOss oss = new SysOss(); oss.setUrl(uploadResult.getUrl()); oss.setFileSuffix(suffix); oss.setFileName(uploadResult.getFilename()); oss.setOriginalName(originalfileName); oss.setService(storage.getConfigKey()); baseMapper.insert(oss); SysOssVo sysOssVo = MapstructUtils.convert(oss, SysOssVo.class); return this.matchingUrl(sysOssVo); } @Override public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) { if (isValid) { // åä¸äºä¸å¡ä¸çæ ¡éª,夿æ¯å¦éè¦æ ¡éª } List<SysOss> list = baseMapper.selectBatchIds(ids); for (SysOss sysOss : list) { OssClient storage = OssFactory.instance(sysOss.getService()); storage.delete(sysOss.getUrl()); } return baseMapper.deleteBatchIds(ids) > 0; } /** * å¹é Url * * @param oss OSS对象 * @return oss å¹é UrlçOSS对象 */ private SysOssVo matchingUrl(SysOssVo oss) { OssClient storage = OssFactory.instance(oss.getService()); // ä» ä¿®æ¹æ¡¶ç±»å为 private çURLï¼ä¸´æ¶URLæ¶é¿ä¸º120s if (AccessPolicyType.PRIVATE == storage.getAccessPolicy()) { oss.setUrl(storage.getPrivateUrl(oss.getFileName(), 120)); } return oss; } return oss; } } ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/controller/knowledge/KnowledgeController.java
@@ -52,7 +52,7 @@ private final IKnowledgeFragmentService fragmentService; private final PdfImageExtractService pdfImageExtractService; // private final PdfImageExtractService pdfImageExtractService; /** * æ ¹æ®ç¨æ·ä¿¡æ¯æ¥è¯¢æ¬å°ç¥è¯åº @@ -170,11 +170,11 @@ * @param file PDFæä»¶ * @return æä»¶åç§°åå¾çå 容 */ @PostMapping("/extract-images") @Operation(summary = "æåPDFä¸çå¾çå¹¶è°ç¨å¤§æ¨¡å,è¯å«å¾çå 容并è¿å", description = "æåPDFä¸çå¾çå¹¶è°ç¨gpt-4o-mini,è¯å«å¾çå 容并è¿å") public R<List<PdfFileContentResult>> extractImages( @RequestPart("file") MultipartFile file ) throws IOException { return R.ok(pdfImageExtractService.extractImages(file)); } // @PostMapping("/extract-images") // @Operation(summary = "æåPDFä¸çå¾çå¹¶è°ç¨å¤§æ¨¡å,è¯å«å¾çå 容并è¿å", description = "æåPDFä¸çå¾çå¹¶è°ç¨gpt-4o-mini,è¯å«å¾çå 容并è¿å") // public R<List<PdfFileContentResult>> extractImages( // @RequestPart("file") MultipartFile file // ) throws IOException { // return R.ok(pdfImageExtractService.extractImages(file)); // } } ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/knowledge/DealFileService.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,390 @@ package org.ruoyi.chat.service.knowledge; import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.RandomUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import java.util.Collection; import java.util.Date; import java.util.List; import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; import org.ruoyi.chain.loader.ResourceLoaderFactory; import org.ruoyi.constant.DealStatus; import org.ruoyi.domain.KnowledgeAttach; import org.ruoyi.domain.KnowledgeAttachPic; import org.ruoyi.domain.KnowledgeFragment; import org.ruoyi.domain.KnowledgeInfo; import org.ruoyi.domain.PdfFileContentResult; import org.ruoyi.domain.bo.StoreEmbeddingBo; import org.ruoyi.domain.vo.ChatModelVo; import org.ruoyi.domain.vo.KnowledgeAttachVo; import org.ruoyi.domain.vo.KnowledgeInfoVo; import org.ruoyi.mapper.KnowledgeAttachMapper; import org.ruoyi.mapper.KnowledgeAttachPicMapper; import org.ruoyi.mapper.KnowledgeFragmentMapper; import org.ruoyi.mapper.KnowledgeInfoMapper; import org.ruoyi.service.IChatModelService; import org.ruoyi.service.VectorStoreService; import org.ruoyi.service.impl.PdfImageExtractServiceImpl; import org.ruoyi.system.domain.vo.SysOssVo; import org.ruoyi.system.service.ISysOssService; import org.ruoyi.utils.ZipUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; import org.springframework.web.multipart.MultipartFile; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; /** * @Description: * @Date: 2025/5/15 ä¸å4:29 */ @Service @RequiredArgsConstructor public class DealFileService { private static final Logger log = LoggerFactory.getLogger(DealFileService.class); private final KnowledgeInfoMapper baseMapper; private final VectorStoreService vectorStoreService; private final ResourceLoaderFactory resourceLoaderFactory; private final KnowledgeFragmentMapper fragmentMapper; private final KnowledgeAttachMapper attachMapper; private final IChatModelService chatModelService; private final ISysOssService ossService; // private final PdfImageExtractService pdfImageExtractService; private final KnowledgeAttachPicMapper picMapper; @Value("${pdf.extract.service.url}") private String serviceUrl; @Value("${pdf.extract.ai-api.url}") private String aiApiUrl; @Value("${pdf.extract.ai-api.key}") private String aiApiKey; @Async public void dealVectorStatus(KnowledgeAttach attachItem) throws Exception { try { //é宿°æ® æ´æ¹VectorStatus å°è¿è¡ä¸ if (attachMapper.update(new LambdaUpdateWrapper<KnowledgeAttach>() .set(KnowledgeAttach::getVectorStatus, DealStatus.STATUS_20) .eq(KnowledgeAttach::getPicStatus, DealStatus.STATUS_30) .eq(KnowledgeAttach::getPicAnysStatus, DealStatus.STATUS_30) .eq(KnowledgeAttach::getVectorStatus, DealStatus.STATUS_10) .eq(KnowledgeAttach::getId, attachItem.getId()) ) == 0) { return; } List<KnowledgeFragment> knowledgeFragments = fragmentMapper.selectList( new LambdaQueryWrapper<KnowledgeFragment>() .eq(KnowledgeFragment::getKid, attachItem.getKid()) .eq(KnowledgeFragment::getDocId, attachItem.getDocId()) ); if (ObjectUtil.isEmpty(knowledgeFragments)) { throw new Exception("æä»¶æ®µè½ä¸ºç©º"); } List<String> fids = knowledgeFragments.stream() .map(KnowledgeFragment::getFid) .collect(Collectors.toList()); if (ObjectUtil.isEmpty(fids)) { throw new Exception("fids 为空"); } List<String> chunkList = knowledgeFragments.stream() .map(KnowledgeFragment::getContent) .collect(Collectors.toList()); if (ObjectUtil.isEmpty(chunkList)) { throw new Exception("chunkList 为空"); } // éè¿kidæ¥è¯¢ç¥è¯åºä¿¡æ¯ KnowledgeInfoVo knowledgeInfoVo = baseMapper.selectVoOne(Wrappers.<KnowledgeInfo>lambdaQuery() .eq(KnowledgeInfo::getId, attachItem.getKid())); // éè¿å鿍¡åæ¥è¯¢æ¨¡åä¿¡æ¯ ChatModelVo chatModelVo = chatModelService.selectModelByName( knowledgeInfoVo.getEmbeddingModelName()); StoreEmbeddingBo storeEmbeddingBo = new StoreEmbeddingBo(); storeEmbeddingBo.setKid(attachItem.getKid()); storeEmbeddingBo.setDocId(attachItem.getDocId()); storeEmbeddingBo.setFids(fids); storeEmbeddingBo.setChunkList(chunkList); storeEmbeddingBo.setVectorModelName(knowledgeInfoVo.getVectorModelName()); storeEmbeddingBo.setEmbeddingModelName(knowledgeInfoVo.getEmbeddingModelName()); storeEmbeddingBo.setApiKey(chatModelVo.getApiKey()); storeEmbeddingBo.setBaseUrl(chatModelVo.getApiHost()); vectorStoreService.storeEmbeddings(storeEmbeddingBo); //设置å¤ç宿 attachMapper.update(new LambdaUpdateWrapper<KnowledgeAttach>() .set(KnowledgeAttach::getVectorStatus, DealStatus.STATUS_30) .eq(KnowledgeAttach::getPicStatus, DealStatus.STATUS_30) .eq(KnowledgeAttach::getPicAnysStatus, DealStatus.STATUS_30) .eq(KnowledgeAttach::getVectorStatus, DealStatus.STATUS_20) .eq(KnowledgeAttach::getId, attachItem.getId())); } catch (Exception e) { //设置å¤ç失败 attachMapper.update(new LambdaUpdateWrapper<KnowledgeAttach>() .set(KnowledgeAttach::getVectorStatus, DealStatus.STATUS_40) .set(KnowledgeAttach::getRemark, attachItem.getRemark() + e.getMessage()) .eq(KnowledgeAttach::getPicStatus, DealStatus.STATUS_30) .eq(KnowledgeAttach::getPicAnysStatus, DealStatus.STATUS_30) .eq(KnowledgeAttach::getVectorStatus, DealStatus.STATUS_20) .eq(KnowledgeAttach::getId, attachItem.getId())); throw new RuntimeException(e); } } @Async public void dealPicStatus(KnowledgeAttach attachItem) throws Exception { try { //é宿°æ® æ´æ¹picStatus å°è¿è¡ä¸ if (attachMapper.update(new LambdaUpdateWrapper<KnowledgeAttach>() .set(KnowledgeAttach::getPicStatus, DealStatus.STATUS_20) .eq(KnowledgeAttach::getPicStatus, DealStatus.STATUS_10) .eq(KnowledgeAttach::getPicAnysStatus, DealStatus.STATUS_10) .eq(KnowledgeAttach::getVectorStatus, DealStatus.STATUS_10) .eq(KnowledgeAttach::getId, attachItem.getId()) ) == 0) { return; } //è·åéä»¶ if (ObjectUtil.isEmpty(attachItem.getOssId())) { log.error("==========OssId 为空ï¼attachItem={}", attachItem); throw new Exception("OssId 为空"); } //è·åossæä»¶ MultipartFile multipartFile = ossService.downloadByFile(attachItem.getOssId()); //æè§£åºå¾çZIP PdfImageExtractServiceImpl pdfImageExtractService = new PdfImageExtractServiceImpl(serviceUrl, aiApiUrl, aiApiKey); byte[] pngs = pdfImageExtractService.extractImages(multipartFile, "png", true); //è§£åzipï¼å¾å°å¾çæä»¶ MultipartFile[] multipartFiles = ZipUtils.unzipToMultipartFiles(pngs); //ä¸ä¼ æä»¶å°OSSï¼åå ¥è¡¨ for (MultipartFile file : multipartFiles) { //å æ¥æ¾æ¯å¦æç¸åå¾çåç§°ï¼å åå é¤ List<KnowledgeAttachPic> knowledgeAttachPics = picMapper.selectList( new LambdaQueryWrapper<KnowledgeAttachPic>() .eq(KnowledgeAttachPic::getKid, attachItem.getKid()) .eq(KnowledgeAttachPic::getAid, attachItem.getId()) .eq(KnowledgeAttachPic::getDocName, file.getOriginalFilename()) ); if (ObjectUtil.isNotEmpty(knowledgeAttachPics)) { Collection<Long> ossIds = knowledgeAttachPics.stream() .map(KnowledgeAttachPic::getOssId) .collect(Collectors.toList()); ossService.deleteWithValidByIds(ossIds, false); List<Long> collect = knowledgeAttachPics.stream().map(KnowledgeAttachPic::getId) .collect(Collectors.toList()); picMapper.deleteByIds(collect); } SysOssVo upload = ossService.upload(file); KnowledgeAttachPic entity = new KnowledgeAttachPic(); entity.setKid(attachItem.getKid()); entity.setAid(String.valueOf(attachItem.getId())); entity.setDocName(file.getOriginalFilename()); entity.setDocType( file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf(".") + 1)); entity.setOssId(upload.getOssId()); int[] ints = extractPageNumbers(file.getOriginalFilename()); if (ObjectUtil.isNotEmpty(ints)) { assert ints != null; if (ints.length == 2) { entity.setPageNum(ints[0]); entity.setIndexNum(ints[1]); } } picMapper.insert(entity); } //设置å¤ç宿 attachMapper.update(new LambdaUpdateWrapper<KnowledgeAttach>() .set(KnowledgeAttach::getPicStatus, DealStatus.STATUS_30) .eq(KnowledgeAttach::getPicStatus, DealStatus.STATUS_20) .eq(KnowledgeAttach::getPicAnysStatus, DealStatus.STATUS_10) .eq(KnowledgeAttach::getVectorStatus, DealStatus.STATUS_10) .eq(KnowledgeAttach::getId, attachItem.getId())); } catch (Exception e) { //设置å¤ç失败 attachMapper.update(new LambdaUpdateWrapper<KnowledgeAttach>() .set(KnowledgeAttach::getVectorStatus, DealStatus.STATUS_40) .set(KnowledgeAttach::getRemark, attachItem.getRemark() + e.getMessage()) .eq(KnowledgeAttach::getPicStatus, DealStatus.STATUS_20) .eq(KnowledgeAttach::getPicAnysStatus, DealStatus.STATUS_10) .eq(KnowledgeAttach::getVectorStatus, DealStatus.STATUS_10) .eq(KnowledgeAttach::getId, attachItem.getId())); throw new RuntimeException(e); } } @Async public void dealPicAnysStatus(KnowledgeAttachPic picItem) throws Exception { try { //é宿°æ® æ´æ¹ getPicAnysStatus å°è¿è¡ä¸ if (picMapper.update(new LambdaUpdateWrapper<KnowledgeAttachPic>() .set(KnowledgeAttachPic::getPicAnysStatus, DealStatus.STATUS_20) .eq(KnowledgeAttachPic::getPicAnysStatus, DealStatus.STATUS_10) .eq(KnowledgeAttachPic::getId, picItem.getId()) ) == 0) { return; } SysOssVo ossVo = ossService.getById(picItem.getOssId()); if (ObjectUtil.isNotEmpty(ossVo)) { String fileStr = ossService.downloadByByte(picItem.getOssId()); //è°ç¨ç¬¬ä¸æ¹ åæå¾çå 容 PdfImageExtractServiceImpl pdfImageExtractService = new PdfImageExtractServiceImpl( serviceUrl, aiApiUrl, aiApiKey); List<PdfFileContentResult> pdfFileContentResults = pdfImageExtractService.dealFileContent( new String[]{fileStr}); if (ObjectUtil.isNotEmpty(pdfFileContentResults)) { for (PdfFileContentResult resultItem : pdfFileContentResults) { //å¾çè§£æå 容ååå°pic表 picMapper.update(new LambdaUpdateWrapper<KnowledgeAttachPic>() .set(KnowledgeAttachPic::getContent, parseContent(resultItem.getContent())) .set(KnowledgeAttachPic::getPicAnysStatus, DealStatus.STATUS_30) .eq(KnowledgeAttachPic::getId, picItem.getId())); //å°å¾çè§£æå 容 åå ¥æ®µè½è¡¨ fragment KnowledgeAttachVo knowledgeAttachVo = attachMapper.selectVoById(picItem.getAid()); if (ObjectUtil.isNotEmpty(knowledgeAttachVo)) { String fid = RandomUtil.randomString(10); KnowledgeFragment knowledgeFragment = new KnowledgeFragment(); knowledgeFragment.setKid(knowledgeAttachVo.getKid()); knowledgeFragment.setDocId(knowledgeAttachVo.getDocId()); knowledgeFragment.setFid(fid); knowledgeFragment.setIdx(0); knowledgeFragment.setContent(parseContent(resultItem.getContent())); knowledgeFragment.setCreateTime(new Date()); fragmentMapper.insert(knowledgeFragment); //æ´æ°attach表ï¼éè¦ææå¾çé½å¤ç宿¯ // æ¥è¯¢é30ç¶æï¼å®æç¶æï¼çè®°å½æ°é long nonStatus30Count = picMapper.selectCount( new LambdaQueryWrapper<KnowledgeAttachPic>() .ne(KnowledgeAttachPic::getPicAnysStatus, DealStatus.STATUS_30) .eq(KnowledgeAttachPic::getAid, picItem.getAid()) ); if (nonStatus30Count == 0) { // æ§è¡è¡¨æ´æ°æä½ attachMapper.update(new LambdaUpdateWrapper<KnowledgeAttach>() .set(KnowledgeAttach::getPicAnysStatus, DealStatus.STATUS_30) .eq(KnowledgeAttach::getPicStatus, DealStatus.STATUS_30) .eq(KnowledgeAttach::getPicAnysStatus, DealStatus.STATUS_10) .eq(KnowledgeAttach::getVectorStatus, DealStatus.STATUS_10) .eq(KnowledgeAttach::getId, picItem.getAid())); } } } } } } catch (Exception e) { //失败 picMapper.update(new LambdaUpdateWrapper<KnowledgeAttachPic>() .set(KnowledgeAttachPic::getPicAnysStatus, DealStatus.STATUS_40) .set(KnowledgeAttachPic::getRemark, picItem.getRemark() + e.getMessage()) .eq(KnowledgeAttachPic::getPicAnysStatus, DealStatus.STATUS_20) .eq(KnowledgeAttachPic::getId, picItem.getId())); throw new RuntimeException(e); } finally { //å°å¾çåæå¤±è´¥çæ°æ® éæ°è®¾ç½®ç¶æ picMapper.update(new LambdaUpdateWrapper<KnowledgeAttachPic>() .set(KnowledgeAttachPic::getPicAnysStatus, DealStatus.STATUS_10) .eq(KnowledgeAttachPic::getPicAnysStatus, DealStatus.STATUS_40)); } } /** * 仿件å䏿åpageåé¢ç两个æ°å * * @param fileName æä»¶å * @return å å«ä¸¤ä¸ªæ°åçæ°ç»ï¼å¦ææªæ¾å°åè¿ånull */ public static int[] extractPageNumbers(String fileName) { // æ¥æ¾"page_"çä½ç½® int pageIndex = fileName.indexOf("page_"); if (pageIndex == -1) { return null; } // ä»"page_"åå¼å§æªå String afterPage = fileName.substring(pageIndex + 5); // æä¸å线åå² String[] parts = afterPage.split("_"); if (parts.length >= 2) { try { // æå两个æ°å int firstNumber = Integer.parseInt(parts[0]); // 对äºç¬¬äºä¸ªæ°åï¼éè¦å»æå¯è½çæä»¶æ©å±å String secondPart = parts[1]; int dotIndex = secondPart.indexOf("."); if (dotIndex != -1) { secondPart = secondPart.substring(0, dotIndex); } int secondNumber = Integer.parseInt(secondPart); return new int[]{firstNumber, secondNumber}; } catch (NumberFormatException e) { return null; } } return null; } public static String parseContent(String jsonString) { try { // å建ObjectMapperå®ä¾ ObjectMapper objectMapper = new ObjectMapper(); // è§£æJSONå符串 JsonNode rootNode = objectMapper.readTree(jsonString); // è·åchoicesæ°ç»ç第ä¸ä¸ªå ç´ JsonNode choicesNode = rootNode.get("choices"); if (choicesNode != null && choicesNode.isArray() && choicesNode.size() > 0) { // è·å第ä¸ä¸ªchoice JsonNode firstChoice = choicesNode.get(0); // è·åmessageèç¹ JsonNode messageNode = firstChoice.get("message"); if (messageNode != null) { // è·åcontentåæ®µçå¼ JsonNode contentNode = messageNode.get("content"); if (contentNode != null) { return contentNode.asText(); } } } return "æ æ³æ¾å°contentå 容"; } catch (Exception e) { e.printStackTrace(); return "è§£æJSONæ¶åçé误: " + e.getMessage(); } } } ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/knowledge/KnowledgeInfoServiceImpl.java
@@ -1,43 +1,62 @@ package org.ruoyi.chat.service.knowledge; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.RandomUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.ruoyi.chain.loader.ResourceLoader; import org.ruoyi.chain.loader.ResourceLoaderFactory; import org.ruoyi.common.core.domain.model.LoginUser; import org.ruoyi.common.core.utils.MapstructUtils; import org.ruoyi.common.core.utils.StringUtils; import org.ruoyi.common.satoken.utils.LoginHelper; import org.ruoyi.constant.DealStatus; import org.ruoyi.constant.FileType; import org.ruoyi.core.page.PageQuery; import org.ruoyi.core.page.TableDataInfo; import org.ruoyi.domain.ChatModel; import org.ruoyi.domain.KnowledgeAttach; import org.ruoyi.domain.KnowledgeAttachPic; import org.ruoyi.domain.KnowledgeFragment; import org.ruoyi.domain.KnowledgeInfo; import org.ruoyi.domain.PdfFileContentResult; import org.ruoyi.domain.bo.KnowledgeInfoBo; import org.ruoyi.domain.bo.KnowledgeInfoUploadBo; import org.ruoyi.domain.bo.StoreEmbeddingBo; import org.ruoyi.domain.vo.ChatModelVo; import org.ruoyi.domain.vo.KnowledgeAttachVo; import org.ruoyi.domain.vo.KnowledgeInfoVo; import org.ruoyi.mapper.KnowledgeAttachMapper; import org.ruoyi.mapper.KnowledgeAttachPicMapper; import org.ruoyi.mapper.KnowledgeFragmentMapper; import org.ruoyi.mapper.KnowledgeInfoMapper; import org.ruoyi.service.IChatModelService; import org.ruoyi.service.PdfImageExtractService; import org.ruoyi.service.VectorStoreService; import org.ruoyi.service.IKnowledgeInfoService; import org.ruoyi.service.impl.PdfImageExtractServiceImpl; import org.ruoyi.system.domain.vo.SysOssVo; import org.ruoyi.utils.ZipUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.scheduling.annotation.Async; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.multipart.MultipartFile; import org.ruoyi.system.service.ISysOssService; import java.io.IOException; import java.util.*; /** * ç¥è¯åºServiceä¸å¡å±å¤ç @@ -49,216 +68,330 @@ @Service public class KnowledgeInfoServiceImpl implements IKnowledgeInfoService { private static final Logger log = LoggerFactory.getLogger(KnowledgeInfoServiceImpl.class); private final KnowledgeInfoMapper baseMapper; private static final Logger log = LoggerFactory.getLogger(KnowledgeInfoServiceImpl.class); private final KnowledgeInfoMapper baseMapper; private final VectorStoreService vectorStoreService; private final VectorStoreService vectorStoreService; private final ResourceLoaderFactory resourceLoaderFactory; private final ResourceLoaderFactory resourceLoaderFactory; private final KnowledgeFragmentMapper fragmentMapper; private final KnowledgeFragmentMapper fragmentMapper; private final KnowledgeAttachMapper attachMapper; private final KnowledgeAttachMapper attachMapper; private final IChatModelService chatModelService; private final IChatModelService chatModelService; /** * æ¥è¯¢ç¥è¯åº */ @Override public KnowledgeInfoVo queryById(Long id){ return baseMapper.selectVoById(id); private final ISysOssService ossService; // private final PdfImageExtractService pdfImageExtractService; private final KnowledgeAttachPicMapper picMapper; private final DealFileService dealFileService; @Value("${pdf.extract.service.url}") private String serviceUrl; @Value("${pdf.extract.ai-api.url}") private String aiApiUrl; @Value("${pdf.extract.ai-api.key}") private String aiApiKey; /** * æ¥è¯¢ç¥è¯åº */ @Override public KnowledgeInfoVo queryById(Long id) { return baseMapper.selectVoById(id); } /** * æ¥è¯¢ç¥è¯åºå表 */ @Override public TableDataInfo<KnowledgeInfoVo> queryPageList(KnowledgeInfoBo bo, PageQuery pageQuery) { LambdaQueryWrapper<KnowledgeInfo> lqw = buildQueryWrapper(bo); Page<KnowledgeInfoVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw); return TableDataInfo.build(result); } /** * æ¥è¯¢ç¥è¯åºå表 */ @Override public List<KnowledgeInfoVo> queryList(KnowledgeInfoBo bo) { LambdaQueryWrapper<KnowledgeInfo> lqw = buildQueryWrapper(bo); return baseMapper.selectVoList(lqw); } private LambdaQueryWrapper<KnowledgeInfo> buildQueryWrapper(KnowledgeInfoBo bo) { Map<String, Object> params = bo.getParams(); LambdaQueryWrapper<KnowledgeInfo> lqw = Wrappers.lambdaQuery(); lqw.eq(StringUtils.isNotBlank(bo.getKid()), KnowledgeInfo::getKid, bo.getKid()); lqw.eq(bo.getUid() != null, KnowledgeInfo::getUid, bo.getUid()); lqw.like(StringUtils.isNotBlank(bo.getKname()), KnowledgeInfo::getKname, bo.getKname()); lqw.eq(bo.getShare() != null, KnowledgeInfo::getShare, bo.getShare()); lqw.eq(StringUtils.isNotBlank(bo.getDescription()), KnowledgeInfo::getDescription, bo.getDescription()); lqw.eq(StringUtils.isNotBlank(bo.getKnowledgeSeparator()), KnowledgeInfo::getKnowledgeSeparator, bo.getKnowledgeSeparator()); lqw.eq(StringUtils.isNotBlank(bo.getQuestionSeparator()), KnowledgeInfo::getQuestionSeparator, bo.getQuestionSeparator()); lqw.eq(bo.getOverlapChar() != null, KnowledgeInfo::getOverlapChar, bo.getOverlapChar()); lqw.eq(bo.getRetrieveLimit() != null, KnowledgeInfo::getRetrieveLimit, bo.getRetrieveLimit()); lqw.eq(bo.getTextBlockSize() != null, KnowledgeInfo::getTextBlockSize, bo.getTextBlockSize()); return lqw; } /** * æ°å¢ç¥è¯åº */ @Override public Boolean insertByBo(KnowledgeInfoBo bo) { KnowledgeInfo add = MapstructUtils.convert(bo, KnowledgeInfo.class); validEntityBeforeSave(add); boolean flag = baseMapper.insert(add) > 0; if (flag) { bo.setId(add.getId()); } return flag; } /** * ä¿®æ¹ç¥è¯åº */ @Override public Boolean updateByBo(KnowledgeInfoBo bo) { KnowledgeInfo update = MapstructUtils.convert(bo, KnowledgeInfo.class); validEntityBeforeSave(update); return baseMapper.updateById(update) > 0; } /** * ä¿ååçæ°æ®æ ¡éª */ private void validEntityBeforeSave(KnowledgeInfo entity) { //TODO åä¸äºæ°æ®æ ¡éª,å¦å¯ä¸çº¦æ } /** * æ¹éå é¤ç¥è¯åº */ @Override public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) { if (isValid) { //TODO åä¸äºä¸å¡ä¸çæ ¡éª,夿æ¯å¦éè¦æ ¡éª } return baseMapper.deleteBatchIds(ids) > 0; } @Override @Transactional(rollbackFor = Exception.class) public void saveOne(KnowledgeInfoBo bo) { KnowledgeInfo knowledgeInfo = MapstructUtils.convert(bo, KnowledgeInfo.class); if (StringUtils.isBlank(bo.getKid())) { String kid = RandomUtil.randomString(10); if (knowledgeInfo != null) { knowledgeInfo.setKid(kid); knowledgeInfo.setUid(LoginHelper.getLoginUser().getUserId()); } baseMapper.insert(knowledgeInfo); if (knowledgeInfo != null) { vectorStoreService.createSchema(String.valueOf(knowledgeInfo.getId()), bo.getVectorModelName()); } } else { baseMapper.updateById(knowledgeInfo); } } @Override @Transactional(rollbackFor = Exception.class) public void removeKnowledge(String id) { Map<String, Object> map = new HashMap<>(); map.put("kid", id); List<KnowledgeInfoVo> knowledgeInfoList = baseMapper.selectVoByMap(map); check(knowledgeInfoList); // å é¤åéåºä¿¡æ¯ knowledgeInfoList.forEach(knowledgeInfoVo -> { vectorStoreService.removeByKid(String.valueOf(knowledgeInfoVo.getId()), knowledgeInfoVo.getVectorModelName()); }); // å é¤éä»¶åç¥è¯ç段 fragmentMapper.deleteByMap(map); List<KnowledgeAttachVo> knowledgeAttachVos = attachMapper.selectVoByMap(map); if (ObjectUtil.isNotEmpty(knowledgeAttachVos)) { Collection<Long> ossIds = knowledgeAttachVos.stream() .map(KnowledgeAttachVo::getOssId) .collect(Collectors.toList()); //å é¤oss ossService.deleteWithValidByIds(ossIds, false); //å é¤å¾çoss List<KnowledgeAttachPic> knowledgeAttachPics = picMapper.selectList( new LambdaQueryWrapper<KnowledgeAttachPic>() .in(KnowledgeAttachPic::getKid, knowledgeAttachVos.stream().map(KnowledgeAttachVo::getKid) .collect(Collectors.toList())) .in(KnowledgeAttachPic::getAid, knowledgeAttachVos.stream().map(KnowledgeAttachVo::getId) .collect(Collectors.toList())) ); if (ObjectUtil.isNotEmpty(knowledgeAttachPics)) { Collection<Long> tossIds = knowledgeAttachPics.stream() .map(KnowledgeAttachPic::getOssId) .collect(Collectors.toList()); ossService.deleteWithValidByIds(tossIds, false); List<Long> collect = knowledgeAttachPics.stream().map(KnowledgeAttachPic::getId) .collect(Collectors.toList()); picMapper.deleteByIds(collect); } } attachMapper.deleteByMap(map); // å é¤ç¥è¯åº baseMapper.deleteByMap(map); } @Override public void upload(KnowledgeInfoUploadBo bo) { storeContent(bo.getFile(), bo.getKid()); } public void storeContent(MultipartFile file, String kid) { if (file == null || file.isEmpty()) { throw new IllegalArgumentException("File cannot be null or empty"); } /** * æ¥è¯¢ç¥è¯åºå表 */ @Override public TableDataInfo<KnowledgeInfoVo> queryPageList(KnowledgeInfoBo bo, PageQuery pageQuery) { LambdaQueryWrapper<KnowledgeInfo> lqw = buildQueryWrapper(bo); Page<KnowledgeInfoVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw); return TableDataInfo.build(result); } /** * æ¥è¯¢ç¥è¯åºå表 */ @Override public List<KnowledgeInfoVo> queryList(KnowledgeInfoBo bo) { LambdaQueryWrapper<KnowledgeInfo> lqw = buildQueryWrapper(bo); return baseMapper.selectVoList(lqw); } private LambdaQueryWrapper<KnowledgeInfo> buildQueryWrapper(KnowledgeInfoBo bo) { Map<String, Object> params = bo.getParams(); LambdaQueryWrapper<KnowledgeInfo> lqw = Wrappers.lambdaQuery(); lqw.eq(StringUtils.isNotBlank(bo.getKid()), KnowledgeInfo::getKid, bo.getKid()); lqw.eq(bo.getUid() != null, KnowledgeInfo::getUid, bo.getUid()); lqw.like(StringUtils.isNotBlank(bo.getKname()), KnowledgeInfo::getKname, bo.getKname()); lqw.eq(bo.getShare() != null, KnowledgeInfo::getShare, bo.getShare()); lqw.eq(StringUtils.isNotBlank(bo.getDescription()), KnowledgeInfo::getDescription, bo.getDescription()); lqw.eq(StringUtils.isNotBlank(bo.getKnowledgeSeparator()), KnowledgeInfo::getKnowledgeSeparator, bo.getKnowledgeSeparator()); lqw.eq(StringUtils.isNotBlank(bo.getQuestionSeparator()), KnowledgeInfo::getQuestionSeparator, bo.getQuestionSeparator()); lqw.eq(bo.getOverlapChar() != null, KnowledgeInfo::getOverlapChar, bo.getOverlapChar()); lqw.eq(bo.getRetrieveLimit() != null, KnowledgeInfo::getRetrieveLimit, bo.getRetrieveLimit()); lqw.eq(bo.getTextBlockSize() != null, KnowledgeInfo::getTextBlockSize, bo.getTextBlockSize()); return lqw; } /** * æ°å¢ç¥è¯åº */ @Override public Boolean insertByBo(KnowledgeInfoBo bo) { KnowledgeInfo add = MapstructUtils.convert(bo, KnowledgeInfo.class); validEntityBeforeSave(add); boolean flag = baseMapper.insert(add) > 0; if (flag) { bo.setId(add.getId()); SysOssVo uploadDto = null; String fileName = file.getOriginalFilename(); List<String> chunkList = new ArrayList<>(); KnowledgeAttach knowledgeAttach = new KnowledgeAttach(); knowledgeAttach.setKid(kid); String docId = RandomUtil.randomString(10); knowledgeAttach.setDocId(docId); knowledgeAttach.setDocName(fileName); knowledgeAttach.setDocType(fileName.substring(fileName.lastIndexOf(".") + 1)); String content = ""; ResourceLoader resourceLoader = resourceLoaderFactory.getLoaderByFileType( knowledgeAttach.getDocType()); List<String> fids = new ArrayList<>(); try { content = resourceLoader.getContent(file.getInputStream()); chunkList = resourceLoader.getChunkList(content, kid); List<KnowledgeFragment> knowledgeFragmentList = new ArrayList<>(); if (CollUtil.isNotEmpty(chunkList)) { // Upload file to OSS uploadDto = ossService.upload(file); for (int i = 0; i < chunkList.size(); i++) { String fid = RandomUtil.randomString(10); fids.add(fid); KnowledgeFragment knowledgeFragment = new KnowledgeFragment(); knowledgeFragment.setKid(kid); knowledgeFragment.setDocId(docId); knowledgeFragment.setFid(fid); knowledgeFragment.setIdx(i); knowledgeFragment.setContent(chunkList.get(i)); knowledgeFragment.setCreateTime(new Date()); knowledgeFragmentList.add(knowledgeFragment); } return flag; } fragmentMapper.insertBatch(knowledgeFragmentList); } catch (IOException e) { log.error("ä¿åç¥è¯åºä¿¡æ¯å¤±è´¥ï¼{}", e.getMessage()); } /** * ä¿®æ¹ç¥è¯åº */ @Override public Boolean updateByBo(KnowledgeInfoBo bo) { KnowledgeInfo update = MapstructUtils.convert(bo, KnowledgeInfo.class); validEntityBeforeSave(update); return baseMapper.updateById(update) > 0; knowledgeAttach.setContent(content); knowledgeAttach.setCreateTime(new Date()); if (ObjectUtil.isNotEmpty(uploadDto) && ObjectUtil.isNotEmpty(uploadDto.getOssId())) { knowledgeAttach.setOssId(uploadDto.getOssId()); //åªæpdfæä»¶ æéè¦æè§£å¾çååæå¾çå 容 if (FileType.PDF.equals(knowledgeAttach.getDocType())) { knowledgeAttach.setPicStatus(DealStatus.STATUS_10); knowledgeAttach.setPicAnysStatus(DealStatus.STATUS_10); } else { knowledgeAttach.setPicStatus(DealStatus.STATUS_30); knowledgeAttach.setPicAnysStatus(DealStatus.STATUS_30); } //æææä»¶ä¸ä¼ åï¼é½éè¦åæ¥å°åéæ°æ®åº knowledgeAttach.setVectorStatus(DealStatus.STATUS_10); } attachMapper.insert(knowledgeAttach); /** * ä¿ååçæ°æ®æ ¡éª */ private void validEntityBeforeSave(KnowledgeInfo entity){ //TODO åä¸äºæ°æ®æ ¡éª,å¦å¯ä¸çº¦æ } /** * æ£æ¥ç¨æ·æ¯å¦æå é¤ç¥è¯åºæé * * @param knowledgeInfoList ç¥è¯åºå表 */ public void check(List<KnowledgeInfoVo> knowledgeInfoList) { LoginUser loginUser = LoginHelper.getLoginUser(); for (KnowledgeInfoVo knowledgeInfoVo : knowledgeInfoList) { if (!knowledgeInfoVo.getUid().equals(loginUser.getUserId())) { throw new SecurityException("æéä¸è¶³"); } } } /** * æ¹éå é¤ç¥è¯åº */ @Override public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) { if(isValid){ //TODO åä¸äºä¸å¡ä¸çæ ¡éª,夿æ¯å¦éè¦æ ¡éª } return baseMapper.deleteBatchIds(ids) > 0; /** * ç¬¬ä¸æ¥ 宿¶ æè§£PDFæä»¶ä¸çå¾ç */ @Scheduled(fixedDelay = 15000) // æ¯3ç§æ§è¡ä¸æ¬¡ public void dealKnowledgeAttachPic() throws Exception { //å¤ç æè§£PDFæä»¶ä¸çå¾ççè®°å½ List<KnowledgeAttach> knowledgeAttaches = attachMapper.selectList( new LambdaQueryWrapper<KnowledgeAttach>() .eq(KnowledgeAttach::getPicStatus, DealStatus.STATUS_10) .eq(KnowledgeAttach::getPicAnysStatus, DealStatus.STATUS_10) .eq(KnowledgeAttach::getVectorStatus, DealStatus.STATUS_10) ); log.info("===============æè§£PDFæä»¶ä¸çå¾ç size = {}", knowledgeAttaches.size()); if (ObjectUtil.isNotEmpty(knowledgeAttaches)) { for (KnowledgeAttach attachItem : knowledgeAttaches) { dealFileService.dealPicStatus(attachItem); } } } @Override @Transactional(rollbackFor = Exception.class) public void saveOne(KnowledgeInfoBo bo) { KnowledgeInfo knowledgeInfo = MapstructUtils.convert(bo, KnowledgeInfo.class); if (StringUtils.isBlank(bo.getKid())){ String kid = RandomUtil.randomString(10); if (knowledgeInfo != null) { knowledgeInfo.setKid(kid); knowledgeInfo.setUid(LoginHelper.getLoginUser().getUserId()); } baseMapper.insert(knowledgeInfo); if (knowledgeInfo != null) { vectorStoreService.createSchema(String.valueOf(knowledgeInfo.getId()),bo.getVectorModelName()); } }else { baseMapper.updateById(knowledgeInfo); } /** * ç¬¬äºæ¥ 宿¶ è§£æå¾çå 容 */ @Scheduled(fixedDelay = 15000) public void dealKnowledgeAttachPicAnys() throws Exception { //è·åæªå¤ççå¾çè®°å½ List<KnowledgeAttachPic> knowledgeAttachPics = picMapper.selectList( new LambdaQueryWrapper<KnowledgeAttachPic>() .eq(KnowledgeAttachPic::getPicAnysStatus, DealStatus.STATUS_10) ); if (ObjectUtil.isNotEmpty(knowledgeAttachPics)) { for (KnowledgeAttachPic picItem : knowledgeAttachPics) { dealFileService.dealPicAnysStatus(picItem); } } } @Override @Transactional(rollbackFor = Exception.class) public void removeKnowledge(String id) { Map<String,Object> map = new HashMap<>(); map.put("kid",id); List<KnowledgeInfoVo> knowledgeInfoList = baseMapper.selectVoByMap(map); check(knowledgeInfoList); // å é¤åéåºä¿¡æ¯ knowledgeInfoList.forEach(knowledgeInfoVo -> { vectorStoreService.removeByKid(String.valueOf(knowledgeInfoVo.getId()),knowledgeInfoVo.getVectorModelName()); }); // å é¤éä»¶åç¥è¯ç段 fragmentMapper.deleteByMap(map); attachMapper.deleteByMap(map); // å é¤ç¥è¯åº baseMapper.deleteByMap(map); /** * ç¬¬ä¸æ¥ 宿¶ å¤ç éä»¶ä¸ä¼ åä¸ä¼ åéæ°æ®åº */ @Scheduled(fixedDelay = 30000) // æ¯3ç§æ§è¡ä¸æ¬¡ public void dealKnowledgeAttachVector() throws Exception { //å¤ç éè¦ä¸ä¼ åéæ°æ®åºçè®°å½ List<KnowledgeAttach> knowledgeAttaches = attachMapper.selectList( new LambdaQueryWrapper<KnowledgeAttach>() .eq(KnowledgeAttach::getPicStatus, DealStatus.STATUS_30) .eq(KnowledgeAttach::getPicAnysStatus, DealStatus.STATUS_30) .eq(KnowledgeAttach::getVectorStatus, DealStatus.STATUS_10) ); log.info("===============ä¸ä¼ åéæ°æ®åº size = {}", knowledgeAttaches.size()); if (ObjectUtil.isNotEmpty(knowledgeAttaches)) { for (KnowledgeAttach attachItem : knowledgeAttaches) { dealFileService.dealVectorStatus(attachItem); } } @Override public void upload(KnowledgeInfoUploadBo bo) { storeContent(bo.getFile(), bo.getKid()); } public void storeContent(MultipartFile file, String kid) { String fileName = file.getOriginalFilename(); List<String> chunkList = new ArrayList<>(); KnowledgeAttach knowledgeAttach = new KnowledgeAttach(); knowledgeAttach.setKid(kid); String docId = RandomUtil.randomString(10); knowledgeAttach.setDocId(docId); knowledgeAttach.setDocName(fileName); knowledgeAttach.setDocType(fileName.substring(fileName.lastIndexOf(".")+1)); String content = ""; ResourceLoader resourceLoader = resourceLoaderFactory.getLoaderByFileType(knowledgeAttach.getDocType()); List<String> fids = new ArrayList<>(); try { content = resourceLoader.getContent(file.getInputStream()); chunkList = resourceLoader.getChunkList(content, kid); List<KnowledgeFragment> knowledgeFragmentList = new ArrayList<>(); if (CollUtil.isNotEmpty(chunkList)) { for (int i = 0; i < chunkList.size(); i++) { String fid = RandomUtil.randomString(10); fids.add(fid); KnowledgeFragment knowledgeFragment = new KnowledgeFragment(); knowledgeFragment.setKid(kid); knowledgeFragment.setDocId(docId); knowledgeFragment.setFid(fid); knowledgeFragment.setIdx(i); knowledgeFragment.setContent(chunkList.get(i)); knowledgeFragment.setCreateTime(new Date()); knowledgeFragmentList.add(knowledgeFragment); } } fragmentMapper.insertBatch(knowledgeFragmentList); } catch (IOException e) { log.error("ä¿åç¥è¯åºä¿¡æ¯å¤±è´¥ï¼{}", e.getMessage()); } knowledgeAttach.setContent(content); knowledgeAttach.setCreateTime(new Date()); attachMapper.insert(knowledgeAttach); // éè¿kidæ¥è¯¢ç¥è¯åºä¿¡æ¯ KnowledgeInfoVo knowledgeInfoVo = baseMapper.selectVoOne(Wrappers.<KnowledgeInfo>lambdaQuery() .eq(KnowledgeInfo::getId, kid)); // éè¿å鿍¡åæ¥è¯¢æ¨¡åä¿¡æ¯ ChatModelVo chatModelVo = chatModelService.selectModelByName(knowledgeInfoVo.getEmbeddingModelName()); StoreEmbeddingBo storeEmbeddingBo = new StoreEmbeddingBo(); storeEmbeddingBo.setKid(kid); storeEmbeddingBo.setDocId(docId); storeEmbeddingBo.setFids(fids); storeEmbeddingBo.setChunkList(chunkList); storeEmbeddingBo.setVectorModelName(knowledgeInfoVo.getVectorModelName()); storeEmbeddingBo.setEmbeddingModelName(knowledgeInfoVo.getEmbeddingModelName()); storeEmbeddingBo.setApiKey(chatModelVo.getApiKey()); storeEmbeddingBo.setBaseUrl(chatModelVo.getApiHost()); vectorStoreService.storeEmbeddings(storeEmbeddingBo); } } /** * æ£æ¥ç¨æ·æ¯å¦æå é¤ç¥è¯åºæé * * @param knowledgeInfoList ç¥è¯åºå表 */ public void check(List<KnowledgeInfoVo> knowledgeInfoList){ LoginUser loginUser = LoginHelper.getLoginUser(); for (KnowledgeInfoVo knowledgeInfoVo : knowledgeInfoList) { if(!knowledgeInfoVo.getUid().equals(loginUser.getUserId())){ throw new SecurityException("æéä¸è¶³"); } } } } script/sql/update/202505141010.sql
@@ -1,6 +1,10 @@ ALTER TABLE `knowledge_attach` ADD COLUMN `oss_id` bigint(20) NOT NULL COMMENT '对象åå¨ä¸»é®' AFTER `remark`, ADD COLUMN `pic_status` tinyint(1) NOT NULL DEFAULT 10 COMMENT 'æè§£å¾çç¶æ10æªå¼å§ï¼20è¿è¡ä¸ï¼30已宿' AFTER `oss_id`, ADD COLUMN `pic_anys_status` tinyint(1) NOT NULL DEFAULT 10 COMMENT 'åæå¾çç¶æ10æªå¼å§ï¼20è¿è¡ä¸ï¼30已宿' AFTER `pic_status`, ADD COLUMN `vector_status` tinyint(1) NOT NULL DEFAULT 10 COMMENT 'åå ¥åéæ°æ®åºç¶æ10æªå¼å§ï¼20è¿è¡ä¸ï¼30已宿' AFTER `pic_anys_status`, DROP PRIMARY KEY, ADD PRIMARY KEY (`id`) USING BTREE; ALTER TABLE `knowledge_attach` MODIFY COLUMN `remark` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL COMMENT '夿³¨' AFTER `update_time`;