添加银行政府后端接口
This commit is contained in:
@@ -101,9 +101,27 @@ const CompletedSupervision = sequelize.define('CompletedSupervision', {
|
||||
}, {
|
||||
tableName: 'completed_supervisions',
|
||||
timestamps: true,
|
||||
underscored: false,
|
||||
createdAt: 'createdAt',
|
||||
updatedAt: 'updatedAt',
|
||||
comment: '监管任务已结项表'
|
||||
});
|
||||
|
||||
// 定义关联关系
|
||||
CompletedSupervision.associate = (models) => {
|
||||
// 监管任务已结项与用户关联(创建人)
|
||||
CompletedSupervision.belongsTo(models.User, {
|
||||
foreignKey: { name: 'createdBy', field: 'createdBy' },
|
||||
targetKey: 'id',
|
||||
as: 'creator'
|
||||
});
|
||||
|
||||
// 监管任务已结项与用户关联(更新人)
|
||||
CompletedSupervision.belongsTo(models.User, {
|
||||
foreignKey: { name: 'updatedBy', field: 'updatedBy' },
|
||||
targetKey: 'id',
|
||||
as: 'updater'
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = CompletedSupervision;
|
||||
|
||||
@@ -1,82 +1,141 @@
|
||||
/**
|
||||
* 员工模型
|
||||
* @file Employee.js
|
||||
* @description 员工数据模型
|
||||
*/
|
||||
const { DataTypes } = require('sequelize');
|
||||
const { sequelize } = require('../config/database');
|
||||
const bcrypt = require('bcryptjs');
|
||||
|
||||
const Employee = sequelize.define('Employee', {
|
||||
id: {
|
||||
type: DataTypes.INTEGER,
|
||||
primaryKey: true,
|
||||
autoIncrement: true
|
||||
},
|
||||
name: {
|
||||
type: DataTypes.STRING(50),
|
||||
allowNull: false,
|
||||
comment: '员工姓名'
|
||||
},
|
||||
employee_id: {
|
||||
type: DataTypes.STRING(20),
|
||||
allowNull: false,
|
||||
unique: true,
|
||||
comment: '员工编号'
|
||||
},
|
||||
department_id: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: false,
|
||||
comment: '部门ID'
|
||||
},
|
||||
position_id: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: false,
|
||||
comment: '职位ID'
|
||||
},
|
||||
phone: {
|
||||
type: DataTypes.STRING(20),
|
||||
allowNull: true,
|
||||
comment: '联系电话'
|
||||
},
|
||||
email: {
|
||||
type: DataTypes.STRING(100),
|
||||
allowNull: true,
|
||||
comment: '邮箱地址'
|
||||
},
|
||||
hire_date: {
|
||||
type: DataTypes.DATEONLY,
|
||||
allowNull: false,
|
||||
comment: '入职日期'
|
||||
},
|
||||
salary: {
|
||||
type: DataTypes.BIGINT,
|
||||
allowNull: false,
|
||||
defaultValue: 0,
|
||||
comment: '薪资(分)'
|
||||
},
|
||||
status: {
|
||||
type: DataTypes.ENUM('active', 'inactive', 'resigned'),
|
||||
allowNull: false,
|
||||
defaultValue: 'active',
|
||||
comment: '员工状态:在职、离职、已辞职'
|
||||
},
|
||||
created_at: {
|
||||
type: DataTypes.DATE,
|
||||
allowNull: false,
|
||||
defaultValue: DataTypes.NOW
|
||||
},
|
||||
updated_at: {
|
||||
type: DataTypes.DATE,
|
||||
allowNull: false,
|
||||
defaultValue: DataTypes.NOW
|
||||
}
|
||||
}, {
|
||||
sequelize,
|
||||
tableName: 'bank_employees',
|
||||
modelName: 'Employee',
|
||||
timestamps: true,
|
||||
createdAt: 'created_at',
|
||||
updatedAt: 'updated_at'
|
||||
});
|
||||
module.exports = (sequelize) => {
|
||||
const Employee = sequelize.define('Employee', {
|
||||
id: {
|
||||
type: DataTypes.INTEGER,
|
||||
primaryKey: true,
|
||||
autoIncrement: true
|
||||
},
|
||||
employeeNumber: {
|
||||
type: DataTypes.STRING(20),
|
||||
allowNull: false,
|
||||
unique: true,
|
||||
comment: '员工编号'
|
||||
},
|
||||
name: {
|
||||
type: DataTypes.STRING(50),
|
||||
allowNull: false,
|
||||
comment: '员工姓名'
|
||||
},
|
||||
phone: {
|
||||
type: DataTypes.STRING(20),
|
||||
allowNull: false,
|
||||
comment: '联系电话'
|
||||
},
|
||||
email: {
|
||||
type: DataTypes.STRING(100),
|
||||
allowNull: true,
|
||||
comment: '邮箱'
|
||||
},
|
||||
password: {
|
||||
type: DataTypes.STRING(255),
|
||||
allowNull: false,
|
||||
comment: '密码'
|
||||
},
|
||||
isLoanSpecialist: {
|
||||
type: DataTypes.BOOLEAN,
|
||||
defaultValue: false,
|
||||
comment: '是否为贷款专员'
|
||||
},
|
||||
department: {
|
||||
type: DataTypes.STRING(50),
|
||||
allowNull: true,
|
||||
comment: '部门'
|
||||
},
|
||||
position: {
|
||||
type: DataTypes.STRING(50),
|
||||
allowNull: true,
|
||||
comment: '职位'
|
||||
},
|
||||
status: {
|
||||
type: DataTypes.ENUM('active', 'inactive', 'locked'),
|
||||
defaultValue: 'active',
|
||||
comment: '账号状态'
|
||||
},
|
||||
lastLogin: {
|
||||
type: DataTypes.DATE,
|
||||
allowNull: true,
|
||||
comment: '最后登录时间'
|
||||
},
|
||||
loginAttempts: {
|
||||
type: DataTypes.INTEGER,
|
||||
defaultValue: 0,
|
||||
comment: '登录尝试次数'
|
||||
},
|
||||
lockedUntil: {
|
||||
type: DataTypes.DATE,
|
||||
allowNull: true,
|
||||
comment: '锁定到期时间'
|
||||
},
|
||||
createdBy: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: true,
|
||||
comment: '创建人ID'
|
||||
},
|
||||
updatedBy: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: true,
|
||||
comment: '更新人ID'
|
||||
}
|
||||
}, {
|
||||
tableName: 'bank_employees',
|
||||
timestamps: true,
|
||||
paranoid: true,
|
||||
comment: '银行员工表',
|
||||
hooks: {
|
||||
beforeSave: async (employee) => {
|
||||
if (employee.changed('password')) {
|
||||
const salt = await bcrypt.genSalt(10);
|
||||
employee.password = await bcrypt.hash(employee.password, salt);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = Employee;
|
||||
// 实例方法:验证密码
|
||||
Employee.prototype.validPassword = async function(password) {
|
||||
try {
|
||||
return await bcrypt.compare(password, this.password);
|
||||
} catch (error) {
|
||||
console.error('密码验证错误:', error);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
// 实例方法:检查账号是否被锁定
|
||||
Employee.prototype.isLocked = function() {
|
||||
return !!(this.lockedUntil && this.lockedUntil > Date.now());
|
||||
};
|
||||
|
||||
// 实例方法:增加登录尝试次数
|
||||
Employee.prototype.incLoginAttempts = async function() {
|
||||
// 如果已经锁定且锁定时间已过,则重置
|
||||
if (this.lockedUntil && this.lockedUntil < Date.now()) {
|
||||
return this.update({
|
||||
loginAttempts: 1,
|
||||
lockedUntil: null
|
||||
});
|
||||
}
|
||||
|
||||
const updates = { loginAttempts: this.loginAttempts + 1 };
|
||||
|
||||
// 如果达到最大尝试次数,则锁定账号
|
||||
if (this.loginAttempts + 1 >= 5 && !this.isLocked()) {
|
||||
updates.lockedUntil = new Date(Date.now() + 30 * 60 * 1000); // 锁定30分钟
|
||||
}
|
||||
|
||||
return this.update(updates);
|
||||
};
|
||||
|
||||
// 实例方法:重置登录尝试次数
|
||||
Employee.prototype.resetLoginAttempts = async function() {
|
||||
return this.update({
|
||||
loginAttempts: 0,
|
||||
lockedUntil: null
|
||||
});
|
||||
};
|
||||
|
||||
return Employee;
|
||||
};
|
||||
@@ -99,9 +99,27 @@ const InstallationTask = sequelize.define('InstallationTask', {
|
||||
}, {
|
||||
tableName: 'installation_tasks',
|
||||
timestamps: true,
|
||||
underscored: false,
|
||||
createdAt: 'createdAt',
|
||||
updatedAt: 'updatedAt',
|
||||
comment: '待安装任务表'
|
||||
});
|
||||
|
||||
// 定义关联关系
|
||||
InstallationTask.associate = (models) => {
|
||||
// 待安装任务与用户关联(创建人)
|
||||
InstallationTask.belongsTo(models.User, {
|
||||
foreignKey: { name: 'createdBy', field: 'createdBy' },
|
||||
targetKey: 'id',
|
||||
as: 'creator'
|
||||
});
|
||||
|
||||
// 待安装任务与用户关联(更新人)
|
||||
InstallationTask.belongsTo(models.User, {
|
||||
foreignKey: { name: 'updatedBy', field: 'updatedBy' },
|
||||
targetKey: 'id',
|
||||
as: 'updater'
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = InstallationTask;
|
||||
@@ -13,34 +13,20 @@ class LoanApplication extends BaseModel {
|
||||
*/
|
||||
getStatusText() {
|
||||
const statusMap = {
|
||||
pending_review: '待初审',
|
||||
verification_pending: '核验待放款',
|
||||
pending_binding: '待绑定',
|
||||
pending: '待审核',
|
||||
approved: '已通过',
|
||||
rejected: '已拒绝'
|
||||
rejected: '已拒绝',
|
||||
completed: '已完成'
|
||||
};
|
||||
return statusMap[this.status] || this.status;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取申请类型文本
|
||||
* @returns {String} 类型文本
|
||||
*/
|
||||
getTypeText() {
|
||||
const typeMap = {
|
||||
personal: '个人贷款',
|
||||
business: '企业贷款',
|
||||
mortgage: '抵押贷款'
|
||||
};
|
||||
return typeMap[this.type] || this.type;
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化申请金额
|
||||
* @returns {String} 格式化后的金额
|
||||
*/
|
||||
getFormattedAmount() {
|
||||
return `${this.amount.toFixed(2)}元`;
|
||||
return `${this.loan_amount.toFixed(2)}元`;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,144 +37,55 @@ LoanApplication.init({
|
||||
primaryKey: true,
|
||||
autoIncrement: true
|
||||
},
|
||||
applicationNumber: {
|
||||
type: DataTypes.STRING(50),
|
||||
allowNull: false,
|
||||
unique: true,
|
||||
comment: '申请单号'
|
||||
},
|
||||
productName: {
|
||||
type: DataTypes.STRING(200),
|
||||
allowNull: false,
|
||||
comment: '贷款产品名称'
|
||||
},
|
||||
farmerName: {
|
||||
customer_name: {
|
||||
type: DataTypes.STRING(100),
|
||||
allowNull: false,
|
||||
comment: '申请养殖户姓名'
|
||||
comment: '客户姓名'
|
||||
},
|
||||
borrowerName: {
|
||||
type: DataTypes.STRING(100),
|
||||
allowNull: false,
|
||||
comment: '贷款人姓名'
|
||||
},
|
||||
borrowerIdNumber: {
|
||||
customer_phone: {
|
||||
type: DataTypes.STRING(20),
|
||||
allowNull: false,
|
||||
comment: '贷款人身份证号'
|
||||
comment: '客户电话'
|
||||
},
|
||||
assetType: {
|
||||
type: DataTypes.STRING(50),
|
||||
customer_id_card: {
|
||||
type: DataTypes.STRING(18),
|
||||
allowNull: false,
|
||||
comment: '生资种类'
|
||||
comment: '客户身份证号'
|
||||
},
|
||||
applicationQuantity: {
|
||||
type: DataTypes.STRING(100),
|
||||
allowNull: false,
|
||||
comment: '申请数量'
|
||||
},
|
||||
amount: {
|
||||
loan_amount: {
|
||||
type: DataTypes.DECIMAL(15, 2),
|
||||
allowNull: false,
|
||||
comment: '申请额度'
|
||||
comment: '贷款金额'
|
||||
},
|
||||
status: {
|
||||
type: DataTypes.ENUM(
|
||||
'pending_review',
|
||||
'verification_pending',
|
||||
'pending_binding',
|
||||
'approved',
|
||||
'rejected'
|
||||
),
|
||||
allowNull: false,
|
||||
defaultValue: 'pending_review',
|
||||
comment: '申请状态'
|
||||
},
|
||||
type: {
|
||||
type: DataTypes.ENUM('personal', 'business', 'mortgage'),
|
||||
allowNull: false,
|
||||
defaultValue: 'personal',
|
||||
comment: '申请类型'
|
||||
},
|
||||
term: {
|
||||
loan_term: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: false,
|
||||
comment: '申请期限(月)'
|
||||
comment: '贷款期限(月)'
|
||||
},
|
||||
interestRate: {
|
||||
interest_rate: {
|
||||
type: DataTypes.DECIMAL(5, 2),
|
||||
allowNull: false,
|
||||
comment: '预计利率'
|
||||
comment: '贷款利率(%)'
|
||||
},
|
||||
phone: {
|
||||
type: DataTypes.STRING(20),
|
||||
application_date: {
|
||||
type: DataTypes.DATEONLY,
|
||||
allowNull: false,
|
||||
comment: '联系电话'
|
||||
comment: '申请日期'
|
||||
},
|
||||
purpose: {
|
||||
type: DataTypes.TEXT,
|
||||
status: {
|
||||
type: DataTypes.ENUM('pending', 'approved', 'rejected', 'completed'),
|
||||
allowNull: true,
|
||||
comment: '申请用途'
|
||||
},
|
||||
remark: {
|
||||
type: DataTypes.TEXT,
|
||||
allowNull: true,
|
||||
comment: '备注'
|
||||
},
|
||||
applicationTime: {
|
||||
type: DataTypes.DATE,
|
||||
allowNull: false,
|
||||
defaultValue: DataTypes.NOW,
|
||||
comment: '申请时间'
|
||||
},
|
||||
approvedTime: {
|
||||
type: DataTypes.DATE,
|
||||
allowNull: true,
|
||||
comment: '审批通过时间'
|
||||
},
|
||||
rejectedTime: {
|
||||
type: DataTypes.DATE,
|
||||
allowNull: true,
|
||||
comment: '审批拒绝时间'
|
||||
},
|
||||
approvedBy: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: true,
|
||||
comment: '审批人ID'
|
||||
},
|
||||
rejectedBy: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: true,
|
||||
comment: '拒绝人ID'
|
||||
},
|
||||
rejectionReason: {
|
||||
type: DataTypes.TEXT,
|
||||
allowNull: true,
|
||||
comment: '拒绝原因'
|
||||
defaultValue: 'pending',
|
||||
comment: '申请状态'
|
||||
}
|
||||
}, {
|
||||
sequelize: require('../config/database').sequelize,
|
||||
modelName: 'LoanApplication',
|
||||
tableName: 'bank_loan_applications',
|
||||
tableName: 'loan_applications',
|
||||
timestamps: true,
|
||||
createdAt: 'createdAt',
|
||||
updatedAt: 'updatedAt',
|
||||
hooks: {
|
||||
beforeCreate: (application) => {
|
||||
// 生成申请单号
|
||||
if (!application.applicationNumber) {
|
||||
const now = new Date();
|
||||
const timestamp = now.getFullYear().toString() +
|
||||
(now.getMonth() + 1).toString().padStart(2, '0') +
|
||||
now.getDate().toString().padStart(2, '0') +
|
||||
now.getHours().toString().padStart(2, '0') +
|
||||
now.getMinutes().toString().padStart(2, '0') +
|
||||
now.getSeconds().toString().padStart(2, '0');
|
||||
const random = Math.floor(Math.random() * 1000).toString().padStart(3, '0');
|
||||
application.applicationNumber = timestamp + random;
|
||||
}
|
||||
}
|
||||
}
|
||||
underscored: true,
|
||||
createdAt: 'created_at',
|
||||
updatedAt: 'updated_at'
|
||||
});
|
||||
|
||||
module.exports = LoanApplication;
|
||||
|
||||
@@ -14,7 +14,6 @@ class LoanContract extends BaseModel {
|
||||
getStatusText() {
|
||||
const statusMap = {
|
||||
active: '已放款',
|
||||
pending: '待放款',
|
||||
completed: '已完成',
|
||||
defaulted: '违约',
|
||||
cancelled: '已取消'
|
||||
@@ -22,43 +21,12 @@ class LoanContract extends BaseModel {
|
||||
return statusMap[this.status] || this.status;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取合同类型文本
|
||||
* @returns {String} 类型文本
|
||||
*/
|
||||
getTypeText() {
|
||||
const typeMap = {
|
||||
livestock_collateral: '畜禽活体抵押',
|
||||
farmer_loan: '惠农贷',
|
||||
business_loan: '商业贷款',
|
||||
personal_loan: '个人贷款'
|
||||
};
|
||||
return typeMap[this.type] || this.type;
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化合同金额
|
||||
* @returns {String} 格式化后的金额
|
||||
*/
|
||||
getFormattedAmount() {
|
||||
return `${this.amount.toFixed(2)}元`;
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算剩余还款金额
|
||||
* @returns {Number} 剩余金额
|
||||
*/
|
||||
getRemainingAmount() {
|
||||
return this.amount - (this.paidAmount || 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算还款进度百分比
|
||||
* @returns {Number} 进度百分比
|
||||
*/
|
||||
getRepaymentProgress() {
|
||||
if (this.amount <= 0) return 0;
|
||||
return Math.round(((this.paidAmount || 0) / this.amount) * 100);
|
||||
return `${this.loan_amount.toFixed(2)}元`;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,167 +37,63 @@ LoanContract.init({
|
||||
primaryKey: true,
|
||||
autoIncrement: true
|
||||
},
|
||||
contractNumber: {
|
||||
contract_number: {
|
||||
type: DataTypes.STRING(50),
|
||||
allowNull: false,
|
||||
unique: true,
|
||||
comment: '合同编号'
|
||||
},
|
||||
applicationNumber: {
|
||||
type: DataTypes.STRING(50),
|
||||
allowNull: false,
|
||||
comment: '申请单号'
|
||||
},
|
||||
productName: {
|
||||
type: DataTypes.STRING(200),
|
||||
allowNull: false,
|
||||
comment: '贷款产品名称'
|
||||
},
|
||||
farmerName: {
|
||||
customer_name: {
|
||||
type: DataTypes.STRING(100),
|
||||
allowNull: false,
|
||||
comment: '申请养殖户姓名'
|
||||
comment: '客户姓名'
|
||||
},
|
||||
borrowerName: {
|
||||
type: DataTypes.STRING(100),
|
||||
allowNull: false,
|
||||
comment: '贷款人姓名'
|
||||
},
|
||||
borrowerIdNumber: {
|
||||
customer_phone: {
|
||||
type: DataTypes.STRING(20),
|
||||
allowNull: false,
|
||||
comment: '贷款人身份证号'
|
||||
comment: '客户电话'
|
||||
},
|
||||
assetType: {
|
||||
type: DataTypes.STRING(50),
|
||||
customer_id_card: {
|
||||
type: DataTypes.STRING(18),
|
||||
allowNull: false,
|
||||
comment: '生资种类'
|
||||
comment: '客户身份证号'
|
||||
},
|
||||
applicationQuantity: {
|
||||
type: DataTypes.STRING(100),
|
||||
allowNull: false,
|
||||
comment: '申请数量'
|
||||
},
|
||||
amount: {
|
||||
loan_amount: {
|
||||
type: DataTypes.DECIMAL(15, 2),
|
||||
allowNull: false,
|
||||
comment: '合同金额'
|
||||
comment: '贷款金额'
|
||||
},
|
||||
paidAmount: {
|
||||
type: DataTypes.DECIMAL(15, 2),
|
||||
allowNull: false,
|
||||
defaultValue: 0,
|
||||
comment: '已还款金额'
|
||||
},
|
||||
status: {
|
||||
type: DataTypes.ENUM(
|
||||
'active',
|
||||
'pending',
|
||||
'completed',
|
||||
'defaulted',
|
||||
'cancelled'
|
||||
),
|
||||
allowNull: false,
|
||||
defaultValue: 'pending',
|
||||
comment: '合同状态'
|
||||
},
|
||||
type: {
|
||||
type: DataTypes.ENUM(
|
||||
'livestock_collateral',
|
||||
'farmer_loan',
|
||||
'business_loan',
|
||||
'personal_loan'
|
||||
),
|
||||
allowNull: false,
|
||||
comment: '合同类型'
|
||||
},
|
||||
term: {
|
||||
loan_term: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: false,
|
||||
comment: '合同期限(月)'
|
||||
comment: '贷款期限(月)'
|
||||
},
|
||||
interestRate: {
|
||||
interest_rate: {
|
||||
type: DataTypes.DECIMAL(5, 2),
|
||||
allowNull: false,
|
||||
comment: '利率'
|
||||
comment: '贷款利率(%)'
|
||||
},
|
||||
phone: {
|
||||
type: DataTypes.STRING(20),
|
||||
contract_date: {
|
||||
type: DataTypes.DATEONLY,
|
||||
allowNull: false,
|
||||
comment: '联系电话'
|
||||
comment: '合同签订日期'
|
||||
},
|
||||
purpose: {
|
||||
type: DataTypes.TEXT,
|
||||
status: {
|
||||
type: DataTypes.ENUM('active', 'completed', 'defaulted', 'cancelled'),
|
||||
allowNull: true,
|
||||
comment: '贷款用途'
|
||||
},
|
||||
remark: {
|
||||
type: DataTypes.TEXT,
|
||||
allowNull: true,
|
||||
comment: '备注'
|
||||
},
|
||||
contractTime: {
|
||||
type: DataTypes.DATE,
|
||||
allowNull: false,
|
||||
defaultValue: DataTypes.NOW,
|
||||
comment: '合同签订时间'
|
||||
},
|
||||
disbursementTime: {
|
||||
type: DataTypes.DATE,
|
||||
allowNull: true,
|
||||
comment: '放款时间'
|
||||
},
|
||||
maturityTime: {
|
||||
type: DataTypes.DATE,
|
||||
allowNull: true,
|
||||
comment: '到期时间'
|
||||
},
|
||||
completedTime: {
|
||||
type: DataTypes.DATE,
|
||||
allowNull: true,
|
||||
comment: '完成时间'
|
||||
},
|
||||
createdBy: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: true,
|
||||
comment: '创建人ID',
|
||||
references: {
|
||||
model: 'bank_users',
|
||||
key: 'id'
|
||||
}
|
||||
},
|
||||
updatedBy: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: true,
|
||||
comment: '更新人ID',
|
||||
references: {
|
||||
model: 'bank_users',
|
||||
key: 'id'
|
||||
}
|
||||
defaultValue: 'active',
|
||||
comment: '合同状态'
|
||||
}
|
||||
}, {
|
||||
sequelize: require('../config/database').sequelize,
|
||||
modelName: 'LoanContract',
|
||||
tableName: 'bank_loan_contracts',
|
||||
tableName: 'loan_contracts',
|
||||
timestamps: true,
|
||||
createdAt: 'createdAt',
|
||||
updatedAt: 'updatedAt',
|
||||
hooks: {
|
||||
beforeCreate: (contract) => {
|
||||
// 生成合同编号
|
||||
if (!contract.contractNumber) {
|
||||
const now = new Date();
|
||||
const timestamp = now.getFullYear().toString() +
|
||||
(now.getMonth() + 1).toString().padStart(2, '0') +
|
||||
now.getDate().toString().padStart(2, '0') +
|
||||
now.getHours().toString().padStart(2, '0') +
|
||||
now.getMinutes().toString().padStart(2, '0') +
|
||||
now.getSeconds().toString().padStart(2, '0');
|
||||
const random = Math.floor(Math.random() * 1000).toString().padStart(3, '0');
|
||||
contract.contractNumber = 'HT' + timestamp + random;
|
||||
}
|
||||
}
|
||||
}
|
||||
underscored: true,
|
||||
createdAt: 'created_at',
|
||||
updatedAt: 'updated_at',
|
||||
// 移除不存在的字段
|
||||
omitNull: true
|
||||
});
|
||||
|
||||
module.exports = LoanContract;
|
||||
module.exports = LoanContract;
|
||||
@@ -11,108 +11,127 @@ const LoanProduct = sequelize.define('LoanProduct', {
|
||||
productName: {
|
||||
type: DataTypes.STRING(200),
|
||||
allowNull: false,
|
||||
field: 'product_name',
|
||||
comment: '贷款产品名称'
|
||||
},
|
||||
loanAmount: {
|
||||
type: DataTypes.STRING(100),
|
||||
allowNull: false,
|
||||
field: 'loan_amount',
|
||||
comment: '贷款额度'
|
||||
},
|
||||
loanTerm: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: false,
|
||||
field: 'loan_term',
|
||||
comment: '贷款周期(月)'
|
||||
},
|
||||
interestRate: {
|
||||
type: DataTypes.DECIMAL(5, 2),
|
||||
allowNull: false,
|
||||
field: 'interest_rate',
|
||||
comment: '贷款利率(%)'
|
||||
},
|
||||
serviceArea: {
|
||||
type: DataTypes.STRING(200),
|
||||
allowNull: false,
|
||||
field: 'service_area',
|
||||
comment: '服务区域'
|
||||
},
|
||||
servicePhone: {
|
||||
type: DataTypes.STRING(20),
|
||||
allowNull: false,
|
||||
field: 'service_phone',
|
||||
comment: '服务电话'
|
||||
},
|
||||
totalCustomers: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: false,
|
||||
defaultValue: 0,
|
||||
field: 'total_customers',
|
||||
comment: '服务客户总数量'
|
||||
},
|
||||
supervisionCustomers: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: false,
|
||||
defaultValue: 0,
|
||||
field: 'supervision_customers',
|
||||
comment: '监管中客户数量'
|
||||
},
|
||||
completedCustomers: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: false,
|
||||
defaultValue: 0,
|
||||
field: 'completed_customers',
|
||||
comment: '已结项客户数量'
|
||||
},
|
||||
onSaleStatus: {
|
||||
type: DataTypes.BOOLEAN,
|
||||
allowNull: false,
|
||||
defaultValue: true,
|
||||
field: 'on_sale_status',
|
||||
comment: '在售状态'
|
||||
},
|
||||
productDescription: {
|
||||
type: DataTypes.TEXT,
|
||||
allowNull: true,
|
||||
field: 'product_description',
|
||||
comment: '产品描述'
|
||||
},
|
||||
applicationRequirements: {
|
||||
type: DataTypes.TEXT,
|
||||
allowNull: true,
|
||||
field: 'application_requirements',
|
||||
comment: '申请条件'
|
||||
},
|
||||
requiredDocuments: {
|
||||
type: DataTypes.TEXT,
|
||||
allowNull: true,
|
||||
field: 'required_documents',
|
||||
comment: '所需材料'
|
||||
},
|
||||
approvalProcess: {
|
||||
type: DataTypes.TEXT,
|
||||
allowNull: true,
|
||||
field: 'approval_process',
|
||||
comment: '审批流程'
|
||||
},
|
||||
riskLevel: {
|
||||
type: DataTypes.ENUM('LOW', 'MEDIUM', 'HIGH'),
|
||||
allowNull: false,
|
||||
defaultValue: 'MEDIUM',
|
||||
field: 'risk_level',
|
||||
comment: '风险等级'
|
||||
},
|
||||
minLoanAmount: {
|
||||
type: DataTypes.DECIMAL(15, 2),
|
||||
allowNull: true,
|
||||
field: 'min_loan_amount',
|
||||
comment: '最小贷款金额'
|
||||
},
|
||||
maxLoanAmount: {
|
||||
type: DataTypes.DECIMAL(15, 2),
|
||||
allowNull: true,
|
||||
field: 'max_loan_amount',
|
||||
comment: '最大贷款金额'
|
||||
},
|
||||
createdBy: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: false,
|
||||
field: 'created_by',
|
||||
comment: '创建人ID'
|
||||
},
|
||||
updatedBy: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: true,
|
||||
field: 'updated_by',
|
||||
comment: '更新人ID'
|
||||
}
|
||||
}, {
|
||||
tableName: 'loan_products',
|
||||
timestamps: true,
|
||||
createdAt: 'createdAt',
|
||||
updatedAt: 'updatedAt',
|
||||
createdAt: 'created_at',
|
||||
updatedAt: 'updated_at',
|
||||
comment: '贷款商品表'
|
||||
});
|
||||
|
||||
|
||||
@@ -21,61 +21,72 @@ const Project = sequelize.define('Project', {
|
||||
farmName: {
|
||||
type: DataTypes.STRING(200),
|
||||
allowNull: false,
|
||||
field: 'farmName',
|
||||
comment: '养殖场名称'
|
||||
},
|
||||
supervisionObject: {
|
||||
type: DataTypes.STRING(50),
|
||||
allowNull: false,
|
||||
field: 'supervisionObject',
|
||||
comment: '监管对象'
|
||||
},
|
||||
supervisionQuantity: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: false,
|
||||
defaultValue: 0,
|
||||
field: 'supervisionQuantity',
|
||||
comment: '监管数量'
|
||||
},
|
||||
supervisionPeriod: {
|
||||
type: DataTypes.STRING(50),
|
||||
allowNull: false,
|
||||
field: 'supervisionPeriod',
|
||||
comment: '监管周期'
|
||||
},
|
||||
supervisionAmount: {
|
||||
type: DataTypes.DECIMAL(15, 2),
|
||||
allowNull: false,
|
||||
defaultValue: 0.00,
|
||||
field: 'supervisionAmount',
|
||||
comment: '监管金额'
|
||||
},
|
||||
startTime: {
|
||||
type: DataTypes.DATEONLY,
|
||||
allowNull: false,
|
||||
field: 'startTime',
|
||||
comment: '起始时间'
|
||||
},
|
||||
endTime: {
|
||||
type: DataTypes.DATEONLY,
|
||||
allowNull: false,
|
||||
field: 'endTime',
|
||||
comment: '结束时间'
|
||||
},
|
||||
earTag: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: false,
|
||||
defaultValue: 0,
|
||||
field: 'earTag',
|
||||
comment: '耳标数量'
|
||||
},
|
||||
collar: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: false,
|
||||
defaultValue: 0,
|
||||
field: 'collar',
|
||||
comment: '项圈数量'
|
||||
},
|
||||
host: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: false,
|
||||
defaultValue: 0,
|
||||
field: 'host',
|
||||
comment: '主机数量'
|
||||
},
|
||||
loanOfficer: {
|
||||
type: DataTypes.STRING(100),
|
||||
allowNull: true,
|
||||
field: 'loanOfficer',
|
||||
comment: '贷款专员'
|
||||
},
|
||||
description: {
|
||||
@@ -86,16 +97,19 @@ const Project = sequelize.define('Project', {
|
||||
createdBy: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: true,
|
||||
field: 'createdBy',
|
||||
comment: '创建人ID'
|
||||
},
|
||||
updatedBy: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: true,
|
||||
field: 'updatedBy',
|
||||
comment: '更新人ID'
|
||||
}
|
||||
}, {
|
||||
tableName: 'projects',
|
||||
timestamps: true,
|
||||
underscored: false,
|
||||
createdAt: 'createdAt',
|
||||
updatedAt: 'updatedAt',
|
||||
comment: '项目清单表'
|
||||
@@ -105,13 +119,15 @@ const Project = sequelize.define('Project', {
|
||||
Project.associate = (models) => {
|
||||
// 项目与用户关联(创建人)
|
||||
Project.belongsTo(models.User, {
|
||||
foreignKey: 'createdBy',
|
||||
foreignKey: { name: 'createdBy', field: 'createdBy' },
|
||||
targetKey: 'id',
|
||||
as: 'creator'
|
||||
});
|
||||
|
||||
// 项目与用户关联(更新人)
|
||||
Project.belongsTo(models.User, {
|
||||
foreignKey: 'updatedBy',
|
||||
foreignKey: { name: 'updatedBy', field: 'updatedBy' },
|
||||
targetKey: 'id',
|
||||
as: 'updater'
|
||||
});
|
||||
};
|
||||
|
||||
@@ -19,7 +19,8 @@ SupervisionTask.init({
|
||||
type: DataTypes.STRING(50),
|
||||
allowNull: false,
|
||||
unique: true,
|
||||
comment: '申请单号'
|
||||
comment: '申请单号',
|
||||
field: 'applicationNumber'
|
||||
},
|
||||
contractNumber: {
|
||||
type: DataTypes.STRING(50),
|
||||
@@ -145,7 +146,7 @@ SupervisionTask.init({
|
||||
modelName: 'SupervisionTask',
|
||||
tableName: 'supervision_tasks',
|
||||
timestamps: true,
|
||||
underscored: true,
|
||||
underscored: false,
|
||||
createdAt: 'createdAt',
|
||||
updatedAt: 'updatedAt',
|
||||
comment: '监管任务表'
|
||||
@@ -155,11 +156,13 @@ SupervisionTask.init({
|
||||
SupervisionTask.associate = (models) => {
|
||||
SupervisionTask.belongsTo(models.User, {
|
||||
foreignKey: 'createdBy',
|
||||
as: 'creator'
|
||||
as: 'creator',
|
||||
targetKey: 'id'
|
||||
});
|
||||
SupervisionTask.belongsTo(models.User, {
|
||||
foreignKey: 'updatedBy',
|
||||
as: 'updater'
|
||||
as: 'updater',
|
||||
targetKey: 'id'
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -172,6 +172,10 @@ User.init({
|
||||
sequelize,
|
||||
tableName: 'bank_users',
|
||||
modelName: 'User',
|
||||
timestamps: true,
|
||||
underscored: true,
|
||||
createdAt: 'created_at',
|
||||
updatedAt: 'updated_at',
|
||||
hooks: {
|
||||
beforeCreate: async (user) => {
|
||||
if (user.password) {
|
||||
|
||||
@@ -11,7 +11,7 @@ const Role = require('./Role');
|
||||
const Account = require('./Account');
|
||||
const Transaction = require('./Transaction');
|
||||
const LoanProduct = require('./LoanProduct');
|
||||
const Employee = require('./Employee');
|
||||
const Employee = require('./Employee')(sequelize);
|
||||
const Department = require('./Department');
|
||||
const Position = require('./Position');
|
||||
const Report = require('./Report');
|
||||
@@ -62,29 +62,7 @@ Transaction.belongsTo(Account, {
|
||||
// 交易记录与用户关联(通过账户)
|
||||
// 移除不合理的Transaction->User through Account的belongsTo定义,避免错误外键映射
|
||||
|
||||
// 员工与部门关联
|
||||
Employee.belongsTo(Department, {
|
||||
foreignKey: 'department_id',
|
||||
as: 'department',
|
||||
targetKey: 'id'
|
||||
});
|
||||
|
||||
Department.hasMany(Employee, {
|
||||
foreignKey: 'department_id',
|
||||
as: 'employees'
|
||||
});
|
||||
|
||||
// 员工与职位关联
|
||||
Employee.belongsTo(Position, {
|
||||
foreignKey: 'position_id',
|
||||
as: 'position',
|
||||
targetKey: 'id'
|
||||
});
|
||||
|
||||
Position.hasMany(Employee, {
|
||||
foreignKey: 'position_id',
|
||||
as: 'employees'
|
||||
});
|
||||
// 员工关联关系(Employee模型使用字符串字段存储部门和职位,不需要关联)
|
||||
|
||||
// 报表与用户关联
|
||||
Report.belongsTo(User, {
|
||||
@@ -100,7 +78,7 @@ User.hasMany(Report, {
|
||||
|
||||
// 项目与用户关联(创建人)
|
||||
Project.belongsTo(User, {
|
||||
foreignKey: 'createdBy',
|
||||
foreignKey: { name: 'createdBy', field: 'createdBy' },
|
||||
as: 'creator',
|
||||
targetKey: 'id'
|
||||
});
|
||||
@@ -112,7 +90,7 @@ User.hasMany(Project, {
|
||||
|
||||
// 项目与用户关联(更新人)
|
||||
Project.belongsTo(User, {
|
||||
foreignKey: 'updatedBy',
|
||||
foreignKey: { name: 'updatedBy', field: 'updatedBy' },
|
||||
as: 'updater',
|
||||
targetKey: 'id'
|
||||
});
|
||||
@@ -218,41 +196,8 @@ User.hasMany(LoanProduct, {
|
||||
as: 'updatedLoanProducts'
|
||||
});
|
||||
|
||||
// 贷款申请与用户关联(申请人)
|
||||
LoanApplication.belongsTo(User, {
|
||||
foreignKey: 'applicantId',
|
||||
as: 'applicant',
|
||||
targetKey: 'id'
|
||||
});
|
||||
|
||||
User.hasMany(LoanApplication, {
|
||||
foreignKey: 'applicantId',
|
||||
as: 'loanApplications'
|
||||
});
|
||||
|
||||
// 贷款申请与用户关联(审批人)
|
||||
LoanApplication.belongsTo(User, {
|
||||
foreignKey: 'approvedBy',
|
||||
as: 'approver',
|
||||
targetKey: 'id'
|
||||
});
|
||||
|
||||
User.hasMany(LoanApplication, {
|
||||
foreignKey: 'approvedBy',
|
||||
as: 'approvedApplications'
|
||||
});
|
||||
|
||||
// 贷款申请与用户关联(拒绝人)
|
||||
LoanApplication.belongsTo(User, {
|
||||
foreignKey: 'rejectedBy',
|
||||
as: 'rejector',
|
||||
targetKey: 'id'
|
||||
});
|
||||
|
||||
User.hasMany(LoanApplication, {
|
||||
foreignKey: 'rejectedBy',
|
||||
as: 'rejectedApplications'
|
||||
});
|
||||
// 贷款申请暂时不关联用户表,因为当前表结构中没有外键字段
|
||||
// 如果需要关联,需要先添加相应的外键字段到数据库表中
|
||||
|
||||
// 审核记录与贷款申请关联
|
||||
AuditRecord.belongsTo(LoanApplication, {
|
||||
@@ -278,29 +223,8 @@ User.hasMany(AuditRecord, {
|
||||
as: 'auditRecords'
|
||||
});
|
||||
|
||||
// 贷款合同与用户关联(创建人)
|
||||
LoanContract.belongsTo(User, {
|
||||
foreignKey: 'createdBy',
|
||||
as: 'creator',
|
||||
targetKey: 'id'
|
||||
});
|
||||
|
||||
User.hasMany(LoanContract, {
|
||||
foreignKey: 'createdBy',
|
||||
as: 'createdLoanContracts'
|
||||
});
|
||||
|
||||
// 贷款合同与用户关联(更新人)
|
||||
LoanContract.belongsTo(User, {
|
||||
foreignKey: 'updatedBy',
|
||||
as: 'updater',
|
||||
targetKey: 'id'
|
||||
});
|
||||
|
||||
User.hasMany(LoanContract, {
|
||||
foreignKey: 'updatedBy',
|
||||
as: 'updatedLoanContracts'
|
||||
});
|
||||
// 贷款合同暂时不关联用户表,因为当前表结构中没有外键字段
|
||||
// 如果需要关联,需要先添加相应的外键字段到数据库表中
|
||||
|
||||
// 导出所有模型和数据库实例
|
||||
module.exports = {
|
||||
|
||||
Reference in New Issue
Block a user