feat(imitation): add article imitation create flow
Introduce 仿写创作: new list and create views, backend imitation service and prompt templates, worker task routing for imitation jobs, and one-click rewrite triggers from question citation sources. Surface source article URL/title on article list/detail, and restrict failed articles to delete-only. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -7,6 +7,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/jackc/pgx/v5"
|
||||
@@ -28,6 +29,7 @@ type ArticleGenerationWorker struct {
|
||||
rabbitMQ *rabbitmq.Client
|
||||
templateService *tenantapp.TemplateService
|
||||
promptRuleService *tenantapp.PromptRuleGenerationService
|
||||
imitationService *tenantapp.ArticleImitationService
|
||||
kolWorker *KolGenerationWorker
|
||||
logger *zap.Logger
|
||||
retryInterval time.Duration
|
||||
@@ -41,6 +43,7 @@ func NewArticleGenerationWorker(
|
||||
rabbitMQClient *rabbitmq.Client,
|
||||
templateService *tenantapp.TemplateService,
|
||||
promptRuleService *tenantapp.PromptRuleGenerationService,
|
||||
imitationService *tenantapp.ArticleImitationService,
|
||||
kolWorker *KolGenerationWorker,
|
||||
logger *zap.Logger,
|
||||
cfg config.GenerationConfig,
|
||||
@@ -60,6 +63,7 @@ func NewArticleGenerationWorker(
|
||||
rabbitMQ: rabbitMQClient,
|
||||
templateService: templateService,
|
||||
promptRuleService: promptRuleService,
|
||||
imitationService: imitationService,
|
||||
kolWorker: kolWorker,
|
||||
logger: logger,
|
||||
retryInterval: defaultArticleGenerationWorkerRetryInterval,
|
||||
@@ -145,11 +149,7 @@ func (w *ArticleGenerationWorker) handleDelivery(parent context.Context, deliver
|
||||
return
|
||||
}
|
||||
if task.ID > 0 {
|
||||
if task.TaskType == kolGenerationTaskType && w.kolWorker != nil {
|
||||
w.kolWorker.HandleTaskFailure(ctx, task, "task_load", err)
|
||||
} else {
|
||||
_ = tenantapp.HandleClaimedGenerationTaskFailure(ctx, w.pool, w.templateService, w.promptRuleService, task, err)
|
||||
}
|
||||
w.handleTaskFailure(ctx, task, "task_load", err)
|
||||
if ackErr := delivery.Ack(false); ackErr != nil && w.logger != nil {
|
||||
w.logger.Warn("article generation ack failed after task load failure",
|
||||
zap.Error(ackErr),
|
||||
@@ -172,25 +172,28 @@ func (w *ArticleGenerationWorker) handleDelivery(parent context.Context, deliver
|
||||
return
|
||||
}
|
||||
|
||||
task.TaskType = strings.TrimSpace(task.TaskType)
|
||||
switch {
|
||||
case task.ArticleID <= 0 || task.QuotaReservationID <= 0:
|
||||
if task.TaskType == kolGenerationTaskType && w.kolWorker != nil {
|
||||
w.kolWorker.HandleTaskFailure(ctx, task, "task_load", fmt.Errorf("generation task references are incomplete"))
|
||||
} else {
|
||||
_ = tenantapp.HandleClaimedGenerationTaskFailure(ctx, w.pool, w.templateService, w.promptRuleService, task, fmt.Errorf("generation task references are incomplete"))
|
||||
}
|
||||
w.handleTaskFailure(ctx, task, "task_load", fmt.Errorf("generation task references are incomplete"))
|
||||
case task.TaskType == "template":
|
||||
w.templateService.ProcessQueuedTask(ctx, task)
|
||||
case task.TaskType == "custom_generation":
|
||||
w.promptRuleService.ProcessQueuedTask(ctx, task)
|
||||
case task.TaskType == "imitation":
|
||||
if w.imitationService != nil {
|
||||
w.imitationService.ProcessQueuedTask(ctx, task)
|
||||
} else {
|
||||
w.handleTaskFailure(ctx, task, "task_load", fmt.Errorf("imitation generation worker is not configured"))
|
||||
}
|
||||
case task.TaskType == kolGenerationTaskType:
|
||||
if w.kolWorker != nil {
|
||||
w.kolWorker.Process(ctx, task)
|
||||
} else {
|
||||
_ = tenantapp.HandleClaimedGenerationTaskFailure(ctx, w.pool, w.templateService, w.promptRuleService, task, fmt.Errorf("kol generation worker is not configured"))
|
||||
w.handleTaskFailure(ctx, task, "task_load", fmt.Errorf("kol generation worker is not configured"))
|
||||
}
|
||||
default:
|
||||
_ = tenantapp.HandleClaimedGenerationTaskFailure(ctx, w.pool, w.templateService, w.promptRuleService, task, fmt.Errorf("unsupported generation task type: %s", task.TaskType))
|
||||
w.handleTaskFailure(ctx, task, "task_load", fmt.Errorf("unsupported generation task type: %s", task.TaskType))
|
||||
}
|
||||
|
||||
if err := delivery.Ack(false); err != nil && w.logger != nil {
|
||||
@@ -202,6 +205,18 @@ func (w *ArticleGenerationWorker) handleDelivery(parent context.Context, deliver
|
||||
}
|
||||
}
|
||||
|
||||
func (w *ArticleGenerationWorker) handleTaskFailure(ctx context.Context, task tenantapp.ClaimedGenerationTask, stage string, taskErr error) {
|
||||
task.TaskType = strings.TrimSpace(task.TaskType)
|
||||
switch {
|
||||
case task.TaskType == kolGenerationTaskType && w.kolWorker != nil:
|
||||
w.kolWorker.HandleTaskFailure(ctx, task, stage, taskErr)
|
||||
case task.TaskType == "imitation" && w.imitationService != nil:
|
||||
w.imitationService.HandleTaskFailure(ctx, task, stage, taskErr)
|
||||
default:
|
||||
_ = tenantapp.HandleClaimedGenerationTaskFailure(ctx, w.pool, w.templateService, w.promptRuleService, task, taskErr)
|
||||
}
|
||||
}
|
||||
|
||||
func (w *ArticleGenerationWorker) loadTask(ctx context.Context, taskID int64) (tenantapp.ClaimedGenerationTask, bool, error) {
|
||||
var (
|
||||
task tenantapp.ClaimedGenerationTask
|
||||
|
||||
Reference in New Issue
Block a user