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

472 lines
12 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 loanContractController.js
* @description 银行系统贷款合同相关API控制器
*/
const { LoanContract, User } = require('../models');
const { Op } = require('sequelize');
const { validationResult } = require('express-validator');
/**
* 获取贷款合同列表
* @param {Object} req - 请求对象
* @param {Object} res - 响应对象
*/
const getContracts = async (req, res) => {
try {
const {
page = 1,
pageSize = 10,
searchField = 'contractNumber',
searchValue = '',
status = '',
sortField = 'createdAt',
sortOrder = 'DESC'
} = req.query;
// 构建查询条件
const where = {};
// 搜索条件
if (searchValue) {
if (searchField === 'contractNumber') {
where.contract_number = { [Op.like]: `%${searchValue}%` };
} else 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}%` };
} else if (searchField === 'borrowerName') {
where.customer_name = { [Op.like]: `%${searchValue}%` };
} else if (searchField === 'farmerName') {
where.customer_name = { [Op.like]: `%${searchValue}%` };
} else if (searchField === 'productName') {
// 产品名称搜索暂时不处理,因为数据库中没有这个字段
// 可以在前端过滤
}
}
// 状态筛选
if (status) {
where.status = status;
}
// 分页参数
const offset = (parseInt(page) - 1) * parseInt(pageSize);
const limit = parseInt(pageSize);
// 排序参数 - 映射字段名
const fieldMapping = {
'createdAt': 'created_at',
'updatedAt': 'updated_at',
'contractDate': 'contract_date',
'loanAmount': 'loan_amount',
'loanTerm': 'loan_term',
'interestRate': 'interest_rate'
};
const dbSortField = fieldMapping[sortField] || sortField;
const order = [[dbSortField, sortOrder.toUpperCase()]];
// 查询数据
const { count, rows } = await LoanContract.findAndCountAll({
where,
order,
offset,
limit
});
// 格式化数据 - 匹配前端期望的字段
const contracts = rows.map(contract => ({
id: contract.id,
contractNumber: contract.contract_number,
applicationNumber: `APP-${String(contract.id).padStart(6, '0')}`, // 生成申请单号
productName: '养殖贷款', // 默认产品名称
farmerName: contract.customer_name || '未知养殖户',
borrowerName: contract.customer_name || '未知借款人',
borrowerIdNumber: contract.customer_id_card || '',
assetType: '养殖设备', // 默认生资种类
applicationQuantity: '1', // 默认申请数量
amount: parseFloat(contract.loan_amount),
paidAmount: 0, // 默认已还款金额
status: contract.status,
type: 'livestock_collateral', // 默认类型
term: contract.loan_term,
interestRate: parseFloat(contract.interest_rate),
phone: contract.customer_phone || '',
purpose: '养殖经营', // 默认用途
remark: '', // 默认备注
contractTime: contract.contract_date,
disbursementTime: null,
maturityTime: null,
completedTime: null,
remainingAmount: parseFloat(contract.loan_amount), // 剩余金额等于贷款金额
repaymentProgress: 0, // 默认还款进度
created_at: contract.created_at,
updated_at: contract.updated_at
}));
res.json({
success: true,
data: {
contracts,
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 getContractById = async (req, res) => {
try {
const { id } = req.params;
const contract = await LoanContract.findByPk(id, {
include: [
{
model: User,
as: 'creator',
attributes: ['id', 'username', 'real_name', 'email', 'phone']
},
{
model: User,
as: 'updater',
attributes: ['id', 'username', 'real_name']
}
]
});
if (!contract) {
return res.status(404).json({
success: false,
message: '贷款合同不存在'
});
}
// 格式化数据
const formattedContract = {
id: contract.id,
contractNumber: contract.contractNumber,
applicationNumber: contract.applicationNumber,
productName: contract.productName,
farmerName: contract.farmerName,
borrowerName: contract.borrowerName,
borrowerIdNumber: contract.borrowerIdNumber,
assetType: contract.assetType,
applicationQuantity: contract.applicationQuantity,
amount: parseFloat(contract.amount),
paidAmount: parseFloat(contract.paidAmount),
status: contract.status,
type: contract.type,
term: contract.term,
interestRate: parseFloat(contract.interestRate),
phone: contract.phone,
purpose: contract.purpose,
remark: contract.remark,
contractTime: contract.contractTime,
disbursementTime: contract.disbursementTime,
maturityTime: contract.maturityTime,
completedTime: contract.completedTime,
remainingAmount: parseFloat(contract.amount - contract.paidAmount),
repaymentProgress: contract.getRepaymentProgress(),
creator: contract.creator,
updater: contract.updater
};
res.json({
success: true,
data: formattedContract
});
} catch (error) {
console.error('获取贷款合同详情失败:', error);
res.status(500).json({
success: false,
message: '获取贷款合同详情失败'
});
}
};
/**
* 创建贷款合同
* @param {Object} req - 请求对象
* @param {Object} res - 响应对象
*/
const createContract = async (req, res) => {
try {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({
success: false,
message: '请求参数错误',
errors: errors.array()
});
}
const contractData = {
...req.body,
createdBy: req.user?.id
};
const contract = await LoanContract.create(contractData);
res.status(201).json({
success: true,
message: '贷款合同创建成功',
data: contract
});
} catch (error) {
console.error('创建贷款合同失败:', error);
res.status(500).json({
success: false,
message: '创建贷款合同失败'
});
}
};
/**
* 更新贷款合同
* @param {Object} req - 请求对象
* @param {Object} res - 响应对象
*/
const updateContract = 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 updateData = {
...req.body,
updatedBy: req.user?.id
};
const [updatedCount] = await LoanContract.update(updateData, {
where: { id }
});
if (updatedCount === 0) {
return res.status(404).json({
success: false,
message: '贷款合同不存在'
});
}
const updatedContract = await LoanContract.findByPk(id);
res.json({
success: true,
message: '贷款合同更新成功',
data: updatedContract
});
} catch (error) {
console.error('更新贷款合同失败:', error);
res.status(500).json({
success: false,
message: '更新贷款合同失败'
});
}
};
/**
* 删除贷款合同
* @param {Object} req - 请求对象
* @param {Object} res - 响应对象
*/
const deleteContract = async (req, res) => {
try {
const { id } = req.params;
const deletedCount = await LoanContract.destroy({
where: { id }
});
if (deletedCount === 0) {
return res.status(404).json({
success: false,
message: '贷款合同不存在'
});
}
res.json({
success: true,
message: '贷款合同删除成功'
});
} catch (error) {
console.error('删除贷款合同失败:', error);
res.status(500).json({
success: false,
message: '删除贷款合同失败'
});
}
};
/**
* 获取合同统计信息
* @param {Object} req - 请求对象
* @param {Object} res - 响应对象
*/
const getContractStats = async (req, res) => {
try {
const stats = await LoanContract.findAll({
attributes: [
'status',
[LoanContract.sequelize.fn('COUNT', '*'), 'count'],
[LoanContract.sequelize.fn('SUM', LoanContract.sequelize.col('amount')), 'totalAmount'],
[LoanContract.sequelize.fn('SUM', LoanContract.sequelize.col('paidAmount')), 'totalPaidAmount']
],
group: ['status'],
raw: true
});
const totalContracts = await LoanContract.count();
const totalAmount = await LoanContract.sum('amount') || 0;
const totalPaidAmount = await LoanContract.sum('paidAmount') || 0;
const statusStats = {
active: 0,
pending: 0,
completed: 0,
defaulted: 0,
cancelled: 0
};
const amountStats = {
active: 0,
pending: 0,
completed: 0,
defaulted: 0,
cancelled: 0
};
const paidAmountStats = {
active: 0,
pending: 0,
completed: 0,
defaulted: 0,
cancelled: 0
};
stats.forEach(stat => {
statusStats[stat.status] = parseInt(stat.count);
amountStats[stat.status] = parseFloat(stat.totalAmount) || 0;
paidAmountStats[stat.status] = parseFloat(stat.totalPaidAmount) || 0;
});
res.json({
success: true,
data: {
total: {
contracts: totalContracts,
amount: parseFloat(totalAmount),
paidAmount: parseFloat(totalPaidAmount),
remainingAmount: parseFloat(totalAmount - totalPaidAmount)
},
byStatus: {
counts: statusStats,
amounts: amountStats,
paidAmounts: paidAmountStats
}
}
});
} 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 updateData = {
status,
updatedBy: userId
};
// 根据状态设置相应的时间字段
if (status === 'active') {
updateData.disbursementTime = new Date();
} else if (status === 'completed') {
updateData.completedTime = new Date();
}
const [updatedCount] = await LoanContract.update(updateData, {
where: {
id: { [Op.in]: ids }
}
});
res.json({
success: true,
message: `成功更新${updatedCount}个合同的状态`,
data: {
updatedCount,
status
}
});
} catch (error) {
console.error('批量更新合同状态失败:', error);
res.status(500).json({
success: false,
message: '批量更新合同状态失败'
});
}
};
module.exports = {
getContracts,
getContractById,
createContract,
updateContract,
deleteContract,
getContractStats,
batchUpdateStatus
};