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), // 当前剩余积分