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:
2026-02-25 16:53:31 +08:00
parent 3b6ed8a49a
commit 9600d0855e
19 changed files with 2224 additions and 99 deletions

View File

@@ -0,0 +1,8 @@
package tech.easyflow.ai.entity;
import com.mybatisflex.annotation.Table;
import tech.easyflow.ai.entity.base.FaqCategoryBase;
@Table("tb_faq_category")
public class FaqCategory extends FaqCategoryBase {
}

View File

@@ -1,8 +1,31 @@
package tech.easyflow.ai.entity;
import com.mybatisflex.annotation.Column;
import com.mybatisflex.annotation.Table;
import tech.easyflow.ai.entity.base.FaqItemBase;
@Table("tb_faq_item")
public class FaqItem extends FaqItemBase {
@Column(ignore = true)
private String categoryName;
@Column(ignore = true)
private String categoryPath;
public String getCategoryName() {
return categoryName;
}
public void setCategoryName(String categoryName) {
this.categoryName = categoryName;
}
public String getCategoryPath() {
return categoryPath;
}
public void setCategoryPath(String categoryPath) {
this.categoryPath = categoryPath;
}
}

View File

@@ -0,0 +1,158 @@
package tech.easyflow.ai.entity.base;
import com.mybatisflex.annotation.Column;
import com.mybatisflex.annotation.Id;
import com.mybatisflex.annotation.KeyType;
import tech.easyflow.common.entity.DateTreeEntity;
import java.io.Serializable;
import java.math.BigInteger;
import java.util.Date;
public class FaqCategoryBase extends DateTreeEntity implements Serializable {
private static final long serialVersionUID = 1L;
@Id(keyType = KeyType.Generator, value = "snowFlakeId", comment = "主键")
private BigInteger id;
@Column(comment = "知识库ID")
private BigInteger collectionId;
@Column(comment = "父分类ID0表示根")
private BigInteger parentId;
@Column(comment = "祖先路径(逗号分隔)")
private String ancestors;
@Column(comment = "层级(1-3)")
private Integer levelNo;
@Column(comment = "分类名称")
private String categoryName;
@Column(comment = "排序")
private Integer sortNo;
@Column(comment = "是否默认分类")
private Boolean isDefault;
@Column(comment = "数据状态")
private Integer status;
@Column(comment = "创建时间")
private Date created;
@Column(comment = "创建人")
private BigInteger createdBy;
@Column(comment = "更新时间")
private Date modified;
@Column(comment = "更新人")
private BigInteger modifiedBy;
public BigInteger getId() {
return id;
}
public void setId(BigInteger id) {
this.id = id;
}
public BigInteger getCollectionId() {
return collectionId;
}
public void setCollectionId(BigInteger collectionId) {
this.collectionId = collectionId;
}
public BigInteger getParentId() {
return parentId;
}
public void setParentId(BigInteger parentId) {
this.parentId = parentId;
}
public String getAncestors() {
return ancestors;
}
public void setAncestors(String ancestors) {
this.ancestors = ancestors;
}
public Integer getLevelNo() {
return levelNo;
}
public void setLevelNo(Integer levelNo) {
this.levelNo = levelNo;
}
public String getCategoryName() {
return categoryName;
}
public void setCategoryName(String categoryName) {
this.categoryName = categoryName;
}
public Integer getSortNo() {
return sortNo;
}
public void setSortNo(Integer sortNo) {
this.sortNo = sortNo;
}
public Boolean getIsDefault() {
return isDefault;
}
public void setIsDefault(Boolean isDefault) {
this.isDefault = isDefault;
}
public Integer getStatus() {
return status;
}
public void setStatus(Integer status) {
this.status = status;
}
public Date getCreated() {
return created;
}
public void setCreated(Date created) {
this.created = created;
}
public BigInteger getCreatedBy() {
return createdBy;
}
public void setCreatedBy(BigInteger createdBy) {
this.createdBy = createdBy;
}
public Date getModified() {
return modified;
}
public void setModified(Date modified) {
this.modified = modified;
}
public BigInteger getModifiedBy() {
return modifiedBy;
}
public void setModifiedBy(BigInteger modifiedBy) {
this.modifiedBy = modifiedBy;
}
}

