新增AI云文档目录表、AI云文件表

This commit is contained in:
2025-10-28 16:59:16 +08:00
parent d5bd9dc25f
commit 74b37fdbd0
24 changed files with 1875 additions and 2 deletions

View File

@@ -12,5 +12,5 @@ public class KnowledgeBaseConfig {
private String accessKeyId;
private String accessKeySecret;
private String workspaceId;
private String topCategoryId;
}

View File

@@ -0,0 +1,141 @@
package com.gxwebsoft.ai.controller;
import com.gxwebsoft.common.core.web.BaseController;
import com.gxwebsoft.ai.service.AiCloudDocService;
import cn.hutool.core.util.StrUtil;
import com.gxwebsoft.ai.entity.AiCloudDoc;
import com.gxwebsoft.ai.param.AiCloudDocParam;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.gxwebsoft.common.core.web.ApiResult;
import com.gxwebsoft.common.core.web.PageResult;
import com.gxwebsoft.common.core.web.PageParam;
import com.gxwebsoft.common.core.web.BatchParam;
import com.gxwebsoft.common.system.entity.User;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.util.List;
/**
* AI云文档目录表控制器
*
* @author yc
*/
@Tag(name = "AI云文档目录表管理")
@RestController
@RequestMapping("/api/ai/doc")
public class AiCloudDocController extends BaseController {
@Resource
private AiCloudDocService aiCloudDocService;
//@PreAuthorize("hasAuthority('ai:aiCloudDoc:list')")
@Operation(summary = "分页查询AI云文档目录表")
@GetMapping("/page")
public ApiResult<PageResult<AiCloudDoc>> page(AiCloudDocParam param) {
// 使用关联查询
return success(aiCloudDocService.pageRel(param));
}
//@PreAuthorize("hasAuthority('ai:aiCloudDoc:list')")
@Operation(summary = "查询全部AI云文档目录表")
@GetMapping()
public ApiResult<List<AiCloudDoc>> list(AiCloudDocParam param) {
// 使用关联查询
return success(aiCloudDocService.listRel(param));
}
//@PreAuthorize("hasAuthority('ai:aiCloudDoc:list')")
@Operation(summary = "根据id查询AI云文档目录表")
@GetMapping("/{id}")
public ApiResult<AiCloudDoc> get(@PathVariable("id") Integer id) {
// 使用关联查询
return success(aiCloudDocService.getByIdRel(id));
}
//@PreAuthorize("hasAuthority('ai:aiCloudDoc:list')")
@Operation(summary = "根据ids查询AI云文档目录表")
@GetMapping("/byIds/{ids}")
public ApiResult<List<AiCloudDoc>> getByIds(@PathVariable("ids") String ids) {
List<String> idList = StrUtil.split(ids, ',');
List<AiCloudDoc> ret = aiCloudDocService.list(new LambdaQueryWrapper<AiCloudDoc>().in(AiCloudDoc::getId, idList));
return success(ret);
}
//@PreAuthorize("hasAuthority('ai:aiCloudDoc:save')")
@Operation(summary = "添加AI云文档目录表")
@PostMapping()
public ApiResult<?> save(@RequestBody AiCloudDoc aiCloudDoc) {
// 记录当前登录用户id
User loginUser = getLoginUser();
if (loginUser != null) {
aiCloudDoc.setUserId(loginUser.getUserId());
}
if(aiCloudDoc.getParentId()>0) {
AiCloudDoc aiCloudDocParent = aiCloudDocService.getByIdRel(aiCloudDoc.getParentId());
aiCloudDoc.setCategoryId(aiCloudDocParent.getCategoryId());
aiCloudDoc.setCompanyId(aiCloudDocParent.getCompanyId());
}
if (aiCloudDocService.save(aiCloudDoc)) {
return success("添加成功");
}
return fail("添加失败");
}
//@PreAuthorize("hasAuthority('ai:aiCloudDoc:update')")
@Operation(summary = "修改AI云文档目录表")
@PutMapping()
public ApiResult<?> update(@RequestBody AiCloudDoc aiCloudDoc) {
if (aiCloudDocService.updateById(aiCloudDoc)) {
return success("修改成功");
}
return fail("修改失败");
}
//@PreAuthorize("hasAuthority('ai:aiCloudDoc:remove')")
@Operation(summary = "删除AI云文档目录表")
@DeleteMapping("/{id}")
public ApiResult<?> remove(@PathVariable("id") Integer id) {
if (aiCloudDocService.removeById(id)) {
return success("删除成功");
}
return fail("删除失败");
}
//@PreAuthorize("hasAuthority('ai:aiCloudDoc:save')")
@Operation(summary = "批量添加AI云文档目录表")
@PostMapping("/batch")
public ApiResult<?> saveBatch(@RequestBody List<AiCloudDoc> list) {
if (aiCloudDocService.saveBatch(list)) {
return success("添加成功");
}
return fail("添加失败");
}
//@PreAuthorize("hasAuthority('ai:aiCloudDoc:update')")
@Operation(summary = "批量修改AI云文档目录表")
@PutMapping("/batch")
public ApiResult<?> removeBatch(@RequestBody BatchParam<AiCloudDoc> batchParam) {
if (batchParam.update(aiCloudDocService, "id")) {
return success("修改成功");
}
return fail("修改失败");
}
//@PreAuthorize("hasAuthority('ai:aiCloudDoc:remove')")
@Operation(summary = "批量删除AI云文档目录表")
@DeleteMapping("/batch")
public ApiResult<?> removeBatch(@RequestBody List<Integer> ids) {
if (aiCloudDocService.removeByIds(ids)) {
return success("删除成功");
}
return fail("删除失败");
}
}

View File

