feat: add high vip model visibility control
This commit is contained in:
parent
2e837b3769
commit
fbb2e186b9
@ -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() {
|
||||
<div className="text-sm text-muted-foreground">
|
||||
角色: {ROLE_LABELS[user.role] ?? user.role} | 状态: {user.isActive ? "已激活" : "未激活"}
|
||||
</div>
|
||||
<div className="text-sm text-muted-foreground">
|
||||
标签: {user.isHighVip ? "高级VIP" : "普通"}
|
||||
</div>
|
||||
<div className="text-sm text-muted-foreground">
|
||||
月度出图限额: {user.monthlyImageLimit ? `${user.monthlyImageLimit} 张/月` : "无限制"}
|
||||
</div>
|
||||
@ -2342,6 +2346,13 @@ export function AdminSettingsClient() {
|
||||
>
|
||||
{user.isActive ? "禁用" : "激活"}
|
||||
</Button>
|
||||
<Button
|
||||
variant={user.isHighVip ? "default" : "outline"}
|
||||
size="sm"
|
||||
onClick={() => handleUpdateUser(user.id, { isHighVip: !user.isHighVip })}
|
||||
>
|
||||
{user.isHighVip ? "VIP开" : "VIP关"}
|
||||
</Button>
|
||||
<div className="flex items-center gap-1">
|
||||
<Input
|
||||
type="number"
|
||||
|
||||
@ -3,6 +3,7 @@ import {
|
||||
getAgentAiModelWithProvider,
|
||||
getDefaultAiModel,
|
||||
getEnabledAiModelsWithProvider,
|
||||
getUserById,
|
||||
} from "@/lib/db/queries";
|
||||
import { ChatSDKError } from "@/lib/errors";
|
||||
import { isAiModelAgentAuto } from "@/lib/db/ai-model-flags";
|
||||
@ -29,13 +30,21 @@ export async function GET() {
|
||||
return new ChatSDKError("unauthorized:chat").toResponse();
|
||||
}
|
||||
|
||||
const currentUser = await getUserById({ id: session.user.id });
|
||||
const allowHiddenModels =
|
||||
session.user.type === "admin" || Boolean(currentUser?.isHighVip);
|
||||
|
||||
// 只获取主模型(不包括子模型),子模型由后端根据 videoParams.mode 自动选择
|
||||
const modelsWithProvider = await getEnabledAiModelsWithProvider(false, false);
|
||||
const modelsWithProvider = await getEnabledAiModelsWithProvider(false, true);
|
||||
const agentModelWithProvider = await getAgentAiModelWithProvider();
|
||||
const defaultModel = await getDefaultAiModel();
|
||||
const visibleModelsWithProvider = sortModelsForDisplay(
|
||||
modelsWithProvider
|
||||
.filter(({ model }) => !isAiModelAgentAuto(model) && !model.isHidden)
|
||||
.filter(
|
||||
({ model }) =>
|
||||
!isAiModelAgentAuto(model) &&
|
||||
(allowHiddenModels || !model.isHidden)
|
||||
)
|
||||
.map((item) => item.model)
|
||||
);
|
||||
const visibleModelIdSet = new Set(visibleModelsWithProvider.map((m) => m.id));
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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,
|
||||
|
||||
2
lib/db/migrations/0041_add_user_high_vip.sql
Normal file
2
lib/db/migrations/0041_add_user_high_vip.sql
Normal file
@ -0,0 +1,2 @@
|
||||
ALTER TABLE "User"
|
||||
ADD COLUMN IF NOT EXISTS "isHighVip" boolean NOT NULL DEFAULT false;
|
||||
@ -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
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@ -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<string, unknown> = { 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(表示无限制)
|
||||
|
||||
@ -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), // 当前剩余积分
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user