diff --git a/app/(admin)/settings/admin-settings-client.tsx b/app/(admin)/settings/admin-settings-client.tsx index a7970e500..55e2f39c1 100644 --- a/app/(admin)/settings/admin-settings-client.tsx +++ b/app/(admin)/settings/admin-settings-client.tsx @@ -98,6 +98,7 @@ type User = { name: string | null; department: string | null; role: "guest" | "regular" | "admin"; + isHighVip: boolean; isActive: boolean; monthlyImageLimit: number | null; pointsBalance: number; @@ -2286,6 +2287,9 @@ export function AdminSettingsClient() {
角色: {ROLE_LABELS[user.role] ?? user.role} | 状态: {user.isActive ? "已激活" : "未激活"}
+
+ 标签: {user.isHighVip ? "高级VIP" : "普通"} +
月度出图限额: {user.monthlyImageLimit ? `${user.monthlyImageLimit} 张/月` : "无限制"}
@@ -2342,6 +2346,13 @@ export function AdminSettingsClient() { > {user.isActive ? "禁用" : "激活"} +
!isAiModelAgentAuto(model) && !model.isHidden) + .filter( + ({ model }) => + !isAiModelAgentAuto(model) && + (allowHiddenModels || !model.isHidden) + ) .map((item) => item.model) ); const visibleModelIdSet = new Set(visibleModelsWithProvider.map((m) => m.id)); diff --git a/app/(chat)/page.tsx b/app/(chat)/page.tsx index 0c17ae458..62dff8f89 100644 --- a/app/(chat)/page.tsx +++ b/app/(chat)/page.tsx @@ -9,6 +9,7 @@ import { auth } from "../(auth)/auth"; import { getDefaultAiModel, getEnabledAiModelsWithProvider, + getUserById, getSiteSettings, getSystemSetting, } from "@/lib/db/queries"; @@ -79,13 +80,25 @@ async function NewChatPage() { // 尝试获取默认模型 const defaultModel = await getDefaultAiModel(); - if (defaultModel && !isAiModelAgentAuto(defaultModel) && !defaultModel.isHidden) { + const currentUser = session?.user?.id + ? await getUserById({ id: session.user.id }).catch(() => undefined) + : undefined; + const allowHiddenModels = + session?.user?.type === "admin" || Boolean(currentUser?.isHighVip); + + if ( + defaultModel && + !isAiModelAgentAuto(defaultModel) && + (allowHiddenModels || !defaultModel.isHidden) + ) { initialModelId = defaultModel.id; } else { // 如果没有设置默认模型,获取第一个启用的模型 - const enabledModels = await getEnabledAiModelsWithProvider(false, false); + const enabledModels = await getEnabledAiModelsWithProvider(false, true); const visibleModels = enabledModels.filter( - ({ model }) => !isAiModelAgentAuto(model) && !model.isHidden + ({ model }) => + !isAiModelAgentAuto(model) && + (allowHiddenModels || !model.isHidden) ); if (visibleModels.length > 0) { initialModelId = visibleModels[0].model.id; diff --git a/app/api/admin/users/route.ts b/app/api/admin/users/route.ts index 8278430ac..a62454960 100644 --- a/app/api/admin/users/route.ts +++ b/app/api/admin/users/route.ts @@ -11,6 +11,7 @@ import { ChatSDKError } from "@/lib/errors"; const updateUserSchema = z.object({ id: z.string().uuid(), role: z.enum(["guest", "regular", "admin"]).optional(), + isHighVip: z.boolean().optional(), isActive: z.boolean().optional(), email: z.string().email().optional(), monthlyImageLimit: z.number().int().min(0).nullable().optional(), @@ -53,7 +54,7 @@ export async function PATCH(request: Request) { await requireAdmin(); const body = await request.json(); - const { id, role, isActive, email, monthlyImageLimit, pointsBalance, giftPoints, deductPoints, pointsNote } = + const { id, role, isHighVip, isActive, email, monthlyImageLimit, pointsBalance, giftPoints, deductPoints, pointsNote } = updateUserSchema.parse(body); if (giftPoints !== undefined && deductPoints !== undefined) { @@ -77,6 +78,7 @@ export async function PATCH(request: Request) { const result = await updateUser({ id, role, + isHighVip, isActive, email, monthlyImageLimit, diff --git a/lib/db/migrations/0041_add_user_high_vip.sql b/lib/db/migrations/0041_add_user_high_vip.sql new file mode 100644 index 000000000..fba42d6b4 --- /dev/null +++ b/lib/db/migrations/0041_add_user_high_vip.sql @@ -0,0 +1,2 @@ +ALTER TABLE "User" +ADD COLUMN IF NOT EXISTS "isHighVip" boolean NOT NULL DEFAULT false; diff --git a/lib/db/migrations/meta/_journal.json b/lib/db/migrations/meta/_journal.json index 96c0b36e7..3476c8667 100644 --- a/lib/db/migrations/meta/_journal.json +++ b/lib/db/migrations/meta/_journal.json @@ -190,6 +190,13 @@ "when": 1771718400000, "tag": "0040_add_canvas_detail_default_model", "breakpoints": true + }, + { + "idx": 27, + "version": "7", + "when": 1772265600000, + "tag": "0041_add_user_high_vip", + "breakpoints": true } ] } diff --git a/lib/db/queries.ts b/lib/db/queries.ts index 258485880..ac97dfef1 100644 --- a/lib/db/queries.ts +++ b/lib/db/queries.ts @@ -766,6 +766,7 @@ export async function getAllUsers({ department: user.department, dingtalkUserId: user.dingtalkUserId, role: user.role, + isHighVip: user.isHighVip, isActive: user.isActive, monthlyImageLimit: user.monthlyImageLimit, pointsBalance: user.pointsBalance, @@ -807,6 +808,7 @@ export async function updateUser({ id, email, role, + isHighVip, isActive, monthlyImageLimit, pointsBalance, @@ -818,6 +820,7 @@ export async function updateUser({ id: string; email?: string; role?: "guest" | "regular" | "admin"; + isHighVip?: boolean; isActive?: boolean; monthlyImageLimit?: number | null; pointsBalance?: number | null; @@ -838,6 +841,7 @@ export async function updateUser({ const updateData: Record = { updatedAt: new Date() }; if (email !== undefined) updateData.email = email; if (role !== undefined) updateData.role = role; + if (isHighVip !== undefined) updateData.isHighVip = isHighVip; if (isActive !== undefined) updateData.isActive = isActive; if (monthlyImageLimit !== undefined) { // 0 或空转为 null(表示无限制) diff --git a/lib/db/schema.ts b/lib/db/schema.ts index d34a8a55c..e08b58c6d 100644 --- a/lib/db/schema.ts +++ b/lib/db/schema.ts @@ -24,6 +24,7 @@ export const user = pgTable("User", { role: varchar("role", { enum: ["guest", "regular", "admin"] }) .notNull() .default("regular"), + isHighVip: boolean("isHighVip").notNull().default(false), isActive: boolean("isActive").notNull().default(true), monthlyImageLimit: integer("monthlyImageLimit"), // 每月出图限额,NULL或0表示无限制 pointsBalance: integer("pointsBalance").notNull().default(0), // 当前剩余积分