Files
nxxmdata/bank-backend/controllers/loanApplicationController.js
2025-09-25 17:43:54 +08:00

431 lines
11 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 贷款申请控制器
* @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}%` };
} else if (searchField === 'applicationNumber') {
// 数据库中实际没有applicationNumber字段使用id作为替代
where.id = { [Op.like]: `%${searchValue}%` };
}
}
// 状态筛选
if (status) {
where.status = status;
}
// 分页参数
const offset = (parseInt(page) - 1) * parseInt(pageSize);
const limit = parseInt(pageSize);
// 排序参数 - 映射字段名
const fieldMapping = {
'createdAt': 'created_at',
'updatedAt': 'updated_at',
'applicationDate': 'application_date',
'loanAmount': 'loan_amount',
'loanTerm': 'loan_term',
'interestRate': 'interest_rate'
};
const dbSortField = fieldMapping[sortField] || sortField;
const order = [[dbSortField, 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
};