Files
qiweimanager-master/helper/after_sales_file.go

223 lines
6.6 KiB
Go

package main
import (
"fmt"
"os"
"path/filepath"
"strings"
)
const (
afterSalesFileContentLimit = 6000
afterSalesFilePromptLimit = 1800
afterSalesFileStatusReady = "parsed"
afterSalesFileStatusSaved = "saved"
afterSalesFileStatusUnsupported = "unsupported"
afterSalesFileStatusMissing = "missing"
afterSalesFileStatusDownloadFail = "download_failed"
afterSalesFileStatusExtractFailed = "extract_failed"
)
func extractAfterSalesFileFromMessage(msg autoReplyMessage, raw map[string]interface{}, sourceMessageID string) AfterSalesFileAttachment {
if !looksLikeAfterSalesFileMessage(msg, raw) {
return AfterSalesFileAttachment{}
}
attachment := AfterSalesFileAttachment{
Name: strings.TrimSpace(msg.MediaFileName),
Ref: firstNonEmpty(strings.TrimSpace(msg.MediaURL), strings.TrimSpace(msg.MediaFileID)),
SourceMessageID: strings.TrimSpace(sourceMessageID),
}
for _, value := range []string{msg.MediaLocalPath, firstLocalMediaPathFromValue(raw)} {
if path := normalizedExistingFilePath(value); path != "" {
attachment.Path = path
break
}
}
if attachment.Path == "" {
if path, err := ensureAutoReplyMediaLocalPath(msg); err == nil {
attachment.Path = normalizedExistingFilePath(path)
} else {
attachment.ExtractStatus = afterSalesFileStatusDownloadFail
}
}
if attachment.Name == "" {
attachment.Name = firstNonEmpty(filepath.Base(attachment.Path), filepath.Base(strings.TrimSpace(msg.MediaURL)), strings.TrimSpace(msg.MediaFileID), "客户附件")
}
if attachment.Path == "" {
if attachment.ExtractStatus == "" {
attachment.ExtractStatus = afterSalesFileStatusDownloadFail
}
return attachment
}
content, status := extractAfterSalesFileContent(attachment.Path)
attachment.Content = content
attachment.ExtractStatus = status
return normalizeAfterSalesFileAttachment(attachment)
}
func looksLikeAfterSalesFileMessage(msg autoReplyMessage, raw map[string]interface{}) bool {
if strings.TrimSpace(msg.MediaKind) == "file" || msg.RawType == 11045 {
return true
}
if raw != nil {
if typeVal, ok := raw["type"]; ok && intFromAny(typeVal) == 11045 {
return true
}
if event := strings.TrimSpace(stringFromAny(raw["event"])); event == "20005" {
return true
}
}
return false
}
func normalizedExistingFilePath(value string) string {
value = strings.Trim(strings.TrimSpace(value), "\"'")
if value == "" {
return ""
}
if filepath.IsAbs(value) {
if _, err := os.Stat(value); err == nil {
return value
}
return ""
}
candidate := resolveAutoReplyPath(value)
if _, err := os.Stat(candidate); err == nil {
return candidate
}
return ""
}
func extractAfterSalesFileContent(path string) (string, string) {
if strings.TrimSpace(path) == "" {
return "", afterSalesFileStatusMissing
}
info, err := os.Stat(path)
if err != nil {
return "", afterSalesFileStatusMissing
}
if info.IsDir() {
return "", afterSalesFileStatusUnsupported
}
ext := strings.ToLower(filepath.Ext(path))
if ext == ".zip" {
return "", "zip_not_parsed"
}
switch ext {
case ".txt", ".md", ".csv", ".xlsx", ".docx", ".pdf":
default:
return "", afterSalesFileStatusUnsupported
}
blocks, err := parseKnowledgeFile(path, filepath.Dir(path))
if err != nil {
var warning knowledgeParseWarning
if !errorAs(err, &warning) {
return "", afterSalesFileStatusExtractFailed + ": " + truncateText(err.Error(), 160)
}
}
parts := make([]string, 0, len(blocks))
for _, block := range blocks {
text := strings.TrimSpace(block.Content)
if text == "" {
continue
}
if strings.TrimSpace(block.Title) != "" {
text = strings.TrimSpace(block.Title) + "\n" + text
}
parts = append(parts, text)
}
content := truncateText(strings.TrimSpace(strings.Join(parts, "\n\n")), afterSalesFileContentLimit)
if content == "" {
return "", afterSalesFileStatusSaved
}
return content, afterSalesFileStatusReady
}
func normalizeAfterSalesFileAttachment(item AfterSalesFileAttachment) AfterSalesFileAttachment {
item.Name = strings.TrimSpace(item.Name)
item.Path = strings.Trim(strings.TrimSpace(item.Path), "\"'")
item.Ref = strings.TrimSpace(item.Ref)
item.Content = truncateText(strings.TrimSpace(item.Content), afterSalesFileContentLimit)
item.ExtractStatus = strings.TrimSpace(item.ExtractStatus)
item.SourceMessageID = strings.TrimSpace(item.SourceMessageID)
if item.Name == "" {
item.Name = firstNonEmpty(filepath.Base(item.Path), filepath.Base(item.Ref), "客户附件")
}
if item.ExtractStatus == "" {
if item.Content != "" {
item.ExtractStatus = afterSalesFileStatusReady
} else if item.Path != "" {
item.ExtractStatus = afterSalesFileStatusSaved
} else {
item.ExtractStatus = afterSalesFileStatusDownloadFail
}
}
return item
}
func normalizeAfterSalesFileAttachments(items []AfterSalesFileAttachment) []AfterSalesFileAttachment {
seen := make(map[string]struct{})
result := make([]AfterSalesFileAttachment, 0, len(items))
for _, item := range items {
item = normalizeAfterSalesFileAttachment(item)
if item.Path == "" && item.Ref == "" && item.Name == "" && item.Content == "" {
continue
}
key := firstNonEmpty(item.SourceMessageID, item.Path, item.Ref, item.Name+"|"+item.Content)
if _, exists := seen[key]; exists {
continue
}
seen[key] = struct{}{}
result = append(result, item)
}
return result
}
func afterSalesFilePromptText(files []AfterSalesFileAttachment, limit int) string {
if limit <= 0 {
limit = afterSalesFilePromptLimit
}
var b strings.Builder
for _, file := range normalizeAfterSalesFileAttachments(files) {
if b.Len() > 0 {
b.WriteString("\n")
}
b.WriteString("文件:")
b.WriteString(firstNonEmpty(file.Name, filepath.Base(file.Path), file.Ref))
if file.ExtractStatus != "" {
b.WriteString(" 状态:")
b.WriteString(file.ExtractStatus)
}
if file.Content != "" {
b.WriteString("\n内容:\n")
b.WriteString(truncateText(file.Content, limit))
}
}
return strings.TrimSpace(b.String())
}
func formatAfterSalesFileAttachmentsForExcel(files []AfterSalesFileAttachment, includeContent bool) string {
files = normalizeAfterSalesFileAttachments(files)
if len(files) == 0 {
return ""
}
lines := make([]string, 0, len(files))
for _, file := range files {
line := firstNonEmpty(file.Name, filepath.Base(file.Path), file.Ref, "客户附件")
if file.Path != "" {
line += " | " + file.Path
} else if file.Ref != "" {
line += " | " + file.Ref
}
if file.ExtractStatus != "" {
line += " | " + file.ExtractStatus
}
if includeContent && file.Content != "" {
line += fmt.Sprintf("\n%s", truncateText(file.Content, 1200))
}
lines = append(lines, line)
}
return strings.Join(lines, "\n\n")
}