feat: 管理后台订单列表展示用户备注,用户信息摊平显示

- 新增 userNotes 字段,创建订单时从 Sub2API 读取用户 notes 保存
- 管理后台订单列表将用户名、邮箱、备注拆分为独立列,节约行高

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
erio
2026-03-03 04:37:39 +08:00
parent f4709b784f
commit 42da18484c
7 changed files with 15 additions and 4 deletions

View File

@@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE "orders" ADD COLUMN "user_notes" TEXT;

View File

@@ -11,6 +11,7 @@ model Order {
userId Int @map("user_id") userId Int @map("user_id")
userEmail String? @map("user_email") userEmail String? @map("user_email")
userName String? @map("user_name") userName String? @map("user_name")
userNotes String? @map("user_notes")
amount Decimal @db.Decimal(10, 2) amount Decimal @db.Decimal(10, 2)
rechargeCode String @unique @map("recharge_code") rechargeCode String @unique @map("recharge_code")
status OrderStatus @default(PENDING) status OrderStatus @default(PENDING)

View File

@@ -12,6 +12,7 @@ interface AdminOrder {
userId: number; userId: number;
userName: string | null; userName: string | null;
userEmail: string | null; userEmail: string | null;
userNotes: string | null;
amount: number; amount: number;
status: string; status: string;
paymentType: string; paymentType: string;

View File

@@ -34,6 +34,7 @@ export async function GET(request: NextRequest) {
userId: true, userId: true,
userName: true, userName: true,
userEmail: true, userEmail: true,
userNotes: true,
amount: true, amount: true,
status: true, status: true,
paymentType: true, paymentType: true,

View File

@@ -5,6 +5,7 @@ interface Order {
userId: number; userId: number;
userName: string | null; userName: string | null;
userEmail: string | null; userEmail: string | null;
userNotes: string | null;
amount: number; amount: number;
status: string; status: string;
paymentType: string; paymentType: string;
@@ -48,7 +49,9 @@ export default function OrderTable({ orders, onRetry, onCancel, onViewDetail, da
<thead className={dark ? 'bg-slate-800/50' : 'bg-gray-50'}> <thead className={dark ? 'bg-slate-800/50' : 'bg-gray-50'}>
<tr> <tr>
<th className={thCls}></th> <th className={thCls}></th>
<th className={thCls}></th> <th className={thCls}></th>
<th className={thCls}></th>
<th className={thCls}></th>
<th className={thCls}></th> <th className={thCls}></th>
<th className={thCls}></th> <th className={thCls}></th>
<th className={thCls}></th> <th className={thCls}></th>
@@ -71,10 +74,11 @@ export default function OrderTable({ orders, onRetry, onCancel, onViewDetail, da
{order.id.slice(0, 12)}... {order.id.slice(0, 12)}...
</button> </button>
</td> </td>
<td className="whitespace-nowrap px-4 py-3 text-sm"> <td className={`whitespace-nowrap px-4 py-3 text-sm ${dark ? 'text-slate-200' : ''}`}>
<div className={dark ? 'text-slate-200' : ''}>{order.userName || '-'}</div> {order.userName || `#${order.userId}`}
<div className={`text-xs ${dark ? 'text-slate-500' : 'text-gray-400'}`}>{order.userEmail || `ID: ${order.userId}`}</div>
</td> </td>
<td className={tdMuted}>{order.userEmail || '-'}</td>
<td className={tdMuted}>{order.userNotes || '-'}</td>
<td className={`whitespace-nowrap px-4 py-3 text-sm font-medium ${dark ? 'text-slate-200' : ''}`}>¥{order.amount.toFixed(2)}</td> <td className={`whitespace-nowrap px-4 py-3 text-sm font-medium ${dark ? 'text-slate-200' : ''}`}>¥{order.amount.toFixed(2)}</td>
<td className="whitespace-nowrap px-4 py-3 text-sm"> <td className="whitespace-nowrap px-4 py-3 text-sm">
<span className={`inline-flex rounded-full px-2 py-1 text-xs font-semibold ${dark ? statusInfo.dark : statusInfo.light}`}> <span className={`inline-flex rounded-full px-2 py-1 text-xs font-semibold ${dark ? statusInfo.dark : statusInfo.light}`}>

View File

@@ -102,6 +102,7 @@ export async function createOrder(input: CreateOrderInput): Promise<CreateOrderR
userId: input.userId, userId: input.userId,
userEmail: user.email, userEmail: user.email,
userName: user.username, userName: user.username,
userNotes: user.notes || null,
amount: new Prisma.Decimal(input.amount.toFixed(2)), amount: new Prisma.Decimal(input.amount.toFixed(2)),
rechargeCode: '', rechargeCode: '',
status: 'PENDING', status: 'PENDING',

View File

@@ -4,6 +4,7 @@ export interface Sub2ApiUser {
email: string; email: string;
status: string; // "active", "banned", etc. status: string; // "active", "banned", etc.
balance: number; balance: number;
notes?: string;
} }
export interface Sub2ApiRedeemCode { export interface Sub2ApiRedeemCode {