2025-09-24 17:49:32 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 贷款合同控制器
|
|
|
|
|
|
* @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') {
|
2025-09-25 15:53:44 +08:00
|
|
|
|
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}%` };
|
2025-09-25 17:43:54 +08:00
|
|
|
|
} else if (searchField === 'applicationNumber') {
|
|
|
|
|
|
// 数据库中实际没有applicationNumber字段,使用id作为替代
|
|
|
|
|
|
where.id = { [Op.like]: `%${searchValue}%` };
|
2025-09-26 17:52:50 +08:00
|
|
|
|
} else if (searchField === 'borrowerName') {
|
|
|
|
|
|
where.customer_name = { [Op.like]: `%${searchValue}%` };
|
|
|
|
|
|
} else if (searchField === 'farmerName') {
|
|
|
|
|
|
where.customer_name = { [Op.like]: `%${searchValue}%` };
|
|
|
|
|
|
} else if (searchField === 'productName') {
|
|
|
|
|
|
// 产品名称搜索暂时不处理,因为数据库中没有这个字段
|
|
|
|
|
|
// 可以在前端过滤
|
2025-09-24 17:49:32 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 状态筛选
|
|
|
|
|
|
if (status) {
|
|
|
|
|
|
where.status = status;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 分页参数
|
|
|
|
|
|
const offset = (parseInt(page) - 1) * parseInt(pageSize);
|
|
|
|
|
|
const limit = parseInt(pageSize);
|
|
|
|
|
|
|
2025-09-25 17:43:54 +08:00
|
|
|
|
// 排序参数 - 映射字段名
|
|
|
|
|
|
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()]];
|
2025-09-24 17:49:32 +08:00
|
|
|
|
|
|
|
|
|
|
// 查询数据
|
|
|
|
|
|
const { count, rows } = await LoanContract.findAndCountAll({
|
|
|
|
|
|
where,
|
|
|
|
|
|
order,
|
|
|
|
|
|
offset,
|
|
|
|
|
|
limit
|
|
|
|
|
|
});
|
|
|
|
|
|
|
2025-09-26 17:52:50 +08:00
|
|
|
|
// 格式化数据 - 匹配前端期望的字段
|
2025-09-24 17:49:32 +08:00
|
|
|
|
const contracts = rows.map(contract => ({
|
|
|
|
|
|
id: contract.id,
|
2025-09-26 17:52:50 +08:00
|
|
|
|
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, // 默认已还款金额
|
2025-09-24 17:49:32 +08:00
|
|
|
|
status: contract.status,
|
2025-09-26 17:52:50 +08:00
|
|
|
|
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, // 默认还款进度
|
2025-09-25 15:53:44 +08:00
|
|
|
|
created_at: contract.created_at,
|
|
|
|
|
|
updated_at: contract.updated_at
|
2025-09-24 17:49:32 +08:00
|
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
2025-09-29 17:58:42 +08:00
|
|
|
|
const contract = await LoanContract.findByPk(id);
|
2025-09-24 17:49:32 +08:00
|
|
|
|
|
|
|
|
|
|
if (!contract) {
|
|
|
|
|
|
return res.status(404).json({
|
|
|
|
|
|
success: false,
|
|
|
|
|
|
message: '贷款合同不存在'
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-29 17:58:42 +08:00
|
|
|
|
// 格式化数据 - 使用数据库实际字段名
|
2025-09-24 17:49:32 +08:00
|
|
|
|
const formattedContract = {
|
|
|
|
|
|
id: contract.id,
|
2025-09-29 17:58:42 +08:00
|
|
|
|
contractNumber: contract.contract_number,
|
|
|
|
|
|
applicationNumber: contract.application_number || '',
|
|
|
|
|
|
productName: contract.product_name || '',
|
|
|
|
|
|
farmerName: contract.farmer_name || contract.customer_name,
|
|
|
|
|
|
borrowerName: contract.borrower_name || contract.customer_name,
|
|
|
|
|
|
borrowerIdNumber: contract.borrower_id_number || contract.customer_id_card,
|
|
|
|
|
|
assetType: contract.asset_type || '',
|
|
|
|
|
|
applicationQuantity: contract.application_quantity || '',
|
|
|
|
|
|
amount: parseFloat(contract.loan_amount),
|
|
|
|
|
|
paidAmount: parseFloat(contract.paid_amount || 0),
|
2025-09-24 17:49:32 +08:00
|
|
|
|
status: contract.status,
|
2025-09-29 17:58:42 +08:00
|
|
|
|
type: contract.type || 'personal',
|
|
|
|
|
|
term: contract.loan_term,
|
|
|
|
|
|
interestRate: parseFloat(contract.interest_rate),
|
|
|
|
|
|
phone: contract.customer_phone,
|
|
|
|
|
|
purpose: contract.purpose || '',
|
|
|
|
|
|
remark: contract.remark || '',
|
|
|
|
|
|
contractTime: contract.contract_date,
|
|
|
|
|
|
disbursementTime: contract.disbursement_time,
|
|
|
|
|
|
maturityTime: contract.maturity_time,
|
|
|
|
|
|
completedTime: contract.completed_time,
|
|
|
|
|
|
remainingAmount: parseFloat(contract.loan_amount - (contract.paid_amount || 0)),
|
|
|
|
|
|
repaymentProgress: contract.getRepaymentProgress ? contract.getRepaymentProgress() : 0
|
2025-09-24 17:49:32 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
res.json({
|
|
|
|
|
|
success: true,
|
|
|
|
|
|
data: formattedContract
|
|
|
|
|
|
});
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('获取贷款合同详情失败:', error);
|
2025-09-29 17:58:42 +08:00
|
|
|
|
console.error('错误详情:', {
|
|
|
|
|
|
message: error.message,
|
|
|
|
|
|
stack: error.stack,
|
|
|
|
|
|
name: error.name
|
|
|
|
|
|
});
|
2025-09-24 17:49:32 +08:00
|
|
|
|
res.status(500).json({
|
|
|
|
|
|
success: false,
|
2025-09-29 17:58:42 +08:00
|
|
|
|
message: '获取贷款合同详情失败',
|
|
|
|
|
|
error: process.env.NODE_ENV === 'development' ? error.message : undefined
|
2025-09-24 17:49:32 +08:00
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 创建贷款合同
|
|
|
|
|
|
* @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
|
|
|
|
|
|
};
|