{{ content.title }}
{{ content.summary }}
diff --git a/internal/httpapi/portal.go b/internal/httpapi/portal.go
index 9e71b94..ad0ebe5 100644
--- a/internal/httpapi/portal.go
+++ b/internal/httpapi/portal.go
@@ -11,8 +11,11 @@ import (
)
var publicContentKeys = map[string]struct{}{
- "privacy_policy": {},
- "user_agreement": {},
+ "privacy_policy": {},
+ "user_agreement": {},
+ "platform_intro": {},
+ "platform_introduction": {},
+ "frequently_asked_questions": {},
}
func (a *API) home(w http.ResponseWriter, r *http.Request) {
diff --git a/internal/portal/service.go b/internal/portal/service.go
index e069b89..91c31a1 100644
--- a/internal/portal/service.go
+++ b/internal/portal/service.go
@@ -53,6 +53,7 @@ type Notice struct {
ID int64 `json:"id"`
Title string `json:"title"`
Content string `json:"content"`
+ BodyHTML string `json:"body_html,omitempty"`
SourceType string `json:"source_type"`
StartAt string `json:"start_at"`
EndAt string `json:"end_at"`
@@ -207,13 +208,16 @@ func (s *Service) Notices(ctx context.Context, tenantID, memberID int64, page, p
}
func (s *Service) PopupNotice(ctx context.Context, tenantID, memberID int64) (*Notice, error) {
- row, err := s.repository.LatestPopupNotice(ctx, tenantID, memberID)
- if err != nil || row == nil {
+ row, err := s.repository.PublishedContent(ctx, tenantID, "home_popup_content")
+ if err != nil {
return nil, err
}
- return &Notice{ID: row.ID, Title: row.Title, Content: row.Content,
- SourceType: noticeSourceName(row.SourceType), StartAt: row.StartAt, EndAt: row.EndAt,
- CreatedAt: row.CreatedAt}, nil
+ content := strings.TrimSpace(row.BodyText)
+ if content == "" {
+ content = strings.TrimSpace(row.Summary)
+ }
+ return &Notice{ID: 0, Title: row.Title, Content: content, BodyHTML: row.BodyHTML,
+ SourceType: "managed_content", CreatedAt: row.PublishedAt}, nil
}
func (s *Service) MarkNoticeRead(ctx context.Context, tenantID, memberID, noticeID int64) error {
@@ -225,6 +229,20 @@ func (s *Service) Content(ctx context.Context, tenantID int64, key string) (*Con
if !contentKeyPattern.MatchString(key) {
return nil, ErrContentKeyInvalid
}
+ requestedKey := key
+ titleOverride := ""
+ switch key {
+ case "platform_intro", "user_agreement":
+ key = "platform_introduction"
+ if requestedKey == "user_agreement" {
+ titleOverride = "用户协议"
+ }
+ case "privacy_policy":
+ // 当前后台只维护原型的 12 条正式文本,隐私条款沿用平台协议正文,
+ // 避免为了前端路由额外制造一条无法在原型中对应的初始化数据。
+ key = "platform_introduction"
+ titleOverride = "隐私政策"
+ }
row, err := s.repository.PublishedContent(ctx, tenantID, key)
if err != nil {
return nil, err
@@ -233,7 +251,11 @@ func (s *Service) Content(ctx context.Context, tenantID int64, key string) (*Con
if row.AssetIDs.Valid && strings.TrimSpace(row.AssetIDs.String) != "" {
_ = json.Unmarshal([]byte(row.AssetIDs.String), &assetIDs)
}
- return &Content{ContentKey: row.ContentKey, ContentType: row.ContentType, Title: row.Title,
+ title := row.Title
+ if titleOverride != "" {
+ title = titleOverride
+ }
+ return &Content{ContentKey: requestedKey, ContentType: row.ContentType, Title: title,
Summary: row.Summary, BodyHTML: row.BodyHTML, BodyText: row.BodyText, CoverURL: row.CoverURL,
AssetIDs: assetIDs, Version: row.Version, PublishedAt: row.PublishedAt}, nil
}
diff --git a/internal/portal/service_test.go b/internal/portal/service_test.go
index afc4e1b..4934e98 100644
--- a/internal/portal/service_test.go
+++ b/internal/portal/service_test.go
@@ -203,15 +203,29 @@ func TestNoticesRejectsOversizedPage(t *testing.T) {
}
}
-func TestPopupNoticeMapsLatestUnreadNotice(t *testing.T) {
- service := New(fakeRepository{notices: []store.MemberNotice{{
- ID: 9, Title: "公告通知", Content: "新活动已上线", SourceType: 1,
- }}})
+func TestPopupNoticeUsesManagedContent(t *testing.T) {
+ service := New(fakeRepository{content: &store.Content{
+ ContentKey: "home_popup_content", Title: "公告通知", BodyText: "新活动已上线",
+ PublishedAt: "2026-10-16T08:00:00Z",
+ }})
notice, err := service.PopupNotice(context.Background(), 1, 7)
if err != nil {
t.Fatal(err)
}
- if notice == nil || notice.ID != 9 || notice.Content != "新活动已上线" || notice.SourceType != "real" {
+ if notice == nil || notice.ID != 0 || notice.Content != "新活动已上线" || notice.SourceType != "managed_content" {
t.Fatalf("unexpected popup notice: %+v", notice)
}
}
+
+func TestContentAliasesPublicAgreementRoutesToManagedText(t *testing.T) {
+ service := New(fakeRepository{content: &store.Content{
+ ContentKey: "platform_introduction", Title: "平台简介", BodyText: "服务条款正文",
+ }})
+ content, err := service.Content(context.Background(), 1, "user_agreement")
+ if err != nil {
+ t.Fatal(err)
+ }
+ if content.ContentKey != "user_agreement" || content.Title != "用户协议" || content.BodyText != "服务条款正文" {
+ t.Fatalf("unexpected aliased content: %+v", content)
+ }
+}
diff --git a/web/src/mobile/components/AnnouncementPopup.vue b/web/src/mobile/components/AnnouncementPopup.vue
index 32c2307..775c46d 100644
--- a/web/src/mobile/components/AnnouncementPopup.vue
+++ b/web/src/mobile/components/AnnouncementPopup.vue
@@ -15,7 +15,8 @@
平台用户可通过推荐新人成为平台代理,代理能够获得额外动态奖励。
-被推荐用户获得每日佣金后,代理按对应层级比例获得奖励。
+平台用户可通过推荐新人成为平台代理,代理能够获得额外动态奖励。
+被推荐用户获得每日佣金后,代理按对应层级比例获得奖励。
+{{ content.summary }}
{{ platformIntroLead }}
-{{ intro.summary }}
+{{ paragraph }}
-{{ paragraph }}
-暂无常见问题。