feat(enterprise-site): add WordPress support and scheduled auto-publish to sites
Frontend CI / Frontend (push) Successful in 4m17s
Backend CI / Backend (push) Failing after 6m42s

Add WordPress as an enterprise-site CMS type alongside pbootcms, and let
schedule tasks auto-publish generated articles to enterprise sites.

- WordPress connection/publisher service and tests; register wordpress
  media platform and relax cms_type CHECK constraint
- New publish_enterprise_site_targets JSONB column on schedule_tasks with
  array type constraint; thread targets through scheduler dispatch,
  prompt/KOL generation, and worker
- Admin UI: enterprise-site targets in generate/schedule/publish flows,
  KOL package form, MediaView, and i18n strings

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-09 19:44:24 +08:00
parent 41f5623791
commit c7bad83496
30 changed files with 2946 additions and 696 deletions
@@ -25,16 +25,17 @@ import (
)
type PromptRuleGenerationService struct {
pool *pgxpool.Pool
llm llm.Client
rabbitMQ *rabbitmq.Client
knowledge *KnowledgeService
streams *stream.GenerationHub
cfg generationRuntimeConfig
configProvider runtimeConfigProvider
cache sharedcache.Cache
publishJobs *PublishJobService
logger *zap.Logger
pool *pgxpool.Pool
llm llm.Client
rabbitMQ *rabbitmq.Client
knowledge *KnowledgeService
streams *stream.GenerationHub
cfg generationRuntimeConfig
configProvider runtimeConfigProvider
cache sharedcache.Cache
publishJobs *PublishJobService
enterpriseSites *EnterpriseSiteService
logger *zap.Logger
}
type promptRuleGenerationJob struct {
@@ -72,20 +73,21 @@ type GenerateFromRuleItem struct {
}
type SchedulePromptRuleGenerationInput struct {
ScheduleTaskID int64
OperatorID *int64
TenantID int64
PromptRuleID int64
BrandID *int64
Name string
WorkspaceID int64
AutoPublish bool
PublishAccountIDs []string
CoverAssetURL *string
CoverImageAssetID *int64
EnableWebSearch bool
GenerateCount int
ScheduledFor time.Time
ScheduleTaskID int64
OperatorID *int64
TenantID int64
PromptRuleID int64
BrandID *int64
Name string
WorkspaceID int64
AutoPublish bool
PublishAccountIDs []string
PublishEnterpriseSiteTargets []ScheduleEnterpriseSiteTarget
CoverAssetURL *string
CoverImageAssetID *int64
EnableWebSearch bool
GenerateCount int
ScheduledFor time.Time
}
type promptRuleGenerationRecord struct {
@@ -143,6 +145,11 @@ func (s *PromptRuleGenerationService) WithPublishJobService(publishJobs *Publish
return s
}
func (s *PromptRuleGenerationService) WithEnterpriseSiteService(enterpriseSites *EnterpriseSiteService) *PromptRuleGenerationService {
s.enterpriseSites = enterpriseSites
return s
}
func (s *PromptRuleGenerationService) WithLogger(logger *zap.Logger) *PromptRuleGenerationService {
s.logger = logger
return s
@@ -232,6 +239,7 @@ func (s *PromptRuleGenerationService) EnqueueScheduledGeneration(ctx context.Con
if input.AutoPublish {
extra["schedule_auto_publish"] = true
extra["schedule_publish_account_ids"] = input.PublishAccountIDs
extra["schedule_publish_enterprise_site_targets"] = input.PublishEnterpriseSiteTargets
if input.CoverAssetURL != nil {
extra["schedule_cover_asset_url"] = strings.TrimSpace(*input.CoverAssetURL)
}
@@ -323,6 +331,7 @@ func (s *PromptRuleGenerationService) enqueuePromptRuleGeneration(ctx context.Co
if extractBool(extra, "schedule_auto_publish") {
inputParams["schedule_auto_publish"] = true
inputParams["schedule_publish_account_ids"] = extractStringList(extra["schedule_publish_account_ids"], 64)
inputParams["schedule_publish_enterprise_site_targets"] = extractScheduleEnterpriseSiteTargets(extra["schedule_publish_enterprise_site_targets"], scheduleEnterpriseSiteTargetLimit)
if coverURL := strings.TrimSpace(extractString(extra, "schedule_cover_asset_url")); coverURL != "" {
inputParams["schedule_cover_asset_url"] = coverURL
}
@@ -635,69 +644,26 @@ func (s *PromptRuleGenerationService) executeGeneration(ctx context.Context, job
s.streams.Complete(job.ArticleID, job.TaskID, title, content)
}
s.enqueueScheduleAutoPublish(cleanupCtx, job, title)
s.enqueueScheduleAutoPublish(job, title)
}
func (s *PromptRuleGenerationService) enqueueScheduleAutoPublish(ctx context.Context, job promptRuleGenerationJob, title string) {
if s == nil || s.publishJobs == nil || !extractBool(job.InputParams, "schedule_auto_publish") {
func (s *PromptRuleGenerationService) enqueueScheduleAutoPublish(job promptRuleGenerationJob, title string) {
if s == nil || !extractBool(job.InputParams, "schedule_auto_publish") {
return
}
workspaceID := int64(extractInt(job.InputParams, "workspace_id"))
accountIDs := extractStringList(job.InputParams["schedule_publish_account_ids"], 64)
coverURL := strings.TrimSpace(extractString(job.InputParams, "schedule_cover_asset_url"))
if workspaceID <= 0 || job.OperatorID <= 0 || len(accountIDs) == 0 {
s.logAutoPublishWarn("schedule auto publish skipped because configuration is incomplete", nil,
zap.Int64("generation_task_id", job.TaskID),
zap.Int64("article_id", job.ArticleID),
zap.Int64("workspace_id", workspaceID),
zap.Int("account_count", len(accountIDs)),
zap.Bool("has_cover", coverURL != ""),
)
return
}
accounts := make([]CreatePublishJobAccountRequest, 0, len(accountIDs))
for _, accountID := range accountIDs {
accountID = strings.TrimSpace(accountID)
if accountID == "" {
continue
}
accounts = append(accounts, CreatePublishJobAccountRequest{AccountID: accountID})
}
if len(accounts) == 0 {
return
}
publishTitle := strings.TrimSpace(title)
if publishTitle == "" {
publishTitle = "定时生成文章"
}
brandID := int64(extractInt(job.InputParams, "brand_id"))
publishCtx := ctx
if brandID > 0 {
publishCtx = auth.WithCurrentBrandID(ctx, brandID)
}
if _, err := s.publishJobs.Create(publishCtx, auth.Actor{
TenantID: job.TenantID,
UserID: job.OperatorID,
PrimaryWorkspaceID: workspaceID,
}, CreatePublishJobRequest{
Title: publishTitle,
ContentRef: map[string]any{
"article_id": job.ArticleID,
},
Accounts: accounts,
}); err != nil {
s.logAutoPublishWarn("schedule auto publish enqueue failed", err,
zap.Int64("generation_task_id", job.TaskID),
zap.Int64("article_id", job.ArticleID),
zap.Int64("workspace_id", workspaceID),
zap.Int("account_count", len(accounts)),
)
}
StartScheduleAutoPublish(ScheduleAutoPublishInput{
TenantID: job.TenantID,
WorkspaceID: int64(extractInt(job.InputParams, "workspace_id")),
OperatorID: job.OperatorID,
BrandID: int64(extractInt(job.InputParams, "brand_id")),
ArticleID: job.ArticleID,
GenerationTaskID: job.TaskID,
Title: title,
InputParams: job.InputParams,
PublishJobs: s.publishJobs,
EnterpriseSites: s.enterpriseSites,
Logger: s.logger,
})
}
func (s *PromptRuleGenerationService) logAutoPublishWarn(message string, err error, fields ...zap.Field) {