v1
This commit is contained in:
@@ -0,0 +1,102 @@
|
||||
import { NextRequest, NextResponse } from 'next/server'
|
||||
import { prisma } from '@/lib/prisma'
|
||||
import { requireAdmin } from '@/lib/admin-auth'
|
||||
|
||||
// 获取所有组件
|
||||
export async function GET(request: NextRequest) {
|
||||
try {
|
||||
await requireAdmin(request)
|
||||
|
||||
const { searchParams } = new URL(request.url)
|
||||
const page = parseInt(searchParams.get('page') || '1')
|
||||
const limit = parseInt(searchParams.get('limit') || '20')
|
||||
const type = searchParams.get('type')
|
||||
const search = searchParams.get('search')
|
||||
|
||||
const where: any = {}
|
||||
if (type) {
|
||||
where.componentType = {
|
||||
name: type
|
||||
}
|
||||
}
|
||||
if (search) {
|
||||
where.OR = [
|
||||
{ name: { contains: search, mode: 'insensitive' } },
|
||||
{ brand: { contains: search, mode: 'insensitive' } },
|
||||
]
|
||||
}
|
||||
|
||||
const [components, total] = await Promise.all([
|
||||
prisma.component.findMany({
|
||||
where,
|
||||
include: {
|
||||
componentType: true
|
||||
},
|
||||
orderBy: {
|
||||
createdAt: 'desc'
|
||||
},
|
||||
skip: (page - 1) * limit,
|
||||
take: limit
|
||||
}),
|
||||
prisma.component.count({ where })
|
||||
])
|
||||
|
||||
return NextResponse.json({
|
||||
components,
|
||||
pagination: {
|
||||
page,
|
||||
limit,
|
||||
total,
|
||||
pages: Math.ceil(total / limit)
|
||||
}
|
||||
})
|
||||
|
||||
} catch (error: any) {
|
||||
return NextResponse.json(
|
||||
{ message: error.message || '获取组件列表失败' },
|
||||
{ status: error.message === '权限不足' ? 403 : 500 }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// 创建新组件
|
||||
export async function POST(request: NextRequest) {
|
||||
try {
|
||||
await requireAdmin(request)
|
||||
|
||||
const body = await request.json()
|
||||
const { name, brand, model, price, description, imageUrl, stock, specifications, componentTypeId } = body
|
||||
|
||||
if (!name || !brand || !model || !price || !componentTypeId) {
|
||||
return NextResponse.json(
|
||||
{ message: '请填写所有必填字段' },
|
||||
{ status: 400 }
|
||||
)
|
||||
}
|
||||
|
||||
const component = await prisma.component.create({
|
||||
data: {
|
||||
name,
|
||||
brand,
|
||||
model,
|
||||
price: parseFloat(price),
|
||||
description,
|
||||
imageUrl,
|
||||
stock: parseInt(stock) || 0,
|
||||
specifications: specifications ? JSON.stringify(specifications) : null,
|
||||
componentTypeId
|
||||
},
|
||||
include: {
|
||||
componentType: true
|
||||
}
|
||||
})
|
||||
|
||||
return NextResponse.json(component, { status: 201 })
|
||||
|
||||
} catch (error: any) {
|
||||
return NextResponse.json(
|
||||
{ message: error.message || '创建组件失败' },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
import { NextRequest, NextResponse } from 'next/server'
|
||||
import { prisma } from '@/lib/prisma'
|
||||
|
||||
export async function PUT(
|
||||
request: NextRequest,
|
||||
{ params }: { params: Promise<{ id: string }> }
|
||||
) {
|
||||
try {
|
||||
const { id } = await params
|
||||
const { status } = await request.json()
|
||||
|
||||
const order = await prisma.order.update({
|
||||
where: { id: id },
|
||||
data: { status },
|
||||
include: {
|
||||
user: {
|
||||
select: {
|
||||
id: true,
|
||||
email: true,
|
||||
username: true,
|
||||
name: true
|
||||
}
|
||||
},
|
||||
orderItems: {
|
||||
include: {
|
||||
component: {
|
||||
select: {
|
||||
id: true,
|
||||
name: true,
|
||||
brand: true,
|
||||
model: true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
return NextResponse.json(order)
|
||||
} catch (error) {
|
||||
console.error('更新订单状态失败:', error)
|
||||
return NextResponse.json(
|
||||
{ message: '更新订单状态失败' },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
import { NextRequest, NextResponse } from 'next/server'
|
||||
import { prisma } from '@/lib/prisma'
|
||||
|
||||
export async function GET() {
|
||||
try {
|
||||
const orders = await prisma.order.findMany({
|
||||
include: {
|
||||
user: {
|
||||
select: {
|
||||
id: true,
|
||||
email: true,
|
||||
username: true,
|
||||
name: true
|
||||
}
|
||||
}, orderItems: {
|
||||
include: {
|
||||
component: {
|
||||
include: {
|
||||
componentType: true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
orderBy: {
|
||||
createdAt: 'desc'
|
||||
}
|
||||
})
|
||||
|
||||
return NextResponse.json(orders)
|
||||
} catch (error) {
|
||||
console.error('获取订单列表失败:', error)
|
||||
return NextResponse.json(
|
||||
{ message: '获取订单列表失败' },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
import { NextRequest, NextResponse } from 'next/server'
|
||||
import { prisma } from '@/lib/prisma'
|
||||
import { requireAdmin } from '@/lib/admin-auth'
|
||||
|
||||
export async function GET(request: NextRequest) {
|
||||
try {
|
||||
await requireAdmin(request)
|
||||
|
||||
// 按消费金额倒排的前十用户
|
||||
const topUsersBySpending = await prisma.user.findMany({
|
||||
where: {
|
||||
isAdmin: false,
|
||||
orders: {
|
||||
some: {}
|
||||
}
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
username: true,
|
||||
name: true,
|
||||
email: true,
|
||||
orders: {
|
||||
select: {
|
||||
totalAmount: true
|
||||
}
|
||||
}
|
||||
},
|
||||
take: 10
|
||||
})
|
||||
|
||||
const usersWithSpending = topUsersBySpending.map(user => ({
|
||||
id: user.id,
|
||||
username: user.username,
|
||||
name: user.name || user.username,
|
||||
email: user.email,
|
||||
totalSpending: user.orders.reduce((sum, order) => sum + order.totalAmount, 0),
|
||||
orderCount: user.orders.length
|
||||
})).sort((a, b) => b.totalSpending - a.totalSpending)
|
||||
|
||||
// 按销售量倒排序的前十配件
|
||||
const topComponentsBySales = await prisma.component.findMany({
|
||||
select: {
|
||||
id: true,
|
||||
name: true,
|
||||
brand: true,
|
||||
price: true,
|
||||
componentType: {
|
||||
select: {
|
||||
name: true
|
||||
}
|
||||
},
|
||||
orderItems: {
|
||||
select: {
|
||||
quantity: true
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const componentsWithSales = topComponentsBySales.map(component => ({
|
||||
id: component.id,
|
||||
name: component.name,
|
||||
brand: component.brand,
|
||||
price: component.price,
|
||||
typeName: component.componentType.name,
|
||||
totalSales: component.orderItems.reduce((sum, item) => sum + item.quantity, 0),
|
||||
totalRevenue: component.orderItems.reduce((sum, item) => sum + (item.quantity * component.price), 0)
|
||||
})).sort((a, b) => b.totalSales - a.totalSales).slice(0, 10)
|
||||
|
||||
// 总体统计
|
||||
const totalUsers = await prisma.user.count({ where: { isAdmin: false } })
|
||||
const totalOrders = await prisma.order.count()
|
||||
const totalComponents = await prisma.component.count()
|
||||
const totalRevenue = await prisma.order.aggregate({
|
||||
_sum: {
|
||||
totalAmount: true
|
||||
}
|
||||
})
|
||||
|
||||
const recentOrders = await prisma.order.findMany({
|
||||
take: 5,
|
||||
orderBy: {
|
||||
createdAt: 'desc'
|
||||
},
|
||||
include: {
|
||||
user: {
|
||||
select: {
|
||||
username: true,
|
||||
name: true
|
||||
}
|
||||
},
|
||||
orderItems: {
|
||||
include: {
|
||||
component: {
|
||||
select: {
|
||||
name: true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
return NextResponse.json({
|
||||
overview: {
|
||||
totalUsers,
|
||||
totalOrders,
|
||||
totalComponents,
|
||||
totalRevenue: totalRevenue._sum.totalAmount || 0
|
||||
},
|
||||
topUsers: usersWithSpending,
|
||||
topComponents: componentsWithSales,
|
||||
recentOrders
|
||||
})
|
||||
|
||||
} catch (error: any) {
|
||||
console.error('获取统计数据失败:', error)
|
||||
return NextResponse.json(
|
||||
{ message: error.message || '获取统计数据失败' },
|
||||
{ status: error.message === '权限不足' ? 403 : 500 }
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
import { NextRequest, NextResponse } from 'next/server'
|
||||
import { prisma } from '@/lib/prisma'
|
||||
import { verifyPassword, generateToken } from '@/lib/auth'
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
try {
|
||||
const { email, password } = await request.json()
|
||||
|
||||
if (!email || !password) {
|
||||
return NextResponse.json(
|
||||
{ message: '邮箱和密码不能为空' },
|
||||
{ status: 400 }
|
||||
)
|
||||
}
|
||||
|
||||
// 查找用户
|
||||
const user = await prisma.user.findUnique({
|
||||
where: { email }
|
||||
})
|
||||
|
||||
if (!user) {
|
||||
return NextResponse.json(
|
||||
{ message: '用户不存在' },
|
||||
{ status: 401 }
|
||||
)
|
||||
}
|
||||
|
||||
// 验证密码
|
||||
const isValidPassword = await verifyPassword(password, user.password)
|
||||
if (!isValidPassword) {
|
||||
return NextResponse.json(
|
||||
{ message: '密码错误' },
|
||||
{ status: 401 }
|
||||
)
|
||||
}
|
||||
|
||||
// 生成token
|
||||
const token = generateToken({
|
||||
userId: user.id,
|
||||
email: user.email,
|
||||
isAdmin: user.isAdmin
|
||||
})
|
||||
|
||||
// 返回用户信息(不包含密码)
|
||||
const { password: _, ...userWithoutPassword } = user
|
||||
|
||||
return NextResponse.json({
|
||||
message: '登录成功',
|
||||
token,
|
||||
user: userWithoutPassword
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('Login error:', error)
|
||||
return NextResponse.json(
|
||||
{ message: '服务器错误' },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
import { NextRequest, NextResponse } from 'next/server'
|
||||
import { prisma } from '@/lib/prisma'
|
||||
import { hashPassword } from '@/lib/auth'
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
try {
|
||||
const { email, username, password, name, phone, address } = await request.json()
|
||||
|
||||
if (!email || !username || !password) {
|
||||
return NextResponse.json(
|
||||
{ message: '邮箱、用户名和密码不能为空' },
|
||||
{ status: 400 }
|
||||
)
|
||||
}
|
||||
|
||||
// 检查邮箱是否已存在
|
||||
const existingUserByEmail = await prisma.user.findUnique({
|
||||
where: { email }
|
||||
})
|
||||
|
||||
if (existingUserByEmail) {
|
||||
return NextResponse.json(
|
||||
{ message: '该邮箱已被注册' },
|
||||
{ status: 400 }
|
||||
)
|
||||
}
|
||||
|
||||
// 检查用户名是否已存在
|
||||
const existingUserByUsername = await prisma.user.findUnique({
|
||||
where: { username }
|
||||
})
|
||||
|
||||
if (existingUserByUsername) {
|
||||
return NextResponse.json(
|
||||
{ message: '该用户名已被使用' },
|
||||
{ status: 400 }
|
||||
)
|
||||
}
|
||||
|
||||
// 加密密码
|
||||
const hashedPassword = await hashPassword(password)
|
||||
|
||||
// 创建用户
|
||||
const user = await prisma.user.create({
|
||||
data: {
|
||||
email,
|
||||
username,
|
||||
password: hashedPassword,
|
||||
name: name || null,
|
||||
phone: phone || null,
|
||||
address: address || null,
|
||||
isAdmin: false
|
||||
}
|
||||
})
|
||||
|
||||
// 返回用户信息(不包含密码)
|
||||
const { password: _, ...userWithoutPassword } = user
|
||||
|
||||
return NextResponse.json({
|
||||
message: '注册成功',
|
||||
user: userWithoutPassword
|
||||
}, { status: 201 })
|
||||
} catch (error) {
|
||||
console.error('Register error:', error)
|
||||
return NextResponse.json(
|
||||
{ message: '服务器错误' },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
import { NextRequest, NextResponse } from 'next/server'
|
||||
import { prisma } from '@/lib/prisma'
|
||||
import { verifyToken } from '@/lib/auth'
|
||||
|
||||
// 更新购物车项数量
|
||||
export async function PUT(
|
||||
request: NextRequest,
|
||||
{ params }: { params: Promise<{ id: string }> }
|
||||
) {
|
||||
try {
|
||||
const { id } = await params
|
||||
|
||||
const authHeader = request.headers.get('authorization')
|
||||
if (!authHeader || !authHeader.startsWith('Bearer ')) {
|
||||
return NextResponse.json(
|
||||
{ message: '请先登录' },
|
||||
{ status: 401 }
|
||||
)
|
||||
}
|
||||
|
||||
const token = authHeader.substring(7)
|
||||
const decoded = verifyToken(token)
|
||||
if (!decoded) {
|
||||
return NextResponse.json(
|
||||
{ message: '登录已过期' },
|
||||
{ status: 401 }
|
||||
)
|
||||
}
|
||||
|
||||
const { quantity } = await request.json()
|
||||
|
||||
if (!quantity || quantity < 1) {
|
||||
return NextResponse.json(
|
||||
{ message: '数量必须大于0' },
|
||||
{ status: 400 }
|
||||
)
|
||||
}
|
||||
|
||||
// 检查购物车项是否属于当前用户
|
||||
const cartItem = await prisma.cartItem.findFirst({
|
||||
where: {
|
||||
id: id,
|
||||
userId: decoded.userId
|
||||
},
|
||||
include: {
|
||||
component: true
|
||||
}
|
||||
})
|
||||
|
||||
if (!cartItem) {
|
||||
return NextResponse.json(
|
||||
{ message: '购物车项不存在' },
|
||||
{ status: 404 }
|
||||
)
|
||||
}
|
||||
|
||||
// 检查库存
|
||||
if (quantity > cartItem.component.stock) {
|
||||
return NextResponse.json(
|
||||
{ message: '库存不足' },
|
||||
{ status: 400 }
|
||||
)
|
||||
}
|
||||
|
||||
const updatedCartItem = await prisma.cartItem.update({
|
||||
where: { id: id },
|
||||
data: { quantity: quantity },
|
||||
include: {
|
||||
component: {
|
||||
include: {
|
||||
componentType: true
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
return NextResponse.json(updatedCartItem)
|
||||
} catch (error) {
|
||||
console.error('更新购物车项失败:', error)
|
||||
return NextResponse.json(
|
||||
{ message: '更新购物车项失败' },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// 删除购物车项
|
||||
export async function DELETE(
|
||||
request: NextRequest,
|
||||
{ params }: { params: Promise<{ id: string }> }
|
||||
) {
|
||||
try {
|
||||
const { id } = await params
|
||||
|
||||
const authHeader = request.headers.get('authorization')
|
||||
if (!authHeader || !authHeader.startsWith('Bearer ')) {
|
||||
return NextResponse.json(
|
||||
{ message: '请先登录' },
|
||||
{ status: 401 }
|
||||
)
|
||||
}
|
||||
|
||||
const token = authHeader.substring(7)
|
||||
const decoded = verifyToken(token)
|
||||
if (!decoded) {
|
||||
return NextResponse.json(
|
||||
{ message: '登录已过期' },
|
||||
{ status: 401 }
|
||||
)
|
||||
}
|
||||
|
||||
// 检查购物车项是否属于当前用户
|
||||
const cartItem = await prisma.cartItem.findFirst({
|
||||
where: {
|
||||
id: id,
|
||||
userId: decoded.userId
|
||||
}
|
||||
})
|
||||
|
||||
if (!cartItem) {
|
||||
return NextResponse.json(
|
||||
{ message: '购物车项不存在' },
|
||||
{ status: 404 }
|
||||
)
|
||||
}
|
||||
|
||||
await prisma.cartItem.delete({
|
||||
where: { id: id }
|
||||
})
|
||||
|
||||
return NextResponse.json({ message: '已从购物车移除' })
|
||||
} catch (error) {
|
||||
console.error('删除购物车项失败:', error)
|
||||
return NextResponse.json(
|
||||
{ message: '删除购物车项失败' },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,188 @@
|
||||
import { NextRequest, NextResponse } from 'next/server'
|
||||
import { prisma } from '@/lib/prisma'
|
||||
import { verifyToken } from '@/lib/auth'
|
||||
|
||||
// 获取购物车
|
||||
export async function GET(request: NextRequest) {
|
||||
try {
|
||||
const authHeader = request.headers.get('authorization')
|
||||
if (!authHeader || !authHeader.startsWith('Bearer ')) {
|
||||
return NextResponse.json(
|
||||
{ message: '请先登录' },
|
||||
{ status: 401 }
|
||||
)
|
||||
}
|
||||
|
||||
const token = authHeader.substring(7)
|
||||
const decoded = verifyToken(token)
|
||||
if (!decoded) {
|
||||
return NextResponse.json(
|
||||
{ message: '登录已过期' },
|
||||
{ status: 401 }
|
||||
)
|
||||
}
|
||||
|
||||
const cartItems = await prisma.cartItem.findMany({
|
||||
where: { userId: decoded.userId },
|
||||
include: {
|
||||
component: {
|
||||
include: {
|
||||
componentType: true
|
||||
}
|
||||
}
|
||||
},
|
||||
orderBy: { createdAt: 'desc' }
|
||||
})
|
||||
|
||||
return NextResponse.json(cartItems)
|
||||
} catch (error) {
|
||||
console.error('获取购物车失败:', error)
|
||||
return NextResponse.json(
|
||||
{ message: '获取购物车失败' },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// 添加商品到购物车
|
||||
export async function POST(request: NextRequest) {
|
||||
try {
|
||||
const authHeader = request.headers.get('authorization')
|
||||
if (!authHeader || !authHeader.startsWith('Bearer ')) {
|
||||
return NextResponse.json(
|
||||
{ message: '请先登录' },
|
||||
{ status: 401 }
|
||||
)
|
||||
}
|
||||
|
||||
const token = authHeader.substring(7)
|
||||
const decoded = verifyToken(token)
|
||||
if (!decoded) {
|
||||
return NextResponse.json(
|
||||
{ message: '登录已过期' },
|
||||
{ status: 401 }
|
||||
)
|
||||
}
|
||||
|
||||
const { componentId, quantity = 1 } = await request.json()
|
||||
|
||||
if (!componentId) {
|
||||
return NextResponse.json(
|
||||
{ message: '缺少商品ID' },
|
||||
{ status: 400 }
|
||||
)
|
||||
}
|
||||
|
||||
// 检查商品是否存在
|
||||
const component = await prisma.component.findUnique({
|
||||
where: { id: componentId }
|
||||
})
|
||||
|
||||
if (!component) {
|
||||
return NextResponse.json(
|
||||
{ message: '商品不存在' },
|
||||
{ status: 404 }
|
||||
)
|
||||
}
|
||||
|
||||
if (component.stock < quantity) {
|
||||
return NextResponse.json(
|
||||
{ message: '库存不足' },
|
||||
{ status: 400 }
|
||||
)
|
||||
}
|
||||
|
||||
// 检查购物车中是否已存在该商品
|
||||
const existingCartItem = await prisma.cartItem.findUnique({
|
||||
where: {
|
||||
userId_componentId: {
|
||||
userId: decoded.userId,
|
||||
componentId: componentId
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
let cartItem
|
||||
|
||||
if (existingCartItem) {
|
||||
// 更新数量
|
||||
const newQuantity = existingCartItem.quantity + quantity
|
||||
if (newQuantity > component.stock) {
|
||||
return NextResponse.json(
|
||||
{ message: '超出库存限制' },
|
||||
{ status: 400 }
|
||||
)
|
||||
}
|
||||
|
||||
cartItem = await prisma.cartItem.update({
|
||||
where: { id: existingCartItem.id },
|
||||
data: { quantity: newQuantity },
|
||||
include: {
|
||||
component: {
|
||||
include: {
|
||||
componentType: true
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
} else {
|
||||
// 创建新的购物车项
|
||||
cartItem = await prisma.cartItem.create({
|
||||
data: {
|
||||
userId: decoded.userId,
|
||||
componentId: componentId,
|
||||
quantity: quantity
|
||||
},
|
||||
include: {
|
||||
component: {
|
||||
include: {
|
||||
componentType: true
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return NextResponse.json(cartItem, { status: 201 })
|
||||
} catch (error) {
|
||||
console.error('添加到购物车失败:', error)
|
||||
return NextResponse.json(
|
||||
{ message: '添加到购物车失败' },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// 清空购物车
|
||||
export async function DELETE(request: NextRequest) {
|
||||
try {
|
||||
const authHeader = request.headers.get('authorization')
|
||||
if (!authHeader || !authHeader.startsWith('Bearer ')) {
|
||||
return NextResponse.json(
|
||||
{ message: '请先登录' },
|
||||
{ status: 401 }
|
||||
)
|
||||
}
|
||||
|
||||
const token = authHeader.substring(7)
|
||||
const decoded = verifyToken(token)
|
||||
if (!decoded) {
|
||||
return NextResponse.json(
|
||||
{ message: '登录已过期' },
|
||||
{ status: 401 }
|
||||
)
|
||||
}
|
||||
|
||||
await prisma.cartItem.deleteMany({
|
||||
where: { userId: decoded.userId }
|
||||
})
|
||||
|
||||
return NextResponse.json({ message: '购物车已清空' })
|
||||
} catch (error) {
|
||||
console.error('清空购物车失败:', error)
|
||||
return NextResponse.json(
|
||||
{ message: '清空购物车失败' },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import { NextRequest, NextResponse } from 'next/server'
|
||||
import { prisma } from '@/lib/prisma'
|
||||
|
||||
export async function GET() {
|
||||
try {
|
||||
const componentTypes = await prisma.componentType.findMany({
|
||||
include: {
|
||||
_count: {
|
||||
select: {
|
||||
components: true
|
||||
}
|
||||
}
|
||||
},
|
||||
orderBy: {
|
||||
name: 'asc'
|
||||
}
|
||||
})
|
||||
|
||||
return NextResponse.json(componentTypes)
|
||||
} catch (error) {
|
||||
console.error('Component types fetch error:', error)
|
||||
return NextResponse.json(
|
||||
{ message: '获取配件类型失败' },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
import { NextRequest, NextResponse } from 'next/server'
|
||||
import { prisma } from '@/lib/prisma'
|
||||
|
||||
export async function GET(
|
||||
request: NextRequest,
|
||||
{ params }: { params: Promise<{ id: string }> }
|
||||
) {
|
||||
try {
|
||||
const { id } = await params
|
||||
|
||||
const component = await prisma.component.findUnique({
|
||||
where: { id: id },
|
||||
include: {
|
||||
componentType: true
|
||||
}
|
||||
})
|
||||
|
||||
if (!component) {
|
||||
return NextResponse.json(
|
||||
{ message: '配件不存在' },
|
||||
{ status: 404 }
|
||||
)
|
||||
}
|
||||
|
||||
return NextResponse.json(component)
|
||||
} catch (error) {
|
||||
console.error('Component fetch error:', error)
|
||||
return NextResponse.json(
|
||||
{ message: '获取配件详情失败' },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export async function PUT(
|
||||
request: NextRequest,
|
||||
{ params }: { params: Promise<{ id: string }> }
|
||||
) {
|
||||
try {
|
||||
const { id } = await params
|
||||
const data = await request.json()
|
||||
|
||||
const component = await prisma.component.update({
|
||||
where: { id: id },
|
||||
data: {
|
||||
name: data.name,
|
||||
brand: data.brand,
|
||||
model: data.model,
|
||||
price: parseFloat(data.price),
|
||||
description: data.description,
|
||||
imageUrl: data.imageUrl,
|
||||
stock: parseInt(data.stock),
|
||||
specifications: data.specifications,
|
||||
componentTypeId: data.componentTypeId
|
||||
},
|
||||
include: {
|
||||
componentType: true
|
||||
}
|
||||
})
|
||||
|
||||
return NextResponse.json(component)
|
||||
} catch (error) {
|
||||
console.error('Component update error:', error)
|
||||
return NextResponse.json(
|
||||
{ message: '更新配件失败' },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export async function DELETE(
|
||||
request: NextRequest,
|
||||
{ params }: { params: Promise<{ id: string }> }
|
||||
) {
|
||||
try {
|
||||
const { id } = await params
|
||||
|
||||
await prisma.component.delete({
|
||||
where: { id: id }
|
||||
})
|
||||
|
||||
return NextResponse.json({ message: '删除成功' })
|
||||
} catch (error) {
|
||||
console.error('Component delete error:', error)
|
||||
return NextResponse.json(
|
||||
{ message: '删除配件失败' },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,215 @@
|
||||
import { NextRequest, NextResponse } from 'next/server'
|
||||
import { prisma } from '@/lib/prisma'
|
||||
|
||||
interface BatchComponentData {
|
||||
name: string
|
||||
brand: string
|
||||
model: string
|
||||
price: number
|
||||
description?: string
|
||||
imageUrl?: string
|
||||
stock: number
|
||||
specifications?: string
|
||||
componentTypeId?: string
|
||||
typeName?: string
|
||||
type?: string
|
||||
}
|
||||
|
||||
interface BatchResult {
|
||||
success: boolean
|
||||
item: BatchComponentData
|
||||
error?: string
|
||||
componentId?: string
|
||||
}
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
try {
|
||||
// 这个API需要管理员权限,实际应用中需要验证
|
||||
const { components } = await request.json()
|
||||
|
||||
if (!Array.isArray(components) || components.length === 0) {
|
||||
return NextResponse.json(
|
||||
{ message: '请提供有效的配件数据数组' },
|
||||
{ status: 400 }
|
||||
)
|
||||
}
|
||||
|
||||
const results: BatchResult[] = []
|
||||
const createdTypes: { [key: string]: string } = {} // 缓存新创建的类型
|
||||
|
||||
// 获取现有的配件类型,用于匹配
|
||||
const existingTypes = await prisma.componentType.findMany()
|
||||
const typeMap = new Map(existingTypes.map(type => [type.name.toLowerCase(), type.id]))
|
||||
|
||||
for (const componentData of components) {
|
||||
try {
|
||||
// 验证必需字段
|
||||
if (!componentData.name || !componentData.brand || !componentData.model) {
|
||||
results.push({
|
||||
success: false,
|
||||
item: componentData,
|
||||
error: '缺少必需字段: name, brand, model'
|
||||
})
|
||||
continue
|
||||
}
|
||||
|
||||
// 处理价格和库存
|
||||
const price = typeof componentData.price === 'string'
|
||||
? parseFloat(componentData.price)
|
||||
: componentData.price
|
||||
const stock = typeof componentData.stock === 'string'
|
||||
? parseInt(componentData.stock)
|
||||
: componentData.stock
|
||||
|
||||
if (isNaN(price) || price < 0) {
|
||||
results.push({
|
||||
success: false,
|
||||
item: componentData,
|
||||
error: '价格必须是有效的正数'
|
||||
})
|
||||
continue
|
||||
}
|
||||
|
||||
if (isNaN(stock) || stock < 0) {
|
||||
results.push({
|
||||
success: false,
|
||||
item: componentData,
|
||||
error: '库存必须是有效的非负整数'
|
||||
})
|
||||
continue
|
||||
}
|
||||
|
||||
// 处理配件类型
|
||||
let componentTypeId = componentData.componentTypeId
|
||||
|
||||
if (!componentTypeId) {
|
||||
const typeName = componentData.typeName || componentData.type
|
||||
if (!typeName) {
|
||||
results.push({
|
||||
success: false,
|
||||
item: componentData,
|
||||
error: '缺少配件类型信息 (componentTypeId, typeName 或 type)'
|
||||
})
|
||||
continue
|
||||
}
|
||||
|
||||
const lowerTypeName = typeName.toLowerCase()
|
||||
|
||||
// 检查是否已存在
|
||||
if (typeMap.has(lowerTypeName)) {
|
||||
componentTypeId = typeMap.get(lowerTypeName)
|
||||
} else if (createdTypes[lowerTypeName]) {
|
||||
// 检查本次批量导入中是否已创建
|
||||
componentTypeId = createdTypes[lowerTypeName]
|
||||
} else {
|
||||
// 创建新的配件类型
|
||||
try {
|
||||
const newType = await prisma.componentType.create({
|
||||
data: {
|
||||
name: typeName,
|
||||
description: `自动创建的${typeName}类型`
|
||||
}
|
||||
})
|
||||
componentTypeId = newType.id
|
||||
createdTypes[lowerTypeName] = newType.id
|
||||
typeMap.set(lowerTypeName, newType.id)
|
||||
} catch (typeError) {
|
||||
results.push({
|
||||
success: false,
|
||||
item: componentData,
|
||||
error: `创建配件类型失败: ${typeError instanceof Error ? typeError.message : '未知错误'}`
|
||||
})
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 验证配件类型ID
|
||||
if (!componentTypeId) {
|
||||
results.push({
|
||||
success: false,
|
||||
item: componentData,
|
||||
error: '无法确定配件类型'
|
||||
})
|
||||
continue
|
||||
}
|
||||
|
||||
// 检查是否已存在相同的配件
|
||||
const existingComponent = await prisma.component.findFirst({
|
||||
where: {
|
||||
name: componentData.name,
|
||||
brand: componentData.brand,
|
||||
model: componentData.model
|
||||
}
|
||||
})
|
||||
|
||||
if (existingComponent) {
|
||||
results.push({
|
||||
success: false,
|
||||
item: componentData,
|
||||
error: `配件已存在: ${componentData.name} - ${componentData.brand} ${componentData.model}`
|
||||
})
|
||||
continue
|
||||
}
|
||||
|
||||
// 创建配件
|
||||
const newComponent = await prisma.component.create({
|
||||
data: {
|
||||
name: componentData.name.trim(),
|
||||
brand: componentData.brand.trim(),
|
||||
model: componentData.model.trim(),
|
||||
price,
|
||||
description: componentData.description?.trim() || '',
|
||||
imageUrl: componentData.imageUrl?.trim() || '',
|
||||
stock,
|
||||
specifications: componentData.specifications?.trim() || '',
|
||||
componentTypeId
|
||||
},
|
||||
include: {
|
||||
componentType: true
|
||||
}
|
||||
})
|
||||
|
||||
results.push({
|
||||
success: true,
|
||||
item: componentData,
|
||||
componentId: newComponent.id
|
||||
})
|
||||
|
||||
} catch (error) {
|
||||
console.error('处理单个配件时出错:', error)
|
||||
results.push({
|
||||
success: false,
|
||||
item: componentData,
|
||||
error: error instanceof Error ? error.message : '创建配件失败'
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 统计结果
|
||||
const successCount = results.filter(r => r.success).length
|
||||
const failCount = results.filter(r => !r.success).length
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
results,
|
||||
summary: {
|
||||
total: components.length,
|
||||
successful: successCount,
|
||||
failed: failCount,
|
||||
newTypesCreated: Object.keys(createdTypes).length
|
||||
}
|
||||
})
|
||||
|
||||
} catch (error) {
|
||||
console.error('批量导入配件失败:', error)
|
||||
return NextResponse.json(
|
||||
{
|
||||
success: false,
|
||||
message: '批量导入失败',
|
||||
error: error instanceof Error ? error.message : '未知错误'
|
||||
},
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
import { NextRequest, NextResponse } from 'next/server'
|
||||
import { prisma } from '@/lib/prisma'
|
||||
|
||||
export async function GET(request: NextRequest) {
|
||||
try {
|
||||
const { searchParams } = new URL(request.url)
|
||||
const typeId = searchParams.get('type')
|
||||
const brand = searchParams.get('brand')
|
||||
const minPrice = searchParams.get('minPrice')
|
||||
const maxPrice = searchParams.get('maxPrice')
|
||||
const search = searchParams.get('search')
|
||||
const page = parseInt(searchParams.get('page') || '1')
|
||||
const limit = parseInt(searchParams.get('limit') || '12')
|
||||
|
||||
const where: any = {}
|
||||
|
||||
if (typeId) {
|
||||
where.componentTypeId = typeId
|
||||
}
|
||||
|
||||
if (brand) {
|
||||
where.brand = {
|
||||
contains: brand,
|
||||
mode: 'insensitive'
|
||||
}
|
||||
}
|
||||
|
||||
if (minPrice || maxPrice) {
|
||||
where.price = {}
|
||||
if (minPrice) where.price.gte = parseFloat(minPrice)
|
||||
if (maxPrice) where.price.lte = parseFloat(maxPrice)
|
||||
}
|
||||
|
||||
if (search) {
|
||||
where.OR = [
|
||||
{ name: { contains: search, mode: 'insensitive' } },
|
||||
{ brand: { contains: search, mode: 'insensitive' } },
|
||||
{ model: { contains: search, mode: 'insensitive' } },
|
||||
{ description: { contains: search, mode: 'insensitive' } }
|
||||
]
|
||||
}
|
||||
|
||||
const [components, total] = await Promise.all([
|
||||
prisma.component.findMany({
|
||||
where,
|
||||
include: {
|
||||
componentType: true
|
||||
},
|
||||
orderBy: {
|
||||
createdAt: 'desc'
|
||||
},
|
||||
skip: (page - 1) * limit,
|
||||
take: limit
|
||||
}),
|
||||
prisma.component.count({ where })
|
||||
])
|
||||
|
||||
const totalPages = Math.ceil(total / limit)
|
||||
|
||||
return NextResponse.json({
|
||||
components,
|
||||
pagination: {
|
||||
page,
|
||||
limit,
|
||||
total,
|
||||
totalPages,
|
||||
hasNext: page < totalPages,
|
||||
hasPrev: page > 1
|
||||
}
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('Components fetch error:', error)
|
||||
return NextResponse.json(
|
||||
{ message: '获取配件列表失败' },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
try {
|
||||
// 这个API需要管理员权限,暂时简化处理
|
||||
const data = await request.json()
|
||||
|
||||
const component = await prisma.component.create({
|
||||
data: {
|
||||
name: data.name,
|
||||
brand: data.brand,
|
||||
model: data.model,
|
||||
price: parseFloat(data.price),
|
||||
description: data.description,
|
||||
imageUrl: data.imageUrl,
|
||||
stock: parseInt(data.stock),
|
||||
specifications: data.specifications,
|
||||
componentTypeId: data.componentTypeId
|
||||
},
|
||||
include: {
|
||||
componentType: true
|
||||
}
|
||||
})
|
||||
|
||||
return NextResponse.json(component, { status: 201 })
|
||||
} catch (error) {
|
||||
console.error('Component create error:', error)
|
||||
return NextResponse.json(
|
||||
{ message: '创建配件失败' },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,165 @@
|
||||
import { NextRequest, NextResponse } from 'next/server'
|
||||
import { prisma } from '@/lib/prisma'
|
||||
import { verifyToken } from '@/lib/auth'
|
||||
|
||||
export async function GET(
|
||||
request: NextRequest,
|
||||
{ params }: { params: Promise<{ id: string }> }
|
||||
) {
|
||||
try {
|
||||
const { id } = await params
|
||||
|
||||
const authHeader = request.headers.get('authorization')
|
||||
if (!authHeader || !authHeader.startsWith('Bearer ')) {
|
||||
return NextResponse.json(
|
||||
{ message: '请先登录' },
|
||||
{ status: 401 }
|
||||
)
|
||||
}
|
||||
|
||||
const token = authHeader.substring(7)
|
||||
const decoded = verifyToken(token)
|
||||
if (!decoded) {
|
||||
return NextResponse.json(
|
||||
{ message: '登录已过期' },
|
||||
{ status: 401 }
|
||||
)
|
||||
}
|
||||
|
||||
const order = await prisma.order.findFirst({
|
||||
where: {
|
||||
id: id,
|
||||
userId: decoded.userId
|
||||
},
|
||||
include: {
|
||||
orderItems: {
|
||||
include: {
|
||||
component: {
|
||||
include: {
|
||||
componentType: true
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
user: {
|
||||
select: {
|
||||
username: true,
|
||||
email: true
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
if (!order) {
|
||||
return NextResponse.json(
|
||||
{ message: '订单不存在' },
|
||||
{ status: 404 }
|
||||
)
|
||||
}
|
||||
|
||||
return NextResponse.json(order)
|
||||
} catch (error) {
|
||||
console.error('获取订单详情失败:', error)
|
||||
return NextResponse.json(
|
||||
{ message: '获取订单详情失败' },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export async function PUT(
|
||||
request: NextRequest,
|
||||
{ params }: { params: Promise<{ id: string }> }
|
||||
) {
|
||||
try {
|
||||
const { id } = await params
|
||||
|
||||
const authHeader = request.headers.get('authorization')
|
||||
if (!authHeader || !authHeader.startsWith('Bearer ')) {
|
||||
return NextResponse.json(
|
||||
{ message: '请先登录' },
|
||||
{ status: 401 }
|
||||
)
|
||||
}
|
||||
|
||||
const token = authHeader.substring(7)
|
||||
const decoded = verifyToken(token)
|
||||
if (!decoded) {
|
||||
return NextResponse.json(
|
||||
{ message: '登录已过期' },
|
||||
{ status: 401 }
|
||||
)
|
||||
}
|
||||
|
||||
const { action } = await request.json()
|
||||
|
||||
// 查找订单
|
||||
const order = await prisma.order.findFirst({
|
||||
where: {
|
||||
id: id,
|
||||
userId: decoded.userId
|
||||
},
|
||||
include: {
|
||||
orderItems: true
|
||||
}
|
||||
})
|
||||
|
||||
if (!order) {
|
||||
return NextResponse.json(
|
||||
{ message: '订单不存在' },
|
||||
{ status: 404 }
|
||||
)
|
||||
}
|
||||
|
||||
// 处理不同的操作
|
||||
if (action === 'cancel') {
|
||||
// 只有待确认状态的订单可以取消
|
||||
if (order.status !== 'PENDING') {
|
||||
return NextResponse.json(
|
||||
{ message: '当前订单状态不允许取消' },
|
||||
{ status: 400 }
|
||||
)
|
||||
} // 更新订单状态为取消
|
||||
const updatedOrder = await prisma.order.update({
|
||||
where: { id: id },
|
||||
data: { status: 'CANCELLED' },
|
||||
include: {
|
||||
orderItems: {
|
||||
include: {
|
||||
component: {
|
||||
include: {
|
||||
componentType: true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// 恢复库存
|
||||
for (const item of order.orderItems) {
|
||||
await prisma.component.update({
|
||||
where: { id: item.componentId },
|
||||
data: {
|
||||
stock: {
|
||||
increment: item.quantity
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return NextResponse.json(updatedOrder)
|
||||
}
|
||||
|
||||
return NextResponse.json(
|
||||
{ message: '不支持的操作' },
|
||||
{ status: 400 }
|
||||
)
|
||||
} catch (error) {
|
||||
console.error('更新订单失败:', error)
|
||||
return NextResponse.json(
|
||||
{ message: '更新订单失败' },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
import { NextRequest, NextResponse } from 'next/server'
|
||||
import { prisma } from '@/lib/prisma'
|
||||
import { verifyToken } from '@/lib/auth'
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
try {
|
||||
const authHeader = request.headers.get('authorization')
|
||||
if (!authHeader || !authHeader.startsWith('Bearer ')) {
|
||||
return NextResponse.json(
|
||||
{ message: '请先登录' },
|
||||
{ status: 401 }
|
||||
)
|
||||
}
|
||||
|
||||
const token = authHeader.substring(7)
|
||||
const decoded = verifyToken(token)
|
||||
if (!decoded) {
|
||||
return NextResponse.json(
|
||||
{ message: '登录已过期' },
|
||||
{ status: 401 }
|
||||
)
|
||||
}
|
||||
|
||||
const { items } = await request.json()
|
||||
if (!items || items.length === 0) {
|
||||
return NextResponse.json(
|
||||
{ message: '订单商品不能为空' },
|
||||
{ status: 400 }
|
||||
)
|
||||
}
|
||||
|
||||
// 生成订单号
|
||||
const orderNumber = `ORD${Date.now()}`
|
||||
|
||||
// 计算总金额
|
||||
let totalAmount = 0
|
||||
const orderItems = []
|
||||
|
||||
for (const item of items) {
|
||||
const component = await prisma.component.findUnique({
|
||||
where: { id: item.componentId }
|
||||
})
|
||||
|
||||
if (!component) {
|
||||
return NextResponse.json(
|
||||
{ message: `配件不存在: ${item.componentId}` },
|
||||
{ status: 400 }
|
||||
)
|
||||
}
|
||||
|
||||
if (component.stock < item.quantity) {
|
||||
return NextResponse.json(
|
||||
{ message: `配件库存不足: ${component.name}` },
|
||||
{ status: 400 }
|
||||
)
|
||||
}
|
||||
|
||||
const itemTotal = component.price * item.quantity
|
||||
totalAmount += itemTotal
|
||||
|
||||
orderItems.push({
|
||||
componentId: component.id,
|
||||
quantity: item.quantity,
|
||||
price: component.price
|
||||
})
|
||||
}
|
||||
|
||||
// 创建订单
|
||||
const order = await prisma.order.create({
|
||||
data: {
|
||||
orderNumber,
|
||||
totalAmount,
|
||||
userId: decoded.userId,
|
||||
orderItems: {
|
||||
create: orderItems
|
||||
}
|
||||
}, include: {
|
||||
orderItems: {
|
||||
include: {
|
||||
component: {
|
||||
include: {
|
||||
componentType: true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// 更新库存
|
||||
for (const item of items) {
|
||||
await prisma.component.update({
|
||||
where: { id: item.componentId },
|
||||
data: {
|
||||
stock: {
|
||||
decrement: item.quantity
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return NextResponse.json(order, { status: 201 })
|
||||
} catch (error) {
|
||||
console.error('创建订单失败:', error)
|
||||
return NextResponse.json(
|
||||
{ message: '创建订单失败' },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export async function GET(request: NextRequest) {
|
||||
try {
|
||||
const authHeader = request.headers.get('authorization')
|
||||
if (!authHeader || !authHeader.startsWith('Bearer ')) {
|
||||
return NextResponse.json(
|
||||
{ message: '请先登录' },
|
||||
{ status: 401 }
|
||||
)
|
||||
}
|
||||
|
||||
const token = authHeader.substring(7)
|
||||
const decoded = verifyToken(token)
|
||||
if (!decoded) {
|
||||
return NextResponse.json(
|
||||
{ message: '登录已过期' },
|
||||
{ status: 401 }
|
||||
)
|
||||
} const orders = await prisma.order.findMany({
|
||||
where: { userId: decoded.userId },
|
||||
include: {
|
||||
orderItems: {
|
||||
include: {
|
||||
component: {
|
||||
include: {
|
||||
componentType: true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
orderBy: {
|
||||
createdAt: 'desc'
|
||||
}
|
||||
})
|
||||
|
||||
return NextResponse.json(orders)
|
||||
} catch (error) {
|
||||
console.error('获取订单列表失败:', error)
|
||||
return NextResponse.json(
|
||||
{ message: '获取订单列表失败' },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
import { NextRequest, NextResponse } from 'next/server'
|
||||
import { prisma } from '@/lib/prisma'
|
||||
import { verifyToken, hashPassword, verifyPassword } from '@/lib/auth'
|
||||
|
||||
export async function PUT(request: NextRequest) {
|
||||
try {
|
||||
const authHeader = request.headers.get('Authorization')
|
||||
const token = authHeader?.replace('Bearer ', '')
|
||||
|
||||
if (!token) {
|
||||
return NextResponse.json({ message: '未提供认证令牌' }, { status: 401 })
|
||||
}
|
||||
|
||||
const decoded = verifyToken(token)
|
||||
if (!decoded) {
|
||||
return NextResponse.json({ message: '无效的认证令牌' }, { status: 401 })
|
||||
}
|
||||
|
||||
const { currentPassword, newPassword } = await request.json()
|
||||
|
||||
if (!currentPassword || !newPassword) {
|
||||
return NextResponse.json({ message: '请提供当前密码和新密码' }, { status: 400 })
|
||||
}
|
||||
|
||||
if (newPassword.length < 6) {
|
||||
return NextResponse.json({ message: '新密码长度至少6位' }, { status: 400 })
|
||||
}
|
||||
|
||||
// 获取用户当前信息
|
||||
const user = await prisma.user.findUnique({
|
||||
where: { id: decoded.userId }
|
||||
})
|
||||
|
||||
if (!user) {
|
||||
return NextResponse.json({ message: '用户不存在' }, { status: 404 })
|
||||
}
|
||||
|
||||
// 验证当前密码
|
||||
const isCurrentPasswordValid = await verifyPassword(currentPassword, user.password)
|
||||
if (!isCurrentPasswordValid) {
|
||||
return NextResponse.json({ message: '当前密码错误' }, { status: 400 })
|
||||
}
|
||||
|
||||
// 哈希新密码
|
||||
const hashedNewPassword = await hashPassword(newPassword)
|
||||
|
||||
// 更新密码
|
||||
await prisma.user.update({
|
||||
where: { id: decoded.userId },
|
||||
data: {
|
||||
password: hashedNewPassword
|
||||
}
|
||||
})
|
||||
|
||||
return NextResponse.json({ message: '密码修改成功' })
|
||||
|
||||
} catch (error) {
|
||||
console.error('修改密码失败:', error)
|
||||
return NextResponse.json({ message: '修改密码失败' }, { status: 500 })
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
import { NextRequest, NextResponse } from 'next/server'
|
||||
import { prisma } from '@/lib/prisma'
|
||||
import { verifyToken } from '@/lib/auth'
|
||||
|
||||
export async function GET(request: NextRequest) {
|
||||
try {
|
||||
const authHeader = request.headers.get('Authorization')
|
||||
const token = authHeader?.replace('Bearer ', '')
|
||||
|
||||
if (!token) {
|
||||
return NextResponse.json({ message: '未提供认证令牌' }, { status: 401 })
|
||||
}
|
||||
|
||||
const decoded = verifyToken(token)
|
||||
if (!decoded) {
|
||||
return NextResponse.json({ message: '无效的认证令牌' }, { status: 401 })
|
||||
}
|
||||
|
||||
const user = await prisma.user.findUnique({
|
||||
where: { id: decoded.userId },
|
||||
select: {
|
||||
id: true,
|
||||
email: true,
|
||||
username: true,
|
||||
name: true,
|
||||
phone: true,
|
||||
address: true,
|
||||
isAdmin: true,
|
||||
createdAt: true
|
||||
}
|
||||
})
|
||||
|
||||
if (!user) {
|
||||
return NextResponse.json({ message: '用户不存在' }, { status: 404 })
|
||||
}
|
||||
|
||||
return NextResponse.json(user)
|
||||
|
||||
} catch (error) {
|
||||
console.error('获取用户资料失败:', error)
|
||||
return NextResponse.json({ message: '获取用户资料失败' }, { status: 500 })
|
||||
}
|
||||
}
|
||||
|
||||
export async function PUT(request: NextRequest) {
|
||||
try {
|
||||
const authHeader = request.headers.get('Authorization')
|
||||
const token = authHeader?.replace('Bearer ', '')
|
||||
|
||||
if (!token) {
|
||||
return NextResponse.json({ message: '未提供认证令牌' }, { status: 401 })
|
||||
}
|
||||
|
||||
const decoded = verifyToken(token)
|
||||
if (!decoded) {
|
||||
return NextResponse.json({ message: '无效的认证令牌' }, { status: 401 })
|
||||
}
|
||||
|
||||
const { name, phone, address } = await request.json()
|
||||
|
||||
const updatedUser = await prisma.user.update({
|
||||
where: { id: decoded.userId },
|
||||
data: {
|
||||
name,
|
||||
phone,
|
||||
address
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
email: true,
|
||||
username: true,
|
||||
name: true,
|
||||
phone: true,
|
||||
address: true,
|
||||
isAdmin: true,
|
||||
createdAt: true
|
||||
}
|
||||
})
|
||||
|
||||
return NextResponse.json(updatedUser)
|
||||
|
||||
} catch (error) {
|
||||
console.error('更新用户资料失败:', error)
|
||||
return NextResponse.json({ message: '更新用户资料失败' }, { status: 500 })
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,170 @@
|
||||
import { NextRequest, NextResponse } from 'next/server'
|
||||
import { prisma } from '@/lib/prisma'
|
||||
import { verifyToken } from '@/lib/auth'
|
||||
|
||||
export async function GET(request: NextRequest) {
|
||||
try {
|
||||
const authHeader = request.headers.get('authorization')
|
||||
if (!authHeader || !authHeader.startsWith('Bearer ')) {
|
||||
return NextResponse.json(
|
||||
{ message: '请先登录' },
|
||||
{ status: 401 }
|
||||
)
|
||||
}
|
||||
|
||||
const token = authHeader.substring(7)
|
||||
const decoded = verifyToken(token)
|
||||
if (!decoded) {
|
||||
return NextResponse.json(
|
||||
{ message: '登录已过期' },
|
||||
{ status: 401 }
|
||||
)
|
||||
}
|
||||
|
||||
// 获取用户基本信息
|
||||
const user = await prisma.user.findUnique({
|
||||
where: { id: decoded.userId },
|
||||
select: {
|
||||
id: true,
|
||||
createdAt: true,
|
||||
username: true,
|
||||
email: true
|
||||
}
|
||||
})
|
||||
|
||||
if (!user) {
|
||||
return NextResponse.json(
|
||||
{ message: '用户不存在' },
|
||||
{ status: 404 }
|
||||
)
|
||||
}
|
||||
|
||||
// 获取订单统计
|
||||
const orderStats = await prisma.order.groupBy({
|
||||
by: ['status'],
|
||||
where: {
|
||||
userId: decoded.userId
|
||||
},
|
||||
_count: {
|
||||
id: true
|
||||
},
|
||||
_sum: {
|
||||
totalAmount: true
|
||||
}
|
||||
})
|
||||
|
||||
// 获取总订单数和总消费
|
||||
const totalOrders = await prisma.order.count({
|
||||
where: {
|
||||
userId: decoded.userId
|
||||
}
|
||||
})
|
||||
|
||||
const totalSpent = await prisma.order.aggregate({
|
||||
where: {
|
||||
userId: decoded.userId,
|
||||
status: {
|
||||
in: ['CONFIRMED', 'SHIPPED', 'DELIVERED']
|
||||
}
|
||||
},
|
||||
_sum: {
|
||||
totalAmount: true
|
||||
}
|
||||
}) // 获取购物车商品数量
|
||||
const cartItemsCount = await prisma.cartItem.count({
|
||||
where: {
|
||||
userId: decoded.userId
|
||||
}
|
||||
})
|
||||
|
||||
// 获取最近订单
|
||||
const recentOrders = await prisma.order.findMany({
|
||||
where: {
|
||||
userId: decoded.userId
|
||||
},
|
||||
orderBy: {
|
||||
createdAt: 'desc'
|
||||
},
|
||||
take: 5,
|
||||
select: {
|
||||
id: true,
|
||||
status: true,
|
||||
totalAmount: true,
|
||||
createdAt: true
|
||||
}
|
||||
})
|
||||
|
||||
// 按状态分组订单统计
|
||||
const ordersByStatus = {
|
||||
PENDING: 0,
|
||||
CONFIRMED: 0,
|
||||
SHIPPED: 0,
|
||||
DELIVERED: 0,
|
||||
CANCELLED: 0
|
||||
}
|
||||
|
||||
orderStats.forEach(stat => {
|
||||
ordersByStatus[stat.status as keyof typeof ordersByStatus] = stat._count.id
|
||||
})
|
||||
|
||||
// 获取用户最喜欢的配件类型(购买最多的类型)
|
||||
const favoriteComponentType = await prisma.orderItem.groupBy({
|
||||
by: ['componentId'],
|
||||
where: {
|
||||
order: {
|
||||
userId: decoded.userId,
|
||||
status: {
|
||||
in: ['CONFIRMED', 'SHIPPED', 'DELIVERED']
|
||||
}
|
||||
}
|
||||
},
|
||||
_sum: {
|
||||
quantity: true
|
||||
},
|
||||
orderBy: {
|
||||
_sum: {
|
||||
quantity: 'desc'
|
||||
}
|
||||
},
|
||||
take: 1
|
||||
})
|
||||
|
||||
let favoriteType = null
|
||||
if (favoriteComponentType.length > 0) {
|
||||
const component = await prisma.component.findUnique({
|
||||
where: { id: favoriteComponentType[0].componentId },
|
||||
include: {
|
||||
componentType: true
|
||||
}
|
||||
})
|
||||
favoriteType = component?.componentType?.name || null
|
||||
} return NextResponse.json({
|
||||
user: {
|
||||
id: user.id,
|
||||
username: user.username,
|
||||
email: user.email,
|
||||
memberSince: user.createdAt
|
||||
},
|
||||
orderStats: {
|
||||
total: totalOrders,
|
||||
byStatus: ordersByStatus,
|
||||
totalSpent: totalSpent._sum.totalAmount || 0
|
||||
},
|
||||
recentOrders,
|
||||
favoriteComponentType: favoriteType, summary: {
|
||||
totalOrders,
|
||||
totalSpent: totalSpent._sum.totalAmount || 0,
|
||||
pendingOrders: ordersByStatus.PENDING,
|
||||
completedOrders: ordersByStatus.DELIVERED,
|
||||
cartItems: cartItemsCount
|
||||
}
|
||||
})
|
||||
|
||||
} catch (error) {
|
||||
console.error('获取用户统计失败:', error)
|
||||
return NextResponse.json(
|
||||
{ message: '获取统计数据失败' },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user