diff --git a/easyflow-api/easyflow-api-admin/src/main/java/tech/easyflow/admin/controller/ai/DocumentCollectionController.java b/easyflow-api/easyflow-api-admin/src/main/java/tech/easyflow/admin/controller/ai/DocumentCollectionController.java index b2e3865..904592c 100644 --- a/easyflow-api/easyflow-api-admin/src/main/java/tech/easyflow/admin/controller/ai/DocumentCollectionController.java +++ b/easyflow-api/easyflow-api-admin/src/main/java/tech/easyflow/admin/controller/ai/DocumentCollectionController.java @@ -52,6 +52,17 @@ public class DocumentCollectionController extends BaseCurdController onSaveOrUpdateBefore(DocumentCollection entity, boolean isSave) { String alias = entity.getAlias(); + String collectionType = entity.getCollectionType(); + if (isSave && !StringUtils.hasLength(collectionType)) { + entity.setCollectionType(DocumentCollection.TYPE_DOCUMENT); + } else if (!DocumentCollection.TYPE_DOCUMENT.equalsIgnoreCase(collectionType) + && !DocumentCollection.TYPE_FAQ.equalsIgnoreCase(collectionType)) { + if (StringUtils.hasLength(collectionType)) { + throw new BusinessException("知识库类型不正确"); + } + } else { + entity.setCollectionType(collectionType.toUpperCase()); + } if (StringUtils.hasLength(alias)){ DocumentCollection knowledge = service.getByAlias(alias); @@ -94,7 +105,7 @@ public class DocumentCollectionController extends BaseCurdController onRemoveBefore(Collection ids) { QueryWrapper queryWrapper = QueryWrapper.create(); - queryWrapper.in(BotDocumentCollection::getId, ids); + queryWrapper.in(BotDocumentCollection::getDocumentCollectionId, ids); boolean exists = botDocumentCollectionService.exists(queryWrapper); if (exists){ diff --git a/easyflow-api/easyflow-api-admin/src/main/java/tech/easyflow/admin/controller/ai/DocumentController.java b/easyflow-api/easyflow-api-admin/src/main/java/tech/easyflow/admin/controller/ai/DocumentController.java index 3968436..db0d474 100644 --- a/easyflow-api/easyflow-api-admin/src/main/java/tech/easyflow/admin/controller/ai/DocumentController.java +++ b/easyflow-api/easyflow-api-admin/src/main/java/tech/easyflow/admin/controller/ai/DocumentController.java @@ -6,7 +6,6 @@ import com.mybatisflex.core.query.QueryWrapper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.core.io.ClassPathResource; -import org.springframework.http.MediaType; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.bind.annotation.*; import tech.easyflow.ai.entity.Document; @@ -105,13 +104,7 @@ public class DocumentController extends BaseCurdController documentList = documentService.getDocumentList(kbSlug, pageSize, pageNumber,fileName); + DocumentCollection knowledge = getDocumentCollection(kbSlug); + Page documentList = documentService.getDocumentList(knowledge.getId().toString(), pageSize, pageNumber,fileName); return Result.ok(documentList); } @@ -155,6 +149,10 @@ public class DocumentController extends BaseCurdController textSplit(@JsonBody DocumentCollectionSplitParams documentCollectionSplitParams) { + if (documentCollectionSplitParams.getKnowledgeId() == null) { + throw new BusinessException("知识库id不能为空"); + } + getDocumentCollection(documentCollectionSplitParams.getKnowledgeId().toString()); return documentService.textSplit(documentCollectionSplitParams); } @@ -221,4 +219,17 @@ public class DocumentController extends BaseCurdController { + + public FaqItemController(FaqItemService service) { + super(service); + } + + @Override + @GetMapping("list") + @SaCheckPermission("/api/v1/documentCollection/query") + public Result> list(FaqItem entity, Boolean asTree, String sortKey, String sortType) { + return super.list(entity, asTree, sortKey, sortType); + } + + @Override + @GetMapping("page") + @SaCheckPermission("/api/v1/documentCollection/query") + public Result> page(HttpServletRequest request, String sortKey, String sortType, Long pageNumber, Long pageSize) { + return super.page(request, sortKey, sortType, pageNumber, pageSize); + } + + @Override + @GetMapping("detail") + @SaCheckPermission("/api/v1/documentCollection/query") + public Result detail(String id) { + return super.detail(id); + } + + @Override + @PostMapping("save") + @SaCheckPermission("/api/v1/documentCollection/save") + public Result save(@JsonBody FaqItem entity) { + return Result.ok(service.saveFaqItem(entity)); + } + + @Override + @PostMapping("update") + @SaCheckPermission("/api/v1/documentCollection/save") + public Result update(@JsonBody FaqItem entity) { + return Result.ok(service.updateFaqItem(entity)); + } + + @Override + @PostMapping("remove") + @SaCheckPermission("/api/v1/documentCollection/remove") + public Result remove(@JsonBody(value = "id", required = true) Serializable id) { + return Result.ok(service.removeFaqItem(new java.math.BigInteger(String.valueOf(id)))); + } + + @Override + protected String getDefaultOrderBy() { + return "order_no asc"; + } +} diff --git a/easyflow-modules/easyflow-module-ai/pom.xml b/easyflow-modules/easyflow-module-ai/pom.xml index 7562465..760a647 100644 --- a/easyflow-modules/easyflow-module-ai/pom.xml +++ b/easyflow-modules/easyflow-module-ai/pom.xml @@ -64,6 +64,10 @@ cn.hutool hutool-core + + org.jsoup + jsoup + tech.easyflow easyflow-module-system diff --git a/easyflow-modules/easyflow-module-ai/src/main/java/tech/easyflow/ai/config/SearcherFactory.java b/easyflow-modules/easyflow-module-ai/src/main/java/tech/easyflow/ai/config/SearcherFactory.java index 9ac6890..a254c03 100644 --- a/easyflow-modules/easyflow-module-ai/src/main/java/tech/easyflow/ai/config/SearcherFactory.java +++ b/easyflow-modules/easyflow-module-ai/src/main/java/tech/easyflow/ai/config/SearcherFactory.java @@ -4,7 +4,6 @@ import com.easyagents.engine.es.ElasticSearcher; import com.easyagents.search.engine.lucene.LuceneSearcher; import com.easyagents.search.engine.service.DocumentSearcher; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -29,6 +28,9 @@ public class SearcherFactory { public DocumentSearcher getSearcher(String defaultSearcherType) { + if (defaultSearcherType == null) { + defaultSearcherType = "lucene"; + } switch (defaultSearcherType) { case "elasticSearch": return new ElasticSearcher(aiEsConfig); @@ -37,4 +39,4 @@ public class SearcherFactory { return new LuceneSearcher(luceneConfig); } } -} \ No newline at end of file +} diff --git a/easyflow-modules/easyflow-module-ai/src/main/java/tech/easyflow/ai/entity/DocumentCollection.java b/easyflow-modules/easyflow-module-ai/src/main/java/tech/easyflow/ai/entity/DocumentCollection.java index b5e0a71..c8d1c9d 100644 --- a/easyflow-modules/easyflow-module-ai/src/main/java/tech/easyflow/ai/entity/DocumentCollection.java +++ b/easyflow-modules/easyflow-module-ai/src/main/java/tech/easyflow/ai/entity/DocumentCollection.java @@ -34,6 +34,9 @@ import java.util.Map; @Table("tb_document_collection") public class DocumentCollection extends DocumentCollectionBase { + public static final String TYPE_DOCUMENT = "DOCUMENT"; + public static final String TYPE_FAQ = "FAQ"; + /** * 文档块问题集合配置key */ @@ -98,6 +101,14 @@ public class DocumentCollection extends DocumentCollectionBase { return this.getVectorStoreEnable() != null && this.getVectorStoreEnable(); } + public boolean isFaqCollection() { + return TYPE_FAQ.equalsIgnoreCase(this.getCollectionType()); + } + + public boolean isDocumentCollection() { + return !isFaqCollection(); + } + public boolean isSearchEngineEnabled() { return this.getSearchEngineEnable() != null && this.getSearchEngineEnable(); } diff --git a/easyflow-modules/easyflow-module-ai/src/main/java/tech/easyflow/ai/entity/FaqItem.java b/easyflow-modules/easyflow-module-ai/src/main/java/tech/easyflow/ai/entity/FaqItem.java new file mode 100644 index 0000000..e6bf650 --- /dev/null +++ b/easyflow-modules/easyflow-module-ai/src/main/java/tech/easyflow/ai/entity/FaqItem.java @@ -0,0 +1,8 @@ +package tech.easyflow.ai.entity; + +import com.mybatisflex.annotation.Table; +import tech.easyflow.ai.entity.base.FaqItemBase; + +@Table("tb_faq_item") +public class FaqItem extends FaqItemBase { +} diff --git a/easyflow-modules/easyflow-module-ai/src/main/java/tech/easyflow/ai/entity/base/DocumentCollectionBase.java b/easyflow-modules/easyflow-module-ai/src/main/java/tech/easyflow/ai/entity/base/DocumentCollectionBase.java index 55cc5ca..4098a31 100644 --- a/easyflow-modules/easyflow-module-ai/src/main/java/tech/easyflow/ai/entity/base/DocumentCollectionBase.java +++ b/easyflow-modules/easyflow-module-ai/src/main/java/tech/easyflow/ai/entity/base/DocumentCollectionBase.java @@ -4,11 +4,12 @@ import com.mybatisflex.annotation.Column; import com.mybatisflex.annotation.Id; import com.mybatisflex.annotation.KeyType; import com.mybatisflex.core.handler.FastjsonTypeHandler; +import tech.easyflow.common.entity.DateEntity; + import java.io.Serializable; import java.math.BigInteger; import java.util.Date; import java.util.Map; -import tech.easyflow.common.entity.DateEntity; public class DocumentCollectionBase extends DateEntity implements Serializable { @@ -21,6 +22,12 @@ public class DocumentCollectionBase extends DateEntity implements Serializable { @Id(keyType = KeyType.Generator, value = "snowFlakeId", comment = "Id") private BigInteger id; + /** + * 知识库类型: DOCUMENT/FAQ + */ + @Column(comment = "知识库类型: DOCUMENT/FAQ") + private String collectionType; + /** * 别名 */ @@ -161,6 +168,14 @@ public class DocumentCollectionBase extends DateEntity implements Serializable { this.id = id; } + public String getCollectionType() { + return collectionType; + } + + public void setCollectionType(String collectionType) { + this.collectionType = collectionType; + } + public String getAlias() { return alias; } diff --git a/easyflow-modules/easyflow-module-ai/src/main/java/tech/easyflow/ai/entity/base/FaqItemBase.java b/easyflow-modules/easyflow-module-ai/src/main/java/tech/easyflow/ai/entity/base/FaqItemBase.java new file mode 100644 index 0000000..a339aab --- /dev/null +++ b/easyflow-modules/easyflow-module-ai/src/main/java/tech/easyflow/ai/entity/base/FaqItemBase.java @@ -0,0 +1,137 @@ +package tech.easyflow.ai.entity.base; + +import com.mybatisflex.annotation.Column; +import com.mybatisflex.annotation.Id; +import com.mybatisflex.annotation.KeyType; +import com.mybatisflex.core.handler.FastjsonTypeHandler; + +import java.io.Serializable; +import java.math.BigInteger; +import java.util.Date; +import java.util.Map; + +public class FaqItemBase implements Serializable { + + private static final long serialVersionUID = 1L; + + @Id(keyType = KeyType.Generator, value = "snowFlakeId") + private BigInteger id; + + @Column(comment = "知识库ID") + private BigInteger collectionId; + + @Column(comment = "问题") + private String question; + + @Column(comment = "答案HTML") + private String answerHtml; + + @Column(comment = "答案纯文本") + private String answerText; + + @Column(comment = "排序") + private Integer orderNo; + + @Column(typeHandler = FastjsonTypeHandler.class, comment = "扩展项") + private Map options; + + @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 String getQuestion() { + return question; + } + + public void setQuestion(String question) { + this.question = question; + } + + public String getAnswerHtml() { + return answerHtml; + } + + public void setAnswerHtml(String answerHtml) { + this.answerHtml = answerHtml; + } + + public String getAnswerText() { + return answerText; + } + + public void setAnswerText(String answerText) { + this.answerText = answerText; + } + + public Integer getOrderNo() { + return orderNo; + } + + public void setOrderNo(Integer orderNo) { + this.orderNo = orderNo; + } + + public Map getOptions() { + return options; + } + + public void setOptions(Map options) { + this.options = options; + } + + 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; + } +} diff --git a/easyflow-modules/easyflow-module-ai/src/main/java/tech/easyflow/ai/mapper/FaqItemMapper.java b/easyflow-modules/easyflow-module-ai/src/main/java/tech/easyflow/ai/mapper/FaqItemMapper.java new file mode 100644 index 0000000..6529ac6 --- /dev/null +++ b/easyflow-modules/easyflow-module-ai/src/main/java/tech/easyflow/ai/mapper/FaqItemMapper.java @@ -0,0 +1,7 @@ +package tech.easyflow.ai.mapper; + +import com.mybatisflex.core.BaseMapper; +import tech.easyflow.ai.entity.FaqItem; + +public interface FaqItemMapper extends BaseMapper { +} diff --git a/easyflow-modules/easyflow-module-ai/src/main/java/tech/easyflow/ai/service/FaqItemService.java b/easyflow-modules/easyflow-module-ai/src/main/java/tech/easyflow/ai/service/FaqItemService.java new file mode 100644 index 0000000..9b8f869 --- /dev/null +++ b/easyflow-modules/easyflow-module-ai/src/main/java/tech/easyflow/ai/service/FaqItemService.java @@ -0,0 +1,15 @@ +package tech.easyflow.ai.service; + +import com.mybatisflex.core.service.IService; +import tech.easyflow.ai.entity.FaqItem; + +import java.math.BigInteger; + +public interface FaqItemService extends IService { + + boolean saveFaqItem(FaqItem entity); + + boolean updateFaqItem(FaqItem entity); + + boolean removeFaqItem(BigInteger id); +} diff --git a/easyflow-modules/easyflow-module-ai/src/main/java/tech/easyflow/ai/service/impl/DocumentCollectionServiceImpl.java b/easyflow-modules/easyflow-module-ai/src/main/java/tech/easyflow/ai/service/impl/DocumentCollectionServiceImpl.java index 99673c3..84bc8d2 100644 --- a/easyflow-modules/easyflow-module-ai/src/main/java/tech/easyflow/ai/service/impl/DocumentCollectionServiceImpl.java +++ b/easyflow-modules/easyflow-module-ai/src/main/java/tech/easyflow/ai/service/impl/DocumentCollectionServiceImpl.java @@ -17,9 +17,11 @@ import org.springframework.stereotype.Service; import tech.easyflow.ai.config.SearcherFactory; import tech.easyflow.ai.entity.DocumentChunk; import tech.easyflow.ai.entity.DocumentCollection; +import tech.easyflow.ai.entity.FaqItem; import tech.easyflow.ai.entity.Model; import tech.easyflow.ai.mapper.DocumentChunkMapper; import tech.easyflow.ai.mapper.DocumentCollectionMapper; +import tech.easyflow.ai.mapper.FaqItemMapper; import tech.easyflow.ai.service.DocumentChunkService; import tech.easyflow.ai.service.DocumentCollectionService; import tech.easyflow.ai.service.ModelService; @@ -63,6 +65,9 @@ public class DocumentCollectionServiceImpl extends ServiceImpl search(BigInteger id, String keyword) { @@ -123,13 +128,7 @@ public class DocumentCollectionServiceImpl extends ServiceImpl uniqueDocs = combinedFuture.get(); // 阻塞等待所有查询完成 List searchDocuments = new ArrayList<>(uniqueDocs.values()); searchDocuments.sort((doc1, doc2) -> Double.compare(doc2.getScore(), doc1.getScore())); - searchDocuments.forEach(item ->{ - DocumentChunk documentChunk = documentChunkMapper.selectOneById((Serializable) item.getId()); - if (documentChunk != null && !StringUtil.noText(documentChunk.getContent())){ - item.setContent(documentChunk.getContent()); - } - - }); + fillSearchContent(documentCollection, searchDocuments); if (searchDocuments.isEmpty()) { return Collections.emptyList(); } @@ -243,4 +242,33 @@ public class DocumentCollectionServiceImpl extends ServiceImpl searchDocuments) { + if (searchDocuments == null || searchDocuments.isEmpty()) { + return; + } + List ids = searchDocuments.stream() + .map(item -> (Serializable) item.getId()) + .collect(Collectors.toList()); + if (documentCollection.isFaqCollection()) { + Map faqItemMap = faqItemMapper.selectListByIds(ids).stream() + .collect(Collectors.toMap(item -> item.getId().toString(), item -> item, (a, b) -> a)); + searchDocuments.forEach(item -> { + FaqItem faqItem = faqItemMap.get(String.valueOf(item.getId())); + if (faqItem != null) { + item.setContent("问题:" + faqItem.getQuestion() + "\n答案:" + faqItem.getAnswerText()); + } + }); + return; + } + + Map chunkMap = documentChunkMapper.selectListByIds(ids).stream() + .collect(Collectors.toMap(item -> item.getId().toString(), item -> item, (a, b) -> a)); + searchDocuments.forEach(item -> { + DocumentChunk documentChunk = chunkMap.get(String.valueOf(item.getId())); + if (documentChunk != null && !StringUtil.noText(documentChunk.getContent())) { + item.setContent(documentChunk.getContent()); + } + }); + } } diff --git a/easyflow-modules/easyflow-module-ai/src/main/java/tech/easyflow/ai/service/impl/DocumentServiceImpl.java b/easyflow-modules/easyflow-module-ai/src/main/java/tech/easyflow/ai/service/impl/DocumentServiceImpl.java index bd9adda..5793fa7 100644 --- a/easyflow-modules/easyflow-module-ai/src/main/java/tech/easyflow/ai/service/impl/DocumentServiceImpl.java +++ b/easyflow-modules/easyflow-module-ai/src/main/java/tech/easyflow/ai/service/impl/DocumentServiceImpl.java @@ -160,6 +160,13 @@ public class DocumentServiceImpl extends ServiceImpl i @Transactional public Result textSplit(DocumentCollectionSplitParams documentCollectionSplitParams) { try { + DocumentCollection knowledge = knowledgeService.getById(documentCollectionSplitParams.getKnowledgeId()); + if (knowledge == null) { + throw new BusinessException("知识库不存在"); + } + if (knowledge.isFaqCollection()) { + throw new BusinessException("FAQ知识库不支持文档上传"); + } String filePath = documentCollectionSplitParams.getFilePath(); String fileOriginName = documentCollectionSplitParams.getFileOriginName(); InputStream inputStream = storageService.readStream(filePath); @@ -264,6 +271,9 @@ public class DocumentServiceImpl extends ServiceImpl i if (knowledge == null) { throw new BusinessException("知识库不存在"); } + if (knowledge.isFaqCollection()) { + throw new BusinessException("FAQ知识库不支持文档上传"); + } DocumentStore documentStore = null; try { documentStore = knowledge.toDocumentStore(); diff --git a/easyflow-modules/easyflow-module-ai/src/main/java/tech/easyflow/ai/service/impl/FaqItemServiceImpl.java b/easyflow-modules/easyflow-module-ai/src/main/java/tech/easyflow/ai/service/impl/FaqItemServiceImpl.java new file mode 100644 index 0000000..c950019 --- /dev/null +++ b/easyflow-modules/easyflow-module-ai/src/main/java/tech/easyflow/ai/service/impl/FaqItemServiceImpl.java @@ -0,0 +1,285 @@ +package tech.easyflow.ai.service.impl; + +import cn.dev33.satoken.stp.StpUtil; +import com.easyagents.core.model.embedding.EmbeddingModel; +import com.easyagents.core.model.embedding.EmbeddingOptions; +import com.easyagents.core.store.DocumentStore; +import com.easyagents.core.store.StoreOptions; +import com.easyagents.core.store.StoreResult; +import com.easyagents.search.engine.service.DocumentSearcher; +import com.mybatisflex.core.query.QueryWrapper; +import com.mybatisflex.spring.service.impl.ServiceImpl; +import org.jsoup.Jsoup; +import org.jsoup.safety.Safelist; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import tech.easyflow.ai.config.SearcherFactory; +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.DocumentCollectionService; +import tech.easyflow.ai.service.FaqItemService; +import tech.easyflow.ai.service.ModelService; +import tech.easyflow.common.util.StringUtil; +import tech.easyflow.common.web.exceptions.BusinessException; + +import javax.annotation.Resource; +import java.math.BigInteger; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +import static tech.easyflow.ai.entity.DocumentCollection.KEY_CAN_UPDATE_EMBEDDING_MODEL; +import static tech.easyflow.ai.entity.DocumentCollection.KEY_SEARCH_ENGINE_TYPE; + +@Service +public class FaqItemServiceImpl extends ServiceImpl implements FaqItemService { + + private static final Safelist ANSWER_SAFE_LIST = Safelist.basic() + .addTags("h1", "h2", "h3", "h4", "h5", "h6"); + + @Resource + private DocumentCollectionService documentCollectionService; + + @Resource + private ModelService modelService; + + @Autowired + private SearcherFactory searcherFactory; + + @Override + @Transactional + public boolean saveFaqItem(FaqItem entity) { + checkAndNormalize(entity, true); + Date now = new Date(); + BigInteger userId = getCurrentUserId(); + entity.setCreated(now); + entity.setModified(now); + entity.setCreatedBy(userId); + entity.setModifiedBy(userId); + if (entity.getOrderNo() == null) { + entity.setOrderNo(nextOrderNo(entity.getCollectionId())); + } + + boolean success = save(entity); + if (!success) { + return false; + } + + DocumentCollection collection = getFaqCollection(entity.getCollectionId()); + storeToVector(collection, entity, false); + return true; + } + + @Override + @Transactional + public boolean updateFaqItem(FaqItem entity) { + if (entity.getId() == null) { + throw new BusinessException("FAQ条目ID不能为空"); + } + FaqItem old = getById(entity.getId()); + if (old == null) { + throw new BusinessException("FAQ条目不存在"); + } + if (entity.getCollectionId() == null) { + entity.setCollectionId(old.getCollectionId()); + } + checkAndNormalize(entity, false); + + old.setQuestion(entity.getQuestion()); + old.setAnswerHtml(entity.getAnswerHtml()); + old.setAnswerText(entity.getAnswerText()); + if (entity.getOrderNo() != null) { + old.setOrderNo(entity.getOrderNo()); + } + old.setModified(new Date()); + old.setModifiedBy(getCurrentUserId()); + old.setOptions(entity.getOptions()); + + boolean success = updateById(old); + if (!success) { + return false; + } + + DocumentCollection collection = getFaqCollection(old.getCollectionId()); + storeToVector(collection, old, true); + return true; + } + + @Override + @Transactional + public boolean removeFaqItem(BigInteger id) { + FaqItem old = getById(id); + if (old == null) { + throw new BusinessException("FAQ条目不存在"); + } + DocumentCollection collection = getFaqCollection(old.getCollectionId()); + removeFromVector(collection, old); + return removeById(id); + } + + private void checkAndNormalize(FaqItem entity, boolean isSave) { + if (entity == null) { + throw new BusinessException("FAQ条目不能为空"); + } + if (entity.getCollectionId() == null) { + throw new BusinessException("知识库ID不能为空"); + } + if (StringUtil.noText(entity.getQuestion())) { + throw new BusinessException("问题不能为空"); + } + if (StringUtil.noText(entity.getAnswerHtml())) { + throw new BusinessException("答案不能为空"); + } + if (isSave && entity.getId() != null) { + throw new BusinessException("新增FAQ条目不允许传入ID"); + } + + entity.setQuestion(entity.getQuestion().trim()); + String cleanHtml = Jsoup.clean(entity.getAnswerHtml(), ANSWER_SAFE_LIST); + String answerText = Jsoup.parse(cleanHtml).text(); + if (StringUtil.noText(answerText)) { + throw new BusinessException("答案不能为空"); + } + entity.setAnswerHtml(cleanHtml); + entity.setAnswerText(answerText); + } + + private DocumentCollection getFaqCollection(BigInteger collectionId) { + DocumentCollection collection = documentCollectionService.getById(collectionId); + if (collection == null) { + throw new BusinessException("知识库不存在"); + } + if (!collection.isFaqCollection()) { + throw new BusinessException("当前知识库不是FAQ类型"); + } + return collection; + } + + private void storeToVector(DocumentCollection collection, FaqItem entity, boolean isUpdate) { + PreparedStore preparedStore = prepareStore(collection); + com.easyagents.core.document.Document doc = toSearchDocument(entity); + StoreResult result = isUpdate + ? preparedStore.documentStore.update(doc, preparedStore.storeOptions) + : preparedStore.documentStore.store(Collections.singletonList(doc), preparedStore.storeOptions); + if (result == null || !result.isSuccess()) { + throw new BusinessException("FAQ向量化失败"); + } + + if (collection.isSearchEngineEnabled()) { + DocumentSearcher searcher = searcherFactory.getSearcher((String) collection.getOptionsByKey(KEY_SEARCH_ENGINE_TYPE)); + if (searcher != null) { + if (isUpdate) { + searcher.deleteDocument(entity.getId()); + } + searcher.addDocument(doc); + } + } + markCollectionEmbedded(collection, preparedStore.embeddingModel); + } + + private void removeFromVector(DocumentCollection collection, FaqItem entity) { + PreparedStore preparedStore = prepareStore(collection); + preparedStore.documentStore.delete(Collections.singletonList(entity.getId()), preparedStore.storeOptions); + + if (collection.isSearchEngineEnabled()) { + DocumentSearcher searcher = searcherFactory.getSearcher((String) collection.getOptionsByKey(KEY_SEARCH_ENGINE_TYPE)); + if (searcher != null) { + searcher.deleteDocument(entity.getId()); + } + } + } + + private PreparedStore prepareStore(DocumentCollection collection) { + DocumentStore documentStore; + try { + documentStore = collection.toDocumentStore(); + } catch (Exception e) { + throw new BusinessException("向量数据库配置错误"); + } + if (documentStore == null) { + throw new BusinessException("向量数据库配置错误"); + } + Model model = modelService.getModelInstance(collection.getVectorEmbedModelId()); + if (model == null) { + throw new BusinessException("该知识库未配置向量模型"); + } + EmbeddingModel embeddingModel = model.toEmbeddingModel(); + documentStore.setEmbeddingModel(embeddingModel); + + StoreOptions options = StoreOptions.ofCollectionName(collection.getVectorStoreCollection()); + EmbeddingOptions embeddingOptions = new EmbeddingOptions(); + embeddingOptions.setModel(model.getModelName()); + embeddingOptions.setDimensions(collection.getDimensionOfVectorModel()); + options.setEmbeddingOptions(embeddingOptions); + options.setIndexName(options.getCollectionName()); + return new PreparedStore(documentStore, options, embeddingModel); + } + + private com.easyagents.core.document.Document toSearchDocument(FaqItem entity) { + String content = buildSearchContent(entity); + com.easyagents.core.document.Document doc = com.easyagents.core.document.Document.of(content); + doc.setId(entity.getId()); + Map metadata = new HashMap<>(); + metadata.put("question", entity.getQuestion()); + metadata.put("answerText", entity.getAnswerText()); + doc.setMetadataMap(metadata); + return doc; + } + + private String buildSearchContent(FaqItem entity) { + return "问题:" + entity.getQuestion() + "\n答案:" + entity.getAnswerText(); + } + + private void markCollectionEmbedded(DocumentCollection collection, EmbeddingModel embeddingModel) { + Map options = collection.getOptions() == null + ? new HashMap<>() + : new HashMap<>(collection.getOptions()); + if (!Boolean.FALSE.equals(options.get(KEY_CAN_UPDATE_EMBEDDING_MODEL))) { + options.put(KEY_CAN_UPDATE_EMBEDDING_MODEL, false); + DocumentCollection updateCollection = new DocumentCollection(); + updateCollection.setId(collection.getId()); + updateCollection.setOptions(options); + documentCollectionService.updateById(updateCollection); + } + if (collection.getDimensionOfVectorModel() == null) { + int dimension = Model.getEmbeddingDimension(embeddingModel); + DocumentCollection updateCollection = new DocumentCollection(); + updateCollection.setId(collection.getId()); + updateCollection.setDimensionOfVectorModel(dimension); + documentCollectionService.updateById(updateCollection); + } + } + + private Integer nextOrderNo(BigInteger collectionId) { + java.util.List list = list(QueryWrapper.create() + .eq(FaqItem::getCollectionId, collectionId) + .orderBy("order_no desc")); + if (list == null || list.isEmpty() || list.get(0).getOrderNo() == null) { + return 0; + } + return list.get(0).getOrderNo() + 1; + } + + private BigInteger getCurrentUserId() { + if (!StpUtil.isLogin()) { + return null; + } + return BigInteger.valueOf(StpUtil.getLoginIdAsLong()); + } + + private static class PreparedStore { + private final DocumentStore documentStore; + private final StoreOptions storeOptions; + private final EmbeddingModel embeddingModel; + + private PreparedStore(DocumentStore documentStore, StoreOptions storeOptions, EmbeddingModel embeddingModel) { + this.documentStore = documentStore; + this.storeOptions = storeOptions; + this.embeddingModel = embeddingModel; + } + } +} diff --git a/easyflow-ui-admin/app/package.json b/easyflow-ui-admin/app/package.json index 0644837..7fa4cb8 100644 --- a/easyflow-ui-admin/app/package.json +++ b/easyflow-ui-admin/app/package.json @@ -29,6 +29,8 @@ "@easyflow/types": "workspace:*", "@easyflow/utils": "workspace:*", "@element-plus/icons-vue": "^2.3.2", + "@wangeditor/editor": "^5.1.23", + "@wangeditor/editor-for-vue": "^5.1.12", "@tinyflow-ai/vue": "workspace:*", "@vueuse/core": "catalog:", "dayjs": "catalog:", diff --git a/easyflow-ui-admin/app/src/components/page/CardList.vue b/easyflow-ui-admin/app/src/components/page/CardList.vue index 5fb9003..e09d1de 100644 --- a/easyflow-ui-admin/app/src/components/page/CardList.vue +++ b/easyflow-ui-admin/app/src/components/page/CardList.vue @@ -1,9 +1,9 @@ diff --git a/easyflow-ui-admin/app/src/views/ai/documentCollection/DocumentCollectionModal.vue b/easyflow-ui-admin/app/src/views/ai/documentCollection/DocumentCollectionModal.vue index 7c371d7..e2221d7 100644 --- a/easyflow-ui-admin/app/src/views/ai/documentCollection/DocumentCollectionModal.vue +++ b/easyflow-ui-admin/app/src/views/ai/documentCollection/DocumentCollectionModal.vue @@ -68,6 +68,7 @@ const vecotrDatabaseList = ref([ ]); const defaultEntity = { + collectionType: 'DOCUMENT', alias: '', deptId: '', icon: '', @@ -108,6 +109,9 @@ const entity = ref(normalizeEntity(defaultEntity)); const btnLoading = ref(false); const rules = ref({ + collectionType: [ + { required: true, message: $t('message.required'), trigger: 'change' }, + ], deptId: [ { required: true, message: $t('message.required'), trigger: 'blur' }, ], @@ -131,6 +135,16 @@ const rules = ref({ { required: true, message: $t('message.required'), trigger: 'blur' }, ], }); +const collectionTypeList = [ + { + label: $t('documentCollection.collectionTypeDocument'), + value: 'DOCUMENT', + }, + { + label: $t('documentCollection.collectionTypeFaq'), + value: 'FAQ', + }, +]; function openDialog(row: any = {}) { if (row.id) { @@ -217,6 +231,19 @@ defineExpose({ :placeholder="$t('documentCollection.placeholder.title')" /> + + + + + +import type {IDomEditor} from '@wangeditor/editor'; + +import {onBeforeUnmount, ref, shallowRef, watch} from 'vue'; + +import {$t} from '@easyflow/locales'; + +import {ElButton, ElDialog, ElForm, ElFormItem, ElInput, ElMessage} from 'element-plus'; +import DOMPurify from 'dompurify'; +import {Editor, Toolbar} from '@wangeditor/editor-for-vue'; +import '@wangeditor/editor/dist/css/style.css'; + +const props = defineProps({ + modelValue: { + type: Boolean, + default: false, + }, + data: { + type: Object as any, + default: () => ({}), + }, +}); + +const emit = defineEmits(['submit', 'update:modelValue']); + +const editorRef = shallowRef(null); +const form = ref({ + id: '', + collectionId: '', + question: '', + answerHtml: '', + orderNo: 0, +}); + +watch( + () => props.data, + (newData: any) => { + form.value = { + id: newData?.id || '', + collectionId: newData?.collectionId || '', + question: newData?.question || '', + answerHtml: newData?.answerHtml || '', + orderNo: newData?.orderNo ?? 0, + }; + }, + { immediate: true, deep: true }, +); + +const toolbarConfig = { + excludeKeys: [ + 'uploadImage', + 'insertImage', + 'group-image', + 'insertVideo', + 'group-video', + 'uploadVideo', + 'todo', + 'emotion', + ], +}; +const editorConfig = { + placeholder: $t('documentCollection.faq.answerPlaceholder'), +}; + +const handleEditorCreated = (editor: IDomEditor) => { + editorRef.value = editor; +}; + +const closeDialog = () => { + emit('update:modelValue', false); +}; + +const handleSubmit = () => { + if (!form.value.question?.trim()) { + ElMessage.error($t('documentCollection.faq.questionRequired')); + return; + } + const sanitizedHtml = DOMPurify.sanitize(form.value.answerHtml || ''); + const pureText = sanitizedHtml.replace(/<[^>]*>/g, '').trim(); + if (!pureText) { + ElMessage.error($t('documentCollection.faq.answerRequired')); + return; + } + emit('submit', { + ...form.value, + question: form.value.question.trim(), + answerHtml: sanitizedHtml, + orderNo: Number(form.value.orderNo) || 0, + }); +}; + +onBeforeUnmount(() => { + const editor = editorRef.value; + if (editor) { + editor.destroy(); + } +}); + + + + + diff --git a/easyflow-ui-admin/app/src/views/ai/documentCollection/FaqTable.vue b/easyflow-ui-admin/app/src/views/ai/documentCollection/FaqTable.vue new file mode 100644 index 0000000..0d30d78 --- /dev/null +++ b/easyflow-ui-admin/app/src/views/ai/documentCollection/FaqTable.vue @@ -0,0 +1,181 @@ + + + + + diff --git a/easyflow-ui-admin/pnpm-lock.yaml b/easyflow-ui-admin/pnpm-lock.yaml index 7a2decc..39ab6b0 100644 --- a/easyflow-ui-admin/pnpm-lock.yaml +++ b/easyflow-ui-admin/pnpm-lock.yaml @@ -643,6 +643,12 @@ importers: '@vueuse/core': specifier: 'catalog:' version: 13.9.0(vue@3.5.24(typescript@5.9.3)) + '@wangeditor/editor': + specifier: ^5.1.23 + version: 5.1.23 + '@wangeditor/editor-for-vue': + specifier: ^5.1.12 + version: 5.1.12(@wangeditor/editor@5.1.23)(vue@3.5.24(typescript@5.9.3)) dayjs: specifier: 'catalog:' version: 1.11.19 @@ -3963,6 +3969,9 @@ packages: peerDependencies: vue: ^3.5.17 + '@transloadit/prettier-bytes@0.0.7': + resolution: {integrity: sha512-VeJbUb0wEKbcwaSlj5n+LscBl9IPgLPkHVGBkh00cztv6X4L/TJXK58LzFuBKX7/GAfiGhIwH67YTLTlzvIzBA==} + '@tsconfig/svelte@5.0.8': resolution: {integrity: sha512-UkNnw1/oFEfecR8ypyHIQuWYdkPvHiwcQ78sh+ymIiYoF+uc5H1UBetbjyqT+vgGJ3qQN6nhucJviX6HesWtKQ==} @@ -4017,6 +4026,9 @@ packages: '@types/estree@1.0.8': resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + '@types/event-emitter@0.3.5': + resolution: {integrity: sha512-zx2/Gg0Eg7gwEiOIIh5w9TrhKKTeQh7CPCOPNc0el4pLSwzebA8SmnHwZs2dWlLONvyulykSwGSQxQHLhjGLvQ==} + '@types/hast@3.0.4': resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==} @@ -4293,6 +4305,23 @@ packages: cpu: [x64] os: [win32] + '@uppy/companion-client@2.2.2': + resolution: {integrity: sha512-5mTp2iq97/mYSisMaBtFRry6PTgZA6SIL7LePteOV5x0/DxKfrZW3DEiQERJmYpHzy7k8johpm2gHnEKto56Og==} + + '@uppy/core@2.3.4': + resolution: {integrity: sha512-iWAqppC8FD8mMVqewavCz+TNaet6HPXitmGXpGGREGrakZ4FeuWytVdrelydzTdXx6vVKkOmI2FLztGg73sENQ==} + + '@uppy/store-default@2.1.1': + resolution: {integrity: sha512-xnpTxvot2SeAwGwbvmJ899ASk5tYXhmZzD/aCFsXePh/v8rNvR2pKlcQUH7cF/y4baUGq3FHO/daKCok/mpKqQ==} + + '@uppy/utils@4.1.3': + resolution: {integrity: sha512-nTuMvwWYobnJcytDO3t+D6IkVq/Qs4Xv3vyoEZ+Iaf8gegZP+rEyoaFT2CK5XLRMienPyqRqNbIfRuFaOWSIFw==} + + '@uppy/xhr-upload@2.1.3': + resolution: {integrity: sha512-YWOQ6myBVPs+mhNjfdWsQyMRWUlrDLMoaG7nvf/G6Y3GKZf8AyjFDjvvJ49XWQ+DaZOftGkHmF1uh/DBeGivJQ==} + peerDependencies: + '@uppy/core': ^2.3.3 + '@vee-validate/zod@4.15.1': resolution: {integrity: sha512-329Z4TDBE5Vx0FdbA8S4eR9iGCFFUNGbxjpQ20ff5b5wGueScjocUIx9JHPa79LTG06RnlUR4XogQsjN4tecKA==} peerDependencies: @@ -4535,6 +4564,93 @@ packages: peerDependencies: vue: ^3.5.17 + '@wangeditor/basic-modules@1.1.7': + resolution: {integrity: sha512-cY9CPkLJaqF05STqfpZKWG4LpxTMeGSIIF1fHvfm/mz+JXatCagjdkbxdikOuKYlxDdeqvOeBmsUBItufDLXZg==} + peerDependencies: + '@wangeditor/core': 1.x + dom7: ^3.0.0 + lodash.throttle: ^4.1.1 + nanoid: ^3.2.0 + slate: ^0.72.0 + snabbdom: ^3.1.0 + + '@wangeditor/code-highlight@1.0.3': + resolution: {integrity: sha512-iazHwO14XpCuIWJNTQTikqUhGKyqj+dUNWJ9288Oym9M2xMVHvnsOmDU2sgUDWVy+pOLojReMPgXCsvvNlOOhw==} + peerDependencies: + '@wangeditor/core': 1.x + dom7: ^3.0.0 + slate: ^0.72.0 + snabbdom: ^3.1.0 + + '@wangeditor/core@1.1.19': + resolution: {integrity: sha512-KevkB47+7GhVszyYF2pKGKtCSj/YzmClsD03C3zTt+9SR2XWT5T0e3yQqg8baZpcMvkjs1D8Dv4fk8ok/UaS2Q==} + peerDependencies: + '@uppy/core': ^2.1.1 + '@uppy/xhr-upload': ^2.0.3 + dom7: ^3.0.0 + is-hotkey: ^0.2.0 + lodash.camelcase: ^4.3.0 + lodash.clonedeep: ^4.5.0 + lodash.debounce: ^4.0.8 + lodash.foreach: ^4.5.0 + lodash.isequal: ^4.5.0 + lodash.throttle: ^4.1.1 + lodash.toarray: ^4.4.0 + nanoid: ^3.2.0 + slate: ^0.72.0 + snabbdom: ^3.1.0 + + '@wangeditor/editor-for-vue@5.1.12': + resolution: {integrity: sha512-0Ds3D8I+xnpNWezAeO7HmPRgTfUxHLMd9JKcIw+QzvSmhC5xUHbpCcLU+KLmeBKTR/zffnS5GQo6qi3GhTMJWQ==} + peerDependencies: + '@wangeditor/editor': '>=5.1.0' + vue: ^3.5.17 + + '@wangeditor/editor@5.1.23': + resolution: {integrity: sha512-0RxfeVTuK1tktUaPROnCoFfaHVJpRAIE2zdS0mpP+vq1axVQpLjM8+fCvKzqYIkH0Pg+C+44hJpe3VVroSkEuQ==} + + '@wangeditor/list-module@1.0.5': + resolution: {integrity: sha512-uDuYTP6DVhcYf7mF1pTlmNn5jOb4QtcVhYwSSAkyg09zqxI1qBqsfUnveeDeDqIuptSJhkh81cyxi+MF8sEPOQ==} + peerDependencies: + '@wangeditor/core': 1.x + dom7: ^3.0.0 + slate: ^0.72.0 + snabbdom: ^3.1.0 + + '@wangeditor/table-module@1.1.4': + resolution: {integrity: sha512-5saanU9xuEocxaemGdNi9t8MCDSucnykEC6jtuiT72kt+/Hhh4nERYx1J20OPsTCCdVr7hIyQenFD1iSRkIQ6w==} + peerDependencies: + '@wangeditor/core': 1.x + dom7: ^3.0.0 + lodash.isequal: ^4.5.0 + lodash.throttle: ^4.1.1 + nanoid: ^3.2.0 + slate: ^0.72.0 + snabbdom: ^3.1.0 + + '@wangeditor/upload-image-module@1.0.2': + resolution: {integrity: sha512-z81lk/v71OwPDYeQDxj6cVr81aDP90aFuywb8nPD6eQeECtOymrqRODjpO6VGvCVxVck8nUxBHtbxKtjgcwyiA==} + peerDependencies: + '@uppy/core': ^2.0.3 + '@uppy/xhr-upload': ^2.0.3 + '@wangeditor/basic-modules': 1.x + '@wangeditor/core': 1.x + dom7: ^3.0.0 + lodash.foreach: ^4.5.0 + slate: ^0.72.0 + snabbdom: ^3.1.0 + + '@wangeditor/video-module@1.1.4': + resolution: {integrity: sha512-ZdodDPqKQrgx3IwWu4ZiQmXI8EXZ3hm2/fM6E3t5dB8tCaIGWQZhmqd6P5knfkRAd3z2+YRSRbxOGfoRSp/rLg==} + peerDependencies: + '@uppy/core': ^2.1.4 + '@uppy/xhr-upload': ^2.0.7 + '@wangeditor/core': 1.x + dom7: ^3.0.0 + nanoid: ^3.2.0 + slate: ^0.72.0 + snabbdom: ^3.1.0 + '@xyflow/svelte@1.4.2': resolution: {integrity: sha512-E6mw8wt3NXS5imGJLWKrdOEOGHAkbFhsVwzb9MPG8ohkPjQ8lMeDM9o3fBSoDNp7xr16s7Q3AOVS2JzVyoY2og==} peerDependencies: @@ -5134,6 +5250,9 @@ packages: resolution: {integrity: sha512-6FqVXeETqWPoGcfzrXb37E50NP0LXT8kAMu5ooZayhWWdgEY4lBEEcbQNXtkuKQsGduxiIcI4gOTsxTmuq/bSg==} engines: {node: '>= 14'} + compute-scroll-into-view@1.0.20: + resolution: {integrity: sha512-UCB0ioiyj8CRjtrvaceBLqqhZCVP+1B8+NWQhmdsm0VXOJtobBCf1dBQmebCCo34qZmUwZfIH2MZLqNHazrfjg==} + concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} @@ -5414,6 +5533,10 @@ packages: resolution: {integrity: sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==} engines: {node: '>=12'} + d@1.0.2: + resolution: {integrity: sha512-MOqHvMWF9/9MX6nza0KgvFH4HpMU0EF5uUDXqX/BtxtU8NfB0QzRtJ8Oe/6SuS4kbhyzVJwjd97EA4PKrzJ8bw==} + engines: {node: '>=0.12'} + dargs@8.1.0: resolution: {integrity: sha512-wAV9QHOsNbwnWdNW2FYvE1P56wtgSbM+3SZcdGiWQILwVjACCXDCI3Ai8QlCjMDB8YK5zySiXZYBiwGmNY3lnw==} engines: {node: '>=12'} @@ -5607,6 +5730,9 @@ packages: dom-zindex@1.0.6: resolution: {integrity: sha512-FKWIhiU96bi3xpP9ewRMgANsoVmMUBnMnmpCT6dPMZOunVYJQmJhSRruoI0XSPoHeIif3kyEuiHbFrOJwEJaEA==} + dom7@3.0.0: + resolution: {integrity: sha512-oNlcUdHsC4zb7Msx7JN3K0Nro1dzJ48knvBOnDPKJ2GV9wl1i5vydJZUSyOfrkKFDZEud/jBsTk92S/VGSAe/g==} + domelementtype@2.3.0: resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==} @@ -5789,6 +5915,17 @@ packages: es-toolkit@1.41.0: resolution: {integrity: sha512-bDd3oRmbVgqZCJS6WmeQieOrzpl3URcWBUVDXxOELlUW2FuW+0glPOz1n0KnRie+PdyvUZcXz2sOn00c6pPRIA==} + es5-ext@0.10.64: + resolution: {integrity: sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg==} + engines: {node: '>=0.10'} + + es6-iterator@2.0.3: + resolution: {integrity: sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==} + + es6-symbol@3.1.4: + resolution: {integrity: sha512-U9bFFjX8tFiATgtkJ1zg25+KviIXpgRvRHS8sau3GfhVzThRQrOeksPeT0BWW2MNZs1OEWJ1DPXOQMn0KKRkvg==} + engines: {node: '>=0.12'} + esbuild@0.25.3: resolution: {integrity: sha512-qKA6Pvai73+M2FtftpNKRxJ78GIjmFXFxd/1DVBqGo/qNhLSfv+G12n9pNoWdytJC8U00TrViOwpjT0zgqQS8Q==} engines: {node: '>=18'} @@ -6011,6 +6148,10 @@ packages: esm-env@1.2.2: resolution: {integrity: sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA==} + esniff@2.0.1: + resolution: {integrity: sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==} + engines: {node: '>=0.10'} + espree@10.4.0: resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -6056,6 +6197,9 @@ packages: resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} engines: {node: '>= 0.6'} + event-emitter@0.3.5: + resolution: {integrity: sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==} + event-target-shim@5.0.1: resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==} engines: {node: '>=6'} @@ -6089,6 +6233,9 @@ packages: exsolve@1.0.7: resolution: {integrity: sha512-VO5fQUzZtI6C+vx4w/4BWJpg3s/5l+6pRQEHzFRM8WFi4XffSP1Z+4qi7GbjWbvRQEbdIco5mIMq+zX4rPuLrw==} + ext@1.7.0: + resolution: {integrity: sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==} + extend@3.0.2: resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} @@ -6568,6 +6715,9 @@ packages: resolution: {integrity: sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ==} engines: {node: '>=8'} + html-void-elements@2.0.1: + resolution: {integrity: sha512-0quDb7s97CfemeJAnW9wC0hw78MtW7NU3hqtCD75g2vFlDLt36llsYD7uB7SUzojLMP24N5IatXf7ylGXiGG9A==} + html-void-elements@3.0.0: resolution: {integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==} @@ -6604,6 +6754,9 @@ packages: resolution: {integrity: sha512-eKCa6bwnJhvxj14kZk5NCPc6Hb6BdsU9DZcOnmQKSnO1VKrfV0zCvtttPZUsBvjmNDn8rpcJfpwSYnHBjc95MQ==} engines: {node: '>=18.18.0'} + i18next@20.6.1: + resolution: {integrity: sha512-yCMYTMEJ9ihCwEQQ3phLo7I/Pwycf8uAx+sRHwwk5U9Aui/IZYgQRyMqXafQOw5QQ7DM1Z+WyEXWIqSuJHhG2A==} + iconv-lite@0.6.3: resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} engines: {node: '>=0.10.0'} @@ -6631,6 +6784,9 @@ packages: engines: {node: '>=0.10.0'} hasBin: true + immer@9.0.21: + resolution: {integrity: sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==} + immutable@5.1.4: resolution: {integrity: sha512-p6u1bG3YSnINT5RQmx/yRZBpenIl30kVxkTLDyHLIMk0gict704Q9n+thfDI7lTRm9vXdDYutVzXhzcThxTnXA==} @@ -6767,6 +6923,9 @@ packages: resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} engines: {node: '>=0.10.0'} + is-hotkey@0.2.0: + resolution: {integrity: sha512-UknnZK4RakDmTgz4PI1wIph5yxSs/mvChWs9ifnlXsKuXgWmOkY/hAE0H/k2MIqH0RlRye0i1oC07MCRSD28Mw==} + is-in-ci@1.0.0: resolution: {integrity: sha512-eUuAjybVTHMYWm/U+vBO1sY/JOCgoPCXRxzdju0K+K0BiGW0SChEL1MLC0PoCIR1OlPo5YAp8HuQoUlsWEICwg==} engines: {node: '>=18'} @@ -6890,6 +7049,9 @@ packages: resolution: {integrity: sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==} engines: {node: '>=18'} + is-url@1.2.4: + resolution: {integrity: sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww==} + is-weakmap@2.0.2: resolution: {integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==} engines: {node: '>= 0.4'} @@ -7208,15 +7370,25 @@ packages: lodash.camelcase@4.3.0: resolution: {integrity: sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==} + lodash.clonedeep@4.5.0: + resolution: {integrity: sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==} + lodash.debounce@4.0.8: resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==} lodash.defaults@4.2.0: resolution: {integrity: sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==} + lodash.foreach@4.5.0: + resolution: {integrity: sha512-aEXTF4d+m05rVOAUG3z4vZZ4xVexLKZGF0lIxuHZ1Hplpk/3B6Z1+/ICICYRLm7c41Z2xiejbkCkJoTlypoXhQ==} + lodash.isarguments@3.1.0: resolution: {integrity: sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==} + lodash.isequal@4.5.0: + resolution: {integrity: sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==} + deprecated: This package is deprecated. Use require('node:util').isDeepStrictEqual instead. + lodash.isplainobject@4.0.6: resolution: {integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==} @@ -7241,6 +7413,12 @@ packages: lodash.startcase@4.4.0: resolution: {integrity: sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==} + lodash.throttle@4.1.1: + resolution: {integrity: sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ==} + + lodash.toarray@4.4.0: + resolution: {integrity: sha512-QyffEA3i5dma5q2490+SgCvDN0pXLmRGSyAANuVi0HQ01Pkfr9fuoKQW8wm1wGBnJITs/mS7wQvS6VshUEBFCw==} + lodash.truncate@4.4.2: resolution: {integrity: sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==} @@ -7491,6 +7669,9 @@ packages: resolution: {integrity: sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==} engines: {node: '>= 0.6'} + mime-match@1.0.2: + resolution: {integrity: sha512-VXp/ugGDVh3eCLOBCiHZMYWQaTNUHv2IJrut+yXA6+JbLPXHglHwfS/5A5L0ll+jkCY7fIzRJcH6OIunF+c6Cg==} + mime-types@2.1.35: resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} engines: {node: '>= 0.6'} @@ -7624,6 +7805,9 @@ packages: mz@2.7.0: resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} + namespace-emitter@2.0.1: + resolution: {integrity: sha512-N/sMKHniSDJBjfrkbS/tpkPj4RAbvW3mr8UAzvlMHyun93XEm83IAvhWtJVHo+RHn/oO8Job5YN4b+wRjSVp5g==} + nanoid@3.3.11: resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} @@ -7651,6 +7835,9 @@ packages: engines: {node: '>= 4.4.x'} hasBin: true + next-tick@1.1.0: + resolution: {integrity: sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==} + nitropack@2.12.9: resolution: {integrity: sha512-t6qqNBn2UDGMWogQuORjbL2UPevB8PvIPsPHmqvWpeGOlPr4P8Oc5oA8t3wFwGmaolM2M/s2SwT23nx9yARmOg==} engines: {node: ^20.19.0 || >=22.12.0} @@ -8502,6 +8689,9 @@ packages: resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} engines: {node: ^10 || ^12 || >=14} + preact@10.28.4: + resolution: {integrity: sha512-uKFfOHWuSNpRFVTnljsCluEFq57OKT+0QdOiQo8XWnQ/pSvg7OpX5eNOejELXJMWy+BwM2nobz0FkvzmnpCNsQ==} + prelude-ls@1.2.1: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} @@ -8953,6 +9143,9 @@ packages: sax@1.4.3: resolution: {integrity: sha512-yqYn1JhPczigF94DMS+shiDMjDowYO6y9+wB/4WgO0Y19jWYk0lQ4tuG5KI7kj4FTp1wxPj5IFfcrz/s1c3jjQ==} + scroll-into-view-if-needed@2.2.31: + resolution: {integrity: sha512-dGCXy99wZQivjmjIqihaBQNjryrz5rueJY7eHfTdyWEiR4ttYpsajb14rn9s5d4DY4EcY6+4+U/maARBXJedkA==} + scslre@0.3.0: resolution: {integrity: sha512-3A6sD0WYP7+QrjbfNA2FN3FsOaGGFoekCVgTyypy53gPxhbkCIjtO6YWgdrfM+n/8sI8JeXZOIxsHjMTNxQ4nQ==} engines: {node: ^14.0.0 || >=16.0.0} @@ -9073,6 +9266,14 @@ packages: resolution: {integrity: sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==} engines: {node: '>=14.16'} + slate-history@0.66.0: + resolution: {integrity: sha512-6MWpxGQZiMvSINlCbMW43E2YBSVMCMCIwQfBzGssjWw4kb0qfvj0pIdblWNRQZD0hR6WHP+dHHgGSeVdMWzfng==} + peerDependencies: + slate: '>=0.65.3' + + slate@0.72.8: + resolution: {integrity: sha512-/nJwTswQgnRurpK+bGJFH1oM7naD5qDmHd89JyiKNT2oOKD8marW0QSBtuFnwEbL5aGCS8AmrhXQgNOsn4osAw==} + slice-ansi@4.0.0: resolution: {integrity: sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==} engines: {node: '>=10'} @@ -9088,6 +9289,10 @@ packages: smob@1.5.0: resolution: {integrity: sha512-g6T+p7QO8npa+/hNx9ohv1E5pVCmWrVCUzUXJyLdMmftX6ER0oiWY/w9knEonLpnOp6b6FenKnMfR8gqwWdwig==} + snabbdom@3.6.3: + resolution: {integrity: sha512-W2lHLLw2qR2Vv0DcMmcxXqcfdBaIcoN+y/86SmHv8fn4DazEQSH6KN3TjZcWvwujW56OHiiirsbHWZb4vx/0fg==} + engines: {node: '>=12.17.0'} + sortablejs@1.15.6: resolution: {integrity: sha512-aNfiuwMEpfBM/CN6LY0ibyhxPfPbyFeBTYJKCvzkJ2GkUpazIt3H+QIPAMHwqQ7tMKaHz1Qj+rJJCqljnf4p3A==} @@ -9141,6 +9346,9 @@ packages: sprintf-js@1.0.3: resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} + ssr-window@3.0.0: + resolution: {integrity: sha512-q+8UfWDg9Itrg0yWK7oe5p/XRCJpJF9OBtXfOPgSJl+u3Xd5KI328RUEvUqSMVM9CiQUEf1QdBzJMkYGErj9QA==} + ssri@12.0.0: resolution: {integrity: sha512-S7iGNosepx9RadX82oimUkvr0Ct7IjJbEbs4mJcTxst8um95J3sDYU1RBEOvdu6oL1Wek2ODI5i4MAw+dZ6cAQ==} engines: {node: ^18.17.0 || >=20.5.0} @@ -9505,6 +9713,9 @@ packages: tiny-emitter@2.1.0: resolution: {integrity: sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==} + tiny-warning@1.0.3: + resolution: {integrity: sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==} + tinybench@2.9.0: resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} @@ -9641,6 +9852,9 @@ packages: resolution: {integrity: sha512-xxCJm+Bckc6kQBknN7i9fnP/xobQRsRQxR01CztFkp/h++yfVxUUcmMgfR2HttJx/dpWjS9ubVuyspJv24Q9DA==} engines: {node: '>=20'} + type@2.7.3: + resolution: {integrity: sha512-8j+1QmAbPvLZow5Qpi6NCaN8FB60p/6x8/vfNqOk/hC+HuvFZhL4+WfekuhQLiqFZXOgQdrs3B+XxEmCc6b3FQ==} + typed-array-buffer@1.0.3: resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==} engines: {node: '>= 0.4'} @@ -10258,6 +10472,9 @@ packages: resolution: {integrity: sha512-c9bZp7b5YtRj2wOe6dlj32MK+Bx/M/d+9VB2SHM1OtsUHR0aV0tdP6DWh/iMt0kWi1t5g1Iudu6hQRNd1A4PVA==} engines: {node: '>=18'} + wildcard@1.1.2: + resolution: {integrity: sha512-DXukZJxpHA8LuotRwL0pP1+rS6CS7FF2qStDDE1C7DDg2rLud2PXRMuEDYIPhgEezwnlHNL4c+N6MfMTjCGTng==} + word-wrap@1.2.5: resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} engines: {node: '>=0.10.0'} @@ -13022,6 +13239,8 @@ snapshots: '@tanstack/virtual-core': 3.13.12 vue: 3.5.24(typescript@5.9.3) + '@transloadit/prettier-bytes@0.0.7': {} + '@tsconfig/svelte@5.0.8': {} '@tybys/wasm-util@0.10.1': @@ -13082,6 +13301,8 @@ snapshots: '@types/estree@1.0.8': {} + '@types/event-emitter@0.3.5': {} + '@types/hast@3.0.4': dependencies: '@types/unist': 3.0.3 @@ -13354,6 +13575,35 @@ snapshots: '@unrs/resolver-binding-win32-x64-msvc@1.11.1': optional: true + '@uppy/companion-client@2.2.2': + dependencies: + '@uppy/utils': 4.1.3 + namespace-emitter: 2.0.1 + + '@uppy/core@2.3.4': + dependencies: + '@transloadit/prettier-bytes': 0.0.7 + '@uppy/store-default': 2.1.1 + '@uppy/utils': 4.1.3 + lodash.throttle: 4.1.1 + mime-match: 1.0.2 + namespace-emitter: 2.0.1 + nanoid: 3.3.11 + preact: 10.28.4 + + '@uppy/store-default@2.1.1': {} + + '@uppy/utils@4.1.3': + dependencies: + lodash.throttle: 4.1.1 + + '@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4)': + dependencies: + '@uppy/companion-client': 2.2.2 + '@uppy/core': 2.3.4 + '@uppy/utils': 4.1.3 + nanoid: 3.3.11 + '@vee-validate/zod@4.15.1(vue@3.5.24(typescript@5.9.3))(zod@3.25.76)': dependencies: type-fest: 4.41.0 @@ -13705,6 +13955,114 @@ snapshots: vue: 3.5.24(typescript@5.9.3) xe-utils: 3.7.9 + '@wangeditor/basic-modules@1.1.7(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3))(dom7@3.0.0)(lodash.throttle@4.1.1)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3)': + dependencies: + '@wangeditor/core': 1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3) + dom7: 3.0.0 + is-url: 1.2.4 + lodash.throttle: 4.1.1 + nanoid: 3.3.11 + slate: 0.72.8 + snabbdom: 3.6.3 + + '@wangeditor/code-highlight@1.0.3(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3))(dom7@3.0.0)(slate@0.72.8)(snabbdom@3.6.3)': + dependencies: + '@wangeditor/core': 1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3) + dom7: 3.0.0 + prismjs: 1.30.0 + slate: 0.72.8 + snabbdom: 3.6.3 + + '@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3)': + dependencies: + '@types/event-emitter': 0.3.5 + '@uppy/core': 2.3.4 + '@uppy/xhr-upload': 2.1.3(@uppy/core@2.3.4) + dom7: 3.0.0 + event-emitter: 0.3.5 + html-void-elements: 2.0.1 + i18next: 20.6.1 + is-hotkey: 0.2.0 + lodash.camelcase: 4.3.0 + lodash.clonedeep: 4.5.0 + lodash.debounce: 4.0.8 + lodash.foreach: 4.5.0 + lodash.isequal: 4.5.0 + lodash.throttle: 4.1.1 + lodash.toarray: 4.4.0 + nanoid: 3.3.11 + scroll-into-view-if-needed: 2.2.31 + slate: 0.72.8 + slate-history: 0.66.0(slate@0.72.8) + snabbdom: 3.6.3 + + '@wangeditor/editor-for-vue@5.1.12(@wangeditor/editor@5.1.23)(vue@3.5.24(typescript@5.9.3))': + dependencies: + '@wangeditor/editor': 5.1.23 + vue: 3.5.24(typescript@5.9.3) + + '@wangeditor/editor@5.1.23': + dependencies: + '@uppy/core': 2.3.4 + '@uppy/xhr-upload': 2.1.3(@uppy/core@2.3.4) + '@wangeditor/basic-modules': 1.1.7(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3))(dom7@3.0.0)(lodash.throttle@4.1.1)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3) + '@wangeditor/code-highlight': 1.0.3(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3))(dom7@3.0.0)(slate@0.72.8)(snabbdom@3.6.3) + '@wangeditor/core': 1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3) + '@wangeditor/list-module': 1.0.5(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3))(dom7@3.0.0)(slate@0.72.8)(snabbdom@3.6.3) + '@wangeditor/table-module': 1.1.4(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3))(dom7@3.0.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3) + '@wangeditor/upload-image-module': 1.0.2(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(@wangeditor/basic-modules@1.1.7(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3))(dom7@3.0.0)(lodash.throttle@4.1.1)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3))(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3))(dom7@3.0.0)(lodash.foreach@4.5.0)(slate@0.72.8)(snabbdom@3.6.3) + '@wangeditor/video-module': 1.1.4(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3))(dom7@3.0.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3) + dom7: 3.0.0 + is-hotkey: 0.2.0 + lodash.camelcase: 4.3.0 + lodash.clonedeep: 4.5.0 + lodash.debounce: 4.0.8 + lodash.foreach: 4.5.0 + lodash.isequal: 4.5.0 + lodash.throttle: 4.1.1 + lodash.toarray: 4.4.0 + nanoid: 3.3.11 + slate: 0.72.8 + snabbdom: 3.6.3 + + '@wangeditor/list-module@1.0.5(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3))(dom7@3.0.0)(slate@0.72.8)(snabbdom@3.6.3)': + dependencies: + '@wangeditor/core': 1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3) + dom7: 3.0.0 + slate: 0.72.8 + snabbdom: 3.6.3 + + '@wangeditor/table-module@1.1.4(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3))(dom7@3.0.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3)': + dependencies: + '@wangeditor/core': 1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3) + dom7: 3.0.0 + lodash.isequal: 4.5.0 + lodash.throttle: 4.1.1 + nanoid: 3.3.11 + slate: 0.72.8 + snabbdom: 3.6.3 + + '@wangeditor/upload-image-module@1.0.2(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(@wangeditor/basic-modules@1.1.7(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3))(dom7@3.0.0)(lodash.throttle@4.1.1)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3))(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3))(dom7@3.0.0)(lodash.foreach@4.5.0)(slate@0.72.8)(snabbdom@3.6.3)': + dependencies: + '@uppy/core': 2.3.4 + '@uppy/xhr-upload': 2.1.3(@uppy/core@2.3.4) + '@wangeditor/basic-modules': 1.1.7(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3))(dom7@3.0.0)(lodash.throttle@4.1.1)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3) + '@wangeditor/core': 1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3) + dom7: 3.0.0 + lodash.foreach: 4.5.0 + slate: 0.72.8 + snabbdom: 3.6.3 + + '@wangeditor/video-module@1.1.4(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3))(dom7@3.0.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3)': + dependencies: + '@uppy/core': 2.3.4 + '@uppy/xhr-upload': 2.1.3(@uppy/core@2.3.4) + '@wangeditor/core': 1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3) + dom7: 3.0.0 + nanoid: 3.3.11 + slate: 0.72.8 + snabbdom: 3.6.3 + '@xyflow/svelte@1.4.2(svelte@5.53.3)': dependencies: '@svelte-put/shortcut': 4.1.0(svelte@5.53.3) @@ -14341,6 +14699,8 @@ snapshots: normalize-path: 3.0.0 readable-stream: 4.7.0 + compute-scroll-into-view@1.0.20: {} + concat-map@0.0.1: {} confbox@0.1.8: {} @@ -14682,6 +15042,11 @@ snapshots: d3-selection: 3.0.0 d3-transition: 3.0.1(d3-selection@3.0.0) + d@1.0.2: + dependencies: + es5-ext: 0.10.64 + type: 2.7.3 + dargs@8.1.0: {} data-uri-to-buffer@4.0.1: {} @@ -14839,6 +15204,10 @@ snapshots: dom-zindex@1.0.6: {} + dom7@3.0.0: + dependencies: + ssr-window: 3.0.0 + domelementtype@2.3.0: {} domhandler@4.3.1: @@ -15075,6 +15444,24 @@ snapshots: es-toolkit@1.41.0: {} + es5-ext@0.10.64: + dependencies: + es6-iterator: 2.0.3 + es6-symbol: 3.1.4 + esniff: 2.0.1 + next-tick: 1.1.0 + + es6-iterator@2.0.3: + dependencies: + d: 1.0.2 + es5-ext: 0.10.64 + es6-symbol: 3.1.4 + + es6-symbol@3.1.4: + dependencies: + d: 1.0.2 + ext: 1.7.0 + esbuild@0.25.3: optionalDependencies: '@esbuild/aix-ppc64': 0.25.3 @@ -15374,6 +15761,13 @@ snapshots: esm-env@1.2.2: {} + esniff@2.0.1: + dependencies: + d: 1.0.2 + es5-ext: 0.10.64 + event-emitter: 0.3.5 + type: 2.7.3 + espree@10.4.0: dependencies: acorn: 8.15.0 @@ -15414,6 +15808,11 @@ snapshots: etag@1.8.1: {} + event-emitter@0.3.5: + dependencies: + d: 1.0.2 + es5-ext: 0.10.64 + event-target-shim@5.0.1: {} eventemitter3@5.0.1: {} @@ -15461,6 +15860,10 @@ snapshots: exsolve@1.0.7: {} + ext@1.7.0: + dependencies: + type: 2.7.3 + extend@3.0.2: {} extendable-error@0.1.7: {} @@ -16042,6 +16445,8 @@ snapshots: html-tags@3.3.1: {} + html-void-elements@2.0.1: {} + html-void-elements@3.0.0: {} htmlparser2@10.0.0: @@ -16083,6 +16488,10 @@ snapshots: human-signals@8.0.1: {} + i18next@20.6.1: + dependencies: + '@babel/runtime': 7.28.4 + iconv-lite@0.6.3: dependencies: safer-buffer: 2.1.2 @@ -16102,6 +16511,8 @@ snapshots: image-size@0.5.5: optional: true + immer@9.0.21: {} + immutable@5.1.4: {} import-fresh@3.3.1: @@ -16236,6 +16647,8 @@ snapshots: dependencies: is-extglob: 2.1.1 + is-hotkey@0.2.0: {} + is-in-ci@1.0.0: {} is-inside-container@1.0.0: @@ -16330,6 +16743,8 @@ snapshots: is-unicode-supported@2.1.0: {} + is-url@1.2.4: {} + is-weakmap@2.0.2: {} is-weakref@1.1.1: @@ -16627,12 +17042,18 @@ snapshots: lodash.camelcase@4.3.0: {} + lodash.clonedeep@4.5.0: {} + lodash.debounce@4.0.8: {} lodash.defaults@4.2.0: {} + lodash.foreach@4.5.0: {} + lodash.isarguments@3.1.0: {} + lodash.isequal@4.5.0: {} + lodash.isplainobject@4.0.6: {} lodash.kebabcase@4.1.1: {} @@ -16649,6 +17070,10 @@ snapshots: lodash.startcase@4.4.0: {} + lodash.throttle@4.1.1: {} + + lodash.toarray@4.4.0: {} + lodash.truncate@4.4.2: {} lodash.uniq@4.5.0: {} @@ -17090,6 +17515,10 @@ snapshots: mime-db@1.54.0: {} + mime-match@1.0.2: + dependencies: + wildcard: 1.1.2 + mime-types@2.1.35: dependencies: mime-db: 1.52.0 @@ -17213,6 +17642,8 @@ snapshots: object-assign: 4.1.1 thenify-all: 1.6.0 + namespace-emitter@2.0.1: {} + nanoid@3.3.11: {} nanoid@5.1.6: {} @@ -17229,6 +17660,8 @@ snapshots: sax: 1.4.3 optional: true + next-tick@1.1.0: {} + nitropack@2.12.9: dependencies: '@cloudflare/kv-asset-handler': 0.4.0 @@ -18175,6 +18608,8 @@ snapshots: picocolors: 1.1.1 source-map-js: 1.2.1 + preact@10.28.4: {} + prelude-ls@1.2.1: {} prettier-linter-helpers@1.0.0: @@ -18630,6 +19065,10 @@ snapshots: sax@1.4.3: {} + scroll-into-view-if-needed@2.2.31: + dependencies: + compute-scroll-into-view: 1.0.20 + scslre@0.3.0: dependencies: '@eslint-community/regexpp': 4.12.2 @@ -18783,6 +19222,17 @@ snapshots: slash@5.1.0: {} + slate-history@0.66.0(slate@0.72.8): + dependencies: + is-plain-object: 5.0.0 + slate: 0.72.8 + + slate@0.72.8: + dependencies: + immer: 9.0.21 + is-plain-object: 5.0.0 + tiny-warning: 1.0.3 + slice-ansi@4.0.0: dependencies: ansi-styles: 4.3.0 @@ -18801,6 +19251,8 @@ snapshots: smob@1.5.0: {} + snabbdom@3.6.3: {} + sortablejs@1.15.6: {} source-map-js@1.2.1: {} @@ -18842,6 +19294,8 @@ snapshots: sprintf-js@1.0.3: {} + ssr-window@3.0.0: {} + ssri@12.0.0: dependencies: minipass: 7.1.2 @@ -19311,6 +19765,8 @@ snapshots: tiny-emitter@2.1.0: {} + tiny-warning@1.0.3: {} + tinybench@2.9.0: {} tinyexec@0.3.2: {} @@ -19412,6 +19868,8 @@ snapshots: dependencies: tagged-tag: 1.0.0 + type@2.7.3: {} + typed-array-buffer@1.0.3: dependencies: call-bound: 1.0.4 @@ -20206,6 +20664,8 @@ snapshots: dependencies: string-width: 7.2.0 + wildcard@1.1.2: {} + word-wrap@1.2.5: {} workbox-background-sync@7.3.0: diff --git a/sql/01-easyflow-v2.ddl.sql b/sql/01-easyflow-v2.ddl.sql index d8579ed..09880d0 100644 --- a/sql/01-easyflow-v2.ddl.sql +++ b/sql/01-easyflow-v2.ddl.sql @@ -240,6 +240,7 @@ DROP TABLE IF EXISTS `tb_document_collection`; CREATE TABLE `tb_document_collection` ( `id` bigint(0) UNSIGNED NOT NULL COMMENT 'Id', + `collection_type` varchar(16) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'DOCUMENT' COMMENT '知识库类型: DOCUMENT/FAQ', `alias` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '别名', `dept_id` bigint(0) UNSIGNED NOT NULL COMMENT '部门ID', `tenant_id` bigint(0) UNSIGNED NOT NULL COMMENT '租户ID', @@ -263,7 +264,8 @@ CREATE TABLE `tb_document_collection` `english_name` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '英文名称', `category_id` bigint(0) UNSIGNED NULL DEFAULT NULL COMMENT '分类ID', PRIMARY KEY (`id`) USING BTREE, - UNIQUE INDEX `tb_ai_knowledge_alias_uindex`(`alias`) USING BTREE + UNIQUE INDEX `tb_ai_knowledge_alias_uindex`(`alias`) USING BTREE, + INDEX `idx_collection_type`(`collection_type`) USING BTREE ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '知识库' ROW_FORMAT = Dynamic; -- ---------------------------- @@ -283,6 +285,28 @@ CREATE TABLE `tb_document_collection_category` PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic; +-- ---------------------------- +-- Table structure for tb_faq_item +-- ---------------------------- +DROP TABLE IF EXISTS `tb_faq_item`; +CREATE TABLE `tb_faq_item` +( + `id` bigint UNSIGNED NOT NULL COMMENT '主键', + `collection_id` bigint UNSIGNED NOT NULL COMMENT '知识库ID', + `question` varchar(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '问题', + `answer_html` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL COMMENT '答案HTML', + `answer_text` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL COMMENT '答案纯文本', + `order_no` int NULL DEFAULT 0 COMMENT '排序', + `options` text CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL COMMENT '扩展项', + `created` datetime NULL DEFAULT NULL COMMENT '创建时间', + `created_by` bigint UNSIGNED NULL DEFAULT NULL COMMENT '创建人', + `modified` datetime NULL DEFAULT NULL COMMENT '更新时间', + `modified_by` bigint UNSIGNED NULL DEFAULT NULL COMMENT '更新人', + PRIMARY KEY (`id`) USING BTREE, + INDEX `idx_faq_collection_id`(`collection_id`) USING BTREE, + INDEX `idx_faq_collection_order`(`collection_id`, `order_no`) USING BTREE +) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = 'FAQ条目' ROW_FORMAT = Dynamic; + -- ---------------------------- -- Table structure for tb_document_history -- ---------------------------- diff --git a/sql/04-easyflow-v2.p2-faq.sql b/sql/04-easyflow-v2.p2-faq.sql new file mode 100644 index 0000000..697cbc0 --- /dev/null +++ b/sql/04-easyflow-v2.p2-faq.sql @@ -0,0 +1,38 @@ +SET NAMES utf8mb4; +SET FOREIGN_KEY_CHECKS = 0; + +-- P2: FAQ知识库增强(新增类型与FAQ条目表) + +-- 1) document_collection 增加知识库类型 +ALTER TABLE tb_document_collection + ADD COLUMN collection_type varchar(16) NOT NULL DEFAULT 'DOCUMENT' COMMENT '知识库类型: DOCUMENT/FAQ' AFTER id; + +-- 2) 回填历史数据 +UPDATE tb_document_collection +SET collection_type = 'DOCUMENT' +WHERE collection_type IS NULL OR collection_type = ''; + +-- 3) 增加类型索引 +ALTER TABLE tb_document_collection + ADD INDEX idx_collection_type (collection_type); + +-- 4) FAQ条目表 +CREATE TABLE IF NOT EXISTS tb_faq_item +( + id bigint UNSIGNED NOT NULL COMMENT '主键', + collection_id bigint UNSIGNED NOT NULL COMMENT '知识库ID', + question varchar(1024) NOT NULL COMMENT '问题', + answer_html longtext NULL COMMENT '答案HTML', + answer_text longtext NULL COMMENT '答案纯文本', + order_no int NULL DEFAULT 0 COMMENT '排序', + options text NULL COMMENT '扩展项', + created datetime NULL COMMENT '创建时间', + created_by bigint UNSIGNED NULL COMMENT '创建人', + modified datetime NULL COMMENT '更新时间', + modified_by bigint UNSIGNED NULL COMMENT '更新人', + PRIMARY KEY (id), + INDEX idx_faq_collection_id (collection_id), + INDEX idx_faq_collection_order (collection_id, order_no) +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT ='FAQ条目'; + +SET FOREIGN_KEY_CHECKS = 1;