Files
niumalll/backend/routes/finance.js
2025-09-02 21:59:27 +08:00

490 lines
13 KiB
JavaScript

const express = require('express');
const router = express.Router();
const Joi = require('joi');
// 模拟财务数据
let settlements = [
{
id: 1,
orderId: 1,
settlementCode: 'SET001',
supplierName: '山东优质牲畜合作社',
buyerName: '北京肉类加工有限公司',
cattleCount: 50,
unitPrice: 25000,
totalAmount: 1250000,
paymentMethod: 'bank_transfer',
paymentStatus: 'paid',
settlementDate: '2024-01-20',
paymentDate: '2024-01-22',
invoiceNumber: 'INV001',
invoiceStatus: 'issued',
taxAmount: 125000,
actualPayment: 1125000,
bankAccount: '1234567890123456789',
bankName: '中国农业银行',
createdAt: new Date('2024-01-20'),
updatedAt: new Date('2024-01-22')
},
{
id: 2,
orderId: 2,
settlementCode: 'SET002',
supplierName: '内蒙古草原牲畜有限公司',
buyerName: '天津屠宰加工厂',
cattleCount: 80,
unitPrice: 24000,
totalAmount: 1920000,
paymentMethod: 'cash',
paymentStatus: 'pending',
settlementDate: '2024-01-25',
paymentDate: null,
invoiceNumber: 'INV002',
invoiceStatus: 'pending',
taxAmount: 192000,
actualPayment: 1728000,
bankAccount: '9876543210987654321',
bankName: '中国建设银行',
createdAt: new Date('2024-01-25'),
updatedAt: new Date('2024-01-25')
}
];
let payments = [
{
id: 1,
settlementId: 1,
paymentCode: 'PAY001',
amount: 1125000,
paymentMethod: 'bank_transfer',
status: 'success',
transactionId: 'TXN20240122001',
paidAt: '2024-01-22T10:30:00Z',
createdAt: new Date('2024-01-22T10:30:00Z')
}
];
// 验证schemas
const settlementCreateSchema = Joi.object({
orderId: Joi.number().integer().required(),
cattleCount: Joi.number().integer().min(1).required(),
unitPrice: Joi.number().min(0).required(),
paymentMethod: Joi.string().valid('bank_transfer', 'cash', 'check', 'online').required(),
settlementDate: Joi.date().iso().required(),
invoiceNumber: Joi.string().min(3).max(50)
});
const paymentCreateSchema = Joi.object({
settlementId: Joi.number().integer().required(),
amount: Joi.number().min(0).required(),
paymentMethod: Joi.string().valid('bank_transfer', 'cash', 'check', 'online').required(),
transactionId: Joi.string().max(100)
});
// 获取结算列表
router.get('/settlements', (req, res) => {
try {
const {
page = 1,
pageSize = 20,
keyword,
paymentStatus,
startDate,
endDate
} = req.query;
let filteredSettlements = [...settlements];
// 关键词搜索
if (keyword) {
filteredSettlements = filteredSettlements.filter(settlement =>
settlement.settlementCode.includes(keyword) ||
settlement.supplierName.includes(keyword) ||
settlement.buyerName.includes(keyword)
);
}
// 支付状态筛选
if (paymentStatus) {
filteredSettlements = filteredSettlements.filter(settlement => settlement.paymentStatus === paymentStatus);
}
// 时间范围筛选
if (startDate) {
filteredSettlements = filteredSettlements.filter(settlement =>
new Date(settlement.settlementDate) >= new Date(startDate)
);
}
if (endDate) {
filteredSettlements = filteredSettlements.filter(settlement =>
new Date(settlement.settlementDate) <= new Date(endDate)
);
}
// 分页处理
const startIndex = (page - 1) * pageSize;
const endIndex = startIndex + parseInt(pageSize);
const paginatedSettlements = filteredSettlements.slice(startIndex, endIndex);
res.json({
success: true,
data: {
list: paginatedSettlements,
pagination: {
page: parseInt(page),
pageSize: parseInt(pageSize),
total: filteredSettlements.length,
totalPages: Math.ceil(filteredSettlements.length / pageSize)
}
}
});
} catch (error) {
res.status(500).json({
success: false,
message: '获取结算列表失败',
error: error.message
});
}
});
// 获取结算详情
router.get('/settlements/:id', (req, res) => {
try {
const { id } = req.params;
const settlement = settlements.find(s => s.id === parseInt(id));
if (!settlement) {
return res.status(404).json({
success: false,
message: '结算记录不存在'
});
}
// 获取相关支付记录
const relatedPayments = payments.filter(p => p.settlementId === settlement.id);
res.json({
success: true,
data: {
...settlement,
payments: relatedPayments
}
});
} catch (error) {
res.status(500).json({
success: false,
message: '获取结算详情失败',
error: error.message
});
}
});
// 创建结算记录
router.post('/settlements', (req, res) => {
try {
const { error, value } = settlementCreateSchema.validate(req.body);
if (error) {
return res.status(400).json({
success: false,
message: '参数验证失败',
errors: error.details.map(detail => detail.message)
});
}
const settlementCode = `SET${String(Date.now()).slice(-6)}`;
const totalAmount = value.cattleCount * value.unitPrice;
const taxAmount = totalAmount * 0.1; // 假设税率10%
const actualPayment = totalAmount - taxAmount;
const newSettlement = {
id: Math.max(...settlements.map(s => s.id)) + 1,
...value,
settlementCode,
totalAmount,
taxAmount,
actualPayment,
paymentStatus: 'pending',
paymentDate: null,
invoiceStatus: 'pending',
supplierName: '供应商名称', // 实际应从订单获取
buyerName: '采购商名称', // 实际应从订单获取
bankAccount: '',
bankName: '',
createdAt: new Date(),
updatedAt: new Date()
};
settlements.push(newSettlement);
res.status(201).json({
success: true,
message: '结算记录创建成功',
data: newSettlement
});
} catch (error) {
res.status(500).json({
success: false,
message: '创建结算记录失败',
error: error.message
});
}
});
// 更新结算状态
router.put('/settlements/:id/status', (req, res) => {
try {
const { id } = req.params;
const { paymentStatus, invoiceStatus } = req.body;
const settlementIndex = settlements.findIndex(s => s.id === parseInt(id));
if (settlementIndex === -1) {
return res.status(404).json({
success: false,
message: '结算记录不存在'
});
}
if (paymentStatus) {
settlements[settlementIndex].paymentStatus = paymentStatus;
if (paymentStatus === 'paid') {
settlements[settlementIndex].paymentDate = new Date().toISOString().split('T')[0];
}
}
if (invoiceStatus) {
settlements[settlementIndex].invoiceStatus = invoiceStatus;
}
settlements[settlementIndex].updatedAt = new Date();
res.json({
success: true,
message: '结算状态更新成功',
data: settlements[settlementIndex]
});
} catch (error) {
res.status(500).json({
success: false,
message: '更新结算状态失败',
error: error.message
});
}
});
// 获取支付记录列表
router.get('/payments', (req, res) => {
try {
const {
page = 1,
pageSize = 20,
settlementId,
status
} = req.query;
let filteredPayments = [...payments];
// 按结算单筛选
if (settlementId) {
filteredPayments = filteredPayments.filter(payment => payment.settlementId === parseInt(settlementId));
}
// 按状态筛选
if (status) {
filteredPayments = filteredPayments.filter(payment => payment.status === status);
}
// 分页处理
const startIndex = (page - 1) * pageSize;
const endIndex = startIndex + parseInt(pageSize);
const paginatedPayments = filteredPayments.slice(startIndex, endIndex);
res.json({
success: true,
data: {
list: paginatedPayments,
pagination: {
page: parseInt(page),
pageSize: parseInt(pageSize),
total: filteredPayments.length,
totalPages: Math.ceil(filteredPayments.length / pageSize)
}
}
});
} catch (error) {
res.status(500).json({
success: false,
message: '获取支付记录失败',
error: error.message
});
}
});
// 创建支付记录
router.post('/payments', (req, res) => {
try {
const { error, value } = paymentCreateSchema.validate(req.body);
if (error) {
return res.status(400).json({
success: false,
message: '参数验证失败',
errors: error.details.map(detail => detail.message)
});
}
const paymentCode = `PAY${String(Date.now()).slice(-6)}`;
const newPayment = {
id: Math.max(...payments.map(p => p.id)) + 1,
...value,
paymentCode,
status: 'processing',
paidAt: null,
createdAt: new Date()
};
payments.push(newPayment);
// 模拟支付处理
setTimeout(() => {
const paymentIndex = payments.findIndex(p => p.id === newPayment.id);
if (paymentIndex !== -1) {
payments[paymentIndex].status = 'success';
payments[paymentIndex].paidAt = new Date().toISOString();
payments[paymentIndex].transactionId = `TXN${Date.now()}`;
// 更新对应结算单状态
const settlementIndex = settlements.findIndex(s => s.id === value.settlementId);
if (settlementIndex !== -1) {
settlements[settlementIndex].paymentStatus = 'paid';
settlements[settlementIndex].paymentDate = new Date().toISOString().split('T')[0];
settlements[settlementIndex].updatedAt = new Date();
}
}
}, 3000); // 3秒后处理完成
res.status(201).json({
success: true,
message: '支付申请已提交',
data: newPayment
});
} catch (error) {
res.status(500).json({
success: false,
message: '创建支付记录失败',
error: error.message
});
}
});
// 获取财务统计
router.get('/stats/overview', (req, res) => {
try {
const totalSettlements = settlements.length;
const paidCount = settlements.filter(s => s.paymentStatus === 'paid').length;
const pendingCount = settlements.filter(s => s.paymentStatus === 'pending').length;
const totalAmount = settlements.reduce((sum, s) => sum + s.totalAmount, 0);
const paidAmount = settlements
.filter(s => s.paymentStatus === 'paid')
.reduce((sum, s) => sum + s.actualPayment, 0);
const pendingAmount = settlements
.filter(s => s.paymentStatus === 'pending')
.reduce((sum, s) => sum + s.actualPayment, 0);
const totalTaxAmount = settlements.reduce((sum, s) => sum + s.taxAmount, 0);
// 本月统计
const currentMonth = new Date().getMonth();
const currentYear = new Date().getFullYear();
const monthlySettlements = settlements.filter(s => {
const settleDate = new Date(s.settlementDate);
return settleDate.getMonth() === currentMonth && settleDate.getFullYear() === currentYear;
});
const monthlyAmount = monthlySettlements.reduce((sum, s) => sum + s.totalAmount, 0);
res.json({
success: true,
data: {
totalSettlements,
paidCount,
pendingCount,
totalAmount,
paidAmount,
pendingAmount,
totalTaxAmount,
monthlyAmount,
paymentRate: totalSettlements > 0 ? Math.round((paidCount / totalSettlements) * 100) : 0
}
});
} catch (error) {
res.status(500).json({
success: false,
message: '获取财务统计失败',
error: error.message
});
}
});
// 获取财务报表
router.get('/reports/monthly', (req, res) => {
try {
const { year = new Date().getFullYear(), month } = req.query;
let targetSettlements = settlements;
// 筛选指定年份
targetSettlements = targetSettlements.filter(s => {
const settleDate = new Date(s.settlementDate);
return settleDate.getFullYear() === parseInt(year);
});
// 如果指定了月份,进一步筛选
if (month) {
targetSettlements = targetSettlements.filter(s => {
const settleDate = new Date(s.settlementDate);
return settleDate.getMonth() === parseInt(month) - 1;
});
}
// 按月份分组统计
const monthlyStats = {};
for (let i = 1; i <= 12; i++) {
monthlyStats[i] = {
month: i,
settlementCount: 0,
totalAmount: 0,
paidAmount: 0,
pendingAmount: 0
};
}
targetSettlements.forEach(settlement => {
const settleMonth = new Date(settlement.settlementDate).getMonth() + 1;
monthlyStats[settleMonth].settlementCount++;
monthlyStats[settleMonth].totalAmount += settlement.totalAmount;
if (settlement.paymentStatus === 'paid') {
monthlyStats[settleMonth].paidAmount += settlement.actualPayment;
} else {
monthlyStats[settleMonth].pendingAmount += settlement.actualPayment;
}
});
res.json({
success: true,
data: {
year: parseInt(year),
monthlyStats: Object.values(monthlyStats)
}
});
} catch (error) {
res.status(500).json({
success: false,
message: '获取财务报表失败',
error: error.message
});
}
});
module.exports = router;