Files
2025-08-26 10:06:23 +08:00

364 lines
9.9 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

class QueryBuilder {
constructor() {
this.conditions = {};
this.limit = null;
this.offset = null;
this.groupBy = null;
}
where(condition, ...params) {
this.conditions[condition] = params;
return this;
}
setLimit(limit) {
this.limit = limit;
return this;
}
setOffset(offset) {
this.offset = offset;
return this;
}
setGroupBy(groupBy) {
this.groupBy = groupBy;
return this;
}
sqdata(sql, params) {
return new Promise((resolve, reject) => {
global.sqlReq.query(sql, params, (err, result) => {
if (err) {
reject(err);
}
resolve(result);
});
});
}
getParams() {
return Object.values(this.conditions).flat();
}
buildConditions() {
return Object.keys(this.conditions).map(condition => `${condition}`).join(' AND ');
}
}
class SelectBuilder extends QueryBuilder {
constructor() {
super();
this.selectFields = [];
this.tables = [];
this.orderByField = '';
this.orderByDirection = 'ASC';
this.subQueries = []; // 用于存储子查询
this.unions = []; // 存储UNION查询
}
// 添加UNION查询
union(queryBuilder, type = 'UNION') {
this.unions.push({queryBuilder, type});
return this;
}
// 添加UNION ALL查询
unionAll(queryBuilder) {
this.union(queryBuilder, 'UNION ALL');
return this;
}
// 构建主查询部分不含ORDER BY/LIMIT/OFFSET
buildMainQuery() {
const subQuerySQL = this.subQueries.map(({alias, subQuery}) => `(${subQuery}) AS ${alias}`);
const selectClause = this.selectFields.concat(subQuerySQL).join(', ');
let sql = `SELECT ${selectClause}
FROM ${this.tables.join(' ')}`;
const conditionClauses = this.buildConditions();
if (conditionClauses) {
sql += ` WHERE ${conditionClauses}`;
}
if (this.groupBy) {
sql += ` GROUP BY ${this.groupBy}`;
}
const params = this.getParams();
return {sql, params};
}
// 供UNION查询调用的构建方法
buildForUnion() {
return this.buildMainQuery();
}
select(fields) {
this.selectFields = fields.split(',').map(field => field.trim());
return this;
}
// 添加子查询
addSubQuery(alias, subQuery) {
this.subQueries.push({alias, subQuery});
return this;
}
whereLike(fields, keyword) {
const likeConditions = fields.map(field => `${field} LIKE ?`).join(' OR ');
this.conditions[likeConditions] = fields.map(() => `%${keyword}%`);
return this;
}
from(table) {
this.tables.push(table);
return this;
}
leftJoin(table, condition) {
this.tables.push(`LEFT JOIN ${table} ON ${condition}`);
return this;
}
orderBy(field, direction = 'ASC') {
this.orderByField = field;
this.orderByDirection = direction.toUpperCase();
return this;
}
paginate(page, pageSize) {
if (page <= 0 || pageSize <= 0) {
throw new Error('分页参数必须大于0');
}
this.limit = pageSize;
this.offset = (page - 1) * pageSize;
return this;
}
async chidBuild() {
let sql = `SELECT ${this.selectFields.join(', ')}
FROM ${this.tables.join(' ')}`;
let conditionClauses = this.buildConditions();
if (conditionClauses) {
sql += ` WHERE ${conditionClauses}`;
}
if (this.orderByField) {
sql += ` ORDER BY ${this.orderByField} ${this.orderByDirection}`;
}
if (this.limit !== null) {
sql += ` LIMIT ${this.limit}`;
}
if (this.offset !== null) {
sql += ` OFFSET ${this.offset}`;
}
return sql;
}
async build() {
const main = this.buildMainQuery();
let fullSql = `(${main.sql})`;
const allParams = [...main.params];
// 处理UNION部分
for (const union of this.unions) {
const unionBuilder = union.queryBuilder;
if (!(unionBuilder instanceof SelectBuilder)) {
throw new Error('UNION query must be a SelectBuilder instance');
}
const unionResult = unionBuilder.buildForUnion();
fullSql += ` ${union.type} (${unionResult.sql})`;
allParams.push(...unionResult.params);
}
// 添加ORDER BY、LIMIT、OFFSET
if (this.orderByField) {
fullSql += ` ORDER BY ${this.orderByField} ${this.orderByDirection}`;
}
if (this.limit !== null) {
fullSql += ` LIMIT ${this.limit}`;
}
if (this.offset !== null) {
fullSql += ` OFFSET ${this.offset}`;
}
console.log(fullSql,allParams);
return await this.sqdata(fullSql, allParams);
}
}
class UpdateBuilder extends QueryBuilder {
constructor() {
super();
this.table = '';
this.updateFields = {};
}
update(table) {
this.table = table;
return this;
}
set(field, value) {
if (value && value.increment && typeof value === 'object' ) {
this.updateFields[field] = {increment: value.increment};
} else {
this.updateFields[field] = value;
}
return this;
}
async build() {
let sql = `UPDATE ${this.table}
SET `;
let updateClauses = Object.keys(this.updateFields).map(field => {
const value = this.updateFields[field];
if (value && value.increment && typeof value === 'object' ) {
return `${field} = ${field} + ?`;
}
return `${field} = ?`;
}).join(', ');
sql += updateClauses;
let conditionClauses = this.buildConditions();
if (conditionClauses) {
sql += ` WHERE ${conditionClauses}`;
}
// 处理参数,确保自增字段也传入增量值
const params = [
...Object.values(this.updateFields).map(value =>
(value && value.increment && typeof value === 'object' ) ? value.increment : value
),
...this.getParams()
];
return await this.sqdata(sql, params);
}
}
class InsertBuilder extends QueryBuilder {
constructor() {
super();
this.table = '';
this.insertValues = [];
this.updateValues = {};
}
insertInto(table) {
this.table = table;
return this;
}
// 仍然保留单条记录的插入
values(values) {
if (Array.isArray(values)) {
this.insertValues = values;
} else {
this.insertValues = [values]; // 将单条记录包装成数组
}
return this;
}
// 新增方法,支持一次插入多条记录
valuesMultiple(records) {
if (!Array.isArray(records) || records.length === 0) {
throw new Error('Values must be a non-empty array');
}
// 确保每一条记录都是对象
records.forEach(record => {
if (typeof record !== 'object') {
throw new Error('Each record must be an object');
}
});
this.insertValues = records;
return this;
}
// 新增 upsert 方法,支持更新或插入
upsert(values, updateFields) {
// values: 要插入的记录
// updateFields: 如果记录存在时,需要更新的字段
if (!Array.isArray(values) || values.length === 0) {
throw new Error('Values must be a non-empty array');
}
// 检查每条记录是否是对象
values.forEach(record => {
if (typeof record !== 'object') {
throw new Error('Each record must be an object');
}
});
this.insertValues = values;
this.updateValues = updateFields || {};
return this;
}
async build() {
if (this.insertValues.length === 0) {
throw new Error("No values to insert");
}
// 获取表单列名,假设所有记录有相同的字段
const columns = Object.keys(this.insertValues[0]);
// 构建 VALUES 子句,支持批量插入
const valuePlaceholders = this.insertValues.map(() =>
`(${columns.map(() => '?').join(', ')})`
).join(', ');
// 展平所有的插入值
const params = this.insertValues.flatMap(record =>
columns.map(column => record[column])
);
// 如果有 updateFields构建 ON DUPLICATE KEY UPDATE 子句
let updateClause = '';
if (Object.keys(this.updateValues).length > 0) {
updateClause = ' ON DUPLICATE KEY UPDATE ' +
Object.keys(this.updateValues).map(field => {
return `${field} = VALUES(${field})`;
}).join(', ');
}
// 生成 SQL 语句
const sql = `INSERT INTO ${this.table} (${columns.join(', ')})
VALUES ${valuePlaceholders} ${updateClause}`;
// 执行查询
return await this.sqdata(sql, params);
}
}
class DeleteBuilder extends QueryBuilder {
constructor() {
super();
this.table = '';
}
deleteFrom(table) {
this.table = table;
return this;
}
async build() {
let sql = `DELETE
FROM ${this.table}`;
let conditionClauses = this.buildConditions();
if (conditionClauses) {
sql += ` WHERE ${conditionClauses}`;
}
return await this.sqdata(sql, this.getParams());
}
}
module.exports = {
SelectBuilder,
UpdateBuilder,
InsertBuilder,
DeleteBuilder,
};