chore: update app generation workflow
Desktop Client Build / Resolve Build Metadata (push) Successful in 26s
Frontend CI / Frontend (push) Successful in 2m58s
Backend CI / Backend (push) Successful in 16m11s
Desktop Client Build / Build Desktop Client (push) Successful in 24m1s
Desktop Client Build / Publish Client Artifacts to NAS (push) Successful in 28s
Desktop Client Build / Resolve Build Metadata (push) Successful in 26s
Frontend CI / Frontend (push) Successful in 2m58s
Backend CI / Backend (push) Successful in 16m11s
Desktop Client Build / Build Desktop Client (push) Successful in 24m1s
Desktop Client Build / Publish Client Artifacts to NAS (push) Successful in 28s
This commit is contained in:
@@ -57,7 +57,7 @@
|
|||||||
<g>
|
<g>
|
||||||
<path class="cls-2" d="M58.37,156.95l-3.17,12.69h-25.13l-2.92,11.84h21.36l-3.26,12.87h-21.36l-3.35,13.55h26.08l-3.17,12.87H2.01l15.87-63.82h40.49Z"/>
|
<path class="cls-2" d="M58.37,156.95l-3.17,12.69h-25.13l-2.92,11.84h21.36l-3.26,12.87h-21.36l-3.35,13.55h26.08l-3.17,12.87H2.01l15.87-63.82h40.49Z"/>
|
||||||
<path class="cls-2" d="M101.76,193.75l-6.69,27.02h-12.35v-4.37c-4.98,3.6-9.86,5.58-14.67,5.58-9.61,0-13.81-7.38-11.67-16.3,2.4-9.52,11.15-14.49,30.88-17.24,1.2-3.86.09-5.49-4.29-5.49s-9.01,1.89-14.32,4.63l-3.95-10.72c7.46-3.86,14.84-6.09,21.87-6.09,13.21,0,18.96,7.72,15.18,22.99ZM85.89,198.55c-9.86,1.29-14.32,4.63-15.35,8.84-.6,2.06.77,2.74,3.34,2.74,3.69,0,6.43-1.97,9.18-4.37l2.83-7.21Z"/>
|
<path class="cls-2" d="M101.76,193.75l-6.69,27.02h-12.35v-4.37c-4.98,3.6-9.86,5.58-14.67,5.58-9.61,0-13.81-7.38-11.67-16.3,2.4-9.52,11.15-14.49,30.88-17.24,1.2-3.86.09-5.49-4.29-5.49s-9.01,1.89-14.32,4.63l-3.95-10.72c7.46-3.86,14.84-6.09,21.87-6.09,13.21,0,18.96,7.72,15.18,22.99ZM85.89,198.55c-9.86,1.29-14.32,4.63-15.35,8.84-.6,2.06.77,2.74,3.34,2.74,3.69,0,6.43-1.97,9.18-4.37l2.83-7.21Z"/>
|
||||||
<path class="cls-2" d="M133.15,183.89h-17.33l2.92-11.92h36.63l-1.8,8.15-26.93,28.73h20.16l-2.92,11.92h-39.46l1.8-8.15,26.93-28.73Z"/>
|
<path class="cls-2" transform="translate(-211.41 0)" d="M315.83,214.68l8.23-8.66c3.6,3.43,7.21,4.72,10.46,4.72,4.63,0,7.46-2.14,8.23-5.32.6-2.14-3.34-3.17-7.29-5.4-5.83-1.97-12.18-6.35-9.95-15.18,2.14-8.49,10.46-14.07,21.19-14.07,8.32,0,13.04,3.52,16.81,6.69l-8.23,8.23c-2.83-2.4-5.83-3.69-8.49-3.69-4.03,0-6.78,1.89-7.55,4.98-.51,2.14,3.17,2.83,7.21,4.89,6.09,2.06,12.44,6,10.04,15.61-2.06,8.23-10.04,14.5-22.73,14.5-6.95,0-13.81-3.17-17.93-7.29Z"/>
|
||||||
<path class="cls-2" d="M146.44,238.44l4.46-11.24c.86.17,2.06.43,2.57.43,5.23,0,8.83-3.09,10.64-6.52l1.46-1.8-6.69-47.35h15.01l1.29,20.93c.34,4.46.26,9.01.09,13.81l1.03-1.37c2.14-4.63,4.37-9.18,6.52-13.81l10.64-19.56h14.75l-28.99,49.58c-7.89,12.52-14.15,18.01-25.13,18.01-3.77,0-5.66-.51-7.63-1.12Z"/>
|
<path class="cls-2" d="M146.44,238.44l4.46-11.24c.86.17,2.06.43,2.57.43,5.23,0,8.83-3.09,10.64-6.52l1.46-1.8-6.69-47.35h15.01l1.29,20.93c.34,4.46.26,9.01.09,13.81l1.03-1.37c2.14-4.63,4.37-9.18,6.52-13.81l10.64-19.56h14.75l-28.99,49.58c-7.89,12.52-14.15,18.01-25.13,18.01-3.77,0-5.66-.51-7.63-1.12Z"/>
|
||||||
<path class="cls-2" d="M242.16,156.95c14.07,0,24.87,5.66,20.76,22.22-3.6,14.41-16.3,20.41-29.76,20.41h-8.06l-5.23,21.19h-15.35l15.87-63.82h21.79ZM248.68,176.25c1.46-5.66-1.72-7.2-9.09-7.2h-6.86l-4.63,18.44h6.09c7.46,0,12.78-4.2,14.5-11.24Z"/>
|
<path class="cls-2" d="M242.16,156.95c14.07,0,24.87,5.66,20.76,22.22-3.6,14.41-16.3,20.41-29.76,20.41h-8.06l-5.23,21.19h-15.35l15.87-63.82h21.79ZM248.68,176.25c1.46-5.66-1.72-7.2-9.09-7.2h-6.86l-4.63,18.44h6.09c7.46,0,12.78-4.2,14.5-11.24Z"/>
|
||||||
<path class="cls-2" d="M266.51,201.47l7.29-29.51h15.27l-7.2,28.99c-1.72,6.78-.77,7.98,2.49,7.98,4.03,0,6.95-1.97,10.38-5.58l8.15-31.39h15.27l-12.09,48.8h-12.35l.51-6.69-.17.51c-5.23,4.8-10.21,7.38-16.3,7.38-11.24,0-14.32-8.32-11.24-20.5Z"/>
|
<path class="cls-2" d="M266.51,201.47l7.29-29.51h15.27l-7.2,28.99c-1.72,6.78-.77,7.98,2.49,7.98,4.03,0,6.95-1.97,10.38-5.58l8.15-31.39h15.27l-12.09,48.8h-12.35l.51-6.69-.17.51c-5.23,4.8-10.21,7.38-16.3,7.38-11.24,0-14.32-8.32-11.24-20.5Z"/>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 9.0 KiB After Width: | Height: | Size: 9.4 KiB |
@@ -43,6 +43,10 @@ mac:
|
|||||||
gatekeeperAssess: false
|
gatekeeperAssess: false
|
||||||
entitlements: build/entitlements.mac.plist
|
entitlements: build/entitlements.mac.plist
|
||||||
entitlementsInherit: build/entitlements.mac.inherit.plist
|
entitlementsInherit: build/entitlements.mac.inherit.plist
|
||||||
|
# Playwright 只作为 CDP client 使用;lib/bin 里的字体、图片、脚本不是可执行代码。
|
||||||
|
# 跳过这些资源的单独签名,避免 timestamp 服务抖动被放大成打包失败。
|
||||||
|
signIgnore:
|
||||||
|
- /node_modules/playwright-core/(lib|bin)/
|
||||||
electronLanguages:
|
electronLanguages:
|
||||||
- en
|
- en
|
||||||
- zh_CN
|
- zh_CN
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@geo/desktop-client",
|
"name": "@geo/desktop-client",
|
||||||
"version": "0.1.3",
|
"version": "0.1.6",
|
||||||
"private": true,
|
"private": true,
|
||||||
"description": "省心推客户端 — 连接和管理您的媒体账号,轻松发布和监控内容表现。",
|
"description": "省心推客户端 — 连接和管理您的媒体账号,轻松发布和监控内容表现。",
|
||||||
"author": {
|
"author": {
|
||||||
|
|||||||
@@ -57,7 +57,7 @@
|
|||||||
<g>
|
<g>
|
||||||
<path class="cls-2" d="M58.37,156.95l-3.17,12.69h-25.13l-2.92,11.84h21.36l-3.26,12.87h-21.36l-3.35,13.55h26.08l-3.17,12.87H2.01l15.87-63.82h40.49Z"/>
|
<path class="cls-2" d="M58.37,156.95l-3.17,12.69h-25.13l-2.92,11.84h21.36l-3.26,12.87h-21.36l-3.35,13.55h26.08l-3.17,12.87H2.01l15.87-63.82h40.49Z"/>
|
||||||
<path class="cls-2" d="M101.76,193.75l-6.69,27.02h-12.35v-4.37c-4.98,3.6-9.86,5.58-14.67,5.58-9.61,0-13.81-7.38-11.67-16.3,2.4-9.52,11.15-14.49,30.88-17.24,1.2-3.86.09-5.49-4.29-5.49s-9.01,1.89-14.32,4.63l-3.95-10.72c7.46-3.86,14.84-6.09,21.87-6.09,13.21,0,18.96,7.72,15.18,22.99ZM85.89,198.55c-9.86,1.29-14.32,4.63-15.35,8.84-.6,2.06.77,2.74,3.34,2.74,3.69,0,6.43-1.97,9.18-4.37l2.83-7.21Z"/>
|
<path class="cls-2" d="M101.76,193.75l-6.69,27.02h-12.35v-4.37c-4.98,3.6-9.86,5.58-14.67,5.58-9.61,0-13.81-7.38-11.67-16.3,2.4-9.52,11.15-14.49,30.88-17.24,1.2-3.86.09-5.49-4.29-5.49s-9.01,1.89-14.32,4.63l-3.95-10.72c7.46-3.86,14.84-6.09,21.87-6.09,13.21,0,18.96,7.72,15.18,22.99ZM85.89,198.55c-9.86,1.29-14.32,4.63-15.35,8.84-.6,2.06.77,2.74,3.34,2.74,3.69,0,6.43-1.97,9.18-4.37l2.83-7.21Z"/>
|
||||||
<path class="cls-2" d="M133.15,183.89h-17.33l2.92-11.92h36.63l-1.8,8.15-26.93,28.73h20.16l-2.92,11.92h-39.46l1.8-8.15,26.93-28.73Z"/>
|
<path class="cls-2" transform="translate(-211.41 0)" d="M315.83,214.68l8.23-8.66c3.6,3.43,7.21,4.72,10.46,4.72,4.63,0,7.46-2.14,8.23-5.32.6-2.14-3.34-3.17-7.29-5.4-5.83-1.97-12.18-6.35-9.95-15.18,2.14-8.49,10.46-14.07,21.19-14.07,8.32,0,13.04,3.52,16.81,6.69l-8.23,8.23c-2.83-2.4-5.83-3.69-8.49-3.69-4.03,0-6.78,1.89-7.55,4.98-.51,2.14,3.17,2.83,7.21,4.89,6.09,2.06,12.44,6,10.04,15.61-2.06,8.23-10.04,14.5-22.73,14.5-6.95,0-13.81-3.17-17.93-7.29Z"/>
|
||||||
<path class="cls-2" d="M146.44,238.44l4.46-11.24c.86.17,2.06.43,2.57.43,5.23,0,8.83-3.09,10.64-6.52l1.46-1.8-6.69-47.35h15.01l1.29,20.93c.34,4.46.26,9.01.09,13.81l1.03-1.37c2.14-4.63,4.37-9.18,6.52-13.81l10.64-19.56h14.75l-28.99,49.58c-7.89,12.52-14.15,18.01-25.13,18.01-3.77,0-5.66-.51-7.63-1.12Z"/>
|
<path class="cls-2" d="M146.44,238.44l4.46-11.24c.86.17,2.06.43,2.57.43,5.23,0,8.83-3.09,10.64-6.52l1.46-1.8-6.69-47.35h15.01l1.29,20.93c.34,4.46.26,9.01.09,13.81l1.03-1.37c2.14-4.63,4.37-9.18,6.52-13.81l10.64-19.56h14.75l-28.99,49.58c-7.89,12.52-14.15,18.01-25.13,18.01-3.77,0-5.66-.51-7.63-1.12Z"/>
|
||||||
<path class="cls-2" d="M242.16,156.95c14.07,0,24.87,5.66,20.76,22.22-3.6,14.41-16.3,20.41-29.76,20.41h-8.06l-5.23,21.19h-15.35l15.87-63.82h21.79ZM248.68,176.25c1.46-5.66-1.72-7.2-9.09-7.2h-6.86l-4.63,18.44h6.09c7.46,0,12.78-4.2,14.5-11.24Z"/>
|
<path class="cls-2" d="M242.16,156.95c14.07,0,24.87,5.66,20.76,22.22-3.6,14.41-16.3,20.41-29.76,20.41h-8.06l-5.23,21.19h-15.35l15.87-63.82h21.79ZM248.68,176.25c1.46-5.66-1.72-7.2-9.09-7.2h-6.86l-4.63,18.44h6.09c7.46,0,12.78-4.2,14.5-11.24Z"/>
|
||||||
<path class="cls-2" d="M266.51,201.47l7.29-29.51h15.27l-7.2,28.99c-1.72,6.78-.77,7.98,2.49,7.98,4.03,0,6.95-1.97,10.38-5.58l8.15-31.39h15.27l-12.09,48.8h-12.35l.51-6.69-.17.51c-5.23,4.8-10.21,7.38-16.3,7.38-11.24,0-14.32-8.32-11.24-20.5Z"/>
|
<path class="cls-2" d="M266.51,201.47l7.29-29.51h15.27l-7.2,28.99c-1.72,6.78-.77,7.98,2.49,7.98,4.03,0,6.95-1.97,10.38-5.58l8.15-31.39h15.27l-12.09,48.8h-12.35l.51-6.69-.17.51c-5.23,4.8-10.21,7.38-16.3,7.38-11.24,0-14.32-8.32-11.24-20.5Z"/>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 9.0 KiB After Width: | Height: | Size: 9.4 KiB |
@@ -409,6 +409,30 @@ func (s *ArticleImitationService) executeGeneration(ctx context.Context, job art
|
|||||||
quotaRepo := repository.NewQuotaRepository(tx)
|
quotaRepo := repository.NewQuotaRepository(tx)
|
||||||
auditTx := repository.NewAuditRepository(tx)
|
auditTx := repository.NewAuditRepository(tx)
|
||||||
|
|
||||||
|
if err := EnsureGenerationArticleWritable(ctx, tx, job.ArticleID, job.TenantID); err != nil {
|
||||||
|
if IsGenerationArticleUnavailable(err) {
|
||||||
|
rollbackGenerationTx(tx)
|
||||||
|
if cleanupErr := HandleGenerationTaskArticleUnavailable(ctx, s.pool, s.cache, ClaimedGenerationTask{
|
||||||
|
ID: job.TaskID,
|
||||||
|
TenantID: job.TenantID,
|
||||||
|
OperatorID: job.OperatorID,
|
||||||
|
ArticleID: job.ArticleID,
|
||||||
|
QuotaReservationID: job.ReservationID,
|
||||||
|
TaskType: articleImitationTaskType,
|
||||||
|
InputParams: cloneInputParams(job.InputParams),
|
||||||
|
}, articleImitationTaskType, false); cleanupErr != nil && s.logger != nil {
|
||||||
|
s.logger.Warn("imitation generation article unavailable cleanup failed",
|
||||||
|
zap.Error(cleanupErr),
|
||||||
|
zap.Int64("task_id", job.TaskID),
|
||||||
|
zap.Int64("article_id", job.ArticleID),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
failWithRollback("persist_article_status", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
versionID, err := articleRepo.CreateArticleVersion(ctx, repository.CreateArticleVersionInput{
|
versionID, err := articleRepo.CreateArticleVersion(ctx, repository.CreateArticleVersionInput{
|
||||||
ArticleID: job.ArticleID,
|
ArticleID: job.ArticleID,
|
||||||
Title: title,
|
Title: title,
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import (
|
|||||||
"github.com/jackc/pgx/v5"
|
"github.com/jackc/pgx/v5"
|
||||||
"github.com/jackc/pgx/v5/pgxpool"
|
"github.com/jackc/pgx/v5/pgxpool"
|
||||||
|
|
||||||
|
sharedcache "github.com/geo-platform/tenant-api/internal/shared/cache"
|
||||||
"github.com/geo-platform/tenant-api/internal/shared/llm"
|
"github.com/geo-platform/tenant-api/internal/shared/llm"
|
||||||
"github.com/geo-platform/tenant-api/internal/tenant/repository"
|
"github.com/geo-platform/tenant-api/internal/tenant/repository"
|
||||||
)
|
)
|
||||||
@@ -26,6 +27,12 @@ type ClaimedGenerationTask struct {
|
|||||||
|
|
||||||
const generationCleanupTimeout = 30 * time.Second
|
const generationCleanupTimeout = 30 * time.Second
|
||||||
|
|
||||||
|
var errGenerationArticleUnavailable = errors.New("generation article is unavailable")
|
||||||
|
|
||||||
|
type generationDBTX interface {
|
||||||
|
QueryRow(context.Context, string, ...interface{}) pgx.Row
|
||||||
|
}
|
||||||
|
|
||||||
type promptRuleTaskContext struct {
|
type promptRuleTaskContext struct {
|
||||||
PromptRuleID int64
|
PromptRuleID int64
|
||||||
BrandID int64
|
BrandID int64
|
||||||
@@ -46,6 +53,124 @@ func rollbackGenerationTx(tx pgx.Tx) {
|
|||||||
_ = tx.Rollback(ctx)
|
_ = tx.Rollback(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func generationStringPtr(value string) *string {
|
||||||
|
return &value
|
||||||
|
}
|
||||||
|
|
||||||
|
func EnsureGenerationArticleWritable(ctx context.Context, db generationDBTX, articleID, tenantID int64) error {
|
||||||
|
if articleID <= 0 || tenantID <= 0 {
|
||||||
|
return errGenerationArticleUnavailable
|
||||||
|
}
|
||||||
|
|
||||||
|
var lockedArticleID int64
|
||||||
|
if err := db.QueryRow(ctx, `
|
||||||
|
SELECT id
|
||||||
|
FROM articles
|
||||||
|
WHERE id = $1
|
||||||
|
AND tenant_id = $2
|
||||||
|
AND deleted_at IS NULL
|
||||||
|
FOR UPDATE
|
||||||
|
`, articleID, tenantID).Scan(&lockedArticleID); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsGenerationArticleUnavailable(err error) bool {
|
||||||
|
return errors.Is(err, errGenerationArticleUnavailable) || errors.Is(err, pgx.ErrNoRows)
|
||||||
|
}
|
||||||
|
|
||||||
|
func HandleGenerationTaskArticleUnavailable(
|
||||||
|
ctx context.Context,
|
||||||
|
pool *pgxpool.Pool,
|
||||||
|
cache sharedcache.Cache,
|
||||||
|
task ClaimedGenerationTask,
|
||||||
|
taskType string,
|
||||||
|
updateKolTaskRecord bool,
|
||||||
|
) error {
|
||||||
|
cleanupCtx, cancel := newGenerationCleanupContext()
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
const errMsg = "文章已删除,生成结果已丢弃"
|
||||||
|
now := time.Now()
|
||||||
|
auditRepo := repository.NewAuditRepository(pool)
|
||||||
|
quotaRepo := repository.NewQuotaRepository(pool)
|
||||||
|
usageRepo := repository.NewKolUsageRepository(pool)
|
||||||
|
|
||||||
|
var cleanupErr error
|
||||||
|
if task.ID > 0 {
|
||||||
|
if err := auditRepo.UpdateGenerationTaskStatus(cleanupCtx, repository.GenerationTaskStatusInput{
|
||||||
|
ID: task.ID,
|
||||||
|
TenantID: task.TenantID,
|
||||||
|
Status: "cancelled",
|
||||||
|
ErrorMessage: generationStringPtr(errMsg),
|
||||||
|
CompletedAt: &now,
|
||||||
|
}); err != nil {
|
||||||
|
cleanupErr = errors.Join(cleanupErr, fmt.Errorf("cancel generation task: %w", err))
|
||||||
|
}
|
||||||
|
if updateKolTaskRecord {
|
||||||
|
_ = auditRepo.UpdateTaskRecordStatus(cleanupCtx, repository.TaskRecordInput{
|
||||||
|
TenantID: task.TenantID,
|
||||||
|
TaskType: kolGenerationTaskRecordType,
|
||||||
|
ResourceType: kolGenerationTaskRecordScope,
|
||||||
|
ResourceID: task.ID,
|
||||||
|
Status: "cancelled",
|
||||||
|
ErrorMessage: generationStringPtr(errMsg),
|
||||||
|
})
|
||||||
|
_ = usageRepo.MarkFailedByTask(cleanupCtx, task.TenantID, task.ID, errMsg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if task.QuotaReservationID > 0 {
|
||||||
|
var balance int
|
||||||
|
balanceErr := error(nil)
|
||||||
|
if task.OperatorID > 0 {
|
||||||
|
balance, balanceErr = quotaRepo.GetCurrentBalance(cleanupCtx, task.TenantID, "article_generation")
|
||||||
|
}
|
||||||
|
refunded, err := quotaRepo.RefundPendingReservation(cleanupCtx, task.QuotaReservationID, task.TenantID)
|
||||||
|
if err != nil {
|
||||||
|
cleanupErr = errors.Join(cleanupErr, fmt.Errorf("refund generation reservation: %w", err))
|
||||||
|
} else if refunded {
|
||||||
|
if balanceErr != nil {
|
||||||
|
cleanupErr = errors.Join(cleanupErr, fmt.Errorf("load quota balance for refund: %w", balanceErr))
|
||||||
|
} else if task.OperatorID > 0 {
|
||||||
|
reason := "generation_cancel_refund"
|
||||||
|
referenceType := "generation_task"
|
||||||
|
taskReferenceID := task.ID
|
||||||
|
if _, err := quotaRepo.InsertQuotaLedger(cleanupCtx, repository.QuotaLedgerInput{
|
||||||
|
TenantID: task.TenantID,
|
||||||
|
OperatorID: task.OperatorID,
|
||||||
|
QuotaType: "article_generation",
|
||||||
|
Delta: 1,
|
||||||
|
BalanceAfter: balance + 1,
|
||||||
|
Reason: &reason,
|
||||||
|
ReferenceType: &referenceType,
|
||||||
|
ReferenceID: &taskReferenceID,
|
||||||
|
}); err != nil {
|
||||||
|
cleanupErr = errors.Join(cleanupErr, fmt.Errorf("insert generation cancel refund ledger: %w", err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if task.ArticleID > 0 {
|
||||||
|
invalidateArticleCaches(cleanupCtx, cache, task.TenantID, &task.ArticleID)
|
||||||
|
}
|
||||||
|
|
||||||
|
RecordGenerationTaskEvent(GenerationTaskEventTransition, GenerationTaskLogContext{
|
||||||
|
TaskID: task.ID,
|
||||||
|
TenantID: task.TenantID,
|
||||||
|
OperatorID: task.OperatorID,
|
||||||
|
ArticleID: task.ArticleID,
|
||||||
|
ReservationID: task.QuotaReservationID,
|
||||||
|
TaskType: taskType,
|
||||||
|
Stage: "article_deleted",
|
||||||
|
StatusAfter: "cancelled",
|
||||||
|
Result: GenerationTaskResultSkipped,
|
||||||
|
})
|
||||||
|
return cleanupErr
|
||||||
|
}
|
||||||
|
|
||||||
func HandleClaimedGenerationTaskFailure(
|
func HandleClaimedGenerationTaskFailure(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
pool *pgxpool.Pool,
|
pool *pgxpool.Pool,
|
||||||
|
|||||||
@@ -593,6 +593,30 @@ func (s *PromptRuleGenerationService) executeGeneration(ctx context.Context, job
|
|||||||
quotaRepo := repository.NewQuotaRepository(tx)
|
quotaRepo := repository.NewQuotaRepository(tx)
|
||||||
auditTx := repository.NewAuditRepository(tx)
|
auditTx := repository.NewAuditRepository(tx)
|
||||||
|
|
||||||
|
if err := EnsureGenerationArticleWritable(ctx, tx, job.ArticleID, job.TenantID); err != nil {
|
||||||
|
if IsGenerationArticleUnavailable(err) {
|
||||||
|
rollbackGenerationTx(tx)
|
||||||
|
if cleanupErr := HandleGenerationTaskArticleUnavailable(ctx, s.pool, s.cache, ClaimedGenerationTask{
|
||||||
|
ID: job.TaskID,
|
||||||
|
TenantID: job.TenantID,
|
||||||
|
OperatorID: job.OperatorID,
|
||||||
|
ArticleID: job.ArticleID,
|
||||||
|
QuotaReservationID: job.ReservationID,
|
||||||
|
TaskType: "custom_generation",
|
||||||
|
InputParams: cloneInputParams(job.InputParams),
|
||||||
|
}, "custom_generation", false); cleanupErr != nil && s.logger != nil {
|
||||||
|
s.logger.Warn("prompt rule generation article unavailable cleanup failed",
|
||||||
|
zap.Error(cleanupErr),
|
||||||
|
zap.Int64("task_id", job.TaskID),
|
||||||
|
zap.Int64("article_id", job.ArticleID),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
failWithRollback("persist_article_status", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
versionID, err := articleRepo.CreateArticleVersion(ctx, repository.CreateArticleVersionInput{
|
versionID, err := articleRepo.CreateArticleVersion(ctx, repository.CreateArticleVersionInput{
|
||||||
ArticleID: job.ArticleID,
|
ArticleID: job.ArticleID,
|
||||||
Title: title,
|
Title: title,
|
||||||
|
|||||||
@@ -545,6 +545,30 @@ func (s *TemplateService) executeGeneration(ctx context.Context, job generationJ
|
|||||||
quotaRepo := repository.NewQuotaRepository(tx)
|
quotaRepo := repository.NewQuotaRepository(tx)
|
||||||
auditTx := repository.NewAuditRepository(tx)
|
auditTx := repository.NewAuditRepository(tx)
|
||||||
|
|
||||||
|
if err := EnsureGenerationArticleWritable(ctx, tx, job.ArticleID, job.TenantID); err != nil {
|
||||||
|
if IsGenerationArticleUnavailable(err) {
|
||||||
|
rollbackGenerationTx(tx)
|
||||||
|
if cleanupErr := HandleGenerationTaskArticleUnavailable(ctx, s.pool, s.cache, ClaimedGenerationTask{
|
||||||
|
ID: job.TaskID,
|
||||||
|
TenantID: job.TenantID,
|
||||||
|
OperatorID: job.OperatorID,
|
||||||
|
ArticleID: job.ArticleID,
|
||||||
|
QuotaReservationID: job.ReservationID,
|
||||||
|
TaskType: "template",
|
||||||
|
InputParams: cloneInputParams(job.Params),
|
||||||
|
}, "template", false); cleanupErr != nil && s.logger != nil {
|
||||||
|
s.logger.Warn("template generation article unavailable cleanup failed",
|
||||||
|
zap.Error(cleanupErr),
|
||||||
|
zap.Int64("task_id", job.TaskID),
|
||||||
|
zap.Int64("article_id", job.ArticleID),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
failWithRollback("persist_article_status", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
versionID, err := articleRepo.CreateArticleVersion(ctx, repository.CreateArticleVersionInput{
|
versionID, err := articleRepo.CreateArticleVersion(ctx, repository.CreateArticleVersionInput{
|
||||||
ArticleID: job.ArticleID,
|
ArticleID: job.ArticleID,
|
||||||
Title: title,
|
Title: title,
|
||||||
|
|||||||
@@ -129,9 +129,9 @@ const updateGenerationTaskStatus = `-- name: UpdateGenerationTaskStatus :exec
|
|||||||
UPDATE generation_tasks SET status = $1::text, error_message = $2,
|
UPDATE generation_tasks SET status = $1::text, error_message = $2,
|
||||||
started_at = COALESCE($3, started_at),
|
started_at = COALESCE($3, started_at),
|
||||||
completed_at = COALESCE($4, completed_at),
|
completed_at = COALESCE($4, completed_at),
|
||||||
lease_token = CASE WHEN $1::text IN ('completed', 'failed') THEN NULL ELSE lease_token END,
|
lease_token = CASE WHEN $1::text IN ('completed', 'failed', 'cancelled', 'canceled', 'aborted') THEN NULL ELSE lease_token END,
|
||||||
lease_owner = CASE WHEN $1::text IN ('completed', 'failed') THEN NULL ELSE lease_owner END,
|
lease_owner = CASE WHEN $1::text IN ('completed', 'failed', 'cancelled', 'canceled', 'aborted') THEN NULL ELSE lease_owner END,
|
||||||
lease_expires_at = CASE WHEN $1::text IN ('completed', 'failed') THEN NULL ELSE lease_expires_at END,
|
lease_expires_at = CASE WHEN $1::text IN ('completed', 'failed', 'cancelled', 'canceled', 'aborted') THEN NULL ELSE lease_expires_at END,
|
||||||
updated_at = NOW()
|
updated_at = NOW()
|
||||||
WHERE id = $5 AND tenant_id = $6
|
WHERE id = $5 AND tenant_id = $6
|
||||||
`
|
`
|
||||||
|
|||||||
@@ -42,8 +42,8 @@ WHERE tenant_id = sqlc.arg(tenant_id)
|
|||||||
UPDATE generation_tasks SET status = sqlc.arg(status)::text, error_message = sqlc.arg(error_message),
|
UPDATE generation_tasks SET status = sqlc.arg(status)::text, error_message = sqlc.arg(error_message),
|
||||||
started_at = COALESCE(sqlc.arg(started_at), started_at),
|
started_at = COALESCE(sqlc.arg(started_at), started_at),
|
||||||
completed_at = COALESCE(sqlc.arg(completed_at), completed_at),
|
completed_at = COALESCE(sqlc.arg(completed_at), completed_at),
|
||||||
lease_token = CASE WHEN sqlc.arg(status)::text IN ('completed', 'failed') THEN NULL ELSE lease_token END,
|
lease_token = CASE WHEN sqlc.arg(status)::text IN ('completed', 'failed', 'cancelled', 'canceled', 'aborted') THEN NULL ELSE lease_token END,
|
||||||
lease_owner = CASE WHEN sqlc.arg(status)::text IN ('completed', 'failed') THEN NULL ELSE lease_owner END,
|
lease_owner = CASE WHEN sqlc.arg(status)::text IN ('completed', 'failed', 'cancelled', 'canceled', 'aborted') THEN NULL ELSE lease_owner END,
|
||||||
lease_expires_at = CASE WHEN sqlc.arg(status)::text IN ('completed', 'failed') THEN NULL ELSE lease_expires_at END,
|
lease_expires_at = CASE WHEN sqlc.arg(status)::text IN ('completed', 'failed', 'cancelled', 'canceled', 'aborted') THEN NULL ELSE lease_expires_at END,
|
||||||
updated_at = NOW()
|
updated_at = NOW()
|
||||||
WHERE id = sqlc.arg(id) AND tenant_id = sqlc.arg(tenant_id);
|
WHERE id = sqlc.arg(id) AND tenant_id = sqlc.arg(tenant_id);
|
||||||
|
|||||||
@@ -179,6 +179,15 @@ func (w *KolGenerationWorker) Process(ctx context.Context, task tenantapp.Claime
|
|||||||
auditTx := repository.NewAuditRepository(tx)
|
auditTx := repository.NewAuditRepository(tx)
|
||||||
usageRepo := repository.NewKolUsageRepository(tx)
|
usageRepo := repository.NewKolUsageRepository(tx)
|
||||||
|
|
||||||
|
if err := tenantapp.EnsureGenerationArticleWritable(ctx, tx, task.ArticleID, task.TenantID); err != nil {
|
||||||
|
if tenantapp.IsGenerationArticleUnavailable(err) {
|
||||||
|
w.rollbackAndCancelUnavailableArticle(ctx, tx, task)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
w.rollbackAndFail(ctx, tx, task, "persist_article_status", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
versionID, err := articleRepo.CreateArticleVersion(ctx, repository.CreateArticleVersionInput{
|
versionID, err := articleRepo.CreateArticleVersion(ctx, repository.CreateArticleVersionInput{
|
||||||
ArticleID: task.ArticleID,
|
ArticleID: task.ArticleID,
|
||||||
Title: title,
|
Title: title,
|
||||||
@@ -270,6 +279,19 @@ func (w *KolGenerationWorker) rollbackAndFail(ctx context.Context, tx pgx.Tx, ta
|
|||||||
w.HandleTaskFailure(ctx, task, stage, taskErr)
|
w.HandleTaskFailure(ctx, task, stage, taskErr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (w *KolGenerationWorker) rollbackAndCancelUnavailableArticle(ctx context.Context, tx pgx.Tx, task tenantapp.ClaimedGenerationTask) {
|
||||||
|
cleanupCtx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
_ = tx.Rollback(cleanupCtx)
|
||||||
|
if err := tenantapp.HandleGenerationTaskArticleUnavailable(ctx, w.pool, w.cache, task, kolGenerationTaskType, true); err != nil && w.logger != nil {
|
||||||
|
w.logger.Warn("kol generation article unavailable cleanup failed",
|
||||||
|
zap.Error(err),
|
||||||
|
zap.Int64("task_id", task.ID),
|
||||||
|
zap.Int64("article_id", task.ArticleID),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
errMissingRenderedPrompt = errors.New("kol generation task missing rendered prompt")
|
errMissingRenderedPrompt = errors.New("kol generation task missing rendered prompt")
|
||||||
errEmptyGeneratedContent = errors.New("kol generation returned empty content")
|
errEmptyGeneratedContent = errors.New("kol generation returned empty content")
|
||||||
|
|||||||
Reference in New Issue
Block a user