feat: harden support, localize knowledge and add operator availability [skip ci]

Source-only publication of reviewed commit 64e96a7f423673dc29ad56485be9fc79bce8af1c.

Passed in isolation: focused frontend/backend tests, race checks, TypeScript typecheck, lint, Go compilation, Flowgram build, and deterministic SDK build/tests.

Not accepted as a production release: Next.js export/native assembly remains blocked by environment memory limits; browser interaction and MySQL staging acceptance remain unverified. No production server, database/schema, runtime configuration, deployment, release tag, or workflow execution was performed.

Expected source tree: 0a4a7528e9.
This commit is contained in:
2026-10-16 15:21:42 +00:00
parent 299ea50fb5
commit 2f7a491666
176 changed files with 11869 additions and 2716 deletions

View File

@@ -1,3 +1,4 @@
// Modified in the independent AgentDesk fork: localized knowledge workflows and safe display errors.
package services
import (
@@ -43,27 +44,27 @@ type knowledgeFAQImportRow struct {
Remark string
}
func (s *knowledgeFAQService) BuildKnowledgeFAQImportTemplate() (*response.KnowledgeFAQExportedFile, error) {
func (s *knowledgeFAQService) BuildKnowledgeFAQImportTemplate(locales ...string) (*response.KnowledgeFAQExportedFile, error) {
workbook := excelize.NewFile()
defer workbook.Close()
if err := writeKnowledgeFAQWorkbookHeader(workbook); err != nil {
if err := writeKnowledgeFAQWorkbookHeader(workbook, locales...); err != nil {
return nil, err
}
_ = workbook.SetCellValue(knowledgeFAQExcelSheetName, "A2", "如何重置密码?")
_ = workbook.SetCellValue(knowledgeFAQExcelSheetName, "B2", "进入个人设置后点击重置密码。")
_ = workbook.SetCellValue(knowledgeFAQExcelSheetName, "C2", "忘记密码怎么办\n重置密码在哪里")
_ = workbook.SetCellValue(knowledgeFAQExcelSheetName, "D2", "账号")
_ = workbook.SetCellValue(knowledgeFAQExcelSheetName, "A2", i18nx.Getf(knowledgeFAQExcelLocale(locales), "knowledge.excel.exampleQuestion"))
_ = workbook.SetCellValue(knowledgeFAQExcelSheetName, "B2", i18nx.Getf(knowledgeFAQExcelLocale(locales), "knowledge.excel.exampleAnswer"))
_ = workbook.SetCellValue(knowledgeFAQExcelSheetName, "C2", i18nx.Getf(knowledgeFAQExcelLocale(locales), "knowledge.excel.exampleSimilar"))
_ = workbook.SetCellValue(knowledgeFAQExcelSheetName, "D2", i18nx.Getf(knowledgeFAQExcelLocale(locales), "knowledge.excel.exampleRemark"))
return buildKnowledgeFAQExcelFile("knowledge-faq-import-template.xlsx", workbook)
}
func (s *knowledgeFAQService) ExportKnowledgeFAQs(knowledgeBaseID int64) (*response.KnowledgeFAQExportedFile, error) {
func (s *knowledgeFAQService) ExportKnowledgeFAQs(knowledgeBaseID int64, locales ...string) (*response.KnowledgeFAQExportedFile, error) {
if _, err := s.requireFAQKnowledgeBase(knowledgeBaseID); err != nil {
return nil, err
}
list := repositories.KnowledgeFAQRepository.FindAllByKnowledgeBaseID(sqls.DB(), knowledgeBaseID)
workbook := excelize.NewFile()
defer workbook.Close()
if err := writeKnowledgeFAQWorkbookHeader(workbook); err != nil {
if err := writeKnowledgeFAQWorkbookHeader(workbook, locales...); err != nil {
return nil, err
}
for index, item := range list {
@@ -182,7 +183,7 @@ func (s *knowledgeFAQService) ImportKnowledgeFAQs(req request.ImportKnowledgeFAQ
return result, nil
}
func writeKnowledgeFAQWorkbookHeader(workbook *excelize.File) error {
func writeKnowledgeFAQWorkbookHeader(workbook *excelize.File, locales ...string) error {
sheet := workbook.GetSheetName(0)
if sheet == "" {
sheet = knowledgeFAQExcelSheetName
@@ -192,7 +193,13 @@ func writeKnowledgeFAQWorkbookHeader(workbook *excelize.File) error {
return err
}
}
headers := []string{"标准问题", "答案", "相似问", "备注"}
locale := knowledgeFAQExcelLocale(locales)
headers := []string{
i18nx.Getf(locale, "knowledge.excel.question"),
i18nx.Getf(locale, "knowledge.excel.answer"),
i18nx.Getf(locale, "knowledge.excel.similarQuestions"),
i18nx.Getf(locale, "knowledge.excel.remark"),
}
for index, header := range headers {
cell, err := excelize.CoordinatesToCellName(index+1, 1)
if err != nil {
@@ -274,7 +281,7 @@ func parseKnowledgeFAQImportRows(reader io.Reader, locale string) ([]knowledgeFA
SimilarQuestions: parseKnowledgeFAQSimilarQuestions(getKnowledgeFAQExcelCell(current, headerMap["similarQuestions"])),
Remark: getKnowledgeFAQExcelCell(current, headerMap["remark"]),
}
if row.Question == "" {
if strings.TrimSpace(row.Question) == "" {
result.Failed++
result.Errors = append(result.Errors, response.KnowledgeFAQImportError{Row: rowNo, Message: i18nx.Getf(locale, "error.e0340")})
continue
@@ -284,7 +291,7 @@ func parseKnowledgeFAQImportRows(reader io.Reader, locale string) ([]knowledgeFA
result.Errors = append(result.Errors, response.KnowledgeFAQImportError{Row: rowNo, Message: i18nx.Getf(locale, "error.e0341")})
continue
}
if row.Answer == "" {
if strings.TrimSpace(row.Answer) == "" {
result.Failed++
result.Errors = append(result.Errors, response.KnowledgeFAQImportError{Row: rowNo, Message: i18nx.Getf(locale, "error.e0292")})
continue
@@ -302,22 +309,37 @@ func parseKnowledgeFAQImportRows(reader io.Reader, locale string) ([]knowledgeFA
func buildKnowledgeFAQHeaderMap(headerRow []string) map[string]int {
accepted := map[string]string{
"标准问题": "question",
"问题": "question",
"question": "question",
"答案": "answer",
"answer": "answer",
"相似问": "similarQuestions",
"相似问题": "similarQuestions",
"similarquestions": "similarQuestions",
"备注": "remark",
"remark": "remark",
"标准问题": "question",
"问题": "question",
"question": "question",
"primary question": "question",
"standard question": "question",
"основной вопрос": "question",
"стандартный вопрос": "question",
"вопрос": "question",
"答案": "answer",
"answer": "answer",
"ответ": "answer",
"相似问": "similarQuestions",
"相似问题": "similarQuestions",
"similarquestions": "similarQuestions",
"similar questions": "similarQuestions",
"similar_questions": "similarQuestions",
"похожие вопросы": "similarQuestions",
"похожие вопрос": "similarQuestions",
"схожие вопросы": "similarQuestions",
"备注": "remark",
"remark": "remark",
"remarks": "remark",
"notes": "remark",
"примечание": "remark",
"примечания": "remark",
}
result := make(map[string]int)
result := map[string]int{"similarQuestions": -1, "remark": -1}
for index, header := range headerRow {
key := strings.ToLower(strings.TrimSpace(strings.TrimPrefix(header, "\uFEFF")))
if field, ok := accepted[key]; ok {
if _, exists := result[field]; !exists {
if existing, exists := result[field]; !exists || existing < 0 {
result[field] = index
}
}
@@ -329,7 +351,8 @@ func getKnowledgeFAQExcelCell(row []string, index int) string {
if index < 0 || index >= len(row) {
return ""
}
return strings.TrimSpace(row[index])
// Keep authored Q/A and notes verbatim; trim only for validation and headers.
return row[index]
}
func isKnowledgeFAQExcelRowEmpty(row []string) bool {
@@ -346,8 +369,8 @@ func parseKnowledgeFAQSimilarQuestions(value string) []string {
items := make([]string, 0, len(lines))
seen := make(map[string]struct{}, len(lines))
for _, line := range lines {
item := strings.TrimSpace(strings.ReplaceAll(line, "\r", ""))
if item == "" {
item := strings.TrimSuffix(line, "\r")
if strings.TrimSpace(item) == "" {
continue
}
if _, ok := seen[item]; ok {
@@ -369,3 +392,10 @@ func decodeKnowledgeFAQSimilarQuestions(value string) []string {
}
return items
}
func knowledgeFAQExcelLocale(locales []string) string {
if len(locales) > 0 {
return i18nx.NormalizeLocale(locales[0])
}
return i18nx.DefaultLocale
}