feat(schedule-tasks): support auto-publish via media accounts
Replace the legacy target_platform string on schedule tasks with a workspace-aware auto-publish payload (auto_publish + publish_account_ids JSONB + cover_asset_url + cover_image_asset_id) and migrate the schema, sqlc queries, generated models, domain struct, ScheduleTaskService DTOs, and dispatch worker to round-trip the new fields. PromptRuleGeneration gains a WithPublishJobService hook so executeGeneration can enqueue an auto-publish job once the article is ready, and worker-generate wires the publish-job service in. On the admin-web side, extract PublishArticleModal's account-card builders into a shared publish-account-cards module, rebuild GenerateTaskDrawer's schedule mode around account selection plus a CoverPickerModal, and surface the new "auto publish" column on ScheduleTaskTab. Shared types and i18n strings cover the new fields. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -2,6 +2,7 @@ package scheduler
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
@@ -37,19 +38,23 @@ type ScheduleDispatchWorker struct {
|
||||
}
|
||||
|
||||
type dueScheduleTask struct {
|
||||
ID int64
|
||||
TenantID int64
|
||||
OperatorID *int64
|
||||
PromptRuleID int64
|
||||
BrandID *int64
|
||||
Name string
|
||||
CronExpr string
|
||||
TargetPlatform *string
|
||||
EnableWebSearch bool
|
||||
GenerateCount int
|
||||
StartAt *time.Time
|
||||
EndAt *time.Time
|
||||
ScheduledFor time.Time
|
||||
ID int64
|
||||
TenantID int64
|
||||
WorkspaceID int64
|
||||
OperatorID *int64
|
||||
PromptRuleID int64
|
||||
BrandID *int64
|
||||
Name string
|
||||
CronExpr string
|
||||
AutoPublish bool
|
||||
PublishAccountIDs []string
|
||||
CoverAssetURL *string
|
||||
CoverImageAssetID *int64
|
||||
EnableWebSearch bool
|
||||
GenerateCount int
|
||||
StartAt *time.Time
|
||||
EndAt *time.Time
|
||||
ScheduledFor time.Time
|
||||
}
|
||||
|
||||
type scheduleTaskCacheUpdate struct {
|
||||
@@ -250,7 +255,8 @@ func (w *ScheduleDispatchWorker) claimDueSchedules(ctx context.Context) ([]dueSc
|
||||
defer tx.Rollback(ctx)
|
||||
|
||||
rows, err := tx.Query(ctx, `
|
||||
SELECT id, tenant_id, operator_id, prompt_rule_id, brand_id, name, cron_expr, target_platform,
|
||||
SELECT id, tenant_id, workspace_id, operator_id, prompt_rule_id, brand_id, name, cron_expr,
|
||||
auto_publish, publish_account_ids, cover_asset_url, cover_image_asset_id,
|
||||
enable_web_search, generate_count, start_at, end_at, next_run_at
|
||||
FROM schedule_tasks
|
||||
WHERE deleted_at IS NULL
|
||||
@@ -273,21 +279,27 @@ func (w *ScheduleDispatchWorker) claimDueSchedules(ctx context.Context) ([]dueSc
|
||||
}, 0, w.batchSize)
|
||||
for rows.Next() {
|
||||
var (
|
||||
task dueScheduleTask
|
||||
operatorID pgtype.Int8
|
||||
startAt pgtype.Timestamptz
|
||||
endAt pgtype.Timestamptz
|
||||
nextRunAt pgtype.Timestamptz
|
||||
task dueScheduleTask
|
||||
workspaceID pgtype.Int8
|
||||
operatorID pgtype.Int8
|
||||
publishAccountIDsJSON []byte
|
||||
startAt pgtype.Timestamptz
|
||||
endAt pgtype.Timestamptz
|
||||
nextRunAt pgtype.Timestamptz
|
||||
)
|
||||
if err := rows.Scan(
|
||||
&task.ID,
|
||||
&task.TenantID,
|
||||
&workspaceID,
|
||||
&operatorID,
|
||||
&task.PromptRuleID,
|
||||
&task.BrandID,
|
||||
&task.Name,
|
||||
&task.CronExpr,
|
||||
&task.TargetPlatform,
|
||||
&task.AutoPublish,
|
||||
&publishAccountIDsJSON,
|
||||
&task.CoverAssetURL,
|
||||
&task.CoverImageAssetID,
|
||||
&task.EnableWebSearch,
|
||||
&task.GenerateCount,
|
||||
&startAt,
|
||||
@@ -300,7 +312,11 @@ func (w *ScheduleDispatchWorker) claimDueSchedules(ctx context.Context) ([]dueSc
|
||||
|
||||
task.StartAt = timePtrFromTimestamp(startAt)
|
||||
task.EndAt = timePtrFromTimestamp(endAt)
|
||||
if workspaceID.Valid {
|
||||
task.WorkspaceID = workspaceID.Int64
|
||||
}
|
||||
task.OperatorID = int64PtrFromInt8(operatorID)
|
||||
task.PublishAccountIDs = decodeDueSchedulePublishAccountIDs(publishAccountIDsJSON)
|
||||
if task.GenerateCount <= 0 {
|
||||
task.GenerateCount = 1
|
||||
}
|
||||
@@ -372,16 +388,20 @@ func (w *ScheduleDispatchWorker) dispatch(parent context.Context, task dueSchedu
|
||||
for idx := 0; idx < generateCount; idx++ {
|
||||
ctx, cancel := context.WithTimeout(parent, w.timeout)
|
||||
resp, err := w.promptRuleService.EnqueueScheduledGeneration(ctx, tenantapp.SchedulePromptRuleGenerationInput{
|
||||
ScheduleTaskID: task.ID,
|
||||
OperatorID: task.OperatorID,
|
||||
TenantID: task.TenantID,
|
||||
PromptRuleID: task.PromptRuleID,
|
||||
BrandID: task.BrandID,
|
||||
Name: task.Name,
|
||||
TargetPlatform: task.TargetPlatform,
|
||||
EnableWebSearch: task.EnableWebSearch,
|
||||
GenerateCount: generateCount,
|
||||
ScheduledFor: task.ScheduledFor,
|
||||
ScheduleTaskID: task.ID,
|
||||
OperatorID: task.OperatorID,
|
||||
TenantID: task.TenantID,
|
||||
PromptRuleID: task.PromptRuleID,
|
||||
BrandID: task.BrandID,
|
||||
Name: task.Name,
|
||||
WorkspaceID: task.WorkspaceID,
|
||||
AutoPublish: task.AutoPublish,
|
||||
PublishAccountIDs: task.PublishAccountIDs,
|
||||
CoverAssetURL: task.CoverAssetURL,
|
||||
CoverImageAssetID: task.CoverImageAssetID,
|
||||
EnableWebSearch: task.EnableWebSearch,
|
||||
GenerateCount: generateCount,
|
||||
ScheduledFor: task.ScheduledFor,
|
||||
})
|
||||
cancel()
|
||||
if err != nil {
|
||||
@@ -522,3 +542,26 @@ func int64PtrFromInt8(value pgtype.Int8) *int64 {
|
||||
result := value.Int64
|
||||
return &result
|
||||
}
|
||||
|
||||
func decodeDueSchedulePublishAccountIDs(raw []byte) []string {
|
||||
if len(raw) == 0 {
|
||||
return []string{}
|
||||
}
|
||||
var values []string
|
||||
if err := json.Unmarshal(raw, &values); err != nil {
|
||||
return []string{}
|
||||
}
|
||||
result := make([]string, 0, len(values))
|
||||
seen := make(map[string]struct{}, len(values))
|
||||
for _, value := range values {
|
||||
if value == "" {
|
||||
continue
|
||||
}
|
||||
if _, ok := seen[value]; ok {
|
||||
continue
|
||||
}
|
||||
seen[value] = struct{}{}
|
||||
result = append(result, value)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user