feat(faq): support rich-text image upload and RAG output
- add FAQ image upload API with MinIO path and mime/size validation - enable FAQ editor image upload and text-or-image validation - include FAQ image metadata in vector content and retrieval output - enforce markdown image preservation via bot system prompt rule
This commit is contained in:
@@ -4,31 +4,56 @@ import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||
import com.mybatisflex.core.paginate.Page;
|
||||
import com.mybatisflex.core.query.QueryWrapper;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
import tech.easyflow.ai.entity.DocumentCollection;
|
||||
import tech.easyflow.ai.entity.FaqItem;
|
||||
import tech.easyflow.ai.service.DocumentCollectionService;
|
||||
import tech.easyflow.ai.service.FaqCategoryService;
|
||||
import tech.easyflow.ai.service.FaqItemService;
|
||||
import tech.easyflow.common.annotation.UsePermission;
|
||||
import tech.easyflow.common.domain.Result;
|
||||
import tech.easyflow.common.filestorage.FileStorageService;
|
||||
import tech.easyflow.common.vo.UploadResVo;
|
||||
import tech.easyflow.common.web.controller.BaseCurdController;
|
||||
import tech.easyflow.common.web.exceptions.BusinessException;
|
||||
import tech.easyflow.common.web.jsonbody.JsonBody;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.io.Serializable;
|
||||
import java.math.BigInteger;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/faqItem")
|
||||
@UsePermission(moduleName = "/api/v1/documentCollection")
|
||||
public class FaqItemController extends BaseCurdController<FaqItemService, FaqItem> {
|
||||
|
||||
private static final long MAX_IMAGE_SIZE_BYTES = 5L * 1024L * 1024L;
|
||||
private static final Set<String> ALLOWED_IMAGE_TYPES = new HashSet<>(Arrays.asList(
|
||||
"image/jpeg",
|
||||
"image/png",
|
||||
"image/webp",
|
||||
"image/gif"
|
||||
));
|
||||
|
||||
private final FaqCategoryService faqCategoryService;
|
||||
|
||||
@Resource
|
||||
private DocumentCollectionService documentCollectionService;
|
||||
|
||||
@Resource(name = "default")
|
||||
private FileStorageService fileStorageService;
|
||||
|
||||
public FaqItemController(FaqItemService service, FaqCategoryService faqCategoryService) {
|
||||
super(service);
|
||||
this.faqCategoryService = faqCategoryService;
|
||||
@@ -118,8 +143,43 @@ public class FaqItemController extends BaseCurdController<FaqItemService, FaqIte
|
||||
return Result.ok(service.removeFaqItem(new java.math.BigInteger(String.valueOf(id))));
|
||||
}
|
||||
|
||||
@PostMapping(value = "uploadImage", produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
@SaCheckPermission("/api/v1/documentCollection/save")
|
||||
public Result<UploadResVo> uploadImage(MultipartFile file, BigInteger collectionId) {
|
||||
if (collectionId == null) {
|
||||
throw new BusinessException("知识库ID不能为空");
|
||||
}
|
||||
if (file == null || file.isEmpty()) {
|
||||
throw new BusinessException("图片不能为空");
|
||||
}
|
||||
if (file.getSize() > MAX_IMAGE_SIZE_BYTES) {
|
||||
throw new BusinessException("图片大小不能超过5MB");
|
||||
}
|
||||
if (!isAllowedImageType(file)) {
|
||||
throw new BusinessException("仅支持 JPG/PNG/WEBP/GIF 图片");
|
||||
}
|
||||
|
||||
DocumentCollection collection = documentCollectionService.getById(collectionId);
|
||||
if (collection == null) {
|
||||
throw new BusinessException("知识库不存在");
|
||||
}
|
||||
if (!collection.isFaqCollection()) {
|
||||
throw new BusinessException("当前知识库不是FAQ类型");
|
||||
}
|
||||
|
||||
String path = fileStorageService.save(file, "faq/" + collectionId);
|
||||
UploadResVo resVo = new UploadResVo();
|
||||
resVo.setPath(path);
|
||||
return Result.ok(resVo);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getDefaultOrderBy() {
|
||||
return "order_no asc";
|
||||
}
|
||||
|
||||
private boolean isAllowedImageType(MultipartFile file) {
|
||||
String contentType = file.getContentType();
|
||||
return StringUtils.hasText(contentType) && ALLOWED_IMAGE_TYPES.contains(contentType.toLowerCase());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user