feat: 套餐分组清理 + 续费延期 + UI统一
- Schema: groupId 改为 nullable,新增迁移 - GET 套餐列表自动检测并清除 Sub2API 中已删除的分组绑定 - PUT 保存时校验分组存在性,已删除则自动解绑并返回 409 - 续费逻辑:同分组有活跃订阅时从到期日计算天数再 createAndRedeem - 提取 PlanInfoDisplay 共享组件,SubscriptionConfirm 复用 - 默认模型统一到 /v1/messages badge 内 - 前端编辑表单适配 nullable groupId,未绑定时禁用保存
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import { verifyAdminToken, unauthorizedResponse } from '@/lib/admin-auth';
|
||||
import { prisma } from '@/lib/db';
|
||||
import { getGroup } from '@/lib/sub2api/client';
|
||||
|
||||
export async function PUT(request: NextRequest, { params }: { params: Promise<{ id: string }> }) {
|
||||
if (!(await verifyAdminToken(request))) return unauthorizedResponse(request);
|
||||
@@ -14,6 +15,14 @@ export async function PUT(request: NextRequest, { params }: { params: Promise<{
|
||||
return NextResponse.json({ error: '订阅套餐不存在' }, { status: 404 });
|
||||
}
|
||||
|
||||
// 确定最终 groupId:如果传了 group_id 用传入值,否则用现有值
|
||||
const finalGroupId = body.group_id !== undefined ? (body.group_id ? Number(body.group_id) : null) : existing.groupId;
|
||||
|
||||
// 必须绑定分组才能保存
|
||||
if (finalGroupId === null || finalGroupId === undefined) {
|
||||
return NextResponse.json({ error: '必须关联一个 Sub2API 分组' }, { status: 400 });
|
||||
}
|
||||
|
||||
// 如果更新了 group_id,检查唯一性
|
||||
if (body.group_id !== undefined && Number(body.group_id) !== existing.groupId) {
|
||||
const conflict = await prisma.subscriptionPlan.findUnique({
|
||||
@@ -27,6 +36,20 @@ export async function PUT(request: NextRequest, { params }: { params: Promise<{
|
||||
}
|
||||
}
|
||||
|
||||
// 校验分组在 Sub2API 中仍然存在
|
||||
const group = await getGroup(finalGroupId);
|
||||
if (!group) {
|
||||
// 分组已被删除,自动解绑
|
||||
await prisma.subscriptionPlan.update({
|
||||
where: { id },
|
||||
data: { groupId: null, forSale: false },
|
||||
});
|
||||
return NextResponse.json(
|
||||
{ error: '该分组在 Sub2API 中已被删除,已自动解绑,请重新选择分组' },
|
||||
{ status: 409 },
|
||||
);
|
||||
}
|
||||
|
||||
if (body.price !== undefined && (typeof body.price !== 'number' || body.price <= 0 || body.price > 99999999.99)) {
|
||||
return NextResponse.json({ error: 'price 必须是 0.01 ~ 99999999.99 之间的数值' }, { status: 400 });
|
||||
}
|
||||
@@ -63,7 +86,7 @@ export async function PUT(request: NextRequest, { params }: { params: Promise<{
|
||||
|
||||
return NextResponse.json({
|
||||
id: plan.id,
|
||||
groupId: String(plan.groupId),
|
||||
groupId: plan.groupId != null ? String(plan.groupId) : null,
|
||||
groupName: null,
|
||||
name: plan.name,
|
||||
description: plan.description,
|
||||
|
||||
Reference in New Issue
Block a user