feat(channel): 同步 vivid 最新渠道设计 — 手动全量同步端点/并发0=不限/同名别名报错

对齐 vivid 6039423/5802749/a1f808f/cfb6d49/aba8474/b01636f:
- 新增 POST /channels/sync-models:已保存渠道手动全量同步,上游不可达/
  空清单直接报错(保存路径仍降级为告警);渠道行按钮不再弹勾选框,
  表单内对已保存渠道同样直同步,新渠道保持预览+勾选即绑定
- 渠道并发 0 = 不限制(原 0=默认5),上限放开到 int32,槽位 Lua 本就
  max=0 放行
- 别名与上游模型同名提交即报错「留空即同名」,前后端双校验
- 渠道列表去掉上游地址与模型两列(同 vivid),状态徽章悬浮显示失败原因

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
2026-10-19 01:43:30 +08:00
co-authored by Devin
parent 5226e11cfc
commit 72d4eec203
7 changed files with 118 additions and 49 deletions
+5 -5
View File
@@ -64,8 +64,8 @@ const videoGenBudget = 30 * time.Minute
// imageGenBudget caps one image generation end-to-end.
const imageGenBudget = 8 * time.Minute
// defaultChannelConcurrency applies when a channel's Concurrency is 0.
const defaultChannelConcurrency = 5
// Channel concurrency semantics: 0 = unlimited (the Lua slot script passes
// max=0 straight through), matching vivid's channel design.
// maxChannelFailover caps how many channels one request may burn on
// retryable/temporary errors before giving up, so an upstream-wide blip can't
@@ -100,11 +100,11 @@ type V1Service struct {
conc *ConcurrencyService
}
// chanAcquire takes one per-channel slot (capped at max; 0 = default), tagged
// chanAcquire takes one per-channel slot (capped at max; 0 = unlimited), tagged
// with the generation's eventID.
func (s *V1Service) chanAcquire(ctx context.Context, channelID, eventID string, max int) bool {
if max < 1 {
max = defaultChannelConcurrency
if max < 0 {
max = 0
}
return s.conc.Acquire(ctx, "conc:c:"+channelID, max, eventID)
}