Files
nxxmdata/backend/models/BaseModel.js
2025-08-25 15:00:46 +08:00

187 lines
4.8 KiB
JavaScript

/**
* 数据库模型基类
* @file BaseModel.js
* @description 提供所有模型的基础功能和通用方法
*/
const { Model, DataTypes } = require('sequelize');
const queryOptimizer = require('../utils/query-optimizer');
class BaseModel extends Model {
/**
* 初始化模型
* @param {Object} sequelize Sequelize实例
* @param {Object} attributes 模型属性
* @param {Object} options 模型选项
*/
static init(attributes, options = {}) {
// 确保options中包含sequelize实例
if (!options.sequelize) {
throw new Error('必须提供sequelize实例');
}
// 默认配置
const defaultOptions = {
// 使用下划线命名法
underscored: true,
// 默认时间戳字段
timestamps: true,
createdAt: 'created_at',
updatedAt: 'updated_at',
// 默认不使用软删除
paranoid: false,
// 字符集和排序规则
charset: 'utf8mb4',
collate: 'utf8mb4_unicode_ci'
};
// 合并选项
const mergedOptions = { ...defaultOptions, ...options };
// 调用父类的init方法
return super.init(attributes, mergedOptions);
}
/**
* 分页查询
* @param {Object} options 查询选项
* @param {Number} page 页码
* @param {Number} pageSize 每页记录数
* @returns {Promise<Object>} 分页结果
*/
static async paginate(options = {}, page = 1, pageSize = 10) {
// 确保页码和每页记录数为正整数
page = Math.max(1, parseInt(page));
pageSize = Math.max(1, parseInt(pageSize));
// 计算偏移量
const offset = (page - 1) * pageSize;
// 合并分页参数
const paginateOptions = {
...options,
limit: pageSize,
offset
};
// 执行查询
const { count, rows } = await this.findAndCountAll(paginateOptions);
// 计算总页数
const totalPages = Math.ceil(count / pageSize);
// 返回分页结果
return {
data: rows,
pagination: {
total: count,
page,
pageSize,
totalPages,
hasMore: page < totalPages
}
};
}
/**
* 批量创建或更新记录
* @param {Array} records 记录数组
* @param {Array|String} uniqueFields 唯一字段或字段数组
* @returns {Promise<Array>} 创建或更新的记录
*/
static async bulkUpsert(records, uniqueFields) {
if (!Array.isArray(records) || records.length === 0) {
return [];
}
// 确保uniqueFields是数组
const fields = Array.isArray(uniqueFields) ? uniqueFields : [uniqueFields];
// 获取所有字段
const allFields = Object.keys(this.rawAttributes);
// 批量创建或更新
const result = await this.bulkCreate(records, {
updateOnDuplicate: allFields.filter(field => {
// 排除主键和时间戳字段
return (
field !== 'id' &&
field !== 'created_at' &&
field !== 'updated_at' &&
field !== 'deleted_at'
);
}),
// 返回创建或更新后的记录
returning: true
});
return result;
}
/**
* 执行原始SQL查询
* @param {String} sql SQL语句
* @param {Object} replacements 替换参数
* @param {String} type 查询类型
* @returns {Promise<Array|Object>} 查询结果
*/
static async rawQuery(sql, replacements = {}, type = 'SELECT') {
const sequelize = this.sequelize;
const QueryTypes = sequelize.QueryTypes;
// 执行查询前分析查询性能
if (process.env.NODE_ENV === 'development') {
try {
const explainResult = await queryOptimizer.explainQuery(sql, replacements);
console.log('查询分析结果:', explainResult);
} catch (error) {
console.warn('查询分析失败:', error.message);
}
}
// 执行查询
return sequelize.query(sql, {
replacements,
type: QueryTypes[type],
model: this,
mapToModel: type === 'SELECT'
});
}
/**
* 获取模型的表信息
* @returns {Promise<Object>} 表信息
*/
static async getTableInfo() {
const tableName = this.getTableName();
return queryOptimizer.getTableInfo(tableName);
}
/**
* 获取模型的索引信息
* @returns {Promise<Array>} 索引信息
*/
static async getIndexes() {
const tableName = this.getTableName();
return queryOptimizer.getTableIndexes(tableName);
}
/**
* 分析表结构
* @returns {Promise<Object>} 分析结果
*/
static async analyzeTable() {
const tableName = this.getTableName();
return queryOptimizer.analyzeTable(tableName);
}
/**
* 优化表
* @returns {Promise<Object>} 优化结果
*/
static async optimizeTable() {
const tableName = this.getTableName();
return queryOptimizer.optimizeTable(tableName);
}
}
module.exports = BaseModel;