Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.web.bind.annotation.*;

/**
Expand Down Expand Up @@ -48,7 +49,12 @@ public BaseResponse<Long> addKnowledgeBase(@RequestBody KnowledgeBaseAddRequest
ThrowUtils.throwIf(!knowledgeBaseService.isNameUnique(knowledgeBase.getName(), null), ErrorCode.OPERATION_ERROR, "知识库名称已存在");
knowledgeBase.setUserId(SecurityUtils.getLoginUserId());
knowledgeBase.setDocumentCount(0);
boolean result = knowledgeBaseService.save(knowledgeBase);
boolean result;
try {
result = knowledgeBaseService.save(knowledgeBase);
} catch (DataIntegrityViolationException e) {
throw new BusinessException(ErrorCode.OPERATION_ERROR, "知识库名称已存在");
}
ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR);
return ResultUtils.success(knowledgeBase.getId());
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package com.stephen.cloud.ai.controller;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.stephen.cloud.ai.service.KnowledgeBaseService;
import com.stephen.cloud.api.ai.model.dto.knowledgebase.KnowledgeBaseAddRequest;
import com.stephen.cloud.common.auth.utils.SecurityUtils;
import com.stephen.cloud.common.common.ErrorCode;
import com.stephen.cloud.common.web.exception.GlobalExceptionHandler;
import org.junit.jupiter.api.Test;
import org.mockito.MockedStatic;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.http.MediaType;
import org.springframework.test.util.ReflectionTestUtils;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

class KnowledgeBaseControllerTest {

private final ObjectMapper objectMapper = new ObjectMapper();

@Test
void addKnowledgeBaseShouldReturnFriendlyMessageWhenUniqueConstraintIsHit() throws Exception {
KnowledgeBaseService knowledgeBaseService = mock(KnowledgeBaseService.class);
when(knowledgeBaseService.isNameUnique("排序算法知识库", null)).thenReturn(true);
when(knowledgeBaseService.save(any()))
.thenThrow(new DataIntegrityViolationException("Duplicate entry"));

KnowledgeBaseController controller = new KnowledgeBaseController();
ReflectionTestUtils.setField(controller, "knowledgeBaseService", knowledgeBaseService);

MockMvc mockMvc = MockMvcBuilders.standaloneSetup(controller)
.setControllerAdvice(new GlobalExceptionHandler())
.build();

KnowledgeBaseAddRequest request = new KnowledgeBaseAddRequest();
request.setName("排序算法知识库");
request.setDescription("排序算法知识库");

try (MockedStatic<SecurityUtils> securityUtils = org.mockito.Mockito.mockStatic(SecurityUtils.class)) {
securityUtils.when(SecurityUtils::getLoginUserId).thenReturn(1L);

mockMvc.perform(post("/ai/kb/add")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(request)))
.andExpect(status().isOk())
.andExpect(jsonPath("$.code").value(ErrorCode.OPERATION_ERROR.getCode()))
.andExpect(jsonPath("$.message").value("知识库名称已存在"));
}
}
}
8 changes: 8 additions & 0 deletions sql/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,13 @@ done
mysql -u root -p < ai_chat_record.sql
```

### 3. 保持表结构为最新版本

`algorithm.sql` 已包含最新的 `knowledge_base` 表结构,其中知识库唯一索引已更新为 `(name, is_delete)`,可支持逻辑删除后重建同名知识库。

如果是新环境,直接执行最新版 `algorithm.sql` 即可。
如果是已有环境,请将现网 `knowledge_base` 表的唯一索引同步到 `algorithm.sql` 中的最新定义后,再发布包含后端异常兜底的服务版本。

## 📝 表设计规范

- **字符集**: 统一使用 `utf8mb4`,支持 Emoji 存储。
Expand All @@ -44,3 +51,4 @@ mysql -u root -p < ai_chat_record.sql
1. **环境差异**: 生产环境建议通过 Nacos 或环境变量动态注入数据库连接信息。
2. **性能优化**: 日志类表(如 `api_access_log`)建议定期归档或清理。
3. **安全**: 避免修改 `root` 权限直接运行,建议为每个库创建独立账号。
4. **知识库重名修复**: 若知识库采用逻辑删除,唯一索引需要包含 `is_delete`,否则“删除后重建同名知识库”会在数据库层失败。
2 changes: 1 addition & 1 deletion sql/algorithm.sql
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ CREATE TABLE `knowledge_base`
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`is_delete` tinyint NOT NULL DEFAULT 0 COMMENT '是否删除',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_name` (`name`) COMMENT '知识库名称唯一索引',
UNIQUE KEY `uk_name_is_delete` (`name`, `is_delete`) COMMENT '知识库名称+删除状态唯一索引',
KEY `idx_user_id` (`user_id`) COMMENT '创建用户ID索引'
) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4
Expand Down
Loading