180 lines
6.3 KiB
TypeScript
180 lines
6.3 KiB
TypeScript
import { NextRequest, NextResponse } from "next/server"
|
||
import { auth } from "@/auth"
|
||
import { db } from "@/lib/db"
|
||
import { z } from "zod"
|
||
|
||
const createProductSchema = z.object({
|
||
principal: z.number().positive("本金必须大于0"),
|
||
depositDate: z.string().nullable().optional(),
|
||
endDate: z.string().nullable().optional(),
|
||
currentNetValue: z.number().nullable().optional(),
|
||
annualRate: z.number().nullable().optional(),
|
||
currency: z.string().default("RMB"),
|
||
notes: z.string().nullable().optional(),
|
||
}).refine((data) => {
|
||
// 如果有开始时间、结束时间、本金、当前净值,则不需要年化利率(会自动计算)
|
||
// 否则至少需要当前净值或年化利率中的一个
|
||
const hasCompleteInfo = data.depositDate && data.endDate && data.principal && data.currentNetValue
|
||
const hasEitherValue = data.currentNetValue !== null || data.annualRate !== null
|
||
|
||
return hasCompleteInfo || hasEitherValue
|
||
}, {
|
||
message: "请提供足够的信息:要么提供当前净值或年化利率,要么提供完整的时间和净值信息",
|
||
path: ["currentNetValue", "annualRate"]
|
||
})
|
||
|
||
export async function GET() {
|
||
try {
|
||
const session = await auth()
|
||
|
||
if (!session?.user?.id) {
|
||
return NextResponse.json({ error: "Unauthorized" }, { status: 401 })
|
||
}
|
||
|
||
const products = await db.financeProduct.findMany({
|
||
where: {
|
||
userId: session.user.id,
|
||
},
|
||
orderBy: {
|
||
createdAt: "desc",
|
||
},
|
||
})
|
||
|
||
return NextResponse.json(products)
|
||
} catch (error) {
|
||
console.error("Error fetching products:", error)
|
||
return NextResponse.json(
|
||
{ error: "Internal server error" },
|
||
{ status: 500 }
|
||
)
|
||
}
|
||
}
|
||
|
||
export async function POST(request: NextRequest) {
|
||
try {
|
||
const session = await auth()
|
||
|
||
if (!session?.user?.id) {
|
||
return NextResponse.json({ error: "Unauthorized" }, { status: 401 })
|
||
}
|
||
|
||
const body = await request.json()
|
||
console.log("Received request body:", body)
|
||
|
||
// 预处理数据:将空字符串转换为null
|
||
const processedBody = {
|
||
...body,
|
||
currentNetValue: body.currentNetValue === "" || body.currentNetValue === null ? null : Number(body.currentNetValue),
|
||
annualRate: body.annualRate === "" || body.annualRate === null ? null : Number(body.annualRate),
|
||
principal: Number(body.principal),
|
||
depositDate: body.depositDate || null,
|
||
endDate: body.endDate || null,
|
||
currency: body.currency || "RMB",
|
||
notes: body.notes || null,
|
||
}
|
||
|
||
console.log("Processed request body:", processedBody)
|
||
|
||
const validatedData = createProductSchema.parse(processedBody)
|
||
console.log("Validated data:", validatedData)
|
||
|
||
// 计算相关字段
|
||
let calculatedValues = {
|
||
profit: null as number | null,
|
||
dailyProfit: null as number | null,
|
||
monthlyProfit: null as number | null,
|
||
calculatedAnnualRate: null as number | null,
|
||
averageAnnualProfit: null as number | null,
|
||
}
|
||
|
||
// 验证日期
|
||
let depositDate: Date | null = null
|
||
let endDate: Date | null = null
|
||
let days = 0
|
||
|
||
if (validatedData.depositDate && validatedData.endDate) {
|
||
depositDate = new Date(validatedData.depositDate)
|
||
endDate = new Date(validatedData.endDate)
|
||
|
||
if (isNaN(depositDate.getTime()) || isNaN(endDate.getTime())) {
|
||
return NextResponse.json(
|
||
{ error: "无效的日期格式" },
|
||
{ status: 400 }
|
||
)
|
||
}
|
||
|
||
days = Math.max(1, Math.floor((endDate.getTime() - depositDate.getTime()) / (1000 * 60 * 60 * 24)))
|
||
}
|
||
|
||
// 如果有当前净值,计算收益
|
||
if (validatedData.currentNetValue !== null && validatedData.currentNetValue !== undefined) {
|
||
calculatedValues.profit = validatedData.currentNetValue - validatedData.principal
|
||
}
|
||
|
||
// 自动计算年化利率(如果没有提供年化利率但有完整信息)
|
||
if (!validatedData.annualRate && validatedData.currentNetValue && depositDate && endDate && days > 0) {
|
||
const totalReturn = validatedData.currentNetValue - validatedData.principal
|
||
const dailyReturn = totalReturn / days
|
||
const annualReturn = (dailyReturn * 365) / validatedData.principal
|
||
validatedData.annualRate = annualReturn * 100 // 转换为百分比
|
||
console.log("Auto-calculated annual rate:", validatedData.annualRate)
|
||
}
|
||
|
||
// 如果有年化利率和日期,计算当前净值(当没有提供当前净值时)
|
||
if (validatedData.annualRate && depositDate && endDate && days > 0 && !validatedData.currentNetValue) {
|
||
const calculatedNetValue = validatedData.principal * (1 + ((validatedData.annualRate / 100) * days) / 365)
|
||
validatedData.currentNetValue = calculatedNetValue
|
||
calculatedValues.profit = calculatedNetValue - validatedData.principal
|
||
console.log("Auto-calculated net value:", validatedData.currentNetValue)
|
||
}
|
||
|
||
// 计算日收益和月收益
|
||
if (calculatedValues.profit !== null && days > 0) {
|
||
calculatedValues.dailyProfit = calculatedValues.profit / days
|
||
calculatedValues.monthlyProfit = calculatedValues.dailyProfit * 30
|
||
calculatedValues.calculatedAnnualRate = ((calculatedValues.dailyProfit * 365) / validatedData.principal) * 100
|
||
calculatedValues.averageAnnualProfit = calculatedValues.dailyProfit * 365
|
||
}
|
||
|
||
console.log("Calculated values:", calculatedValues)
|
||
|
||
const product = await db.financeProduct.create({
|
||
data: {
|
||
userId: session.user.id,
|
||
principal: validatedData.principal,
|
||
depositDate: depositDate,
|
||
endDate: endDate,
|
||
currentNetValue: validatedData.currentNetValue || null,
|
||
annualRate: validatedData.annualRate || null,
|
||
currency: validatedData.currency,
|
||
notes: validatedData.notes,
|
||
...calculatedValues,
|
||
},
|
||
})
|
||
|
||
console.log("Created product:", product)
|
||
|
||
return NextResponse.json(product)
|
||
} catch (error) {
|
||
console.error("Error in POST /api/products:", error)
|
||
|
||
if (error instanceof z.ZodError) {
|
||
console.error("Zod validation errors:", error.errors)
|
||
return NextResponse.json(
|
||
{
|
||
error: "数据验证失败",
|
||
details: error.errors.map(err => ({
|
||
field: err.path.join('.'),
|
||
message: err.message
|
||
}))
|
||
},
|
||
{ status: 400 }
|
||
)
|
||
}
|
||
|
||
return NextResponse.json(
|
||
{ error: "Internal server error" },
|
||
{ status: 500 }
|
||
)
|
||
}
|
||
}
|