187 lines
4.8 KiB
JavaScript
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; |