@@ -0,0 +1,209 @@
package com.gxwebsoft.ai.controller;
import com.gxwebsoft.common.core.web.BaseController;
import com.gxwebsoft.ai.service.AiCloudFileService;
import com.gxwebsoft.ai.entity.AiCloudFile;
import com.gxwebsoft.ai.param.AiCloudFileParam;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.gxwebsoft.common.core.web.ApiResult;
import com.gxwebsoft.common.core.web.PageResult;
import com.gxwebsoft.common.core.web.BatchParam;
import com.gxwebsoft.common.system.entity.User;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
/**
* AI云文件表控制器
*
* @author yc
*/
@Tag(name = "AI云文件表管理")
@RestController
@RequestMapping("/api/ai/file")
public class AiCloudFileController extends BaseController {
@Resource
private AiCloudFileService aiCloudFileService;
//@PreAuthorize("hasAuthority('ai:aiCloudFile:list')")
@Operation(summary = "分页查询AI云文件表")
@GetMapping("/page")
public ApiResult<PageResult<AiCloudFile>> page(AiCloudFileParam param) {
return success(aiCloudFileService.pageRel(param));
}
//@PreAuthorize("hasAuthority('ai:aiCloudFile:list')")
@Operation(summary = "查询全部AI云文件表")
@GetMapping()
public ApiResult<List<AiCloudFile>> list(AiCloudFileParam param) {
return success(aiCloudFileService.listRel(param));
}
//@PreAuthorize("hasAuthority('ai:aiCloudFile:list')")
@Operation(summary = "根据id查询AI云文件表")
@GetMapping("/{id}")
public ApiResult<AiCloudFile> get(@PathVariable("id") Integer id) {
return success(aiCloudFileService.getByIdRel(id));
}
//@PreAuthorize("hasAuthority('ai:aiCloudFile:list')")
@Operation(summary = "根据ids查询AI云文件表")
@GetMapping("/byIds/{ids}")
public ApiResult<List<AiCloudFile>> getByIds(@PathVariable("ids") String ids) {
List<String> idList = cn.hutool.core.util.StrUtil.split(ids, ',');
List<AiCloudFile> ret = aiCloudFileService.list(new LambdaQueryWrapper<AiCloudFile>().in(AiCloudFile::getId, idList));
return success(ret);
}
//@PreAuthorize("hasAuthority('ai:aiCloudFile:save')")
@Operation(summary = "添加AI云文件表")
@PostMapping()
public ApiResult<?> save(@RequestBody AiCloudFile aiCloudFile) {
User loginUser = getLoginUser();
if (loginUser != null) {
aiCloudFile.setUserId(loginUser.getUserId());
}
if (aiCloudFileService.save(aiCloudFile)) {
return success("添加成功");
}
return fail("添加失败");
}
//@PreAuthorize("hasAuthority('ai:aiCloudFile:update')")
@Operation(summary = "修改AI云文件表")
@PutMapping()
public ApiResult<?> update(@RequestBody AiCloudFile aiCloudFile) {
if (aiCloudFileService.updateById(aiCloudFile)) {
return success("修改成功");
}
return fail("修改失败");
}
//@PreAuthorize("hasAuthority('ai:aiCloudFile:remove')")
@Operation(summary = "删除AI云文件表")
@DeleteMapping("/{id}")
public ApiResult<?> remove(@PathVariable("id") Integer id) {
if (aiCloudFileService.removeFileWithCloudAndIndex(id)) {
return success("删除成功");
}
return fail("删除失败");
}
//@PreAuthorize("hasAuthority('ai:aiCloudFile:save')")
@Operation(summary = "批量添加AI云文件表")
@PostMapping("/batch")
public ApiResult<?> saveBatch(@RequestBody List<AiCloudFile> list) {
if (aiCloudFileService.saveBatch(list)) {
return success("添加成功");
}
return fail("添加失败");
}
//@PreAuthorize("hasAuthority('ai:aiCloudFile:update')")
@Operation(summary = "批量修改AI云文件表")
@PutMapping("/batch")
public ApiResult<?> removeBatch(@RequestBody BatchParam<AiCloudFile> batchParam) {
if (batchParam.update(aiCloudFileService, "id")) {
return success("修改成功");
}
return fail("修改失败");
}
//@PreAuthorize("hasAuthority('ai:aiCloudFile:remove')")
@Operation(summary = "批量删除AI云文件表")
@DeleteMapping("/batch")
public ApiResult<?> removeBatch(@RequestBody List<Integer> ids) {
if (aiCloudFileService.removeFilesWithCloudAndIndex(ids)) {
return success("删除成功");
}
return fail("删除失败");
}
@Operation(summary = "上传文档到云中心")
@PostMapping("/uploadFiles")
public ApiResult<?> uploadFiles(@RequestParam Integer docId, @RequestParam String categoryId, @RequestParam("files") MultipartFile[] files) {
try {
if (files == null || files.length == 0) {
return fail("请选择要上传的文件");
}
User loginUser = getLoginUser();
List<CompletableFuture<String>> futures = new ArrayList<>();
List<String> successFiles = new ArrayList<>();
List<String> failedFiles = new ArrayList<>();
// 收集所有异步任务
for (MultipartFile file : files) {
if (file.isEmpty()) {
continue;
}
if (file.getSize() > 100 * 1024 * 1024) {
failedFiles.add(file.getOriginalFilename() + "(文件过大)");
continue;
}
// 调用Service层的异步方法并收集Future
CompletableFuture<String> future = aiCloudFileService.processFileAsync(categoryId, docId, file, loginUser);
futures.add(future);
}
// 等待所有异步任务完成
CompletableFuture<Void> allFutures = CompletableFuture.allOf(
futures.toArray(new CompletableFuture[0])
);
// 获取所有任务结果
CompletableFuture<List<String>> allResults = allFutures.thenApply(v ->
futures.stream()
.map(CompletableFuture::join)
.collect(Collectors.toList())
);
// 等待所有任务完成并处理结果
List<String> results = allResults.get(10, TimeUnit.MINUTES); // 设置10分钟超时
List<String> fileIds = new ArrayList<>();
// 统计成功和失败的文件
for (int i = 0; i < futures.size(); i++) {
CompletableFuture<String> future = futures.get(i);
try {
String fileId = future.get();
fileIds.add(fileId);
successFiles.add(files[i].getOriginalFilename());
} catch (Exception e) {
failedFiles.add(files[i].getOriginalFilename() + "(" + e.getCause().getMessage() + ")");
}
}
// 构建返回消息
StringBuilder message = new StringBuilder("文件上传完成");
if (!successFiles.isEmpty()) {
message.append(",成功上传: ").append(successFiles.size()).append("");
}
if (!failedFiles.isEmpty()) {
message.append(",失败: ").append(String.join("", failedFiles));
}
// 返回前提交上传的文档到单位默认知识库
aiCloudFileService.submitDocuments(docId, fileIds);
return success(message.toString());
} catch (Exception e) {
return fail("上传失败: " + e.getMessage());
}
}
}

View File

@@ -0,0 +1,61 @@
package com.gxwebsoft.ai.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import java.time.LocalDateTime;
import com.baomidou.mybatisplus.annotation.TableLogic;
import java.io.Serializable;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* AI云文档目录表
*
* @author yc
*/
@Data
@EqualsAndHashCode(callSuper = false)
@Schema(name = "AiCloudDoc对象", description = "AI云文档目录表")
public class AiCloudDoc implements Serializable {
private static final long serialVersionUID = 1L;
@Schema(description = "ID")
@TableId(value = "id", type = IdType.AUTO)
private Integer id;
@Schema(description = "云目录ID")
private String categoryId;
@Schema(description = "单位ID")
private Integer companyId;
@Schema(description = "上级目录ID")
private Integer parentId;
@Schema(description = "目录名称")
private String name;
@Schema(description = "排序(数字越小越靠前)")
private Integer sortNumber;
@Schema(description = "状态, 0正常, 1冻结")
private Integer status;
@Schema(description = "是否删除, 0否, 1是")
@TableLogic
private Integer deleted;
@Schema(description = "创建用户ID")
private Integer userId;
@Schema(description = "租户id")
private Integer tenantId;
@Schema(description = "创建时间")
private LocalDateTime createTime;
@Schema(description = "修改时间")
private LocalDateTime updateTime;
}

View File

@@ -0,0 +1,76 @@
package com.gxwebsoft.ai.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import java.time.LocalDateTime;
import com.baomidou.mybatisplus.annotation.TableLogic;
import java.io.Serializable;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* AI云文件表
*
* @author yc
*/
@Data
@EqualsAndHashCode(callSuper = false)
@Schema(name = "AiCloudFile对象", description = "AI云文件表")
public class AiCloudFile implements Serializable {
private static final long serialVersionUID = 1L;
@Schema(description = "ID")
@TableId(value = "id", type = IdType.AUTO)
private Integer id;
@Schema(description = "文档目录ID")
private Integer docId;
@Schema(description = "文件名")
private String fileName;
@Schema(description = "文件大小(字节)")
private Long fileSize;
@Schema(description = "文件类型")
private String fileType;
@Schema(description = "工作空间ID")
private String workspaceId;
@Schema(description = "云文件ID")
private String fileId;
@Schema(description = "文件地址URL")
private String fileUrl;
@Schema(description = "文件扩展名")
private String fileExt;
@Schema(description = "上传时间")
private LocalDateTime uploadTime;
@Schema(description = "排序(数字越小越靠前)")
private Integer sortNumber;
@Schema(description = "状态, 0正常, 1冻结")
private Integer status;
@Schema(description = "是否删除, 0否, 1是")
@TableLogic
private Integer deleted;
@Schema(description = "上传用户ID")
private Integer userId;
@Schema(description = "租户id")
private Integer tenantId;
@Schema(description = "创建时间")
private LocalDateTime createTime;
@Schema(description = "修改时间")
private LocalDateTime updateTime;
}

View File

@@ -0,0 +1,37 @@
package com.gxwebsoft.ai.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.gxwebsoft.ai.entity.AiCloudDoc;
import com.gxwebsoft.ai.param.AiCloudDocParam;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* AI云文档目录表Mapper
*
* @author yc
*/
public interface AiCloudDocMapper extends BaseMapper<AiCloudDoc> {
/**
* 分页查询
*
* @param page 分页对象
* @param param 查询参数
* @return List<AiCloudDoc>
*/
List<AiCloudDoc> selectPageRel(@Param("page") IPage<AiCloudDoc> page,
@Param("param") AiCloudDocParam param);
/**
* 查询全部
*
* @param param 查询参数
* @return List<AiCloudDoc>
*/
List<AiCloudDoc> selectListRel(@Param("param") AiCloudDocParam param);
}

