添加银行端后端接口
This commit is contained in:
480
bank-backend/controllers/completedSupervisionController.js
Normal file
480
bank-backend/controllers/completedSupervisionController.js
Normal file
@@ -0,0 +1,480 @@
|
||||
const { CompletedSupervision, User } = require('../models')
|
||||
const { Op } = require('sequelize')
|
||||
|
||||
// 获取监管任务已结项列表
|
||||
const getCompletedSupervisions = async (req, res) => {
|
||||
try {
|
||||
const {
|
||||
page = 1,
|
||||
limit = 10,
|
||||
search = '',
|
||||
contractNumber = '',
|
||||
settlementStatus = ''
|
||||
} = req.query
|
||||
|
||||
const offset = (page - 1) * limit
|
||||
const where = {}
|
||||
|
||||
// 搜索条件
|
||||
if (search) {
|
||||
where[Op.or] = [
|
||||
{ applicationNumber: { [Op.like]: `%${search}%` } },
|
||||
{ customerName: { [Op.like]: `%${search}%` } },
|
||||
{ productName: { [Op.like]: `%${search}%` } }
|
||||
]
|
||||
}
|
||||
|
||||
// 合同编号筛选
|
||||
if (contractNumber) {
|
||||
where.contractNumber = contractNumber
|
||||
}
|
||||
|
||||
// 结清状态筛选
|
||||
if (settlementStatus) {
|
||||
where.settlementStatus = settlementStatus
|
||||
}
|
||||
|
||||
const { count, rows } = await CompletedSupervision.findAndCountAll({
|
||||
where,
|
||||
include: [
|
||||
{
|
||||
model: User,
|
||||
as: 'creator',
|
||||
attributes: ['id', 'username', 'real_name']
|
||||
},
|
||||
{
|
||||
model: User,
|
||||
as: 'updater',
|
||||
attributes: ['id', 'username', 'real_name']
|
||||
}
|
||||
],
|
||||
order: [['importTime', 'DESC']],
|
||||
limit: parseInt(limit),
|
||||
offset: parseInt(offset)
|
||||
})
|
||||
|
||||
const totalPages = Math.ceil(count / limit)
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: '获取监管任务已结项列表成功',
|
||||
data: {
|
||||
tasks: rows,
|
||||
pagination: {
|
||||
current: parseInt(page),
|
||||
pageSize: parseInt(limit),
|
||||
total: count,
|
||||
totalPages,
|
||||
hasNextPage: parseInt(page) < totalPages,
|
||||
hasPrevPage: parseInt(page) > 1
|
||||
}
|
||||
}
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('获取监管任务已结项列表失败:', error)
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: '获取监管任务已结项列表失败',
|
||||
error: error.message
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 根据ID获取监管任务已结项详情
|
||||
const getCompletedSupervisionById = async (req, res) => {
|
||||
try {
|
||||
const { id } = req.params
|
||||
|
||||
const task = await CompletedSupervision.findByPk(id, {
|
||||
include: [
|
||||
{
|
||||
model: User,
|
||||
as: 'creator',
|
||||
attributes: ['id', 'username', 'real_name']
|
||||
},
|
||||
{
|
||||
model: User,
|
||||
as: 'updater',
|
||||
attributes: ['id', 'username', 'real_name']
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
if (!task) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: '监管任务已结项不存在'
|
||||
})
|
||||
}
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: '获取监管任务已结项详情成功',
|
||||
data: task
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('获取监管任务已结项详情失败:', error)
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: '获取监管任务已结项详情失败',
|
||||
error: error.message
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 创建监管任务已结项
|
||||
const createCompletedSupervision = async (req, res) => {
|
||||
try {
|
||||
const {
|
||||
applicationNumber,
|
||||
contractNumber,
|
||||
productName,
|
||||
customerName,
|
||||
idType,
|
||||
idNumber,
|
||||
assetType,
|
||||
assetQuantity,
|
||||
totalRepaymentPeriods,
|
||||
settlementStatus,
|
||||
settlementDate,
|
||||
settlementAmount,
|
||||
remainingAmount,
|
||||
settlementNotes
|
||||
} = req.body
|
||||
|
||||
// 验证必填字段
|
||||
const requiredFields = [
|
||||
'applicationNumber', 'contractNumber', 'productName',
|
||||
'customerName', 'idType', 'idNumber', 'assetType',
|
||||
'assetQuantity', 'totalRepaymentPeriods'
|
||||
]
|
||||
|
||||
for (const field of requiredFields) {
|
||||
if (!req.body[field]) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: `${field} 是必填字段`
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 验证申请单号唯一性
|
||||
const existingTask = await CompletedSupervision.findOne({
|
||||
where: { applicationNumber }
|
||||
})
|
||||
|
||||
if (existingTask) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: '申请单号已存在'
|
||||
})
|
||||
}
|
||||
|
||||
// 验证状态枚举值
|
||||
const validStatuses = ['settled', 'unsettled', 'partial']
|
||||
if (settlementStatus && !validStatuses.includes(settlementStatus)) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: '结清状态值无效'
|
||||
})
|
||||
}
|
||||
|
||||
// 验证证件类型枚举值
|
||||
const validIdTypes = ['ID_CARD', 'PASSPORT', 'OTHER']
|
||||
if (!validIdTypes.includes(idType)) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: '证件类型值无效'
|
||||
})
|
||||
}
|
||||
|
||||
const task = await CompletedSupervision.create({
|
||||
applicationNumber,
|
||||
contractNumber,
|
||||
productName,
|
||||
customerName,
|
||||
idType,
|
||||
idNumber,
|
||||
assetType,
|
||||
assetQuantity,
|
||||
totalRepaymentPeriods,
|
||||
settlementStatus: settlementStatus || 'unsettled',
|
||||
settlementDate: settlementDate || null,
|
||||
importTime: req.body.importTime || new Date(),
|
||||
settlementAmount: settlementAmount || null,
|
||||
remainingAmount: remainingAmount || null,
|
||||
settlementNotes: settlementNotes || null,
|
||||
createdBy: req.user.id
|
||||
})
|
||||
|
||||
// 获取创建的任务详情(包含关联数据)
|
||||
const createdTask = await CompletedSupervision.findByPk(task.id, {
|
||||
include: [
|
||||
{
|
||||
model: User,
|
||||
as: 'creator',
|
||||
attributes: ['id', 'username', 'real_name']
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
res.status(201).json({
|
||||
success: true,
|
||||
message: '创建监管任务已结项成功',
|
||||
data: createdTask
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('创建监管任务已结项失败:', error)
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: '创建监管任务已结项失败',
|
||||
error: error.message
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 更新监管任务已结项
|
||||
const updateCompletedSupervision = async (req, res) => {
|
||||
try {
|
||||
const { id } = req.params
|
||||
const updateData = req.body
|
||||
|
||||
const task = await CompletedSupervision.findByPk(id)
|
||||
if (!task) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: '监管任务已结项不存在'
|
||||
})
|
||||
}
|
||||
|
||||
// 验证状态枚举值
|
||||
if (updateData.settlementStatus) {
|
||||
const validStatuses = ['settled', 'unsettled', 'partial']
|
||||
if (!validStatuses.includes(updateData.settlementStatus)) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: '结清状态值无效'
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 验证证件类型枚举值
|
||||
if (updateData.idType) {
|
||||
const validIdTypes = ['ID_CARD', 'PASSPORT', 'OTHER']
|
||||
if (!validIdTypes.includes(updateData.idType)) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: '证件类型值无效'
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 如果申请单号有变化,检查唯一性
|
||||
if (updateData.applicationNumber && updateData.applicationNumber !== task.applicationNumber) {
|
||||
const existingTask = await CompletedSupervision.findOne({
|
||||
where: {
|
||||
applicationNumber: updateData.applicationNumber,
|
||||
id: { [Op.ne]: id }
|
||||
}
|
||||
})
|
||||
|
||||
if (existingTask) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: '申请单号已存在'
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
await task.update({
|
||||
...updateData,
|
||||
updatedBy: req.user.id
|
||||
})
|
||||
|
||||
// 获取更新后的任务详情
|
||||
const updatedTask = await CompletedSupervision.findByPk(id, {
|
||||
include: [
|
||||
{
|
||||
model: User,
|
||||
as: 'creator',
|
||||
attributes: ['id', 'username', 'real_name']
|
||||
},
|
||||
{
|
||||
model: User,
|
||||
as: 'updater',
|
||||
attributes: ['id', 'username', 'real_name']
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: '更新监管任务已结项成功',
|
||||
data: updatedTask
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('更新监管任务已结项失败:', error)
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: '更新监管任务已结项失败',
|
||||
error: error.message
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 删除监管任务已结项
|
||||
const deleteCompletedSupervision = async (req, res) => {
|
||||
try {
|
||||
const { id } = req.params
|
||||
|
||||
const task = await CompletedSupervision.findByPk(id)
|
||||
if (!task) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: '监管任务已结项不存在'
|
||||
})
|
||||
}
|
||||
|
||||
await task.destroy()
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: '删除监管任务已结项成功'
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('删除监管任务已结项失败:', error)
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: '删除监管任务已结项失败',
|
||||
error: error.message
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 获取监管任务已结项统计信息
|
||||
const getCompletedSupervisionStats = async (req, res) => {
|
||||
try {
|
||||
const stats = await CompletedSupervision.findAll({
|
||||
attributes: [
|
||||
'settlementStatus',
|
||||
[CompletedSupervision.sequelize.fn('COUNT', CompletedSupervision.sequelize.col('id')), 'count']
|
||||
],
|
||||
group: ['settlementStatus'],
|
||||
raw: true
|
||||
})
|
||||
|
||||
const totalCount = await CompletedSupervision.count()
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: '获取监管任务已结项统计成功',
|
||||
data: {
|
||||
stats,
|
||||
totalCount
|
||||
}
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('获取监管任务已结项统计失败:', error)
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: '获取监管任务已结项统计失败',
|
||||
error: error.message
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 批量更新结清状态
|
||||
const batchUpdateStatus = async (req, res) => {
|
||||
try {
|
||||
const { ids, settlementStatus } = req.body
|
||||
|
||||
if (!Array.isArray(ids) || ids.length === 0) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: '请选择要更新的任务'
|
||||
})
|
||||
}
|
||||
|
||||
if (!settlementStatus) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: '请选择要更新的状态'
|
||||
})
|
||||
}
|
||||
|
||||
const validStatuses = ['settled', 'unsettled', 'partial']
|
||||
if (!validStatuses.includes(settlementStatus)) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: '结清状态值无效'
|
||||
})
|
||||
}
|
||||
|
||||
const updateData = {
|
||||
settlementStatus,
|
||||
updatedBy: req.user.id
|
||||
}
|
||||
|
||||
// 如果状态是已结清,设置结清日期
|
||||
if (settlementStatus === 'settled') {
|
||||
updateData.settlementDate = new Date()
|
||||
}
|
||||
|
||||
await CompletedSupervision.update(updateData, {
|
||||
where: { id: { [Op.in]: ids } }
|
||||
})
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: '批量更新结清状态成功'
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('批量更新结清状态失败:', error)
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: '批量更新结清状态失败',
|
||||
error: error.message
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 批量删除监管任务已结项
|
||||
const batchDelete = async (req, res) => {
|
||||
try {
|
||||
const { ids } = req.body
|
||||
|
||||
if (!Array.isArray(ids) || ids.length === 0) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: '请选择要删除的任务'
|
||||
})
|
||||
}
|
||||
|
||||
await CompletedSupervision.destroy({
|
||||
where: { id: { [Op.in]: ids } }
|
||||
})
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: '批量删除监管任务已结项成功'
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('批量删除监管任务已结项失败:', error)
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: '批量删除监管任务已结项失败',
|
||||
error: error.message
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getCompletedSupervisions,
|
||||
getCompletedSupervisionById,
|
||||
createCompletedSupervision,
|
||||
updateCompletedSupervision,
|
||||
deleteCompletedSupervision,
|
||||
getCompletedSupervisionStats,
|
||||
batchUpdateStatus,
|
||||
batchDelete
|
||||
}
|
||||
482
bank-backend/controllers/installationTaskController.js
Normal file
482
bank-backend/controllers/installationTaskController.js
Normal file
@@ -0,0 +1,482 @@
|
||||
const { InstallationTask, User } = require('../models')
|
||||
const { Op } = require('sequelize')
|
||||
|
||||
// 获取待安装任务列表
|
||||
const getInstallationTasks = async (req, res) => {
|
||||
try {
|
||||
const {
|
||||
page = 1,
|
||||
limit = 10,
|
||||
search = '',
|
||||
installationStatus = '',
|
||||
dateRange = ''
|
||||
} = req.query
|
||||
|
||||
const offset = (page - 1) * limit
|
||||
const where = {}
|
||||
|
||||
// 搜索条件
|
||||
if (search) {
|
||||
where[Op.or] = [
|
||||
{ contractNumber: { [Op.like]: `%${search}%` } },
|
||||
{ applicationNumber: { [Op.like]: `%${search}%` } },
|
||||
{ customerName: { [Op.like]: `%${search}%` } }
|
||||
]
|
||||
}
|
||||
|
||||
// 状态筛选
|
||||
if (installationStatus) {
|
||||
where.installationStatus = installationStatus
|
||||
}
|
||||
|
||||
// 日期范围筛选
|
||||
if (dateRange) {
|
||||
const [startDate, endDate] = dateRange.split(',')
|
||||
if (startDate && endDate) {
|
||||
where.taskGenerationTime = {
|
||||
[Op.between]: [new Date(startDate), new Date(endDate)]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const { count, rows } = await InstallationTask.findAndCountAll({
|
||||
where,
|
||||
include: [
|
||||
{
|
||||
model: User,
|
||||
as: 'creator',
|
||||
attributes: ['id', 'username', 'real_name']
|
||||
},
|
||||
{
|
||||
model: User,
|
||||
as: 'updater',
|
||||
attributes: ['id', 'username', 'real_name']
|
||||
}
|
||||
],
|
||||
order: [['taskGenerationTime', 'DESC']],
|
||||
limit: parseInt(limit),
|
||||
offset: parseInt(offset)
|
||||
})
|
||||
|
||||
const totalPages = Math.ceil(count / limit)
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: '获取待安装任务列表成功',
|
||||
data: {
|
||||
tasks: rows,
|
||||
pagination: {
|
||||
current: parseInt(page),
|
||||
pageSize: parseInt(limit),
|
||||
total: count,
|
||||
totalPages,
|
||||
hasNextPage: parseInt(page) < totalPages,
|
||||
hasPrevPage: parseInt(page) > 1
|
||||
}
|
||||
}
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('获取待安装任务列表失败:', error)
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: '获取待安装任务列表失败',
|
||||
error: error.message
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 根据ID获取待安装任务详情
|
||||
const getInstallationTaskById = async (req, res) => {
|
||||
try {
|
||||
const { id } = req.params
|
||||
|
||||
const task = await InstallationTask.findByPk(id, {
|
||||
include: [
|
||||
{
|
||||
model: User,
|
||||
as: 'creator',
|
||||
attributes: ['id', 'username', 'real_name']
|
||||
},
|
||||
{
|
||||
model: User,
|
||||
as: 'updater',
|
||||
attributes: ['id', 'username', 'real_name']
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
if (!task) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: '待安装任务不存在'
|
||||
})
|
||||
}
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: '获取待安装任务详情成功',
|
||||
data: task
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('获取待安装任务详情失败:', error)
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: '获取待安装任务详情失败',
|
||||
error: error.message
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 创建待安装任务
|
||||
const createInstallationTask = async (req, res) => {
|
||||
try {
|
||||
const {
|
||||
applicationNumber,
|
||||
contractNumber,
|
||||
productName,
|
||||
customerName,
|
||||
idType,
|
||||
idNumber,
|
||||
assetType,
|
||||
equipmentToInstall,
|
||||
installationNotes,
|
||||
installerName,
|
||||
installerPhone,
|
||||
installationAddress
|
||||
} = req.body
|
||||
|
||||
// 验证必填字段
|
||||
const requiredFields = [
|
||||
'applicationNumber', 'contractNumber', 'productName',
|
||||
'customerName', 'idType', 'idNumber', 'assetType', 'equipmentToInstall'
|
||||
]
|
||||
|
||||
for (const field of requiredFields) {
|
||||
if (!req.body[field]) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: `${field} 是必填字段`
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 验证申请单号唯一性
|
||||
const existingTask = await InstallationTask.findOne({
|
||||
where: { applicationNumber }
|
||||
})
|
||||
|
||||
if (existingTask) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: '申请单号已存在'
|
||||
})
|
||||
}
|
||||
|
||||
// 验证状态枚举值
|
||||
const validStatuses = ['pending', 'in-progress', 'completed', 'failed']
|
||||
if (req.body.installationStatus && !validStatuses.includes(req.body.installationStatus)) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: '安装状态值无效'
|
||||
})
|
||||
}
|
||||
|
||||
// 验证证件类型枚举值
|
||||
const validIdTypes = ['ID_CARD', 'PASSPORT', 'OTHER']
|
||||
if (!validIdTypes.includes(idType)) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: '证件类型值无效'
|
||||
})
|
||||
}
|
||||
|
||||
const task = await InstallationTask.create({
|
||||
applicationNumber,
|
||||
contractNumber,
|
||||
productName,
|
||||
customerName,
|
||||
idType,
|
||||
idNumber,
|
||||
assetType,
|
||||
equipmentToInstall,
|
||||
installationStatus: req.body.installationStatus || 'pending',
|
||||
taskGenerationTime: req.body.taskGenerationTime || new Date(),
|
||||
completionTime: req.body.completionTime || null,
|
||||
installationNotes,
|
||||
installerName,
|
||||
installerPhone,
|
||||
installationAddress,
|
||||
createdBy: req.user.id
|
||||
})
|
||||
|
||||
// 获取创建的任务详情(包含关联数据)
|
||||
const createdTask = await InstallationTask.findByPk(task.id, {
|
||||
include: [
|
||||
{
|
||||
model: User,
|
||||
as: 'creator',
|
||||
attributes: ['id', 'username', 'real_name']
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
res.status(201).json({
|
||||
success: true,
|
||||
message: '创建待安装任务成功',
|
||||
data: createdTask
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('创建待安装任务失败:', error)
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: '创建待安装任务失败',
|
||||
error: error.message
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 更新待安装任务
|
||||
const updateInstallationTask = async (req, res) => {
|
||||
try {
|
||||
const { id } = req.params
|
||||
const updateData = req.body
|
||||
|
||||
const task = await InstallationTask.findByPk(id)
|
||||
if (!task) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: '待安装任务不存在'
|
||||
})
|
||||
}
|
||||
|
||||
// 验证状态枚举值
|
||||
if (updateData.installationStatus) {
|
||||
const validStatuses = ['pending', 'in-progress', 'completed', 'failed']
|
||||
if (!validStatuses.includes(updateData.installationStatus)) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: '安装状态值无效'
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 验证证件类型枚举值
|
||||
if (updateData.idType) {
|
||||
const validIdTypes = ['ID_CARD', 'PASSPORT', 'OTHER']
|
||||
if (!validIdTypes.includes(updateData.idType)) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: '证件类型值无效'
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 如果申请单号有变化,检查唯一性
|
||||
if (updateData.applicationNumber && updateData.applicationNumber !== task.applicationNumber) {
|
||||
const existingTask = await InstallationTask.findOne({
|
||||
where: {
|
||||
applicationNumber: updateData.applicationNumber,
|
||||
id: { [Op.ne]: id }
|
||||
}
|
||||
})
|
||||
|
||||
if (existingTask) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: '申请单号已存在'
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
await task.update({
|
||||
...updateData,
|
||||
updatedBy: req.user.id
|
||||
})
|
||||
|
||||
// 获取更新后的任务详情
|
||||
const updatedTask = await InstallationTask.findByPk(id, {
|
||||
include: [
|
||||
{
|
||||
model: User,
|
||||
as: 'creator',
|
||||
attributes: ['id', 'username', 'real_name']
|
||||
},
|
||||
{
|
||||
model: User,
|
||||
as: 'updater',
|
||||
attributes: ['id', 'username', 'real_name']
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: '更新待安装任务成功',
|
||||
data: updatedTask
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('更新待安装任务失败:', error)
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: '更新待安装任务失败',
|
||||
error: error.message
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 删除待安装任务
|
||||
const deleteInstallationTask = async (req, res) => {
|
||||
try {
|
||||
const { id } = req.params
|
||||
|
||||
const task = await InstallationTask.findByPk(id)
|
||||
if (!task) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: '待安装任务不存在'
|
||||
})
|
||||
}
|
||||
|
||||
await task.destroy()
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: '删除待安装任务成功'
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('删除待安装任务失败:', error)
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: '删除待安装任务失败',
|
||||
error: error.message
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 获取待安装任务统计信息
|
||||
const getInstallationTaskStats = async (req, res) => {
|
||||
try {
|
||||
const stats = await InstallationTask.findAll({
|
||||
attributes: [
|
||||
'installationStatus',
|
||||
[InstallationTask.sequelize.fn('COUNT', InstallationTask.sequelize.col('id')), 'count']
|
||||
],
|
||||
group: ['installationStatus'],
|
||||
raw: true
|
||||
})
|
||||
|
||||
const totalCount = await InstallationTask.count()
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: '获取待安装任务统计成功',
|
||||
data: {
|
||||
stats,
|
||||
totalCount
|
||||
}
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('获取待安装任务统计失败:', error)
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: '获取待安装任务统计失败',
|
||||
error: error.message
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 批量更新安装状态
|
||||
const batchUpdateStatus = async (req, res) => {
|
||||
try {
|
||||
const { ids, installationStatus } = req.body
|
||||
|
||||
if (!Array.isArray(ids) || ids.length === 0) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: '请选择要更新的任务'
|
||||
})
|
||||
}
|
||||
|
||||
if (!installationStatus) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: '请选择要更新的状态'
|
||||
})
|
||||
}
|
||||
|
||||
const validStatuses = ['pending', 'in-progress', 'completed', 'failed']
|
||||
if (!validStatuses.includes(installationStatus)) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: '安装状态值无效'
|
||||
})
|
||||
}
|
||||
|
||||
const updateData = {
|
||||
installationStatus,
|
||||
updatedBy: req.user.id
|
||||
}
|
||||
|
||||
// 如果状态是已完成,设置完成时间
|
||||
if (installationStatus === 'completed') {
|
||||
updateData.completionTime = new Date()
|
||||
}
|
||||
|
||||
await InstallationTask.update(updateData, {
|
||||
where: { id: { [Op.in]: ids } }
|
||||
})
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: '批量更新安装状态成功'
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('批量更新安装状态失败:', error)
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: '批量更新安装状态失败',
|
||||
error: error.message
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 批量删除待安装任务
|
||||
const batchDelete = async (req, res) => {
|
||||
try {
|
||||
const { ids } = req.body
|
||||
|
||||
if (!Array.isArray(ids) || ids.length === 0) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: '请选择要删除的任务'
|
||||
})
|
||||
}
|
||||
|
||||
await InstallationTask.destroy({
|
||||
where: { id: { [Op.in]: ids } }
|
||||
})
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: '批量删除待安装任务成功'
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('批量删除待安装任务失败:', error)
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: '批量删除待安装任务失败',
|
||||
error: error.message
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getInstallationTasks,
|
||||
getInstallationTaskById,
|
||||
createInstallationTask,
|
||||
updateInstallationTask,
|
||||
deleteInstallationTask,
|
||||
getInstallationTaskStats,
|
||||
batchUpdateStatus,
|
||||
batchDelete
|
||||
}
|
||||
468
bank-backend/controllers/loanApplicationController.js
Normal file
468
bank-backend/controllers/loanApplicationController.js
Normal file
@@ -0,0 +1,468 @@
|
||||
/**
|
||||
* 贷款申请控制器
|
||||
* @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 === 'applicationNumber') {
|
||||
where.applicationNumber = { [Op.like]: `%${searchValue}%` };
|
||||
} else if (searchField === 'customerName') {
|
||||
where[Op.or] = [
|
||||
{ borrowerName: { [Op.like]: `%${searchValue}%` } },
|
||||
{ farmerName: { [Op.like]: `%${searchValue}%` } }
|
||||
];
|
||||
} else if (searchField === 'productName') {
|
||||
where.productName = { [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,
|
||||
include: [
|
||||
{
|
||||
model: User,
|
||||
as: 'applicant',
|
||||
attributes: ['id', 'username', 'real_name']
|
||||
},
|
||||
{
|
||||
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']]
|
||||
}
|
||||
],
|
||||
order,
|
||||
offset,
|
||||
limit
|
||||
});
|
||||
|
||||
// 格式化数据
|
||||
const applications = rows.map(app => ({
|
||||
id: app.id,
|
||||
applicationNumber: app.applicationNumber,
|
||||
productName: app.productName,
|
||||
farmerName: app.farmerName,
|
||||
borrowerName: app.borrowerName,
|
||||
borrowerIdNumber: app.borrowerIdNumber,
|
||||
assetType: app.assetType,
|
||||
applicationQuantity: app.applicationQuantity,
|
||||
amount: parseFloat(app.amount),
|
||||
status: app.status,
|
||||
type: app.type,
|
||||
term: app.term,
|
||||
interestRate: parseFloat(app.interestRate),
|
||||
phone: app.phone,
|
||||
purpose: app.purpose,
|
||||
remark: app.remark,
|
||||
applicationTime: app.applicationTime,
|
||||
approvedTime: app.approvedTime,
|
||||
rejectedTime: app.rejectedTime,
|
||||
auditRecords: app.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: {
|
||||
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
|
||||
};
|
||||
466
bank-backend/controllers/loanContractController.js
Normal file
466
bank-backend/controllers/loanContractController.js
Normal file
@@ -0,0 +1,466 @@
|
||||
/**
|
||||
* 贷款合同控制器
|
||||
* @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.contractNumber = { [Op.like]: `%${searchValue}%` };
|
||||
} else if (searchField === 'applicationNumber') {
|
||||
where.applicationNumber = { [Op.like]: `%${searchValue}%` };
|
||||
} else if (searchField === 'borrowerName') {
|
||||
where.borrowerName = { [Op.like]: `%${searchValue}%` };
|
||||
} else if (searchField === 'farmerName') {
|
||||
where.farmerName = { [Op.like]: `%${searchValue}%` };
|
||||
} else if (searchField === 'productName') {
|
||||
where.productName = { [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 LoanContract.findAndCountAll({
|
||||
where,
|
||||
include: [
|
||||
{
|
||||
model: User,
|
||||
as: 'creator',
|
||||
attributes: ['id', 'username', 'real_name']
|
||||
},
|
||||
{
|
||||
model: User,
|
||||
as: 'updater',
|
||||
attributes: ['id', 'username', 'real_name']
|
||||
}
|
||||
],
|
||||
order,
|
||||
offset,
|
||||
limit
|
||||
});
|
||||
|
||||
// 格式化数据
|
||||
const contracts = rows.map(contract => ({
|
||||
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: {
|
||||
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
|
||||
};
|
||||
@@ -1,26 +1,16 @@
|
||||
/**
|
||||
* 贷款产品控制器
|
||||
* @file loanProductController.js
|
||||
* @description 处理贷款产品相关的请求
|
||||
*/
|
||||
const { LoanProduct } = require('../models');
|
||||
const { validationResult } = require('express-validator');
|
||||
const { LoanProduct, User } = require('../models');
|
||||
const { Op } = require('sequelize');
|
||||
|
||||
/**
|
||||
* 获取贷款产品列表
|
||||
* @param {Object} req 请求对象
|
||||
* @param {Object} res 响应对象
|
||||
*/
|
||||
exports.getLoanProducts = async (req, res) => {
|
||||
// 获取贷款商品列表
|
||||
const getLoanProducts = async (req, res) => {
|
||||
try {
|
||||
const {
|
||||
page = 1,
|
||||
limit = 10,
|
||||
search = '',
|
||||
status = '',
|
||||
type = '',
|
||||
sortBy = 'created_at',
|
||||
onSaleStatus,
|
||||
riskLevel,
|
||||
sortBy = 'createdAt',
|
||||
sortOrder = 'DESC'
|
||||
} = req.query;
|
||||
|
||||
@@ -30,228 +20,320 @@ exports.getLoanProducts = async (req, res) => {
|
||||
// 搜索条件
|
||||
if (search) {
|
||||
whereClause[Op.or] = [
|
||||
{ name: { [Op.like]: `%${search}%` } },
|
||||
{ code: { [Op.like]: `%${search}%` } },
|
||||
{ description: { [Op.like]: `%${search}%` } }
|
||||
{ productName: { [Op.like]: `%${search}%` } },
|
||||
{ serviceArea: { [Op.like]: `%${search}%` } },
|
||||
{ servicePhone: { [Op.like]: `%${search}%` } }
|
||||
];
|
||||
}
|
||||
|
||||
// 状态筛选
|
||||
if (status) {
|
||||
whereClause.status = status;
|
||||
// 在售状态筛选
|
||||
if (onSaleStatus !== undefined) {
|
||||
whereClause.onSaleStatus = onSaleStatus === 'true';
|
||||
}
|
||||
|
||||
// 类型筛选
|
||||
if (type) {
|
||||
whereClause.type = type;
|
||||
// 风险等级筛选
|
||||
if (riskLevel) {
|
||||
whereClause.riskLevel = riskLevel;
|
||||
}
|
||||
|
||||
const { count, rows: products } = await LoanProduct.findAndCountAll({
|
||||
const { count, rows } = await LoanProduct.findAndCountAll({
|
||||
where: whereClause,
|
||||
include: [
|
||||
{
|
||||
model: User,
|
||||
as: 'creator',
|
||||
attributes: ['id', 'username', 'real_name']
|
||||
},
|
||||
{
|
||||
model: User,
|
||||
as: 'updater',
|
||||
attributes: ['id', 'username', 'real_name']
|
||||
}
|
||||
],
|
||||
order: [[sortBy, sortOrder.toUpperCase()]],
|
||||
limit: parseInt(limit),
|
||||
offset: parseInt(offset)
|
||||
});
|
||||
|
||||
const totalPages = Math.ceil(count / limit);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: '获取贷款产品列表成功',
|
||||
message: '获取贷款商品列表成功',
|
||||
data: {
|
||||
products,
|
||||
products: rows,
|
||||
pagination: {
|
||||
current: parseInt(page),
|
||||
pageSize: parseInt(limit),
|
||||
total: count,
|
||||
pages: Math.ceil(count / limit)
|
||||
totalPages,
|
||||
hasNextPage: page < totalPages,
|
||||
hasPrevPage: page > 1
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('获取贷款产品列表错误:', error);
|
||||
console.error('获取贷款商品列表失败:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: '服务器内部错误',
|
||||
message: '获取贷款商品列表失败',
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 创建贷款产品
|
||||
* @param {Object} req 请求对象
|
||||
* @param {Object} res 响应对象
|
||||
*/
|
||||
exports.createLoanProduct = async (req, res) => {
|
||||
// 根据ID获取贷款商品详情
|
||||
const getLoanProductById = async (req, res) => {
|
||||
try {
|
||||
const errors = validationResult(req);
|
||||
if (!errors.isEmpty()) {
|
||||
return res.status(400).json({
|
||||
const { id } = req.params;
|
||||
|
||||
const product = await LoanProduct.findByPk(id, {
|
||||
include: [
|
||||
{
|
||||
model: User,
|
||||
as: 'creator',
|
||||
attributes: ['id', 'username', 'real_name']
|
||||
},
|
||||
{
|
||||
model: User,
|
||||
as: 'updater',
|
||||
attributes: ['id', 'username', 'real_name']
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
if (!product) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: '输入数据验证失败',
|
||||
errors: errors.array()
|
||||
message: '贷款商品不存在'
|
||||
});
|
||||
}
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: '获取贷款商品详情成功',
|
||||
data: product
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('获取贷款商品详情失败:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: '获取贷款商品详情失败',
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// 创建贷款商品
|
||||
const createLoanProduct = async (req, res) => {
|
||||
try {
|
||||
const {
|
||||
name,
|
||||
code,
|
||||
type,
|
||||
description,
|
||||
min_amount,
|
||||
max_amount,
|
||||
interest_rate,
|
||||
term_min,
|
||||
term_max,
|
||||
requirements,
|
||||
status = 'draft'
|
||||
productName,
|
||||
loanAmount,
|
||||
loanTerm,
|
||||
interestRate,
|
||||
serviceArea,
|
||||
servicePhone,
|
||||
productDescription,
|
||||
applicationRequirements,
|
||||
requiredDocuments,
|
||||
approvalProcess,
|
||||
riskLevel = 'MEDIUM',
|
||||
minLoanAmount,
|
||||
maxLoanAmount
|
||||
} = req.body;
|
||||
|
||||
// 检查产品代码是否已存在
|
||||
// 验证必填字段
|
||||
if (!productName || !loanAmount || !loanTerm || !interestRate || !serviceArea || !servicePhone) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: '请填写所有必填字段'
|
||||
});
|
||||
}
|
||||
|
||||
// 验证产品名称唯一性
|
||||
const existingProduct = await LoanProduct.findOne({
|
||||
where: { code }
|
||||
where: { productName }
|
||||
});
|
||||
|
||||
if (existingProduct) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: '产品代码已存在'
|
||||
message: '贷款产品名称已存在'
|
||||
});
|
||||
}
|
||||
|
||||
// 验证数值字段
|
||||
if (loanTerm <= 0) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: '贷款周期必须大于0'
|
||||
});
|
||||
}
|
||||
|
||||
if (interestRate < 0 || interestRate > 100) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: '贷款利率必须在0-100之间'
|
||||
});
|
||||
}
|
||||
|
||||
if (minLoanAmount && maxLoanAmount && minLoanAmount > maxLoanAmount) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: '最小贷款金额不能大于最大贷款金额'
|
||||
});
|
||||
}
|
||||
|
||||
const product = await LoanProduct.create({
|
||||
name,
|
||||
code,
|
||||
type,
|
||||
description,
|
||||
min_amount: min_amount * 100, // 转换为分
|
||||
max_amount: max_amount * 100,
|
||||
interest_rate,
|
||||
term_min,
|
||||
term_max,
|
||||
requirements,
|
||||
status
|
||||
productName,
|
||||
loanAmount,
|
||||
loanTerm: parseInt(loanTerm),
|
||||
interestRate: parseFloat(interestRate),
|
||||
serviceArea,
|
||||
servicePhone,
|
||||
productDescription,
|
||||
applicationRequirements,
|
||||
requiredDocuments,
|
||||
approvalProcess,
|
||||
riskLevel,
|
||||
minLoanAmount: minLoanAmount ? parseFloat(minLoanAmount) : null,
|
||||
maxLoanAmount: maxLoanAmount ? parseFloat(maxLoanAmount) : null,
|
||||
createdBy: req.user.id,
|
||||
updatedBy: req.user.id
|
||||
});
|
||||
|
||||
// 获取创建后的完整信息
|
||||
const createdProduct = await LoanProduct.findByPk(product.id, {
|
||||
include: [
|
||||
{
|
||||
model: User,
|
||||
as: 'creator',
|
||||
attributes: ['id', 'username', 'real_name']
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
res.status(201).json({
|
||||
success: true,
|
||||
message: '创建贷款产品成功',
|
||||
data: product
|
||||
message: '创建贷款商品成功',
|
||||
data: createdProduct
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('创建贷款产品错误:', error);
|
||||
console.error('创建贷款商品失败:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: '服务器内部错误',
|
||||
message: '创建贷款商品失败',
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取贷款产品详情
|
||||
* @param {Object} req 请求对象
|
||||
* @param {Object} res 响应对象
|
||||
*/
|
||||
exports.getLoanProductById = async (req, res) => {
|
||||
// 更新贷款商品
|
||||
const updateLoanProduct = async (req, res) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
|
||||
const product = await LoanProduct.findByPk(id);
|
||||
|
||||
if (!product) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: '贷款产品不存在'
|
||||
});
|
||||
}
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: '获取贷款产品详情成功',
|
||||
data: product
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('获取贷款产品详情错误:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: '服务器内部错误',
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 更新贷款产品
|
||||
* @param {Object} req 请求对象
|
||||
* @param {Object} res 响应对象
|
||||
*/
|
||||
exports.updateLoanProduct = 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;
|
||||
|
||||
// 如果更新金额,转换为分
|
||||
if (updateData.min_amount) {
|
||||
updateData.min_amount = updateData.min_amount * 100;
|
||||
}
|
||||
if (updateData.max_amount) {
|
||||
updateData.max_amount = updateData.max_amount * 100;
|
||||
}
|
||||
|
||||
const product = await LoanProduct.findByPk(id);
|
||||
|
||||
if (!product) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: '贷款产品不存在'
|
||||
message: '贷款商品不存在'
|
||||
});
|
||||
}
|
||||
|
||||
await product.update(updateData);
|
||||
// 如果更新产品名称,检查唯一性
|
||||
if (updateData.productName && updateData.productName !== product.productName) {
|
||||
const existingProduct = await LoanProduct.findOne({
|
||||
where: {
|
||||
productName: updateData.productName,
|
||||
id: { [Op.ne]: id }
|
||||
}
|
||||
});
|
||||
|
||||
if (existingProduct) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: '贷款产品名称已存在'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 验证数值字段
|
||||
if (updateData.loanTerm && updateData.loanTerm <= 0) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: '贷款周期必须大于0'
|
||||
});
|
||||
}
|
||||
|
||||
if (updateData.interestRate && (updateData.interestRate < 0 || updateData.interestRate > 100)) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: '贷款利率必须在0-100之间'
|
||||
});
|
||||
}
|
||||
|
||||
if (updateData.minLoanAmount && updateData.maxLoanAmount &&
|
||||
updateData.minLoanAmount > updateData.maxLoanAmount) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: '最小贷款金额不能大于最大贷款金额'
|
||||
});
|
||||
}
|
||||
|
||||
// 更新数据
|
||||
Object.keys(updateData).forEach(key => {
|
||||
if (updateData[key] !== undefined) {
|
||||
product[key] = updateData[key];
|
||||
}
|
||||
});
|
||||
|
||||
product.updatedBy = req.user.id;
|
||||
await product.save();
|
||||
|
||||
// 获取更新后的完整信息
|
||||
const updatedProduct = await LoanProduct.findByPk(id, {
|
||||
include: [
|
||||
{
|
||||
model: User,
|
||||
as: 'creator',
|
||||
attributes: ['id', 'username', 'real_name']
|
||||
},
|
||||
{
|
||||
model: User,
|
||||
as: 'updater',
|
||||
attributes: ['id', 'username', 'real_name']
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: '更新贷款产品成功',
|
||||
data: product
|
||||
message: '更新贷款商品成功',
|
||||
data: updatedProduct
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('更新贷款产品错误:', error);
|
||||
console.error('更新贷款商品失败:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: '服务器内部错误',
|
||||
message: '更新贷款商品失败',
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 删除贷款产品
|
||||
* @param {Object} req 请求对象
|
||||
* @param {Object} res 响应对象
|
||||
*/
|
||||
exports.deleteLoanProduct = async (req, res) => {
|
||||
// 删除贷款商品
|
||||
const deleteLoanProduct = async (req, res) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
|
||||
const product = await LoanProduct.findByPk(id);
|
||||
|
||||
if (!product) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: '贷款产品不存在'
|
||||
message: '贷款商品不存在'
|
||||
});
|
||||
}
|
||||
|
||||
@@ -259,105 +341,140 @@ exports.deleteLoanProduct = async (req, res) => {
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: '删除贷款产品成功'
|
||||
message: '删除贷款商品成功'
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('删除贷款产品错误:', error);
|
||||
console.error('删除贷款商品失败:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: '服务器内部错误',
|
||||
message: '删除贷款商品失败',
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 更新贷款产品状态
|
||||
* @param {Object} req 请求对象
|
||||
* @param {Object} res 响应对象
|
||||
*/
|
||||
exports.updateLoanProductStatus = async (req, res) => {
|
||||
// 获取贷款商品统计信息
|
||||
const getLoanProductStats = async (req, res) => {
|
||||
try {
|
||||
const errors = validationResult(req);
|
||||
if (!errors.isEmpty()) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: '输入数据验证失败',
|
||||
errors: errors.array()
|
||||
});
|
||||
}
|
||||
const totalProducts = await LoanProduct.count();
|
||||
const onSaleProducts = await LoanProduct.count({ where: { onSaleStatus: true } });
|
||||
const offSaleProducts = await LoanProduct.count({ where: { onSaleStatus: false } });
|
||||
|
||||
const riskLevelStats = await LoanProduct.findAll({
|
||||
attributes: [
|
||||
'riskLevel',
|
||||
[LoanProduct.sequelize.fn('COUNT', LoanProduct.sequelize.col('id')), 'count']
|
||||
],
|
||||
group: ['riskLevel']
|
||||
});
|
||||
|
||||
const { id } = req.params;
|
||||
const { status } = req.body;
|
||||
|
||||
const product = await LoanProduct.findByPk(id);
|
||||
|
||||
if (!product) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: '贷款产品不存在'
|
||||
});
|
||||
}
|
||||
|
||||
await product.update({ status });
|
||||
const totalCustomers = await LoanProduct.sum('totalCustomers');
|
||||
const supervisionCustomers = await LoanProduct.sum('supervisionCustomers');
|
||||
const completedCustomers = await LoanProduct.sum('completedCustomers');
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: '更新贷款产品状态成功',
|
||||
data: product
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('更新贷款产品状态错误:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: '服务器内部错误',
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取贷款产品统计
|
||||
* @param {Object} req 请求对象
|
||||
* @param {Object} res 响应对象
|
||||
*/
|
||||
exports.getLoanProductStats = async (req, res) => {
|
||||
try {
|
||||
const stats = await LoanProduct.findAll({
|
||||
attributes: [
|
||||
'status',
|
||||
[LoanProduct.sequelize.fn('COUNT', LoanProduct.sequelize.col('id')), 'count']
|
||||
],
|
||||
group: ['status'],
|
||||
raw: true
|
||||
});
|
||||
|
||||
const typeStats = await LoanProduct.findAll({
|
||||
attributes: [
|
||||
'type',
|
||||
[LoanProduct.sequelize.fn('COUNT', LoanProduct.sequelize.col('id')), 'count']
|
||||
],
|
||||
group: ['type'],
|
||||
raw: true
|
||||
});
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: '获取贷款产品统计成功',
|
||||
message: '获取贷款商品统计成功',
|
||||
data: {
|
||||
statusStats: stats,
|
||||
typeStats: typeStats
|
||||
totalProducts,
|
||||
onSaleProducts,
|
||||
offSaleProducts,
|
||||
riskLevelStats,
|
||||
totalCustomers: totalCustomers || 0,
|
||||
supervisionCustomers: supervisionCustomers || 0,
|
||||
completedCustomers: completedCustomers || 0
|
||||
}
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('获取贷款产品统计错误:', error);
|
||||
console.error('获取贷款商品统计失败:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: '服务器内部错误',
|
||||
message: '获取贷款商品统计失败',
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// 批量更新在售状态
|
||||
const batchUpdateStatus = async (req, res) => {
|
||||
try {
|
||||
const { ids, onSaleStatus } = req.body;
|
||||
|
||||
if (!Array.isArray(ids) || ids.length === 0) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: '请选择要更新的贷款商品'
|
||||
});
|
||||
}
|
||||
|
||||
if (typeof onSaleStatus !== 'boolean') {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: '在售状态参数无效'
|
||||
});
|
||||
}
|
||||
|
||||
await LoanProduct.update(
|
||||
{
|
||||
onSaleStatus,
|
||||
updatedBy: req.user.id
|
||||
},
|
||||
{
|
||||
where: { id: { [Op.in]: ids } }
|
||||
}
|
||||
);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: `批量${onSaleStatus ? '启用' : '停用'}贷款商品成功`
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('批量更新状态失败:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: '批量更新状态失败',
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// 批量删除贷款商品
|
||||
const batchDelete = async (req, res) => {
|
||||
try {
|
||||
const { ids } = req.body;
|
||||
|
||||
if (!Array.isArray(ids) || ids.length === 0) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: '请选择要删除的贷款商品'
|
||||
});
|
||||
}
|
||||
|
||||
await LoanProduct.destroy({
|
||||
where: { id: { [Op.in]: ids } }
|
||||
});
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: '批量删除贷款商品成功'
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('批量删除失败:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: '批量删除失败',
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
getLoanProducts,
|
||||
getLoanProductById,
|
||||
createLoanProduct,
|
||||
updateLoanProduct,
|
||||
deleteLoanProduct,
|
||||
getLoanProductStats,
|
||||
batchUpdateStatus,
|
||||
batchDelete
|
||||
};
|
||||
Reference in New Issue
Block a user