feat: 并发分组系统 + 品牌/Logo 上传 + 兑换码开关 + 文档分辨率表

并发分组(新功能):
- 新表 concurrency_groups(名称/上限/默认),用户加 concurrency_group_id
- 启动自动建「默认并发」组(上限10、默认),老用户回填、新注册自动绑定
- 并发计数改用 Redis(自愈 sorted-set + TTL + fail-open):
  · 用户并发(画图台 + API key 合计)受其分组上限限制,0=不限制 → 超返回 429
  · 账号级并发也从内存 gate 换成同一套 Redis(6 处调用点)
  · 移除旧的「已有正在生成的任务」单任务锁
- 后台「并发分组」新菜单:增删改、设默认、用户数;默认组不可删(删别的组成员转默认)
- 用户管理:并发列 + 新建/编辑可选分组
- 个人设置页:账户信息卡(用户名/邮箱/角色/余额/并发);/me 暴露 concurrency_group/limit

品牌 / Logo(上传到 RustFS):
- Logo 改成拖拽/点击上传,点保存才上传;替换自动删旧;branding/ 设为公开且被清理任务 pin 住(永不删)
- 有自定义就用:前台左侧 nav + 后台侧栏 + favicon(浏览器标签);没有则默认 V 图标
- 前台页头还原成文字;首页 Hero 子标题用 site.subtitle(默认那句宣传语,设置页预填)
- 邮件验证码标题用站点名;新增 POST/DELETE /settings/logo + POST /settings/asset(首页底图上传)

兑换码开关:
- 系统设置→积分 新增「开启兑换码」(默认开);关闭后后端拒绝兑换、前台隐藏兑换入口(/site 暴露 cdk_redeem_enabled)

文档 / 分辨率:
- 去掉 quality 参数:size(宽x高)同时决定比例 + 分辨率档(长边映射 1K/2K/4K)
- 文档加「分辨率对照表」(14 个比例 × 1K/2K/4K → size 该传的值);guessRatio 与自定义模型 RATIO_OPTS 对齐到 14 个

其它:
- 删模型时同步清掉各上游账号「支持模型」里的该 id
- 首页设置/兑换码弹窗去固定高度滚动条;展示位弹窗浅色主题适配

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-30 13:52:31 +08:00
co-authored by Claude Opus 4.8
parent 1137d0bd1d
commit 5cf6206ee9
37 changed files with 1257 additions and 161 deletions
+39 -2
View File
@@ -97,6 +97,13 @@ async function changePwd() {
// ---- Credits: the REAL server-side balance of the logged-in user ----
// (admin adjustments in 用户管理 write this same field). Top-up is via CDK.
const balance = computed(() => Number(auth.user?.credits || 0))
const roleLabel = computed(() => ({ user: '普通用户', agent: '代理', admin: '管理员' }[auth.user?.role] || '普通用户'))
const concurrencyLabel = computed(() => {
const n = Number(auth.user?.concurrency_limit || 0)
const g = auth.user?.concurrency_group
if (n <= 0) return g ? `不限制 · ${g}` : '不限制'
return g ? `${n} · ${g}` : String(n)
})
const cdkCode = ref('')
const cdkBusy = ref(false)
@@ -184,6 +191,36 @@ function toast(m) {
</button>
</header>
<!-- ===== Account info ===== -->
<section class="card p-6">
<div class="flex items-center gap-2 mb-4">
<Icon name="accounts" class="w-4 h-4 text-violet-300" />
<h2 class="text-sm font-semibold">账户信息</h2>
</div>
<div class="grid grid-cols-2 lg:grid-cols-5 gap-4 text-sm">
<div>
<div class="text-[11px] text-white/40 uppercase tracking-wider mb-1">用户名</div>
<div class="text-white/90 font-medium truncate" :title="auth.user?.name || ''">{{ auth.user?.name || '—' }}</div>
</div>
<div class="col-span-2 lg:col-span-1">
<div class="text-[11px] text-white/40 uppercase tracking-wider mb-1">绑定邮箱</div>
<div class="text-white/90 font-mono text-xs break-all" :title="auth.user?.email || ''">{{ auth.user?.email || '—' }}</div>
</div>
<div>
<div class="text-[11px] text-white/40 uppercase tracking-wider mb-1">角色</div>
<div class="text-white/90">{{ roleLabel }}</div>
</div>
<div>
<div class="text-[11px] text-white/40 uppercase tracking-wider mb-1">积分余额</div>
<div class="text-amber-300 font-semibold tabular-nums">{{ pointsLabel(balance) }}</div>
</div>
<div>
<div class="text-[11px] text-white/40 uppercase tracking-wider mb-1">并发上限</div>
<div class="text-white/90 truncate" :title="concurrencyLabel">{{ concurrencyLabel }}</div>
</div>
</div>
</section>
<!-- ===== Two-column grid ===== -->
<div class="grid lg:grid-cols-2 gap-5">
@@ -280,8 +317,8 @@ function toast(m) {
</button>
</section>
<!-- CDK REDEEM -->
<section class="relative card p-7 md:p-8 overflow-hidden">
<!-- CDK REDEEM — hidden when the admin turns the 兑换码 switch off -->
<section v-if="site.cdkRedeemEnabled" class="relative card p-7 md:p-8 overflow-hidden">
<div class="inline-grid w-10 h-10 rounded-xl bg-emerald-500/15 ring-1 ring-emerald-400/30 grid place-items-center text-emerald-300">
<Icon name="spark" class="w-4 h-4" />
</div>