View File

@@ -20,6 +20,9 @@ public class FaqItemBase implements Serializable {
@Column(comment = "知识库ID")
private BigInteger collectionId;
@Column(comment = "FAQ分类ID")
private BigInteger categoryId;
@Column(comment = "问题")
private String question;
@@ -63,6 +66,14 @@ public class FaqItemBase implements Serializable {
this.collectionId = collectionId;
}
public BigInteger getCategoryId() {
return categoryId;
}
public void setCategoryId(BigInteger categoryId) {
this.categoryId = categoryId;
}
public String getQuestion() {
return question;
}

View File

@@ -0,0 +1,7 @@
package tech.easyflow.ai.mapper;
import com.mybatisflex.core.BaseMapper;
import tech.easyflow.ai.entity.FaqCategory;
public interface FaqCategoryMapper extends BaseMapper<FaqCategory> {
}

View File

@@ -0,0 +1,27 @@
package tech.easyflow.ai.service;
import com.mybatisflex.core.service.IService;
import tech.easyflow.ai.entity.FaqCategory;
import java.math.BigInteger;
import java.util.List;
import java.util.Map;
public interface FaqCategoryService extends IService<FaqCategory> {
BigInteger ensureDefaultCategory(BigInteger collectionId);
BigInteger ensureFaqItemCategory(BigInteger collectionId, BigInteger categoryId);
List<FaqCategory> listByCollection(BigInteger collectionId, Boolean asTree);
boolean saveCategory(FaqCategory entity);
boolean updateCategory(FaqCategory entity);
boolean removeCategory(BigInteger id);
List<BigInteger> findDescendantIds(BigInteger collectionId, BigInteger categoryId);
Map<BigInteger, String> buildPathMap(BigInteger collectionId);
}

View File

@@ -0,0 +1,555 @@
package tech.easyflow.ai.service.impl;
import cn.dev33.satoken.stp.StpUtil;
import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import tech.easyflow.ai.entity.DocumentCollection;
import tech.easyflow.ai.entity.FaqCategory;
import tech.easyflow.ai.entity.FaqItem;
import tech.easyflow.ai.mapper.FaqCategoryMapper;
import tech.easyflow.ai.mapper.FaqItemMapper;
import tech.easyflow.ai.service.DocumentCollectionService;
import tech.easyflow.ai.service.FaqCategoryService;
import tech.easyflow.common.tree.Tree;
import tech.easyflow.common.util.StringUtil;
import tech.easyflow.common.web.exceptions.BusinessException;
import javax.annotation.Resource;
import java.math.BigInteger;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
@Service
public class FaqCategoryServiceImpl extends ServiceImpl<FaqCategoryMapper, FaqCategory> implements FaqCategoryService {
private static final BigInteger ROOT_PARENT_ID = BigInteger.ZERO;
private static final ConcurrentHashMap<BigInteger, Object> DEFAULT_CATEGORY_LOCKS = new ConcurrentHashMap<>();
@Resource
private DocumentCollectionService documentCollectionService;
@Resource
private FaqItemMapper faqItemMapper;
@Override
@Transactional
public BigInteger ensureDefaultCategory(BigInteger collectionId) {
Object lock = DEFAULT_CATEGORY_LOCKS.computeIfAbsent(collectionId, key -> new Object());
synchronized (lock) {
try {
return ensureDefaultCategoryWithLock(collectionId);
} finally {
DEFAULT_CATEGORY_LOCKS.remove(collectionId, lock);
}
}
}
private BigInteger ensureDefaultCategoryWithLock(BigInteger collectionId) {
DocumentCollection collection = checkFaqCollection(collectionId);
QueryWrapper queryWrapper = QueryWrapper.create()
.eq(FaqCategory::getCollectionId, collection.getId())
.eq(FaqCategory::getIsDefault, true)
.orderBy("id asc");
List<FaqCategory> defaultCategories = list(queryWrapper);
FaqCategory defaultCategory;
if (defaultCategories == null || defaultCategories.isEmpty()) {
Date now = new Date();
BigInteger userId = getCurrentUserId();
FaqCategory entity = new FaqCategory();
entity.setCollectionId(collectionId);
entity.setParentId(ROOT_PARENT_ID);
entity.setAncestors("0");
entity.setLevelNo(1);
entity.setCategoryName("默认分类");
entity.setSortNo(0);
entity.setIsDefault(true);
entity.setStatus(0);
entity.setCreated(now);
entity.setCreatedBy(userId);
entity.setModified(now);
entity.setModifiedBy(userId);
save(entity);
defaultCategory = entity;
} else {
defaultCategory = defaultCategories.get(0);
if (defaultCategories.size() > 1) {
normalizeDuplicateDefaultCategories(collectionId, defaultCategory, defaultCategories);
}
}
backfillNullFaqItemCategory(collectionId, defaultCategory.getId());
return defaultCategory.getId();
}
private void normalizeDuplicateDefaultCategories(BigInteger collectionId,
FaqCategory keepCategory,
List<FaqCategory> defaultCategories) {
if (keepCategory == null || defaultCategories == null || defaultCategories.size() <= 1) {
return;
}
Date now = new Date();
BigInteger userId = getCurrentUserId();
for (FaqCategory duplicate : defaultCategories) {
if (duplicate == null || duplicate.getId() == null || keepCategory.getId().equals(duplicate.getId())) {
continue;
}
QueryWrapper faqQuery = QueryWrapper.create()
.eq(FaqItem::getCollectionId, collectionId)
.eq(FaqItem::getCategoryId, duplicate.getId());
FaqItem faqItem = new FaqItem();
faqItem.setCategoryId(keepCategory.getId());
faqItem.setModified(now);
faqItem.setModifiedBy(userId);
faqItemMapper.updateByQuery(faqItem, faqQuery);
QueryWrapper childQuery = QueryWrapper.create()
.eq(FaqCategory::getCollectionId, collectionId)
.eq(FaqCategory::getParentId, duplicate.getId());
long childCount = mapper.selectCountByQuery(childQuery);
if (childCount <= 0) {
removeById(duplicate.getId());
continue;
}
duplicate.setIsDefault(false);
if ("默认分类".equals(duplicate.getCategoryName())) {
duplicate.setCategoryName("默认分类副本-" + duplicate.getId());
}
duplicate.setModified(now);
duplicate.setModifiedBy(userId);
updateById(duplicate);
}
}
@Override
@Transactional
public BigInteger ensureFaqItemCategory(BigInteger collectionId, BigInteger categoryId) {
checkFaqCollection(collectionId);
BigInteger defaultCategoryId = ensureDefaultCategory(collectionId);
if (categoryId == null) {
return defaultCategoryId;
}
FaqCategory category = getById(categoryId);
if (category == null || !collectionId.equals(category.getCollectionId())) {
throw new BusinessException("FAQ分类不存在或不属于当前知识库");
}
if (category.getStatus() != null && category.getStatus() != 0) {
throw new BusinessException("FAQ分类不可用");
}
return categoryId;
}
@Override
public List<FaqCategory> listByCollection(BigInteger collectionId, Boolean asTree) {
checkFaqCollection(collectionId);
ensureDefaultCategory(collectionId);
QueryWrapper queryWrapper = QueryWrapper.create()
.eq(FaqCategory::getCollectionId, collectionId)
.orderBy("is_default desc, sort_no asc, id asc");
List<FaqCategory> categories = list(queryWrapper);
if (asTree == null || asTree) {
return Tree.tryToTree(categories, "id", "parentId");
}
return categories;
}
@Override
@Transactional
public boolean saveCategory(FaqCategory entity) {
if (entity == null) {
throw new BusinessException("FAQ分类不能为空");
}
if (entity.getCollectionId() == null) {
throw new BusinessException("知识库ID不能为空");
}
if (StringUtil.noText(entity.getCategoryName())) {
throw new BusinessException("分类名称不能为空");
}
checkFaqCollection(entity.getCollectionId());
BigInteger parentId = normalizeParentId(entity.getParentId());
validateParentNotDefault(entity.getCollectionId(), parentId);
ParentMeta parentMeta = getParentMeta(entity.getCollectionId(), parentId, null);
Date now = new Date();
BigInteger userId = getCurrentUserId();
entity.setParentId(parentId);
entity.setAncestors(parentMeta.ancestors);
entity.setLevelNo(parentMeta.levelNo);
entity.setCategoryName(entity.getCategoryName().trim());
if (entity.getSortNo() == null) {
entity.setSortNo(nextSortNo(entity.getCollectionId(), parentId));
}
entity.setIsDefault(false);
if (entity.getStatus() == null) {
entity.setStatus(0);
}
entity.setCreated(now);
entity.setCreatedBy(userId);
entity.setModified(now);
entity.setModifiedBy(userId);
return save(entity);
}
@Override
@Transactional
public boolean updateCategory(FaqCategory entity) {
if (entity == null || entity.getId() == null) {
throw new BusinessException("FAQ分类ID不能为空");
}
if (StringUtil.noText(entity.getCategoryName())) {
throw new BusinessException("分类名称不能为空");
}
FaqCategory old = getById(entity.getId());
if (old == null) {
throw new BusinessException("FAQ分类不存在");
}
checkFaqCollection(old.getCollectionId());
BigInteger parentId = normalizeParentId(entity.getParentId() == null ? old.getParentId() : entity.getParentId());
if (Boolean.TRUE.equals(old.getIsDefault()) && !ROOT_PARENT_ID.equals(parentId)) {
throw new BusinessException("默认分类不能设置父级");
}
if (!parentId.equals(normalizeParentId(old.getParentId()))) {
validateParentNotDefault(old.getCollectionId(), parentId);
}
QueryWrapper queryWrapper = QueryWrapper.create()
.eq(FaqCategory::getCollectionId, old.getCollectionId())
.orderBy("sort_no asc, id asc");
List<FaqCategory> allCategories = list(queryWrapper);
Map<BigInteger, FaqCategory> categoryMap = new HashMap<>();
for (FaqCategory category : allCategories) {
categoryMap.put(category.getId(), category);
}
if (!categoryMap.containsKey(old.getId())) {
throw new BusinessException("FAQ分类不存在");
}
if (parentId.equals(old.getId())) {
throw new BusinessException("父分类不能是自己");
}
Set<BigInteger> descendants = findDescendantSet(categoryMap, old.getId());
if (descendants.contains(parentId)) {
throw new BusinessException("父分类不能是当前分类的子分类");
}
ParentMeta parentMeta = getParentMeta(old.getCollectionId(), parentId, old.getId());
Date now = new Date();
BigInteger userId = getCurrentUserId();
old.setParentId(parentId);
old.setAncestors(parentMeta.ancestors);
old.setLevelNo(parentMeta.levelNo);
old.setCategoryName(entity.getCategoryName().trim());
if (entity.getSortNo() != null) {
old.setSortNo(entity.getSortNo());
}
if (entity.getStatus() != null) {
old.setStatus(entity.getStatus());
}
old.setModified(now);
old.setModifiedBy(userId);
categoryMap.put(old.getId(), old);
Map<BigInteger, List<FaqCategory>> childrenMap = buildChildrenMap(categoryMap.values());
List<FaqCategory> changedChildren = new ArrayList<>();
refreshChildrenLevelAndAncestors(old, childrenMap, changedChildren, now, userId);
boolean updated = updateById(old);
if (!updated) {
return false;
}
for (FaqCategory child : changedChildren) {
updateById(child);
}
return true;
}
@Override
@Transactional
public boolean removeCategory(BigInteger id) {
if (id == null) {
throw new BusinessException("FAQ分类ID不能为空");
}
FaqCategory old = getById(id);
if (old == null) {
throw new BusinessException("FAQ分类不存在");
}
checkFaqCollection(old.getCollectionId());
if (Boolean.TRUE.equals(old.getIsDefault())) {
throw new BusinessException("默认分类不允许删除");
}
QueryWrapper childQuery = QueryWrapper.create()
.eq(FaqCategory::getCollectionId, old.getCollectionId())
.eq(FaqCategory::getParentId, old.getId());
long childrenCount = mapper.selectCountByQuery(childQuery);
if (childrenCount > 0) {
throw new BusinessException("该分类下存在子分类,请先处理子分类");
}
QueryWrapper faqQuery = QueryWrapper.create()
.eq(FaqItem::getCollectionId, old.getCollectionId())
.eq(FaqItem::getCategoryId, old.getId());
long faqCount = faqItemMapper.selectCountByQuery(faqQuery);
if (faqCount > 0) {
throw new BusinessException("该分类下存在FAQ条目请先处理FAQ条目");
}
return removeById(id);
}
@Override
public List<BigInteger> findDescendantIds(BigInteger collectionId, BigInteger categoryId) {
if (collectionId == null || categoryId == null) {
return Collections.emptyList();
}
QueryWrapper queryWrapper = QueryWrapper.create()
.eq(FaqCategory::getCollectionId, collectionId)
.orderBy("sort_no asc, id asc");
List<FaqCategory> categories = list(queryWrapper);
Map<BigInteger, FaqCategory> categoryMap = new HashMap<>();
for (FaqCategory category : categories) {
categoryMap.put(category.getId(), category);
}
if (!categoryMap.containsKey(categoryId)) {
return Collections.emptyList();
}
Set<BigInteger> descendantSet = findDescendantSet(categoryMap, categoryId);
return new ArrayList<>(descendantSet);
}
@Override
public Map<BigInteger, String> buildPathMap(BigInteger collectionId) {
if (collectionId == null) {
return Collections.emptyMap();
}
QueryWrapper queryWrapper = QueryWrapper.create()
.eq(FaqCategory::getCollectionId, collectionId)
.orderBy("sort_no asc, id asc");
List<FaqCategory> categories = list(queryWrapper);
Map<BigInteger, FaqCategory> categoryMap = new HashMap<>();
for (FaqCategory category : categories) {
categoryMap.put(category.getId(), category);
}
Map<BigInteger, String> pathMap = new HashMap<>();
for (FaqCategory category : categories) {
buildPath(category.getId(), categoryMap, pathMap, new HashSet<>());
}
return pathMap;
}
private void backfillNullFaqItemCategory(BigInteger collectionId, BigInteger defaultCategoryId) {
QueryWrapper queryWrapper = QueryWrapper.create()
.eq(FaqItem::getCollectionId, collectionId)
.isNull(FaqItem::getCategoryId);
long emptyCategoryCount = faqItemMapper.selectCountByQuery(queryWrapper);
if (emptyCategoryCount <= 0) {
return;
}
FaqItem faqItem = new FaqItem();
faqItem.setCategoryId(defaultCategoryId);
faqItem.setModified(new Date());
faqItem.setModifiedBy(getCurrentUserId());
faqItemMapper.updateByQuery(faqItem, queryWrapper);
}
private String buildPath(BigInteger categoryId,
Map<BigInteger, FaqCategory> categoryMap,
Map<BigInteger, String> pathMap,
Set<BigInteger> visiting) {
if (categoryId == null) {
return "";
}
if (pathMap.containsKey(categoryId)) {
return pathMap.get(categoryId);
}
FaqCategory category = categoryMap.get(categoryId);
if (category == null) {
return "";
}
if (!visiting.add(categoryId)) {
throw new BusinessException("FAQ分类存在循环引用");
}
String path;
BigInteger parentId = normalizeParentId(category.getParentId());
if (ROOT_PARENT_ID.equals(parentId)) {
path = category.getCategoryName();
} else {
String parentPath = buildPath(parentId, categoryMap, pathMap, visiting);
if (StringUtil.noText(parentPath)) {
path = category.getCategoryName();
} else {
path = parentPath + " / " + category.getCategoryName();
}
}
visiting.remove(categoryId);
pathMap.put(categoryId, path);
return path;
}
private Set<BigInteger> findDescendantSet(Map<BigInteger, FaqCategory> categoryMap, BigInteger categoryId) {
Map<BigInteger, List<FaqCategory>> childrenMap = buildChildrenMap(categoryMap.values());
Set<BigInteger> result = new HashSet<>();
ArrayDeque<BigInteger> queue = new ArrayDeque<>();
queue.add(categoryId);
while (!queue.isEmpty()) {
BigInteger currentId = queue.poll();
if (!result.add(currentId)) {
continue;
}
List<FaqCategory> children = childrenMap.getOrDefault(currentId, Collections.emptyList());
for (FaqCategory child : children) {
queue.offer(child.getId());
}
}
return result;
}
private Map<BigInteger, List<FaqCategory>> buildChildrenMap(Iterable<FaqCategory> categories) {
Map<BigInteger, List<FaqCategory>> childrenMap = new HashMap<>();
for (FaqCategory category : categories) {
BigInteger parentId = normalizeParentId(category.getParentId());
childrenMap.computeIfAbsent(parentId, key -> new ArrayList<>()).add(category);
}
return childrenMap;
}
private void refreshChildrenLevelAndAncestors(FaqCategory parent,
Map<BigInteger, List<FaqCategory>> childrenMap,
List<FaqCategory> changedChildren,
Date modified,
BigInteger modifiedBy) {
List<FaqCategory> children = childrenMap.getOrDefault(parent.getId(), Collections.emptyList());
for (FaqCategory child : children) {
int nextLevel = parent.getLevelNo() + 1;
if (nextLevel > 3) {
throw new BusinessException("最多支持三级分类");
}
child.setLevelNo(nextLevel);
child.setAncestors(parent.getAncestors() + "," + parent.getId());
child.setModified(modified);
child.setModifiedBy(modifiedBy);
changedChildren.add(child);
refreshChildrenLevelAndAncestors(child, childrenMap, changedChildren, modified, modifiedBy);
}
}
private ParentMeta getParentMeta(BigInteger collectionId, BigInteger parentId, BigInteger currentId) {
BigInteger normalizedParentId = normalizeParentId(parentId);
if (ROOT_PARENT_ID.equals(normalizedParentId)) {
return new ParentMeta("0", 1);
}
FaqCategory parent = getById(normalizedParentId);
if (parent == null || !collectionId.equals(parent.getCollectionId())) {
throw new BusinessException("父分类不存在");
}
if (currentId != null && currentId.equals(parent.getId())) {
throw new BusinessException("父分类不能是自己");
}
int level = (parent.getLevelNo() == null ? 0 : parent.getLevelNo()) + 1;
if (level > 3) {
throw new BusinessException("最多支持三级分类");
}
return new ParentMeta(parent.getAncestors() + "," + parent.getId(), level);
}
private BigInteger normalizeParentId(BigInteger parentId) {
return parentId == null ? ROOT_PARENT_ID : parentId;
}
private Integer nextSortNo(BigInteger collectionId, BigInteger parentId) {
QueryWrapper queryWrapper = QueryWrapper.create()
.eq(FaqCategory::getCollectionId, collectionId)
.eq(FaqCategory::getParentId, parentId)
.orderBy("sort_no desc, id desc");
List<FaqCategory> siblings = list(queryWrapper);
if (siblings == null || siblings.isEmpty() || siblings.get(0).getSortNo() == null) {
return 0;
}
return siblings.get(0).getSortNo() + 1;
}
private void validateParentNotDefault(BigInteger collectionId, BigInteger parentId) {
if (parentId == null || ROOT_PARENT_ID.equals(parentId)) {
return;
}
FaqCategory parent = getById(parentId);
if (parent == null || !collectionId.equals(parent.getCollectionId())) {
return;
}
if (Boolean.TRUE.equals(parent.getIsDefault())) {
throw new BusinessException("默认分类不允许创建子分类");
}
}
private DocumentCollection checkFaqCollection(BigInteger collectionId) {
if (collectionId == null) {
throw new BusinessException("知识库ID不能为空");
}
DocumentCollection collection = documentCollectionService.getById(collectionId);
if (collection == null) {
throw new BusinessException("知识库不存在");
}
if (!collection.isFaqCollection()) {
throw new BusinessException("当前知识库不是FAQ类型");
}
return collection;
}
private BigInteger getCurrentUserId() {
if (!StpUtil.isLogin()) {
return null;
}
return BigInteger.valueOf(StpUtil.getLoginIdAsLong());
}
private static class ParentMeta {
private final String ancestors;
private final int levelNo;
private ParentMeta(String ancestors, int levelNo) {
this.ancestors = ancestors;
this.levelNo = levelNo;
}
}
}

View File

@@ -19,6 +19,7 @@ import tech.easyflow.ai.entity.DocumentCollection;
import tech.easyflow.ai.entity.FaqItem;
import tech.easyflow.ai.entity.Model;
import tech.easyflow.ai.mapper.FaqItemMapper;
import tech.easyflow.ai.service.FaqCategoryService;
import tech.easyflow.ai.service.DocumentCollectionService;
import tech.easyflow.ai.service.FaqItemService;
import tech.easyflow.ai.service.ModelService;
@@ -47,6 +48,9 @@ public class FaqItemServiceImpl extends ServiceImpl<FaqItemMapper, FaqItem> impl
@Resource
private ModelService modelService;
@Resource
private FaqCategoryService faqCategoryService;
@Autowired
private SearcherFactory searcherFactory;
@@ -54,6 +58,9 @@ public class FaqItemServiceImpl extends ServiceImpl<FaqItemMapper, FaqItem> impl
@Transactional
public boolean saveFaqItem(FaqItem entity) {
checkAndNormalize(entity, true);
DocumentCollection collection = getFaqCollection(entity.getCollectionId());
entity.setCategoryId(faqCategoryService.ensureFaqItemCategory(entity.getCollectionId(), entity.getCategoryId()));
Date now = new Date();
BigInteger userId = getCurrentUserId();
entity.setCreated(now);
@@ -69,7 +76,6 @@ public class FaqItemServiceImpl extends ServiceImpl<FaqItemMapper, FaqItem> impl
return false;
}
DocumentCollection collection = getFaqCollection(entity.getCollectionId());
storeToVector(collection, entity, false);
return true;
}
@@ -89,6 +95,8 @@ public class FaqItemServiceImpl extends ServiceImpl<FaqItemMapper, FaqItem> impl
}
checkAndNormalize(entity, false);
DocumentCollection collection = getFaqCollection(old.getCollectionId());
old.setCategoryId(faqCategoryService.ensureFaqItemCategory(old.getCollectionId(), entity.getCategoryId()));
old.setQuestion(entity.getQuestion());
old.setAnswerHtml(entity.getAnswerHtml());
old.setAnswerText(entity.getAnswerText());
@@ -104,7 +112,6 @@ public class FaqItemServiceImpl extends ServiceImpl<FaqItemMapper, FaqItem> impl
return false;
}
DocumentCollection collection = getFaqCollection(old.getCollectionId());
storeToVector(collection, old, true);
return true;
}
@@ -226,6 +233,7 @@ public class FaqItemServiceImpl extends ServiceImpl<FaqItemMapper, FaqItem> impl
Map<String, Object> metadata = new HashMap<>();
metadata.put("question", entity.getQuestion());
metadata.put("answerText", entity.getAnswerText());
metadata.put("categoryId", entity.getCategoryId());
doc.setMetadataMap(metadata);
return doc;
}