feat: 套餐有效期支持日/周/月单位,订阅履约改用兑换码流程,UI层次感优化
- Prisma: SubscriptionPlan 新增 validityUnit 字段 (day/week/month) - 新增 subscription-utils.ts 计算实际天数及格式化显示 - Sub2API client createAndRedeem 支持 subscription 类型 (group_id, validity_days) - 订阅履约从 assignSubscription 改为 createAndRedeem,在 Sub2API 留痕 - 订单创建动态计算天数(月单位按自然月差值) - 管理后台表单支持有效期数值+单位下拉 - 前端 ChannelCard 渠道卡片视觉层次优化(模型标签渐变、倍率突出、闪电图标) - 按量付费 banner 改为渐变背景+底部倍率说明标签 - 帮助/客服信息区块添加到充值、订阅、支付全流程页面 - 移除系统配置独立页面入口,subscriptions API 返回用户信息
This commit is contained in:
@@ -34,6 +34,9 @@ export async function PUT(request: NextRequest, { params }: { params: Promise<{
|
||||
if (body.price !== undefined) data.price = body.price;
|
||||
if (body.original_price !== undefined) data.originalPrice = body.original_price;
|
||||
if (body.validity_days !== undefined) data.validityDays = body.validity_days;
|
||||
if (body.validity_unit !== undefined && ['day', 'week', 'month'].includes(body.validity_unit)) {
|
||||
data.validityUnit = body.validity_unit;
|
||||
}
|
||||
if (body.features !== undefined) data.features = body.features;
|
||||
if (body.for_sale !== undefined) data.forSale = body.for_sale;
|
||||
if (body.sort_order !== undefined) data.sortOrder = body.sort_order;
|
||||
|
||||
@@ -42,7 +42,7 @@ export async function POST(request: NextRequest) {
|
||||
|
||||
try {
|
||||
const body = await request.json();
|
||||
const { group_id, name, description, price, original_price, validity_days, features, for_sale, sort_order } = body;
|
||||
const { group_id, name, description, price, original_price, validity_days, validity_unit, features, for_sale, sort_order } = body;
|
||||
|
||||
if (!group_id || !name || price === undefined) {
|
||||
return NextResponse.json({ error: '缺少必填字段: group_id, name, price' }, { status: 400 });
|
||||
@@ -68,6 +68,7 @@ export async function POST(request: NextRequest) {
|
||||
price,
|
||||
originalPrice: original_price ?? null,
|
||||
validityDays: validity_days ?? 30,
|
||||
validityUnit: ['day', 'week', 'month'].includes(validity_unit) ? validity_unit : 'day',
|
||||
features: features ?? null,
|
||||
forSale: for_sale ?? false,
|
||||
sortOrder: sort_order ?? 0,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import { verifyAdminToken, unauthorizedResponse } from '@/lib/admin-auth';
|
||||
import { getUserSubscriptions } from '@/lib/sub2api/client';
|
||||
import { getUserSubscriptions, getUser } from '@/lib/sub2api/client';
|
||||
|
||||
export async function GET(request: NextRequest) {
|
||||
if (!(await verifyAdminToken(request))) return unauthorizedResponse(request);
|
||||
@@ -18,7 +18,10 @@ export async function GET(request: NextRequest) {
|
||||
return NextResponse.json({ error: '无效的 user_id' }, { status: 400 });
|
||||
}
|
||||
|
||||
const subscriptions = await getUserSubscriptions(parsedUserId);
|
||||
const [subscriptions, user] = await Promise.all([
|
||||
getUserSubscriptions(parsedUserId),
|
||||
getUser(parsedUserId).catch(() => null),
|
||||
]);
|
||||
|
||||
// 如果提供了 group_id 筛选,过滤结果
|
||||
const groupId = searchParams.get('group_id');
|
||||
@@ -26,7 +29,10 @@ export async function GET(request: NextRequest) {
|
||||
? subscriptions.filter((s) => s.group_id === Number(groupId))
|
||||
: subscriptions;
|
||||
|
||||
return NextResponse.json({ subscriptions: filtered });
|
||||
return NextResponse.json({
|
||||
subscriptions: filtered,
|
||||
user: user ? { id: user.id, username: user.username, email: user.email } : null,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Failed to query subscriptions:', error);
|
||||
return NextResponse.json({ error: '查询订阅信息失败' }, { status: 500 });
|
||||
|
||||
Reference in New Issue
Block a user