Files
nxxmdata/bank-backend/controllers/accountController.js

436 lines
11 KiB
JavaScript

/**
* 账户控制器
* @file accountController.js
* @description 处理银行账户相关的请求
*/
const { Account, User, Transaction } = require('../models');
const { validationResult } = require('express-validator');
const { Op } = require('sequelize');
/**
* 创建账户
* @param {Object} req 请求对象
* @param {Object} res 响应对象
*/
exports.createAccount = async (req, res) => {
try {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({
success: false,
message: '输入数据验证失败',
errors: errors.array()
});
}
const { user_id, account_type, initial_balance = 0 } = req.body;
// 检查用户是否存在
const user = await User.findByPk(user_id);
if (!user) {
return res.status(404).json({
success: false,
message: '用户不存在'
});
}
// 生成账户号码
const accountNumber = await generateAccountNumber();
// 创建账户
const account = await Account.create({
account_number: accountNumber,
user_id,
account_type,
balance: initial_balance * 100, // 转换为分
available_balance: initial_balance * 100,
frozen_amount: 0
});
// 如果有初始余额,创建存款交易记录
if (initial_balance > 0) {
await Transaction.create({
transaction_number: await generateTransactionNumber(),
account_id: account.id,
transaction_type: 'deposit',
amount: initial_balance * 100,
balance_before: 0,
balance_after: initial_balance * 100,
description: '开户存款',
status: 'completed',
processed_at: new Date()
});
}
res.status(201).json({
success: true,
message: '账户创建成功',
data: {
...account.getSafeInfo(),
balance_formatted: account.getBalanceFormatted(),
available_balance_formatted: account.getAvailableBalanceFormatted()
}
});
} catch (error) {
console.error('创建账户错误:', error);
res.status(500).json({
success: false,
message: '服务器内部错误'
});
}
};
/**
* 获取账户列表
* @param {Object} req 请求对象
* @param {Object} res 响应对象
*/
exports.getAccounts = async (req, res) => {
try {
const page = parseInt(req.query.page) || 1;
const limit = parseInt(req.query.limit) || 10;
const offset = (page - 1) * limit;
const { user_id, account_type, status } = req.query;
const whereClause = {};
// 普通用户只能查看自己的账户
if (req.user.role.name !== 'admin') {
whereClause.user_id = req.user.id;
} else if (user_id) {
whereClause.user_id = user_id;
}
if (account_type) {
whereClause.account_type = account_type;
}
if (status) {
whereClause.status = status;
}
const { count, rows } = await Account.findAndCountAll({
where: whereClause,
include: [{
model: User,
as: 'user',
attributes: ['id', 'username', 'real_name', 'email']
}],
limit,
offset,
order: [['created_at', 'DESC']]
});
res.json({
success: true,
data: {
accounts: rows.map(account => ({
...account.getSafeInfo(),
balance_formatted: account.getBalanceFormatted(),
available_balance_formatted: account.getAvailableBalanceFormatted(),
frozen_amount_formatted: account.getFrozenAmountFormatted()
})),
pagination: {
page,
limit,
total: count,
pages: Math.ceil(count / limit)
}
}
});
} catch (error) {
console.error('获取账户列表错误:', error);
console.error('错误堆栈:', error.stack);
res.status(500).json({
success: false,
message: '服务器内部错误',
error: process.env.NODE_ENV === 'development' ? error.message : undefined
});
}
};
/**
* 获取账户详情
* @param {Object} req 请求对象
* @param {Object} res 响应对象
*/
exports.getAccountDetail = async (req, res) => {
try {
const { accountId } = req.params;
const account = await Account.findByPk(accountId, {
include: [{
model: User,
as: 'user',
attributes: ['id', 'username', 'real_name', 'email']
}]
});
if (!account) {
return res.status(404).json({
success: false,
message: '账户不存在'
});
}
// 检查权限
if (req.user.role.name !== 'admin' && account.user_id !== req.user.id) {
return res.status(403).json({
success: false,
message: '无权访问该账户'
});
}
res.json({
success: true,
data: {
...account.getSafeInfo(),
balance_formatted: account.getBalanceFormatted(),
available_balance_formatted: account.getAvailableBalanceFormatted(),
frozen_amount_formatted: account.getFrozenAmountFormatted()
}
});
} catch (error) {
console.error('获取账户详情错误:', error);
res.status(500).json({
success: false,
message: '服务器内部错误'
});
}
};
/**
* 更新账户状态
* @param {Object} req 请求对象
* @param {Object} res 响应对象
*/
exports.updateAccountStatus = async (req, res) => {
try {
const { accountId } = req.params;
const { status } = req.body;
const account = await Account.findByPk(accountId);
if (!account) {
return res.status(404).json({
success: false,
message: '账户不存在'
});
}
await account.update({ status });
res.json({
success: true,
message: '账户状态更新成功',
data: account.getSafeInfo()
});
} catch (error) {
console.error('更新账户状态错误:', error);
res.status(500).json({
success: false,
message: '服务器内部错误'
});
}
};
/**
* 存款
* @param {Object} req 请求对象
* @param {Object} res 响应对象
*/
exports.deposit = async (req, res) => {
try {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({
success: false,
message: '输入数据验证失败',
errors: errors.array()
});
}
const { accountId } = req.params;
const { amount, description } = req.body;
const account = await Account.findByPk(accountId);
if (!account) {
return res.status(404).json({
success: false,
message: '账户不存在'
});
}
// 检查账户状态
if (!account.isActive()) {
return res.status(400).json({
success: false,
message: '账户状态异常,无法进行存款操作'
});
}
const amountInCents = Math.round(amount * 100);
const balanceBefore = account.balance;
const balanceAfter = balanceBefore + amountInCents;
// 开始事务
const transaction = await sequelize.transaction();
try {
// 更新账户余额
await account.update({
balance: balanceAfter,
available_balance: account.available_balance + amountInCents
}, { transaction });
// 创建交易记录
await Transaction.create({
transaction_number: await generateTransactionNumber(),
account_id: account.id,
transaction_type: 'deposit',
amount: amountInCents,
balance_before: balanceBefore,
balance_after: balanceAfter,
description: description || '存款',
status: 'completed',
processed_at: new Date()
}, { transaction });
await transaction.commit();
res.json({
success: true,
message: '存款成功',
data: {
amount: amount,
balance_after: account.formatAmount(balanceAfter),
transaction_number: await generateTransactionNumber()
}
});
} catch (error) {
await transaction.rollback();
throw error;
}
} catch (error) {
console.error('存款错误:', error);
res.status(500).json({
success: false,
message: '服务器内部错误'
});
}
};
/**
* 取款
* @param {Object} req 请求对象
* @param {Object} res 响应对象
*/
exports.withdraw = async (req, res) => {
try {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({
success: false,
message: '输入数据验证失败',
errors: errors.array()
});
}
const { accountId } = req.params;
const { amount, description } = req.body;
const account = await Account.findByPk(accountId);
if (!account) {
return res.status(404).json({
success: false,
message: '账户不存在'
});
}
// 检查账户状态
if (!account.isActive()) {
return res.status(400).json({
success: false,
message: '账户状态异常,无法进行取款操作'
});
}
const amountInCents = Math.round(amount * 100);
// 检查余额是否充足
if (!account.hasSufficientBalance(amountInCents)) {
return res.status(400).json({
success: false,
message: '账户余额不足'
});
}
const balanceBefore = account.balance;
const balanceAfter = balanceBefore - amountInCents;
// 开始事务
const transaction = await sequelize.transaction();
try {
// 更新账户余额
await account.update({
balance: balanceAfter,
available_balance: account.available_balance - amountInCents
}, { transaction });
// 创建交易记录
await Transaction.create({
transaction_number: await generateTransactionNumber(),
account_id: account.id,
transaction_type: 'withdrawal',
amount: amountInCents,
balance_before: balanceBefore,
balance_after: balanceAfter,
description: description || '取款',
status: 'completed',
processed_at: new Date()
}, { transaction });
await transaction.commit();
res.json({
success: true,
message: '取款成功',
data: {
amount: amount,
balance_after: account.formatAmount(balanceAfter),
transaction_number: await generateTransactionNumber()
}
});
} catch (error) {
await transaction.rollback();
throw error;
}
} catch (error) {
console.error('取款错误:', error);
res.status(500).json({
success: false,
message: '服务器内部错误'
});
}
};
/**
* 生成账户号码
* @returns {String} 账户号码
*/
async function generateAccountNumber() {
const bankCode = process.env.BANK_CODE || '001';
const timestamp = Date.now().toString().slice(-8);
const random = Math.floor(Math.random() * 10000).toString().padStart(4, '0');
return `${bankCode}${timestamp}${random}`;
}
/**
* 生成交易流水号
* @returns {String} 交易流水号
*/
async function generateTransactionNumber() {
const timestamp = Date.now().toString();
const random = Math.floor(Math.random() * 10000).toString().padStart(4, '0');
return `TXN${timestamp}${random}`;
}