210 lines
4.6 KiB
JavaScript
210 lines
4.6 KiB
JavaScript
|
|
/**
|
|||
|
|
* 交易记录模型
|
|||
|
|
* @file Transaction.js
|
|||
|
|
* @description 银行交易记录模型定义
|
|||
|
|
*/
|
|||
|
|
const { DataTypes } = require('sequelize');
|
|||
|
|
const BaseModel = require('./BaseModel');
|
|||
|
|
const { sequelize } = require('../config/database');
|
|||
|
|
|
|||
|
|
class Transaction extends BaseModel {
|
|||
|
|
/**
|
|||
|
|
* 获取交易金额(元)
|
|||
|
|
* @returns {String} 格式化后的金额
|
|||
|
|
*/
|
|||
|
|
getAmountFormatted() {
|
|||
|
|
return this.formatAmount(this.amount);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 获取交易后余额(元)
|
|||
|
|
* @returns {String} 格式化后的余额
|
|||
|
|
*/
|
|||
|
|
getBalanceAfterFormatted() {
|
|||
|
|
return this.formatAmount(this.balance_after);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 检查是否为收入交易
|
|||
|
|
* @returns {Boolean} 是否为收入
|
|||
|
|
*/
|
|||
|
|
isIncome() {
|
|||
|
|
return this.transaction_type === 'deposit' ||
|
|||
|
|
this.transaction_type === 'transfer_in' ||
|
|||
|
|
this.transaction_type === 'interest';
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 检查是否为支出交易
|
|||
|
|
* @returns {Boolean} 是否为支出
|
|||
|
|
*/
|
|||
|
|
isExpense() {
|
|||
|
|
return this.transaction_type === 'withdrawal' ||
|
|||
|
|
this.transaction_type === 'transfer_out' ||
|
|||
|
|
this.transaction_type === 'fee';
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 获取交易状态描述
|
|||
|
|
* @returns {String} 状态描述
|
|||
|
|
*/
|
|||
|
|
getStatusDescription() {
|
|||
|
|
const statusMap = {
|
|||
|
|
'pending': '处理中',
|
|||
|
|
'completed': '已完成',
|
|||
|
|
'failed': '失败',
|
|||
|
|
'cancelled': '已取消',
|
|||
|
|
'reversed': '已冲正'
|
|||
|
|
};
|
|||
|
|
return statusMap[this.status] || '未知状态';
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 获取交易类型描述
|
|||
|
|
* @returns {String} 类型描述
|
|||
|
|
*/
|
|||
|
|
getTypeDescription() {
|
|||
|
|
const typeMap = {
|
|||
|
|
'deposit': '存款',
|
|||
|
|
'withdrawal': '取款',
|
|||
|
|
'transfer_in': '转入',
|
|||
|
|
'transfer_out': '转出',
|
|||
|
|
'interest': '利息',
|
|||
|
|
'fee': '手续费',
|
|||
|
|
'loan': '贷款',
|
|||
|
|
'repayment': '还款'
|
|||
|
|
};
|
|||
|
|
return typeMap[this.transaction_type] || '未知类型';
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 检查交易是否可以撤销
|
|||
|
|
* @returns {Boolean} 是否可以撤销
|
|||
|
|
*/
|
|||
|
|
canReverse() {
|
|||
|
|
return this.status === 'completed' &&
|
|||
|
|
this.transaction_type !== 'fee' &&
|
|||
|
|
this.created_at > new Date(Date.now() - 24 * 60 * 60 * 1000); // 24小时内
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 撤销交易
|
|||
|
|
* @returns {Promise<Boolean>} 操作结果
|
|||
|
|
*/
|
|||
|
|
async reverse() {
|
|||
|
|
if (!this.canReverse()) {
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
// 更新交易状态
|
|||
|
|
this.status = 'reversed';
|
|||
|
|
this.reversed_at = new Date();
|
|||
|
|
await this.save();
|
|||
|
|
|
|||
|
|
// 这里应该创建反向交易记录
|
|||
|
|
// 实际实现中需要更复杂的逻辑
|
|||
|
|
return true;
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('撤销交易失败:', error);
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 初始化Transaction模型
|
|||
|
|
Transaction.init({
|
|||
|
|
id: {
|
|||
|
|
type: DataTypes.INTEGER,
|
|||
|
|
primaryKey: true,
|
|||
|
|
autoIncrement: true
|
|||
|
|
},
|
|||
|
|
transaction_number: {
|
|||
|
|
type: DataTypes.STRING(32),
|
|||
|
|
allowNull: false,
|
|||
|
|
unique: true,
|
|||
|
|
comment: '交易流水号'
|
|||
|
|
},
|
|||
|
|
account_id: {
|
|||
|
|
type: DataTypes.INTEGER,
|
|||
|
|
allowNull: false,
|
|||
|
|
references: {
|
|||
|
|
model: 'bank_accounts',
|
|||
|
|
key: 'id'
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
transaction_type: {
|
|||
|
|
type: DataTypes.ENUM(
|
|||
|
|
'deposit', 'withdrawal', 'transfer_in', 'transfer_out',
|
|||
|
|
'interest', 'fee', 'loan', 'repayment'
|
|||
|
|
),
|
|||
|
|
allowNull: false,
|
|||
|
|
comment: '交易类型'
|
|||
|
|
},
|
|||
|
|
amount: {
|
|||
|
|
type: DataTypes.BIGINT,
|
|||
|
|
allowNull: false,
|
|||
|
|
comment: '交易金额(分)'
|
|||
|
|
},
|
|||
|
|
balance_before: {
|
|||
|
|
type: DataTypes.BIGINT,
|
|||
|
|
allowNull: false,
|
|||
|
|
comment: '交易前余额(分)'
|
|||
|
|
},
|
|||
|
|
balance_after: {
|
|||
|
|
type: DataTypes.BIGINT,
|
|||
|
|
allowNull: false,
|
|||
|
|
comment: '交易后余额(分)'
|
|||
|
|
},
|
|||
|
|
counterparty_account: {
|
|||
|
|
type: DataTypes.STRING(20),
|
|||
|
|
allowNull: true,
|
|||
|
|
comment: '对方账户号'
|
|||
|
|
},
|
|||
|
|
counterparty_name: {
|
|||
|
|
type: DataTypes.STRING(100),
|
|||
|
|
allowNull: true,
|
|||
|
|
comment: '对方户名'
|
|||
|
|
},
|
|||
|
|
description: {
|
|||
|
|
type: DataTypes.STRING(255),
|
|||
|
|
allowNull: true,
|
|||
|
|
comment: '交易描述'
|
|||
|
|
},
|
|||
|
|
reference_number: {
|
|||
|
|
type: DataTypes.STRING(50),
|
|||
|
|
allowNull: true,
|
|||
|
|
comment: '参考号'
|
|||
|
|
},
|
|||
|
|
status: {
|
|||
|
|
type: DataTypes.ENUM('pending', 'completed', 'failed', 'cancelled', 'reversed'),
|
|||
|
|
allowNull: false,
|
|||
|
|
defaultValue: 'pending'
|
|||
|
|
},
|
|||
|
|
processed_at: {
|
|||
|
|
type: DataTypes.DATE,
|
|||
|
|
allowNull: true,
|
|||
|
|
comment: '处理时间'
|
|||
|
|
},
|
|||
|
|
reversed_at: {
|
|||
|
|
type: DataTypes.DATE,
|
|||
|
|
allowNull: true,
|
|||
|
|
comment: '撤销时间'
|
|||
|
|
},
|
|||
|
|
created_at: {
|
|||
|
|
type: DataTypes.DATE,
|
|||
|
|
allowNull: false,
|
|||
|
|
defaultValue: DataTypes.NOW
|
|||
|
|
},
|
|||
|
|
updated_at: {
|
|||
|
|
type: DataTypes.DATE,
|
|||
|
|
allowNull: false,
|
|||
|
|
defaultValue: DataTypes.NOW
|
|||
|
|
}
|
|||
|
|
}, {
|
|||
|
|
sequelize,
|
|||
|
|
tableName: 'bank_transactions',
|
|||
|
|
modelName: 'Transaction'
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
module.exports = Transaction;
|