feat(desktop): drop parked review flow, add publish management

SaaS 侧人工审核后才创建 publish job,desktop 不再做二次审核:移除
manual/waiting_user/parked/from_parked 状态机与 LeaseFromParked 查询,
desktop client 只执行发布并新增"发布管理"页(待发布队列 / 历史 / 再次
发送)。同时抽离 @geo/publisher-platforms 共享适配器包、新增 Redis-based
desktop_presence 与 publish_record_support,刷新 admin-web 发布弹窗与
媒体库;plan A / spec 文档同步口径。
This commit is contained in:
2026-04-20 09:52:48 +08:00
parent b16e9f0bd1
commit a617d39a4a
93 changed files with 5519 additions and 3594 deletions
+23 -42
View File
@@ -468,51 +468,40 @@ func (s *MediaService) CreatePublishBatch(ctx context.Context, articleID int64,
}
defer tx.Rollback(ctx)
var articleExists bool
var generateStatus string
if err := tx.QueryRow(ctx, `
SELECT EXISTS(SELECT 1 FROM articles WHERE id = $1 AND tenant_id = $2 AND deleted_at IS NULL),
COALESCE((SELECT generate_status FROM articles WHERE id = $1 AND tenant_id = $2 AND deleted_at IS NULL), '')
`, articleID, actor.TenantID).Scan(&articleExists, &generateStatus); err != nil {
return nil, response.ErrInternal(50037, "article_check_failed", "failed to validate article")
}
if !articleExists {
return nil, response.ErrNotFound(40411, "article_not_found", "article not found")
}
if generateStatus != "completed" {
return nil, response.ErrConflict(40913, "article_not_publishable", "only completed articles can be published")
articleMeta, err := loadPublishableArticleMeta(ctx, tx, actor.TenantID, articleID)
if err != nil {
return nil, err
}
accountSeeds, err := loadPlatformAccountsForPublish(ctx, tx, actor.TenantID, req.PlatformAccountIDs)
if err != nil {
return nil, err
}
if publishBatchRequiresCover(accountSeeds) && strings.TrimSpace(pointerStringValue(req.CoverAssetURL)) == "" {
coverAssetURL := normalizeStringPointer(req.CoverAssetURL)
if publishBatchRequiresCover(accountSeeds) && strings.TrimSpace(pointerStringValue(coverAssetURL)) == "" {
return nil, response.ErrBadRequest(40044, "publish_cover_required", "baijiahao publishing requires a cover image")
}
var publishBatchID int64
if err := tx.QueryRow(ctx, `
INSERT INTO publish_batches (tenant_id, article_id, initiator_user_id, status, publish_type, cover_asset_url)
VALUES ($1, $2, $3, 'publishing', $4, $5)
RETURNING id
`, actor.TenantID, articleID, actor.UserID, publishType, normalizeStringPointer(req.CoverAssetURL)).Scan(&publishBatchID); err != nil {
return nil, response.ErrInternal(50038, "publish_batch_create_failed", "failed to create publish batch")
if coverAssetURL == nil {
coverAssetURL = articleMeta.CoverAssetURL
}
publishBatchID, publishRecordIDs, err := createPublishBatchRecords(
ctx,
tx,
actor.TenantID,
actor.UserID,
articleID,
publishType,
coverAssetURL,
accountSeeds,
)
if err != nil {
return nil, err
}
tasks := make([]PublishBatchTask, 0, len(accountSeeds))
for _, account := range accountSeeds {
var publishRecordID int64
if err := tx.QueryRow(ctx, `
INSERT INTO publish_records (
tenant_id, publish_batch_id, article_id, platform_account_id, platform_id, status
)
VALUES ($1, $2, $3, $4, $5, 'queued')
RETURNING id
`, actor.TenantID, publishBatchID, articleID, account.ID, account.PlatformID).Scan(&publishRecordID); err != nil {
return nil, response.ErrInternal(50039, "publish_record_create_failed", "failed to create publish record")
}
token, tokenErr := newSessionToken()
if tokenErr != nil {
return nil, response.ErrInternal(50034, "session_token_failed", "failed to generate plugin session token")
@@ -527,12 +516,12 @@ func (s *MediaService) CreatePublishBatch(ctx context.Context, articleID int64,
)
VALUES ($1, $2, $3, 'publish', 'publish_record', $4, $5, $6, $7, $8, 'pending')
RETURNING id
`, actor.TenantID, actor.UserID, req.PluginInstallationID, publishRecordID, account.ID, account.PlatformID, token, expireAt).Scan(&pluginSessionID); err != nil {
`, actor.TenantID, actor.UserID, req.PluginInstallationID, publishRecordIDs[account.ID], account.ID, account.PlatformID, token, expireAt).Scan(&pluginSessionID); err != nil {
return nil, response.ErrInternal(50040, "publish_session_create_failed", "failed to create publish session")
}
tasks = append(tasks, PublishBatchTask{
PublishRecordID: publishRecordID,
PublishRecordID: publishRecordIDs[account.ID],
PlatformAccountID: account.ID,
PlatformID: account.PlatformID,
PlatformName: account.PlatformName,
@@ -544,14 +533,6 @@ func (s *MediaService) CreatePublishBatch(ctx context.Context, articleID int64,
})
}
if _, err := tx.Exec(ctx, `
UPDATE articles
SET publish_status = 'publishing', updated_at = NOW()
WHERE id = $1 AND tenant_id = $2 AND deleted_at IS NULL
`, articleID, actor.TenantID); err != nil {
return nil, response.ErrInternal(50041, "article_publish_status_failed", "failed to update article publish status")
}
if err := tx.Commit(ctx); err != nil {
return nil, response.ErrInternal(50042, "publish_batch_commit_failed", "failed to commit publish batch")
}