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

419 lines
11 KiB
JavaScript

/**
* 贷款申请控制器
* @file loanApplicationController.js
* @description 银行系统贷款申请相关API控制器
*/
const { LoanApplication, AuditRecord, User } = require('../models');
const { Op } = require('sequelize');
const { validationResult } = require('express-validator');
/**
* 获取贷款申请列表
* @param {Object} req - 请求对象
* @param {Object} res - 响应对象
*/
const getApplications = async (req, res) => {
try {
const {
page = 1,
pageSize = 10,
searchField = 'applicationNumber',
searchValue = '',
status = '',
sortField = 'createdAt',
sortOrder = 'DESC'
} = req.query;
// 构建查询条件
const where = {};
// 搜索条件
if (searchValue) {
if (searchField === 'customerName') {
where.customer_name = { [Op.like]: `%${searchValue}%` };
} else if (searchField === 'customerPhone') {
where.customer_phone = { [Op.like]: `%${searchValue}%` };
} else if (searchField === 'customerIdCard') {
where.customer_id_card = { [Op.like]: `%${searchValue}%` };
}
}
// 状态筛选
if (status) {
where.status = status;
}
// 分页参数
const offset = (parseInt(page) - 1) * parseInt(pageSize);
const limit = parseInt(pageSize);
// 排序参数
const order = [[sortField, sortOrder.toUpperCase()]];
// 查询数据
const { count, rows } = await LoanApplication.findAndCountAll({
where,
order,
offset,
limit
});
// 格式化数据
const applications = rows.map(app => ({
id: app.id,
customer_name: app.customer_name,
customer_phone: app.customer_phone,
customer_id_card: app.customer_id_card,
loan_amount: parseFloat(app.loan_amount),
loan_term: app.loan_term,
interest_rate: parseFloat(app.interest_rate),
application_date: app.application_date,
status: app.status,
created_at: app.created_at,
updated_at: app.updated_at
}));
res.json({
success: true,
data: {
applications,
pagination: {
current: parseInt(page),
pageSize: parseInt(pageSize),
total: count,
totalPages: Math.ceil(count / parseInt(pageSize))
}
}
});
} catch (error) {
console.error('获取贷款申请列表失败:', error);
res.status(500).json({
success: false,
message: '获取贷款申请列表失败'
});
}
};
/**
* 获取贷款申请详情
* @param {Object} req - 请求对象
* @param {Object} res - 响应对象
*/
const getApplicationById = async (req, res) => {
try {
const { id } = req.params;
const application = await LoanApplication.findByPk(id, {
include: [
{
model: User,
as: 'applicant',
attributes: ['id', 'username', 'real_name', 'email', 'phone']
},
{
model: User,
as: 'approver',
attributes: ['id', 'username', 'real_name']
},
{
model: User,
as: 'rejector',
attributes: ['id', 'username', 'real_name']
},
{
model: AuditRecord,
as: 'auditRecords',
include: [
{
model: User,
as: 'auditorUser',
attributes: ['id', 'username', 'real_name']
}
],
order: [['auditTime', 'DESC']]
}
]
});
if (!application) {
return res.status(404).json({
success: false,
message: '贷款申请不存在'
});
}
// 格式化数据
const formattedApplication = {
id: application.id,
applicationNumber: application.applicationNumber,
productName: application.productName,
farmerName: application.farmerName,
borrowerName: application.borrowerName,
borrowerIdNumber: application.borrowerIdNumber,
assetType: application.assetType,
applicationQuantity: application.applicationQuantity,
amount: parseFloat(application.amount),
status: application.status,
type: application.type,
term: application.term,
interestRate: parseFloat(application.interestRate),
phone: application.phone,
purpose: application.purpose,
remark: application.remark,
applicationTime: application.applicationTime,
approvedTime: application.approvedTime,
rejectedTime: application.rejectedTime,
applicant: application.applicant,
approver: application.approver,
rejector: application.rejector,
auditRecords: application.auditRecords.map(record => ({
id: record.id,
action: record.action,
auditor: record.auditorUser?.real_name || record.auditor,
auditorId: record.auditorId,
comment: record.comment,
time: record.auditTime,
previousStatus: record.previousStatus,
newStatus: record.newStatus
}))
};
res.json({
success: true,
data: formattedApplication
});
} catch (error) {
console.error('获取贷款申请详情失败:', error);
res.status(500).json({
success: false,
message: '获取贷款申请详情失败'
});
}
};
/**
* 审核贷款申请
* @param {Object} req - 请求对象
* @param {Object} res - 响应对象
*/
const auditApplication = async (req, res) => {
try {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({
success: false,
message: '请求参数错误',
errors: errors.array()
});
}
const { id } = req.params;
const { action, comment } = req.body;
const userId = req.user?.id;
// 获取申请信息
const application = await LoanApplication.findByPk(id);
if (!application) {
return res.status(404).json({
success: false,
message: '贷款申请不存在'
});
}
// 检查申请状态
if (application.status === 'approved' || application.status === 'rejected') {
return res.status(400).json({
success: false,
message: '该申请已完成审核,无法重复操作'
});
}
const previousStatus = application.status;
let newStatus = application.status;
// 根据审核动作更新状态
if (action === 'approve') {
newStatus = 'approved';
application.approvedTime = new Date();
application.approvedBy = userId;
} else if (action === 'reject') {
newStatus = 'rejected';
application.rejectedTime = new Date();
application.rejectedBy = userId;
application.rejectionReason = comment;
}
// 更新申请状态
application.status = newStatus;
await application.save();
// 创建审核记录
await AuditRecord.create({
applicationId: id,
action,
auditor: req.user?.real_name || req.user?.username || '系统',
auditorId: userId,
comment,
auditTime: new Date(),
previousStatus,
newStatus
});
res.json({
success: true,
message: action === 'approve' ? '审核通过' : '审核拒绝',
data: {
id: application.id,
status: newStatus,
action,
comment
}
});
} catch (error) {
console.error('审核贷款申请失败:', error);
res.status(500).json({
success: false,
message: '审核贷款申请失败'
});
}
};
/**
* 获取申请统计信息
* @param {Object} req - 请求对象
* @param {Object} res - 响应对象
*/
const getApplicationStats = async (req, res) => {
try {
const stats = await LoanApplication.findAll({
attributes: [
'status',
[LoanApplication.sequelize.fn('COUNT', '*'), 'count'],
[LoanApplication.sequelize.fn('SUM', LoanApplication.sequelize.col('amount')), 'totalAmount']
],
group: ['status'],
raw: true
});
const totalApplications = await LoanApplication.count();
const totalAmount = await LoanApplication.sum('amount') || 0;
const statusStats = {
pending_review: 0,
verification_pending: 0,
pending_binding: 0,
approved: 0,
rejected: 0
};
const amountStats = {
pending_review: 0,
verification_pending: 0,
pending_binding: 0,
approved: 0,
rejected: 0
};
stats.forEach(stat => {
statusStats[stat.status] = parseInt(stat.count);
amountStats[stat.status] = parseFloat(stat.totalAmount) || 0;
});
res.json({
success: true,
data: {
total: {
applications: totalApplications,
amount: parseFloat(totalAmount)
},
byStatus: {
counts: statusStats,
amounts: amountStats
}
}
});
} catch (error) {
console.error('获取申请统计失败:', error);
res.status(500).json({
success: false,
message: '获取申请统计失败'
});
}
};
/**
* 批量更新申请状态
* @param {Object} req - 请求对象
* @param {Object} res - 响应对象
*/
const batchUpdateStatus = async (req, res) => {
try {
const { ids, status } = req.body;
const userId = req.user?.id;
if (!ids || !Array.isArray(ids) || ids.length === 0) {
return res.status(400).json({
success: false,
message: '请选择要操作的申请'
});
}
if (!status) {
return res.status(400).json({
success: false,
message: '请指定目标状态'
});
}
// 更新申请状态
const [updatedCount] = await LoanApplication.update(
{
status,
...(status === 'approved' && { approvedTime: new Date(), approvedBy: userId }),
...(status === 'rejected' && { rejectedTime: new Date(), rejectedBy: userId })
},
{
where: {
id: { [Op.in]: ids }
}
}
);
// 为每个申请创建审核记录
for (const id of ids) {
await AuditRecord.create({
applicationId: id,
action: status === 'approved' ? 'approve' : 'reject',
auditor: req.user?.real_name || req.user?.username || '系统',
auditorId: userId,
comment: `批量${status === 'approved' ? '通过' : '拒绝'}`,
auditTime: new Date(),
newStatus: status
});
}
res.json({
success: true,
message: `成功更新${updatedCount}个申请的状态`,
data: {
updatedCount,
status
}
});
} catch (error) {
console.error('批量更新申请状态失败:', error);
res.status(500).json({
success: false,
message: '批量更新申请状态失败'
});
}
};
module.exports = {
getApplications,
getApplicationById,
auditApplication,
getApplicationStats,
batchUpdateStatus
};