diff --git a/internal/pkg/utils/content_chunk.go b/internal/pkg/utils/content_chunk.go
index 154974a..52b9799 100644
--- a/internal/pkg/utils/content_chunk.go
+++ b/internal/pkg/utils/content_chunk.go
@@ -16,6 +16,7 @@ const (
type ContentChunk struct {
Type ContentChunkType //
Content string // text content or image url
+ AssetID string // image asset id
}
func SplitHTMLContentChunks(content string) ([]ContentChunk, error) {
@@ -57,12 +58,14 @@ func SplitHTMLContentChunks(content string) ([]ContentChunk, error) {
case "img":
flushText()
src := strings.TrimSpace(findContentChunkHTMLAttr(node, "src"))
- if src == "" {
+ assetID := strings.TrimSpace(findContentChunkHTMLAttr(node, "data-asset-id"))
+ if src == "" && assetID == "" {
return
}
chunks = append(chunks, ContentChunk{
Type: ContentChunkTypeImage,
Content: src,
+ AssetID: assetID,
})
return
}
diff --git a/internal/pkg/utils/content_chunk_test.go b/internal/pkg/utils/content_chunk_test.go
index a44bd70..32a4463 100644
--- a/internal/pkg/utils/content_chunk_test.go
+++ b/internal/pkg/utils/content_chunk_test.go
@@ -51,6 +51,13 @@ func TestSplitHTMLContentChunks(t *testing.T) {
{Type: ContentChunkTypeText, Content: "后文"},
},
},
+ {
+ name: "image with asset metadata only",
+ content: `
![a]()
`,
+ want: []ContentChunk{
+ {Type: ContentChunkTypeImage, AssetID: "asset_1"},
+ },
+ },
{
name: "empty html returns empty chunks",
content: "
",
diff --git a/internal/pkg/utils/message.go b/internal/pkg/utils/message.go
index c6944e0..fa181e6 100644
--- a/internal/pkg/utils/message.go
+++ b/internal/pkg/utils/message.go
@@ -3,13 +3,11 @@ package utils
import (
"bytes"
"cs-agent/internal/models"
- "cs-agent/internal/pkg/config"
"cs-agent/internal/pkg/enums"
"cs-agent/internal/repositories"
"cs-agent/internal/services/storage"
"encoding/json"
"fmt"
- "net/url"
"strings"
"github.com/microcosm-cc/bluemonday"
@@ -335,7 +333,6 @@ func normalizeHTMLImageAsset(node *html.Node) (*models.Asset, error) {
assetID := strings.TrimSpace(findHTMLAttr(node, "data-asset-id"))
provider := enums.AssetProvider(strings.TrimSpace(findHTMLAttr(node, "data-provider")))
storageKey := strings.TrimSpace(findHTMLAttr(node, "data-storage-key"))
- src := strings.TrimSpace(findHTMLAttr(node, "src"))
hasAssetID := assetID != ""
hasProvider := provider != ""
@@ -353,94 +350,5 @@ func normalizeHTMLImageAsset(node *html.Node) (*models.Asset, error) {
}
return asset, nil
}
- if src == "" {
- return nil, fmt.Errorf("html message image is missing asset metadata")
- }
- asset := findAssetByMessageImageURL(src)
- if asset == nil {
- return nil, fmt.Errorf("html message image must reference an uploaded asset")
- }
- return asset, nil
-}
-
-func findAssetByMessageImageURL(rawURL string) *models.Asset {
- storageKey, err := resolveStorageKeyFromMessageImageURL(rawURL)
- if err != nil {
- return nil
- }
- return repositories.AssetRepository.GetByStorageKey(sqls.DB(), storageKey)
-}
-
-func FindAssetByMessageImageURL(rawURL string) *models.Asset {
- return findAssetByMessageImageURL(rawURL)
-}
-
-func resolveStorageKeyFromMessageImageURL(rawURL string) (string, error) {
- cfg := config.Current().Storage
- candidates := make([]string, 0, 3)
- if baseURL := strings.TrimSpace(cfg.Local.BaseURL); baseURL != "" {
- candidates = append(candidates, baseURL)
- }
- if baseURL := strings.TrimSpace(cfg.OSS.BaseURL); baseURL != "" {
- candidates = append(candidates, baseURL)
- }
- if ossBucketBaseURL := buildOSSBucketBaseURL(cfg.OSS); ossBucketBaseURL != "" {
- candidates = append(candidates, ossBucketBaseURL)
- }
- for _, baseURL := range candidates {
- if storageKey, err := resolveStorageKeyFromAssetURL(baseURL, rawURL); err == nil && storageKey != "" {
- return storageKey, nil
- }
- }
- return "", fmt.Errorf("image url does not match any storage base url")
-}
-
-func buildOSSBucketBaseURL(cfg config.OSSStorageConfig) string {
- endpoint := strings.TrimSpace(cfg.Endpoint)
- bucket := strings.TrimSpace(cfg.Bucket)
- if endpoint == "" || bucket == "" {
- return ""
- }
- if !strings.Contains(endpoint, "://") {
- endpoint = "https://" + endpoint
- }
- u, err := url.Parse(endpoint)
- if err != nil || strings.TrimSpace(u.Host) == "" {
- return ""
- }
- scheme := strings.TrimSpace(u.Scheme)
- if scheme == "" {
- scheme = "https"
- }
- return fmt.Sprintf("%s://%s.%s", scheme, bucket, u.Host)
-}
-
-func resolveStorageKeyFromAssetURL(baseURL, rawURL string) (string, error) {
- baseURL = strings.TrimRight(strings.TrimSpace(baseURL), "/")
- rawURL = strings.TrimSpace(rawURL)
- if baseURL == "" || rawURL == "" {
- return "", fmt.Errorf("invalid image url")
- }
- if strings.HasPrefix(rawURL, baseURL+"/") {
- return strings.TrimLeft(strings.TrimPrefix(rawURL, baseURL), "/"), nil
- }
-
- baseParsed, baseErr := url.Parse(baseURL)
- rawParsed, rawErr := url.Parse(rawURL)
- if baseErr != nil || rawErr != nil {
- return "", fmt.Errorf("invalid image url")
- }
- if !strings.EqualFold(baseParsed.Host, rawParsed.Host) {
- return "", fmt.Errorf("image url host mismatch")
- }
- basePath := strings.TrimRight(baseParsed.Path, "/")
- rawPath := strings.TrimLeft(rawParsed.Path, "/")
- if basePath == "" {
- return rawPath, nil
- }
- basePath = strings.TrimLeft(basePath, "/")
- if !strings.HasPrefix(rawPath, basePath+"/") {
- return "", fmt.Errorf("image url path mismatch")
- }
- return strings.TrimLeft(strings.TrimPrefix(rawPath, basePath), "/"), nil
+ return nil, fmt.Errorf("html message image must include asset metadata")
}
diff --git a/internal/pkg/utils/message_test.go b/internal/pkg/utils/message_test.go
index 04e7110..b87c8a4 100644
--- a/internal/pkg/utils/message_test.go
+++ b/internal/pkg/utils/message_test.go
@@ -100,7 +100,7 @@ func TestBuildRenderableMessageTransformsPayloadAndHTML(t *testing.T) {
}
}
-func TestNormalizeMessageHTMLAssetsEnrichesImageDataAttrs(t *testing.T) {
+func TestNormalizeMessageHTMLAssetsKeepsValidAttrsAndRemovesSrc(t *testing.T) {
setupMessageTestDB(t)
config.SetCurrent(&config.Config{
Storage: config.StorageConfig{
@@ -120,7 +120,7 @@ func TestNormalizeMessageHTMLAssetsEnrichesImageDataAttrs(t *testing.T) {
Status: enums.AssetStatusSuccess,
})
- got, err := NormalizeMessageHTMLAssets(`
`)
+ got, err := NormalizeMessageHTMLAssets(`
`)
if err != nil {
t.Fatalf("expected normalization success, got error: %v", err)
}
@@ -139,7 +139,7 @@ func TestNormalizeMessageHTMLAssetsEnrichesImageDataAttrs(t *testing.T) {
}
}
-func TestNormalizeMessageHTMLAssetsKeepsUnknownImageSrc(t *testing.T) {
+func TestNormalizeMessageHTMLAssetsRejectsMissingAssetMetadata(t *testing.T) {
setupMessageTestDB(t)
config.SetCurrent(&config.Config{
Storage: config.StorageConfig{
@@ -152,7 +152,7 @@ func TestNormalizeMessageHTMLAssetsKeepsUnknownImageSrc(t *testing.T) {
_, err := NormalizeMessageHTMLAssets(`
`)
if err == nil {
- t.Fatalf("expected unknown image src rejected")
+ t.Fatalf("expected missing image asset metadata rejected")
}
}
diff --git a/internal/services/wxwork_kf_outbound_service.go b/internal/services/wxwork_kf_outbound_service.go
index c2b816f..f084cae 100644
--- a/internal/services/wxwork_kf_outbound_service.go
+++ b/internal/services/wxwork_kf_outbound_service.go
@@ -454,8 +454,8 @@ func (s *wxWorkKFOutboundService) buildHTMLChunks(content string) ([]wxWorkKFOut
Content: text,
})
case utils.ContentChunkTypeImage:
- assetID, resolveErr := s.resolveAssetIDFromImageSrc(chunk.Content)
- if resolveErr != nil {
+ assetID := strings.TrimSpace(chunk.AssetID)
+ if assetID == "" {
chunks = append(chunks, wxWorkKFOutboundChunk{
MessageType: enums.IMMessageTypeText,
Content: "[图片]",
@@ -473,11 +473,3 @@ func (s *wxWorkKFOutboundService) buildHTMLChunks(content string) ([]wxWorkKFOut
}
return chunks, nil
}
-
-func (s *wxWorkKFOutboundService) resolveAssetIDFromImageSrc(src string) (string, error) {
- asset := utils.FindAssetByMessageImageURL(src)
- if asset == nil {
- return "", fmt.Errorf("未找到图片资源")
- }
- return strings.TrimSpace(asset.AssetID), nil
-}