View File

@@ -0,0 +1,37 @@
package com.gxwebsoft.ai.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.gxwebsoft.ai.entity.AiCloudFile;
import com.gxwebsoft.ai.param.AiCloudFileParam;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* AI云文件表Mapper
*
* @author yc
*/
public interface AiCloudFileMapper extends BaseMapper<AiCloudFile> {
/**
* 分页查询
*
* @param page 分页对象
* @param param 查询参数
* @return List<AiCloudFile>
*/
List<AiCloudFile> selectPageRel(@Param("page") IPage<AiCloudFile> page,
@Param("param") AiCloudFileParam param);
/**
* 查询全部
*
* @param param 查询参数
* @return List<AiCloudFile>
*/
List<AiCloudFile> selectListRel(@Param("param") AiCloudFileParam param);
}

View File

@@ -0,0 +1,63 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.gxwebsoft.ai.mapper.AiCloudDocMapper">
<!-- 关联查询sql -->
<sql id="selectSql">
SELECT a.*
FROM ai_cloud_doc a
<where>
<if test="param.id != null">
AND a.id = #{param.id}
</if>
<if test="param.categoryId != null">
AND a.category_id = #{param.categoryId}
</if>
<if test="param.companyId != null">
AND a.company_id = #{param.companyId}
</if>
<if test="param.parentId != null">
AND a.parent_id = #{param.parentId}
</if>
<if test="param.name != null">
AND a.name LIKE CONCAT('%', #{param.name}, '%')
</if>
<if test="param.sortNumber != null">
AND a.sort_number = #{param.sortNumber}
</if>
<if test="param.status != null">
AND a.status = #{param.status}
</if>
<if test="param.deleted != null">
AND a.deleted = #{param.deleted}
</if>
<if test="param.deleted == null">
AND a.deleted = 0
</if>
<if test="param.userId != null">
AND a.user_id = #{param.userId}
</if>
<if test="param.createTimeStart != null">
AND a.create_time &gt;= #{param.createTimeStart}
</if>
<if test="param.createTimeEnd != null">
AND a.create_time &lt;= #{param.createTimeEnd}
</if>
<if test="param.keywords != null">
AND (a.name LIKE CONCAT('%', #{param.keywords}, '%')
OR a.comments LIKE CONCAT('%', #{param.keywords}, '%'))
</if>
</where>
</sql>
<!-- 分页查询 -->
<select id="selectPageRel" resultType="com.gxwebsoft.ai.entity.AiCloudDoc">
<include refid="selectSql"></include>
</select>
<!-- 查询全部 -->
<select id="selectListRel" resultType="com.gxwebsoft.ai.entity.AiCloudDoc">
<include refid="selectSql"></include>
</select>
</mapper>

View File

@@ -0,0 +1,73 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.gxwebsoft.ai.mapper.AiCloudFileMapper">
<!-- 关联查询sql -->
<sql id="selectSql">
SELECT a.*
FROM ai_cloud_file a
<where>
<if test="param.id != null">
AND a.id = #{param.id}
</if>
<if test="param.docId != null">
AND a.doc_id = #{param.docId}
</if>
<if test="param.fileName != null and param.fileName != ''">
AND a.file_name LIKE CONCAT('%', #{param.fileName}, '%')
</if>
<if test="param.fileSize != null">
AND a.file_size = #{param.fileSize}
</if>
<if test="param.fileType != null and param.fileType != ''">
AND a.file_type = #{param.fileType}
</if>
<if test="param.workspaceId != null">
AND a.workspace_id = #{param.workspaceId}
</if>
<if test="param.fileId != null and param.fileId != ''">
AND a.file_id = #{param.fileId}
</if>
<if test="param.fileExt != null and param.fileExt != ''">
AND a.file_ext = #{param.fileExt}
</if>
<if test="param.uploadTimeStart != null">
AND a.upload_time &gt;= #{param.uploadTimeStart}
</if>
<if test="param.uploadTimeEnd != null">
AND a.upload_time &lt;= #{param.uploadTimeEnd}
</if>
<if test="param.sortNumber != null">
AND a.sort_number = #{param.sortNumber}
</if>
<if test="param.status != null">
AND a.status = #{param.status}
</if>
<if test="param.deleted != null">
AND a.deleted = #{param.deleted}
</if>
<if test="param.deleted == null">
AND a.deleted = 0
</if>
<if test="param.userId != null">
AND a.user_id = #{param.userId}
</if>
<if test="param.keywords != null and param.keywords != ''">
AND (a.file_name LIKE CONCAT('%', #{param.keywords}, '%')
OR a.file_type LIKE CONCAT('%', #{param.keywords}, '%')
OR a.file_ext LIKE CONCAT('%', #{param.keywords}, '%'))
</if>
</where>
</sql>
<!-- 分页查询 -->
<select id="selectPageRel" resultType="com.gxwebsoft.ai.entity.AiCloudFile">
<include refid="selectSql"></include>
</select>
<!-- 查询全部 -->
<select id="selectListRel" resultType="com.gxwebsoft.ai.entity.AiCloudFile">
<include refid="selectSql"></include>
</select>
</mapper>

View File

@@ -0,0 +1,57 @@
package com.gxwebsoft.ai.param;
import com.gxwebsoft.common.core.annotation.QueryField;
import com.gxwebsoft.common.core.annotation.QueryType;
import com.gxwebsoft.common.core.web.BaseParam;
import com.fasterxml.jackson.annotation.JsonInclude;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* AI云文档目录表查询参数
*
* @author yc
*/
@Data
@EqualsAndHashCode(callSuper = false)
@JsonInclude(JsonInclude.Include.NON_NULL)
@Schema(name = "AiCloudDocParam对象", description = "AI云文档目录表查询参数")
public class AiCloudDocParam extends BaseParam {
private static final long serialVersionUID = 1L;
@Schema(description = "ID")
@QueryField(type = QueryType.EQ)
private Integer id;
@Schema(description = "云目录ID")
@QueryField(type = QueryType.EQ)
private String categoryId;
@Schema(description = "单位ID")
@QueryField(type = QueryType.EQ)
private Integer companyId;
@Schema(description = "上级目录ID")
@QueryField(type = QueryType.EQ)
private Integer parentId;
@Schema(description = "目录名称")
private String name;
@Schema(description = "排序(数字越小越靠前)")
private Integer sortNumber;
@Schema(description = "状态, 0正常, 1冻结")
@QueryField(type = QueryType.EQ)
private Integer status;
@Schema(description = "是否删除, 0否, 1是")
@QueryField(type = QueryType.EQ)
private Integer deleted;
@Schema(description = "创建用户ID")
@QueryField(type = QueryType.EQ)
private Integer userId;
}

View File

@@ -0,0 +1,74 @@
package com.gxwebsoft.ai.param;
import com.gxwebsoft.common.core.annotation.QueryField;
import com.gxwebsoft.common.core.annotation.QueryType;
import com.gxwebsoft.common.core.web.BaseParam;
import com.fasterxml.jackson.annotation.JsonInclude;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.time.LocalDateTime;
/**
* AI云文件表查询参数
*
* @author yc
*/
@Data
@EqualsAndHashCode(callSuper = false)
@JsonInclude(JsonInclude.Include.NON_NULL)
@Schema(name = "AiCloudFileParam对象", description = "AI云文件表查询参数")
public class AiCloudFileParam extends BaseParam {
private static final long serialVersionUID = 1L;
@Schema(description = "ID")
@QueryField(type = QueryType.EQ)
private Integer id;
@Schema(description = "文档目录ID")
@QueryField(type = QueryType.EQ)
private Integer docId;
@Schema(description = "文件名")
private String fileName;
@Schema(description = "文件大小(字节)")
private Long fileSize;
@Schema(description = "文件类型")
private String fileType;
@Schema(description = "工作空间ID")
private String workspaceId;
@Schema(description = "云文件ID")
private String fileId;
@Schema(description = "文件扩展名")
private String fileExt;
@Schema(description = "上传时间开始")
private LocalDateTime uploadTimeStart;
@Schema(description = "上传时间结束")
private LocalDateTime uploadTimeEnd;
@Schema(description = "排序(数字越小越靠前)")
private Integer sortNumber;
@Schema(description = "状态, 0正常, 1冻结")
@QueryField(type = QueryType.EQ)
private Integer status;
@Schema(description = "是否删除, 0否, 1是")
@QueryField(type = QueryType.EQ)
private Integer deleted;
@Schema(description = "上传用户ID")
@QueryField(type = QueryType.EQ)
private Integer userId;
@Schema(description = "关键词搜索")
private String keywords;
}

View File

@@ -0,0 +1,49 @@
package com.gxwebsoft.ai.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.gxwebsoft.ai.entity.AiCloudDoc;
import com.gxwebsoft.ai.param.AiCloudDocParam;
import com.gxwebsoft.common.core.web.PageResult;
import java.util.List;
/**
* AI云文档目录表Service
*
* @author yc
*/
public interface AiCloudDocService extends IService<AiCloudDoc> {
/**
* 分页关联查询
*
* @param param 查询参数
* @return PageResult<AiCloudDoc>
*/
PageResult<AiCloudDoc> pageRel(AiCloudDocParam param);
/**
* 关联查询全部
*
* @param param 查询参数
* @return List<AiCloudDoc>
*/
List<AiCloudDoc> listRel(AiCloudDocParam param);
/**
* 根据id查询
*
* @param id ID
* @return AiCloudDoc
*/
AiCloudDoc getByIdRel(Integer id);
/**
* 根据目录ID获取本身及所有子孙目录
*
* @param docId 目录ID
* @return List<AiCloudDoc> 目录列表
*/
List<AiCloudDoc> getSelfAndChildren(Integer docId);
}

View File

@@ -0,0 +1,113 @@
package com.gxwebsoft.ai.service;
import com.gxwebsoft.common.system.entity.User;
import org.springframework.web.multipart.MultipartFile;
import com.baomidou.mybatisplus.extension.service.IService;
import com.gxwebsoft.ai.entity.AiCloudFile;
import com.gxwebsoft.ai.param.AiCloudFileParam;
import com.gxwebsoft.common.core.web.PageResult;
import java.util.List;
import java.util.concurrent.CompletableFuture;
/**
* AI云文件表Service
*
* @author yc
*/
public interface AiCloudFileService extends IService<AiCloudFile> {
/**
* 分页关联查询
*
* @param param 查询参数
* @return PageResult<AiCloudFile>
*/
PageResult<AiCloudFile> pageRel(AiCloudFileParam param);
/**
* 关联查询全部
*
* @param param 查询参数
* @return List<AiCloudFile>
*/
List<AiCloudFile> listRel(AiCloudFileParam param);
/**
* 根据id查询
*
* @param id ID
* @return AiCloudFile
*/
AiCloudFile getByIdRel(Integer id);
/**
* 异步处理文件上传
*
* @param client 阿里云客户端
* @param workspaceId 工作空间ID
* @param categoryId 分类ID
* @param docId 文档ID
* @param file 文件
* @param loginUser 登录用户
* @return CompletableFuture<String> 返回包含文件ID的Future
*/
CompletableFuture<String> processFileAsync(String categoryId, Integer docId, MultipartFile file, User loginUser);
/**
* 删除文件(包括云存储和数据库记录)
*
* @param id 文件ID
* @return 是否删除成功
*/
boolean removeFileWithCloud(Integer id);
/**
* 批量删除文件(包括云存储和数据库记录)
*
* @param ids 文件ID列表
* @return 是否删除成功
*/
boolean removeFilesWithCloud(List<Integer> ids);
/**
* 获取文件扩展名
*
* @param filename 文件名
* @return 文件扩展名
*/
String getFileExtension(String filename);
/**
* 获取公司知识库ID
*
* @param docId 文档ID
* @return 知识库ID
*/
String getCompanyKbId(Integer docId);
/**
* 提交文档到知识库
*
* @param docId 文档ID
* @param fileIds 文件ID列表
*/
void submitDocuments(Integer docId, List<String> fileIds);
/**
* 删除文件(包括云存储、数据库记录和知识库索引)
*
* @param id 文件ID
* @return 是否删除成功
*/
boolean removeFileWithCloudAndIndex(Integer id);
/**
* 批量删除文件(包括云存储、数据库记录和知识库索引)
*
* @param ids 文件ID列表
* @return 是否删除成功
*/
boolean removeFilesWithCloudAndIndex(List<Integer> ids);
}

View File

@@ -48,4 +48,9 @@ public interface KnowledgeBaseService {
* 上传知识库文档
*/
boolean uploadDocuments(String kbId, MultipartFile[] files);
/**
* 知识库追加导入已解析的文档
*/
void submitDocuments(String kbId, String fileId);
}

View File

@@ -0,0 +1,90 @@
package com.gxwebsoft.ai.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.gxwebsoft.ai.mapper.AiCloudDocMapper;
import com.gxwebsoft.ai.service.AiCloudDocService;
import com.gxwebsoft.ai.entity.AiCloudDoc;
import com.gxwebsoft.ai.param.AiCloudDocParam;
import com.gxwebsoft.common.core.web.PageParam;
import com.gxwebsoft.common.core.web.PageResult;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
/**
* AI云文档目录表Service实现
*
* @author yc
*/
@Service
public class AiCloudDocServiceImpl extends ServiceImpl<AiCloudDocMapper, AiCloudDoc> implements AiCloudDocService {
@Override
public PageResult<AiCloudDoc> pageRel(AiCloudDocParam param) {
PageParam<AiCloudDoc, AiCloudDocParam> page = new PageParam<>(param);
page.setDefaultOrder("sort_number asc, create_time desc");
List<AiCloudDoc> list = baseMapper.selectPageRel(page, param);
return new PageResult<>(list, page.getTotal());
}
@Override
public List<AiCloudDoc> listRel(AiCloudDocParam param) {
List<AiCloudDoc> list = baseMapper.selectListRel(param);
// 排序
PageParam<AiCloudDoc, AiCloudDocParam> page = new PageParam<>();
page.setDefaultOrder("sort_number asc, create_time desc");
return page.sortRecords(list);
}
@Override
public AiCloudDoc getByIdRel(Integer id) {
AiCloudDocParam param = new AiCloudDocParam();
param.setId(id);
return param.getOne(baseMapper.selectListRel(param));
}
@Override
public List<AiCloudDoc> getSelfAndChildren(Integer docId) {
// 查询所有未删除的目录
AiCloudDocParam param = new AiCloudDocParam();
param.setDeleted(0);
List<AiCloudDoc> allDocs = this.listRel(param);
// 查找指定目录
AiCloudDoc targetDoc = allDocs.stream()
.filter(doc -> doc.getId().equals(docId))
.findFirst()
.orElse(null);
if (targetDoc == null) {
return new ArrayList<>();
}
// 递归获取所有子孙目录
List<AiCloudDoc> result = new ArrayList<>();
result.add(targetDoc);
getChildrenRecursive(allDocs, targetDoc.getId(), result);
return result;
}
/**
* 递归获取所有子目录
*
* @param allDocs 所有目录列表
* @param parentId 父目录ID
* @param result 结果列表
*/
private void getChildrenRecursive(List<AiCloudDoc> allDocs, Integer parentId, List<AiCloudDoc> result) {
List<AiCloudDoc> children = allDocs.stream()
.filter(doc -> parentId.equals(doc.getParentId()))
.collect(Collectors.toList());
for (AiCloudDoc child : children) {
result.add(child);
getChildrenRecursive(allDocs, child.getId(), result);
}
}
}

View File

@@ -0,0 +1,225 @@
package com.gxwebsoft.ai.service.impl;
import com.aliyun.bailian20231229.Client;
import com.aliyun.bailian20231229.models.AddFileResponse;
import com.gxwebsoft.ai.config.KnowledgeBaseConfig;
import com.gxwebsoft.ai.factory.KnowledgeBaseClientFactory;
import com.gxwebsoft.ai.util.AiCloudDataCenterUtil;
import com.gxwebsoft.ai.util.AiCloudKnowledgeBaseUtil;
import com.gxwebsoft.common.system.entity.User;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.gxwebsoft.ai.mapper.AiCloudFileMapper;
import com.gxwebsoft.ai.service.AiCloudFileService;
import com.gxwebsoft.ai.entity.AiCloudFile;
import com.gxwebsoft.ai.entity.AiCloudDoc;
import com.gxwebsoft.ai.service.AiCloudDocService;
import com.gxwebsoft.ai.service.KnowledgeBaseService;
import com.gxwebsoft.ai.param.AiCloudFileParam;
import com.gxwebsoft.common.core.web.PageParam;
import com.gxwebsoft.common.core.web.PageResult;
import com.gxwebsoft.oa.entity.OaCompany;
import com.gxwebsoft.oa.service.OaCompanyService;
import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;
/**
* AI云文件表Service实现
*
* @author yc
*/
@Slf4j
@Service
public class AiCloudFileServiceImpl extends ServiceImpl<AiCloudFileMapper, AiCloudFile> implements AiCloudFileService {
@Resource
private KnowledgeBaseConfig config;
@Resource
private KnowledgeBaseClientFactory clientFactory;
@Resource
private AiCloudDocService aiCloudDocService;
@Resource
private OaCompanyService oaCompanyService;
@Resource
private KnowledgeBaseService knowledgeBaseService;
@Override
public PageResult<AiCloudFile> pageRel(AiCloudFileParam param) {
PageParam<AiCloudFile, AiCloudFileParam> page = new PageParam<>(param);
page.setDefaultOrder("sort_number asc, upload_time desc");
List<AiCloudFile> list = baseMapper.selectPageRel(page, param);
return new PageResult<>(list, page.getTotal());
}
@Override
public List<AiCloudFile> listRel(AiCloudFileParam param) {
List<AiCloudFile> list = baseMapper.selectListRel(param);
// 排序
PageParam<AiCloudFile, AiCloudFileParam> page = new PageParam<>();
page.setDefaultOrder("sort_number asc, upload_time desc");
return page.sortRecords(list);
}
@Override
public AiCloudFile getByIdRel(Integer id) {
AiCloudFileParam param = new AiCloudFileParam();
param.setId(id);
return param.getOne(baseMapper.selectListRel(param));
}
@Async
@Override
public CompletableFuture<String> processFileAsync(String categoryId, Integer docId, MultipartFile file, User loginUser) {
return CompletableFuture.supplyAsync(() -> {
try {
String workspaceId = config.getWorkspaceId();
Client client = clientFactory.createClient();
AddFileResponse addFileResponse = AiCloudDataCenterUtil.uploadFile(client, workspaceId, categoryId, file);
String fileId = addFileResponse.getBody().getData().getFileId();
// 保存文件信息到数据库
AiCloudFile aiCloudFile = new AiCloudFile();
aiCloudFile.setDocId(docId);
aiCloudFile.setFileName(file.getOriginalFilename());
aiCloudFile.setFileSize(file.getSize());
aiCloudFile.setFileType(getFileExtension(file.getOriginalFilename()));
aiCloudFile.setFileExt(getFileExtension(file.getOriginalFilename()));
aiCloudFile.setFileId(fileId);
aiCloudFile.setUploadTime(LocalDateTime.now());
aiCloudFile.setWorkspaceId(workspaceId);
if (loginUser != null) {
aiCloudFile.setUserId(loginUser.getUserId());
aiCloudFile.setTenantId(loginUser.getTenantId());
}
this.save(aiCloudFile);
log.info("文件上传成功: {}", file.getOriginalFilename());
return fileId;
} catch (Exception e) {
log.error("异步处理文件上传失败: {}", e.getMessage(), e);
throw new RuntimeException("文件上传失败: " + file.getOriginalFilename(), e);
}
});
}
@Override
public boolean removeFileWithCloud(Integer id) {
AiCloudFile aiCloudFile = this.getById(id);
if (aiCloudFile == null) {
return false;
}
try {
String workspaceId = config.getWorkspaceId();
Client client = clientFactory.createClient();
AiCloudDataCenterUtil.deleteFile(client, workspaceId, aiCloudFile.getFileId());
} catch (Exception e) {
log.error("删除云文件失败: {}", e.getMessage(), e);
return false;
}
return this.removeById(id);
}
@Override
public boolean removeFilesWithCloud(List<Integer> ids) {
boolean allSuccess = true;
for (Integer id : ids) {
if (!removeFileWithCloud(id)) {
allSuccess = false;
}
}
return allSuccess;
}
@Override
public String getFileExtension(String filename) {
if (filename == null || filename.lastIndexOf(".") == -1) {
return "";
}
return filename.substring(filename.lastIndexOf(".") + 1);
}
@Override
public String getCompanyKbId(Integer docId) {
if (docId == null) {
return "";
}
AiCloudDoc aiCloudDoc = aiCloudDocService.getById(docId);
if (aiCloudDoc == null || aiCloudDoc.getCompanyId() == null) {
return "";
}
OaCompany oaCompany = oaCompanyService.getById(aiCloudDoc.getCompanyId());
return oaCompany != null && oaCompany.getKbId() != null ? oaCompany.getKbId() : "";
}
@Override
public void submitDocuments(Integer docId, List<String> fileIds) {
if (docId == null || fileIds == null || fileIds.isEmpty()) {
return;
}
String kbId = getCompanyKbId(docId);
if (kbId == null || kbId.isEmpty()) {
return;
}
// 异步添加文件
for(String fileId : fileIds) {
if (fileId != null && !fileId.isEmpty()) {
knowledgeBaseService.submitDocuments(kbId, fileId);
}
}
}
@Override
public boolean removeFileWithCloudAndIndex(Integer id) {
AiCloudFile aiCloudFile = this.getById(id);
if (aiCloudFile == null) {
return false;
}
// 删除云文件
if (!removeFileWithCloud(id)) {
return false;
}
// 同时删除默认知识库文件
String kbId = getCompanyKbId(aiCloudFile.getDocId());
if (!kbId.isEmpty()) {
try {
String workspaceId = config.getWorkspaceId();
Client client = clientFactory.createClient();
AiCloudKnowledgeBaseUtil.deleteIndexDocument(client, workspaceId, kbId, Arrays.asList(aiCloudFile.getFileId()));
} catch (Exception e) {
log.error("删除知识库索引失败: {}", e.getMessage(), e);
// 这里可以根据业务需求决定是否抛出异常
}
}
return true;
}
@Override
public boolean removeFilesWithCloudAndIndex(List<Integer> ids) {
boolean allSuccess = true;
for (Integer id : ids) {
if (!removeFileWithCloudAndIndex(id)) {
allSuccess = false;
}
}
return allSuccess;
}
}

View File

@@ -12,10 +12,12 @@ import com.gxwebsoft.ai.constants.KnowledgeBaseConstants;
import com.gxwebsoft.ai.dto.KnowledgeBaseRequest;
import com.gxwebsoft.ai.factory.KnowledgeBaseClientFactory;
import com.gxwebsoft.ai.service.KnowledgeBaseService;
import com.gxwebsoft.ai.util.AiCloudKnowledgeBaseUtil;
import com.gxwebsoft.ai.util.KnowledgeBaseUploader;
import com.gxwebsoft.ai.util.KnowledgeBaseUtil;
import cn.hutool.core.util.StrUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
@@ -157,4 +159,17 @@ public class KnowledgeBaseServiceImpl implements KnowledgeBaseService {
}
}
@Async
@Override
public void submitDocuments(String kbId, String fileId) {
String workspaceId = config.getWorkspaceId();
try {
Client client = clientFactory.createClient();
AiCloudKnowledgeBaseUtil.submitIndexAddDocumentsJob(client, workspaceId, kbId, fileId);
} catch (Exception e) {
throw new RuntimeException("添加文档到知识库失败: " + e.getMessage(), e);
}
}
}

View File

@@ -0,0 +1,319 @@
package com.gxwebsoft.ai.util;
import com.aliyun.bailian20231229.Client;
import com.aliyun.bailian20231229.models.*;
import com.aliyun.teautil.models.RuntimeOptions;
import com.fasterxml.jackson.databind.ObjectMapper;
import cn.hutool.core.util.StrUtil;
import java.util.Map;
import org.springframework.web.multipart.MultipartFile;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.MessageDigest;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
/**
* 云数据中心工具类
* @author yc
*
*/
public class AiCloudDataCenterUtil {
/**
* 类目列表
*
* @param client 客户端对象
* @param workspaceId 业务空间ID
* @param categoryId 父类目ID
* @return 阿里云百炼服务的响应
*/
public static ListCategoryResponse listCategory(Client client, String workspaceId, String parentCategoryId) throws Exception {
ListCategoryRequest listCategoryRequest = new ListCategoryRequest();
listCategoryRequest.setCategoryType("UNSTRUCTURED");
listCategoryRequest.setParentCategoryId(parentCategoryId);
RuntimeOptions runtime = new RuntimeOptions();
Map<String, String> headers = new HashMap<>();
return client.listCategoryWithOptions(workspaceId, listCategoryRequest, headers, runtime);
}
/**
* 删除类目
*
* @param client 客户端对象
* @param workspaceId 业务空间ID
* @param categoryId 类目ID
* @return 阿里云百炼服务的响应
*/
public static DeleteCategoryResponse deleteCategory(Client client, String workspaceId, String categoryId) throws Exception {
RuntimeOptions runtime = new RuntimeOptions();
Map<String, String> headers = new HashMap<>();
return client.deleteCategoryWithOptions(categoryId, workspaceId, headers, runtime);
}
/**
* 添加类目
*
* @param client 客户端对象
* @param workspaceId 业务空间ID
* @param addCategoryRequest 添加类目请求对象
* @return 阿里云百炼服务的响应
*/
public static AddCategoryResponse addCategory(Client client, String workspaceId, String parentCategoryId, String categoryName) throws Exception {
AddCategoryRequest addCategoryRequest = new AddCategoryRequest();
addCategoryRequest.setCategoryType("UNSTRUCTURED");
addCategoryRequest.setParentCategoryId(parentCategoryId);
addCategoryRequest.setCategoryName(categoryName);
RuntimeOptions runtime = new RuntimeOptions();
Map<String, String> headers = new HashMap<>();
return client.addCategoryWithOptions(workspaceId, addCategoryRequest, headers, runtime);
}
/**
* 文件列表
*
* @param client 客户端对象
* @param workspaceId 业务空间ID
* @return 阿里云百炼服务的响应
*/
public static ListFileResponse listFile(Client client, String workspaceId, String categoryId, String fileName) throws Exception {
ListFileRequest listFileRequest = new ListFileRequest();
listFileRequest.setCategoryId(categoryId);
if(StrUtil.isNotBlank(fileName)) {
listFileRequest.setFileName(fileName);
}
RuntimeOptions runtime = new RuntimeOptions();
Map<String, String> headers = new HashMap<>();
return client.listFileWithOptions(workspaceId, listFileRequest, headers, runtime);
}
/**
* 添加文件
*
* @param client 客户端对象
* @param workspaceId 业务空间ID
* @param addFileRequest 添加文件请求对象
* @return 阿里云百炼服务的响应
*/
public static AddFileResponse addFile(Client client, String workspaceId, String categoryId, String leaseId) throws Exception {
AddFileRequest addFileRequest = new AddFileRequest();
addFileRequest.setCategoryId(categoryId);
addFileRequest.setLeaseId(leaseId);
addFileRequest.setParser("DASHSCOPE_DOCMIND");
RuntimeOptions runtime = new RuntimeOptions();
Map<String, String> headers = new HashMap<>();
return client.addFileWithOptions(workspaceId, addFileRequest, headers, runtime);
}
// /**
// * 从授权OSS添加文件弃用
// *
// * @param client 客户端对象
// * @param workspaceId 业务空间ID
// * @param addFilesFromAuthorizedOssRequest 从授权OSS添加文件请求对象
// * @return 阿里云百炼服务的响应
// */
// public static AddFilesFromAuthorizedOssResponse addFilesFromAuthorizedOss(Client client, String workspaceId, AddFilesFromAuthorizedOssRequest addFilesFromAuthorizedOssRequest) throws Exception {
// RuntimeOptions runtime = new RuntimeOptions();
// Map<String, String> headers = new HashMap<>();
// return client.addFilesFromAuthorizedOssWithOptions(workspaceId, addFilesFromAuthorizedOssRequest, headers, runtime);
// }
/**
* 申请文件上传租约
*
* @param client 客户端对象
* @param workspaceId 业务空间ID
* @param categoryId 类目ID
* @param applyFileUploadLeaseRequest 申请文件上传租约请求对象
* @return 阿里云百炼服务的响应
*/
public static ApplyFileUploadLeaseResponse applyFileUploadLease(Client client, String workspaceId, String categoryId, String fileName, String md5, String size) throws Exception {
ApplyFileUploadLeaseRequest applyFileUploadLeaseRequest = new ApplyFileUploadLeaseRequest();
applyFileUploadLeaseRequest.setFileName(fileName);
applyFileUploadLeaseRequest.setMd5(md5);
applyFileUploadLeaseRequest.setSizeInBytes(size);
RuntimeOptions runtime = new RuntimeOptions();
Map<String, String> headers = new HashMap<>();
return client.applyFileUploadLeaseWithOptions(categoryId, workspaceId, applyFileUploadLeaseRequest, headers, runtime);
}
/**
* 删除文件
*
* @param client 客户端对象
* @param workspaceId 业务空间ID
* @param fileId 文件ID
* @return 阿里云百炼服务的响应
*/
public static DeleteFileResponse deleteFile(Client client, String workspaceId, String fileId) throws Exception {
RuntimeOptions runtime = new RuntimeOptions();
Map<String, String> headers = new HashMap<>();
return client.deleteFileWithOptions(fileId, workspaceId, headers, runtime);
}
/**
* 描述文件
*
* @param client 客户端对象
* @param workspaceId 业务空间ID
* @param fileId 文件ID
* @return 阿里云百炼服务的响应
*/
public static DescribeFileResponse describeFile(Client client, String workspaceId, String fileId) throws Exception {
RuntimeOptions runtime = new RuntimeOptions();
Map<String, String> headers = new HashMap<>();
return client.describeFileWithOptions(workspaceId, fileId, headers, runtime);
}
/**
* 更新文件标签
*
* @param client 客户端对象
* @param workspaceId 业务空间ID
* @param fileId 文件ID
* @param updateFileTagRequest 更新文件标签请求对象
* @return 阿里云百炼服务的响应
*/
public static UpdateFileTagResponse updateFileTag(Client client, String workspaceId, String fileId, List<String> tags) throws Exception {
UpdateFileTagRequest updateFileTagRequest = new UpdateFileTagRequest();
updateFileTagRequest.setTags(tags);
RuntimeOptions runtime = new RuntimeOptions();
Map<String, String> headers = new HashMap<>();
return client.updateFileTagWithOptions(workspaceId, fileId, updateFileTagRequest, headers, runtime);
}
// /**
// * 获取解析设置
// *
// * @param client 客户端对象
// * @param workspaceId 业务空间ID
// * @param getParseSettingsRequest 获取解析设置请求对象
// * @return 阿里云百炼服务的响应
// */
// public static GetParseSettingsResponse getParseSettings(Client client, String workspaceId, GetParseSettingsRequest getParseSettingsRequest) throws Exception {
// RuntimeOptions runtime = new RuntimeOptions();
// Map<String, String> headers = new HashMap<>();
// return client.getParseSettingsWithOptions(workspaceId, getParseSettingsRequest, headers, runtime);
// }
//
// /**
// * 获取可用解析器类型
// *
// * @param client 客户端对象
// * @param workspaceId 业务空间ID
// * @param getAvailableParserTypesRequest 获取可用解析器类型请求对象
// * @return 阿里云百炼服务的响应
// */
// public static GetAvailableParserTypesResponse getAvailableParserTypes(Client client, String workspaceId, GetAvailableParserTypesRequest getAvailableParserTypesRequest) throws Exception {
// RuntimeOptions runtime = new RuntimeOptions();
// Map<String, String> headers = new HashMap<>();
// return client.getAvailableParserTypesWithOptions(workspaceId, getAvailableParserTypesRequest, headers, runtime);
// }
//
// /**
// * 更改解析设置
// *
// * @param client 客户端对象
// * @param workspaceId 业务空间ID
// * @param changeParseSettingRequest 更改解析设置请求对象
// * @return 阿里云百炼服务的响应
// */
// public static ChangeParseSettingResponse changeParseSetting(Client client, String workspaceId, ChangeParseSettingRequest changeParseSettingRequest) throws Exception {
// RuntimeOptions runtime = new RuntimeOptions();
// Map<String, String> headers = new HashMap<>();
// return client.changeParseSettingWithOptions(workspaceId, changeParseSettingRequest, headers, runtime);
// }
/**
* 上传文件
*
* @param client 客户端对象
* @param workspaceId 业务空间ID
* @param addFileRequest 添加文件请求对象
* @return 阿里云百炼服务的响应
*/
public static AddFileResponse uploadFile(Client client, String workspaceId, String categoryId, MultipartFile file) throws Exception {
// 准备文档信息
String fileName = file.getOriginalFilename();
String fileMd5 = calculateMD5(file.getInputStream());
String fileSize = String.valueOf(file.getSize());
ApplyFileUploadLeaseResponse applyFileUploadLeaseResponse = applyFileUploadLease(client, workspaceId, categoryId, fileName, fileMd5, fileSize);
String leaseId = applyFileUploadLeaseResponse.getBody().getData().getFileUploadLeaseId();
String uploadUrl = applyFileUploadLeaseResponse.getBody().getData().getParam().getUrl();
// 上传文件
ObjectMapper mapper = new ObjectMapper();
Map<String, String> headers = mapper.readValue(mapper.writeValueAsString(applyFileUploadLeaseResponse.getBody().getData().getParam().getHeaders()), Map.class);
uploadFile(uploadUrl, headers, file);
AddFileResponse addFileResponse = addFile(client, workspaceId, categoryId, leaseId);
String fileId = addFileResponse.getBody().getData().getFileId();
waitForFileParsing(client, workspaceId, fileId);
return addFileResponse;
}
private static void uploadFile(String preSignedUrl, Map<String, String> headers, MultipartFile file) throws Exception {
HttpURLConnection conn = (HttpURLConnection) new URL(preSignedUrl).openConnection();
conn.setRequestMethod("PUT");
conn.setDoOutput(true);
conn.setRequestProperty("X-bailian-extra", headers.get("X-bailian-extra"));
conn.setRequestProperty("Content-Type", headers.get("Content-Type"));
try (InputStream inputStream = file.getInputStream()) {
byte[] buffer = new byte[4096];
int bytesRead;
while ((bytesRead = inputStream.read(buffer)) != -1) {
conn.getOutputStream().write(buffer, 0, bytesRead);
}
}
if (conn.getResponseCode() != 200) {
throw new RuntimeException("上传失败: " + conn.getResponseCode());
}
}
private static String calculateMD5(InputStream inputStream) throws Exception {
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] buffer = new byte[4096];
int bytesRead;
while ((bytesRead = inputStream.read(buffer)) != -1) {
md.update(buffer, 0, bytesRead);
}
StringBuilder sb = new StringBuilder();
for (byte b : md.digest()) {
sb.append(String.format("%02x", b & 0xff));
}
return sb.toString();
}
private static void waitForFileParsing(Client client, String workspaceId, String fileId) throws Exception {
long timeout = 30 * 60 * 1000; // 30分钟超时
long startTime = System.currentTimeMillis();
while (true) {
// 检查超时
if (System.currentTimeMillis() - startTime > timeout) {
throw new RuntimeException("文档解析超时");
}
DescribeFileResponse response = describeFile(client, workspaceId, fileId);
String status = response.getBody().getData().getStatus();
if ("PARSE_SUCCESS".equals(status)) {
break;
} else if ("PARSE_FAILED".equals(status)) {
throw new RuntimeException("文档解析失败");
}
Thread.sleep(3000);
}
}
}

View File

@@ -0,0 +1,159 @@
package com.gxwebsoft.ai.util;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.aliyun.bailian20231229.models.CreateIndexResponse;
import com.aliyun.bailian20231229.models.DeleteFileResponse;
import com.aliyun.bailian20231229.models.DeleteIndexDocumentResponse;
import com.aliyun.bailian20231229.models.DeleteIndexResponse;
import com.aliyun.bailian20231229.models.ListIndexDocumentsResponse;
import com.aliyun.bailian20231229.models.ListIndicesResponse;
import com.aliyun.bailian20231229.models.RetrieveRequest;
import com.aliyun.bailian20231229.models.RetrieveResponse;
import com.aliyun.bailian20231229.models.SubmitIndexAddDocumentsJobRequest;
import com.aliyun.bailian20231229.models.SubmitIndexAddDocumentsJobResponse;
import com.aliyun.teautil.models.RuntimeOptions;
/**
* 知识库工具类
* @author GIIT-YC
*
*/
public class AiCloudKnowledgeBaseUtil {
/**
* 在指定的知识库中检索信息。
*
* @param client 客户端对象bailian20231229Client
* @param workspaceId 业务空间ID
* @param indexId 知识库ID
* @param query 检索查询语句
* @return 阿里云百炼服务的响应
*/
public static RetrieveResponse retrieveIndex(com.aliyun.bailian20231229.Client client, String workspaceId, String indexId, String query) throws Exception {
RetrieveRequest retrieveRequest = new RetrieveRequest();
retrieveRequest.setIndexId(indexId);
retrieveRequest.setQuery(query);
retrieveRequest.setDenseSimilarityTopK(null);
RuntimeOptions runtime = new RuntimeOptions();
return client.retrieveWithOptions(workspaceId, retrieveRequest, null, runtime);
}
/**
* 在阿里云百炼服务中创建知识库(初始化)。
*
* @param client 客户端对象
* @param workspaceId 业务空间ID
* @param name 知识库名称
* @param desc 知识库描述
* @return 阿里云百炼服务的响应对象
*/
public static CreateIndexResponse createIndex(com.aliyun.bailian20231229.Client client, String workspaceId, String name, String desc) throws Exception {
Map<String, String> headers = new HashMap<>();
com.aliyun.bailian20231229.models.CreateIndexRequest createIndexRequest = new com.aliyun.bailian20231229.models.CreateIndexRequest();
createIndexRequest.setStructureType("unstructured");
createIndexRequest.setName(name);
createIndexRequest.setDescription(desc);
createIndexRequest.setSinkType("DEFAULT");
createIndexRequest.setEmbeddingModelName("text-embedding-v4");
com.aliyun.teautil.models.RuntimeOptions runtime = new com.aliyun.teautil.models.RuntimeOptions();
return client.createIndexWithOptions(workspaceId, createIndexRequest, headers, runtime);
}
/**
* 获取指定业务空间下一个或多个知识库的详细信息
*
* @param client 客户端Client
* @param workspaceId 业务空间ID
* @return 阿里云百炼服务的响应
*/
public static ListIndicesResponse listIndices(com.aliyun.bailian20231229.Client client, String workspaceId) throws Exception {
Map<String, String> headers = new HashMap<>();
com.aliyun.bailian20231229.models.ListIndicesRequest listIndicesRequest = new com.aliyun.bailian20231229.models.ListIndicesRequest();
com.aliyun.teautil.models.RuntimeOptions runtime = new com.aliyun.teautil.models.RuntimeOptions();
return client.listIndicesWithOptions(workspaceId, listIndicesRequest, headers, runtime);
}
/**
* 永久性删除指定的知识库
*
* @param client 客户端Client
* @param workspaceId 业务空间ID
* @param indexId 知识库ID
* @return 阿里云百炼服务的响应
*/
public static DeleteIndexResponse deleteIndex(com.aliyun.bailian20231229.Client client, String workspaceId, String indexId) throws Exception {
Map<String, String> headers = new HashMap<>();
com.aliyun.bailian20231229.models.DeleteIndexRequest deleteIndexRequest = new com.aliyun.bailian20231229.models.DeleteIndexRequest();
deleteIndexRequest.setIndexId(indexId);
com.aliyun.teautil.models.RuntimeOptions runtime = new com.aliyun.teautil.models.RuntimeOptions();
return client.deleteIndexWithOptions(workspaceId, deleteIndexRequest, headers, runtime);
}
/**
* 查询知识库下的文档列表
*
* @param client 客户端Client
* @param workspaceId 业务空间ID
* @param indexId 知识库ID
* @return 阿里云百炼服务的响应
*/
public static ListIndexDocumentsResponse listIndexDocuments(com.aliyun.bailian20231229.Client client, String workspaceId, String indexId, Integer pageSize, Integer pageNumber) throws Exception {
com.aliyun.bailian20231229.models.ListIndexDocumentsRequest listIndexDocumentsRequest = new com.aliyun.bailian20231229.models.ListIndexDocumentsRequest();
listIndexDocumentsRequest.setIndexId(indexId);
listIndexDocumentsRequest.setPageSize(pageSize);
listIndexDocumentsRequest.setPageNumber(pageNumber);
return client.listIndexDocuments(workspaceId, listIndexDocumentsRequest);
}
/**
* 删除知识库下的文档
*
* @param client 客户端Client
* @param workspaceId 业务空间ID
* @param indexId 知识库ID
* @param ids 删除文件ID列表
* @return 阿里云百炼服务的响应
*/
public static DeleteIndexDocumentResponse deleteIndexDocument(com.aliyun.bailian20231229.Client client, String workspaceId, String indexId, List<String> ids) throws Exception {
com.aliyun.bailian20231229.models.DeleteIndexDocumentRequest deleteIndexDocumentRequest = new com.aliyun.bailian20231229.models.DeleteIndexDocumentRequest();
deleteIndexDocumentRequest.setIndexId(indexId);
deleteIndexDocumentRequest.setDocumentIds(ids);
return client.deleteIndexDocument(workspaceId, deleteIndexDocumentRequest);
}
/**
* 删除阿里云应用数据文档
*
* @param client 客户端Client
* @param workspaceId 业务空间ID
* @param fileId 删除文件ID
* @return 阿里云百炼服务的响应
*/
public static DeleteFileResponse deleteAppDocument(com.aliyun.bailian20231229.Client client, String workspaceId, String fileId) throws Exception {
return client.deleteFile(fileId, workspaceId);
}
/**
* 向一个非结构化知识库追加导入已解析的文档
*
* @param client 客户端Client
* @param workspaceId 业务空间ID
* @param indexId 知识库ID
* @param fileId 文档ID
* @param sourceType 数据类型
* @return 阿里云百炼服务的响应
*/
public static SubmitIndexAddDocumentsJobResponse submitIndexAddDocumentsJob(com.aliyun.bailian20231229.Client client, String workspaceId, String indexId, String fileId) throws Exception {
Map<String, String> headers = new HashMap<>();
SubmitIndexAddDocumentsJobRequest submitIndexAddDocumentsJobRequest = new SubmitIndexAddDocumentsJobRequest();
submitIndexAddDocumentsJobRequest.setIndexId(indexId);
submitIndexAddDocumentsJobRequest.setDocumentIds(Collections.singletonList(fileId));
submitIndexAddDocumentsJobRequest.setSourceType("DATA_CENTER_FILE");
RuntimeOptions runtime = new RuntimeOptions();
return client.submitIndexAddDocumentsJobWithOptions(workspaceId, submitIndexAddDocumentsJobRequest, headers, runtime);
}
}

View File

@@ -79,9 +79,11 @@ public class OaCompanyController extends BaseController {
if (oaCompanyService.save(oaCompany)) {
try {
String kbId = knowledgeBaseService.createKnowledgeBase(oaCompany.getCompanyName(), oaCompany.getCompanyCode());
//绑定知识库
// 绑定知识库
oaCompany.setKbId(kbId);
oaCompanyService.updateById(oaCompany);
// 初始化11个目录 - 调用Service层方法
oaCompanyService.initCompanyDocDirectories(oaCompany, getLoginUserId());
} catch (Exception e) {
return fail("添加失败:"+e.getMessage());
}

View File

@@ -39,4 +39,11 @@ public interface OaCompanyService extends IService<OaCompany> {
*/
OaCompany getByIdRel(Integer companyId);
/**
* 初始化企业目录
*
* @param oaCompany 企业信息
* @param userId 创建用户ID
*/
void initCompanyDocDirectories(OaCompany oaCompany, Integer userId);
}

View File

@@ -1,14 +1,25 @@
package com.gxwebsoft.oa.service.impl;
import com.aliyun.bailian20231229.Client;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.gxwebsoft.oa.mapper.OaCompanyMapper;
import com.gxwebsoft.oa.service.OaCompanyService;
import com.gxwebsoft.oa.entity.OaCompany;
import com.gxwebsoft.oa.param.OaCompanyParam;
import com.gxwebsoft.ai.config.KnowledgeBaseConfig;
import com.gxwebsoft.ai.entity.AiCloudDoc;
import com.gxwebsoft.ai.factory.KnowledgeBaseClientFactory;
import com.gxwebsoft.ai.service.AiCloudDocService;
import com.gxwebsoft.ai.util.AiCloudDataCenterUtil;
import com.gxwebsoft.common.core.web.PageParam;
import com.gxwebsoft.common.core.web.PageResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
@@ -20,6 +31,15 @@ import java.util.List;
@Service
public class OaCompanyServiceImpl extends ServiceImpl<OaCompanyMapper, OaCompany> implements OaCompanyService {
@Autowired
private AiCloudDocService aiCloudDocService;
@Autowired
private KnowledgeBaseConfig config;
@Autowired
private KnowledgeBaseClientFactory clientFactory;
@Override
public PageResult<OaCompany> pageRel(OaCompanyParam param) {
PageParam<OaCompany, OaCompanyParam> page = new PageParam<>(param);
@@ -44,4 +64,43 @@ public class OaCompanyServiceImpl extends ServiceImpl<OaCompanyMapper, OaCompany
return param.getOne(baseMapper.selectListRel(param));
}
@Override
public void initCompanyDocDirectories(OaCompany oaCompany, Integer userId) {
String workspaceId = config.getWorkspaceId();
String topCategoryId = config.getTopCategoryId();
String categoryId = "";
try {
Client client = clientFactory.createClient();
categoryId = AiCloudDataCenterUtil.addCategory(client, workspaceId, topCategoryId, oaCompany.getCompanyName()).getBody().getData().getCategoryId();
} catch (Exception e) {
e.printStackTrace();
}
List<String> directoryNames = Arrays.asList(
"贯彻决策部署", "单位发展战略执行", "三重一大执行", "目标完成", "财务管理",
"国资管理", "重大投资情况", "治理结构", "人员编制", "廉政情况", "历史审计问题"
);
List<AiCloudDoc> directories = new ArrayList<>();
for (int i = 0; i < directoryNames.size(); i++) {
AiCloudDoc doc = new AiCloudDoc();
doc.setCategoryId(categoryId);
doc.setCompanyId(oaCompany.getCompanyId());
doc.setParentId(0); // 顶级目录
doc.setName(directoryNames.get(i));
doc.setSortNumber(i + 1);
doc.setStatus(0);
doc.setDeleted(0);
doc.setUserId(userId);
doc.setTenantId(oaCompany.getTenantId());
doc.setCreateTime(LocalDateTime.now());
doc.setUpdateTime(LocalDateTime.now());
directories.add(doc);
}
// 批量保存目录
aiCloudDocService.saveBatch(directories);
}
}

View File

@@ -69,6 +69,7 @@ aliyun:
access-key-id: LTAI5tD5YRKuxWz6Eg7qrM4P
access-key-secret: bO8TBDXflOwbtSKimPpG8XrJnyzgTk
workspace-id: llm-4pf5auwewoz34zqu
top-category-id: cate_1db52d0dad764658bb7057293f7e8033_10377381
ai:
template:

View File

@@ -75,6 +75,7 @@ aliyun:
access-key-id: LTAI5tD5YRKuxWz6Eg7qrM4P
access-key-secret: bO8TBDXflOwbtSKimPpG8XrJnyzgTk
workspace-id: llm-4pf5auwewoz34zqu
top-category-id: cate_1db52d0dad764658bb7057293f7e8033_10377381
ai:
template: