feat(ai): add three-level FAQ category management
- add FAQ category table/sql migration and initialize ddl updates - add category service/controller with validation, default category rules, and sorting - support faq item category binding and category-based filtering (include descendants) - redesign FAQ page with category tree actions and UI polish
This commit is contained in:
@@ -0,0 +1,65 @@
|
||||
package tech.easyflow.admin.controller.ai;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||
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 tech.easyflow.ai.entity.FaqCategory;
|
||||
import tech.easyflow.ai.service.FaqCategoryService;
|
||||
import tech.easyflow.common.annotation.UsePermission;
|
||||
import tech.easyflow.common.domain.Result;
|
||||
import tech.easyflow.common.web.controller.BaseCurdController;
|
||||
import tech.easyflow.common.web.exceptions.BusinessException;
|
||||
import tech.easyflow.common.web.jsonbody.JsonBody;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.math.BigInteger;
|
||||
import java.util.List;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/faqCategory")
|
||||
@UsePermission(moduleName = "/api/v1/documentCollection")
|
||||
public class FaqCategoryController extends BaseCurdController<FaqCategoryService, FaqCategory> {
|
||||
|
||||
public FaqCategoryController(FaqCategoryService service) {
|
||||
super(service);
|
||||
}
|
||||
|
||||
@Override
|
||||
@GetMapping("list")
|
||||
@SaCheckPermission("/api/v1/documentCollection/query")
|
||||
public Result<List<FaqCategory>> list(FaqCategory entity, Boolean asTree, String sortKey, String sortType) {
|
||||
BigInteger collectionId = entity == null ? null : entity.getCollectionId();
|
||||
if (collectionId == null) {
|
||||
throw new BusinessException("知识库ID不能为空");
|
||||
}
|
||||
return Result.ok(service.listByCollection(collectionId, asTree));
|
||||
}
|
||||
|
||||
@Override
|
||||
@PostMapping("save")
|
||||
@SaCheckPermission("/api/v1/documentCollection/save")
|
||||
public Result<?> save(@JsonBody FaqCategory entity) {
|
||||
return Result.ok(service.saveCategory(entity));
|
||||
}
|
||||
|
||||
@Override
|
||||
@PostMapping("update")
|
||||
@SaCheckPermission("/api/v1/documentCollection/save")
|
||||
public Result<?> update(@JsonBody FaqCategory entity) {
|
||||
return Result.ok(service.updateCategory(entity));
|
||||
}
|
||||
|
||||
@Override
|
||||
@PostMapping("remove")
|
||||
@SaCheckPermission("/api/v1/documentCollection/remove")
|
||||
public Result<?> remove(@JsonBody(value = "id", required = true) Serializable id) {
|
||||
return Result.ok(service.removeCategory(new BigInteger(String.valueOf(id))));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getDefaultOrderBy() {
|
||||
return "sort_no asc";
|
||||
}
|
||||
}
|
||||
@@ -2,27 +2,36 @@ package tech.easyflow.admin.controller.ai;
|
||||
|
||||
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.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 tech.easyflow.ai.entity.FaqItem;
|
||||
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.web.controller.BaseCurdController;
|
||||
import tech.easyflow.common.web.exceptions.BusinessException;
|
||||
import tech.easyflow.common.web.jsonbody.JsonBody;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.math.BigInteger;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/faqItem")
|
||||
@UsePermission(moduleName = "/api/v1/documentCollection")
|
||||
public class FaqItemController extends BaseCurdController<FaqItemService, FaqItem> {
|
||||
|
||||
public FaqItemController(FaqItemService service) {
|
||||
private final FaqCategoryService faqCategoryService;
|
||||
|
||||
public FaqItemController(FaqItemService service, FaqCategoryService faqCategoryService) {
|
||||
super(service);
|
||||
this.faqCategoryService = faqCategoryService;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -36,7 +45,49 @@ public class FaqItemController extends BaseCurdController<FaqItemService, FaqIte
|
||||
@GetMapping("page")
|
||||
@SaCheckPermission("/api/v1/documentCollection/query")
|
||||
public Result<Page<FaqItem>> page(HttpServletRequest request, String sortKey, String sortType, Long pageNumber, Long pageSize) {
|
||||
return super.page(request, sortKey, sortType, pageNumber, pageSize);
|
||||
if (pageNumber == null || pageNumber < 1) {
|
||||
pageNumber = 1L;
|
||||
}
|
||||
if (pageSize == null || pageSize < 1) {
|
||||
pageSize = 10L;
|
||||
}
|
||||
|
||||
String collectionIdText = request.getParameter("collectionId");
|
||||
if (collectionIdText == null || collectionIdText.trim().isEmpty()) {
|
||||
throw new BusinessException("知识库ID不能为空");
|
||||
}
|
||||
BigInteger collectionId = new BigInteger(collectionIdText);
|
||||
faqCategoryService.ensureDefaultCategory(collectionId);
|
||||
|
||||
QueryWrapper queryWrapper = QueryWrapper.create()
|
||||
.eq(FaqItem::getCollectionId, collectionId);
|
||||
|
||||
String question = request.getParameter("question");
|
||||
if (question != null && !question.trim().isEmpty()) {
|
||||
queryWrapper.like(FaqItem::getQuestion, question.trim());
|
||||
}
|
||||
|
||||
String categoryIdText = request.getParameter("categoryId");
|
||||
if (categoryIdText != null && !categoryIdText.trim().isEmpty()) {
|
||||
BigInteger categoryId = new BigInteger(categoryIdText);
|
||||
List<BigInteger> descendantIds = faqCategoryService.findDescendantIds(collectionId, categoryId);
|
||||
if (descendantIds.isEmpty()) {
|
||||
queryWrapper.eq(FaqItem::getId, BigInteger.ZERO);
|
||||
} else {
|
||||
queryWrapper.in(FaqItem::getCategoryId, descendantIds);
|
||||
}
|
||||
}
|
||||
|
||||
queryWrapper.orderBy(buildOrderBy(sortKey, sortType, getDefaultOrderBy()));
|
||||
Page<FaqItem> page = service.page(new Page<>(pageNumber, pageSize), queryWrapper);
|
||||
Map<BigInteger, String> pathMap = faqCategoryService.buildPathMap(collectionId);
|
||||
|
||||
if (page.getRecords() != null) {
|
||||
for (FaqItem record : page.getRecords()) {
|
||||
record.setCategoryPath(pathMap.get(record.getCategoryId()));
|
||||
}
|
||||
}
|
||||
return Result.ok(page);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
Reference in New Issue
Block a user