mirror of
https://github.com/cool-team-official/cool-admin-midway-packages.git
synced 2025-12-16 00:42:48 +00:00
兼容mysql、postgresql、sqlite
This commit is contained in:
parent
fcf34dc35e
commit
01c8577507
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@cool-midway/core",
|
"name": "@cool-midway/core",
|
||||||
"version": "7.0.8",
|
"version": "7.1.0",
|
||||||
"description": "",
|
"description": "",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"typings": "index.d.ts",
|
"typings": "index.d.ts",
|
||||||
@ -41,7 +41,7 @@
|
|||||||
"jest": "^29.3.1",
|
"jest": "^29.3.1",
|
||||||
"mwts": "^1.3.0",
|
"mwts": "^1.3.0",
|
||||||
"ts-jest": "^29.0.3",
|
"ts-jest": "^29.0.3",
|
||||||
"typeorm": "^0.3.11",
|
"typeorm": "^0.3.19",
|
||||||
"typescript": "~4.9.4"
|
"typescript": "~4.9.4"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
@ -13,7 +13,6 @@ export abstract class BaseEntity extends CoolBaseEntity {
|
|||||||
// 默认自增
|
// 默认自增
|
||||||
@PrimaryGeneratedColumn("increment", {
|
@PrimaryGeneratedColumn("increment", {
|
||||||
comment: "ID",
|
comment: "ID",
|
||||||
// type: "bigint",
|
|
||||||
})
|
})
|
||||||
id: number;
|
id: number;
|
||||||
|
|
||||||
|
|||||||
@ -14,6 +14,9 @@ export * from "./entity/mongo";
|
|||||||
|
|
||||||
// service
|
// service
|
||||||
export * from "./service/base";
|
export * from "./service/base";
|
||||||
|
export * from "./service/mysql";
|
||||||
|
export * from "./service/postgres";
|
||||||
|
export * from "./service/sqlite";
|
||||||
|
|
||||||
// controller
|
// controller
|
||||||
export * from "./controller/base";
|
export * from "./controller/base";
|
||||||
|
|||||||
@ -9,8 +9,7 @@ import {
|
|||||||
Scope,
|
Scope,
|
||||||
ScopeEnum,
|
ScopeEnum,
|
||||||
} from "@midwayjs/decorator";
|
} from "@midwayjs/decorator";
|
||||||
import { CoolCoreException } from "../exception/core";
|
// import * as Importer from "mysql2-import";
|
||||||
import * as Importer from "mysql2-import";
|
|
||||||
import * as fs from "fs";
|
import * as fs from "fs";
|
||||||
import { CoolModuleConfig } from "./config";
|
import { CoolModuleConfig } from "./config";
|
||||||
import * as path from "path";
|
import * as path from "path";
|
||||||
@ -18,6 +17,7 @@ import { InjectDataSource, TypeORMDataSourceManager } from "@midwayjs/typeorm";
|
|||||||
import { DataSource } from "typeorm";
|
import { DataSource } from "typeorm";
|
||||||
import { CoolEventManager } from "../event";
|
import { CoolEventManager } from "../event";
|
||||||
import { CoolModuleMenu } from "./menu";
|
import { CoolModuleMenu } from "./menu";
|
||||||
|
import * as _ from 'lodash';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 模块sql
|
* 模块sql
|
||||||
@ -56,21 +56,21 @@ export class CoolModuleImport {
|
|||||||
async init() {
|
async init() {
|
||||||
// 是否需要导入
|
// 是否需要导入
|
||||||
if (this.coolConfig.initDB) {
|
if (this.coolConfig.initDB) {
|
||||||
await this.checkDbVersion();
|
|
||||||
const modules = this.coolModuleConfig.modules;
|
const modules = this.coolModuleConfig.modules;
|
||||||
const importLockPath = path.join(
|
const importLockPath = path.join(
|
||||||
`${this.app.getBaseDir()}`,
|
`${this.app.getBaseDir()}`,
|
||||||
"..",
|
"..",
|
||||||
"lock"
|
"lock",
|
||||||
|
'db'
|
||||||
);
|
);
|
||||||
if (!fs.existsSync(importLockPath)) {
|
if (!fs.existsSync(importLockPath)) {
|
||||||
fs.mkdirSync(importLockPath);
|
fs.mkdirSync(importLockPath, { recursive: true });
|
||||||
}
|
}
|
||||||
setTimeout(async () => {
|
setTimeout(async () => {
|
||||||
for (const module of modules) {
|
for (const module of modules) {
|
||||||
const lockPath = path.join(importLockPath, module + ".sql.lock");
|
const lockPath = path.join(importLockPath, module + ".db.lock");
|
||||||
if (!fs.existsSync(lockPath)) {
|
if (!fs.existsSync(lockPath)) {
|
||||||
await this.initDataBase(module, lockPath);
|
await this.initDataBase(module, lockPath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.coolEventManager.emit("onDBInit", {});
|
this.coolEventManager.emit("onDBInit", {});
|
||||||
@ -85,71 +85,115 @@ export class CoolModuleImport {
|
|||||||
* @param lockPath 锁定导入
|
* @param lockPath 锁定导入
|
||||||
*/
|
*/
|
||||||
async initDataBase(module: string, lockPath: string) {
|
async initDataBase(module: string, lockPath: string) {
|
||||||
|
// 计算耗时
|
||||||
|
const startTime = new Date().getTime();
|
||||||
// 模块路径
|
// 模块路径
|
||||||
const modulePath = `${this.app.getBaseDir()}/modules/${module}`;
|
const modulePath = `${this.app.getBaseDir()}/modules/${module}`;
|
||||||
// sql 路径
|
// 数据路径
|
||||||
const sqlPath = `${modulePath}/init.sql`;
|
const dataPath = `${modulePath}/db.json`;
|
||||||
// 延迟2秒再导入数据库
|
// 判断文件是否存在
|
||||||
if (fs.existsSync(sqlPath)) {
|
if(fs.existsSync(dataPath)) {
|
||||||
let second = 0;
|
// 获得所有的实体
|
||||||
const t = setInterval(() => {
|
const entityMetadatas = this.defaultDataSource.entityMetadatas;
|
||||||
this.coreLogger.info(
|
const metadatas = _.mapValues(_.keyBy(entityMetadatas, 'tableName'), 'target');
|
||||||
"\x1B[36m [cool:core] midwayjs cool core init " +
|
// 读取数据
|
||||||
module +
|
const data = JSON.parse(fs.readFileSync(dataPath).toString() || '{}');
|
||||||
" database... \x1B[0m"
|
// 导入数据
|
||||||
);
|
for(const key in data) {
|
||||||
second++;
|
try{
|
||||||
}, 1000);
|
const repository = this.defaultDataSource.getRepository(metadatas[key]);
|
||||||
const { host, username, password, database, charset, port } = this
|
if(this.ormConfig.default.type == 'postgres') {
|
||||||
.ormConfig?.default
|
for(const item of data[key]) {
|
||||||
? this.ormConfig.default
|
const result: any = await repository.save(repository.create(item));
|
||||||
: this.ormConfig;
|
if(item.id) {
|
||||||
const importer = new Importer({
|
await repository.update(result.id, { id: item.id });
|
||||||
host,
|
// 更新pgsql序列
|
||||||
password,
|
await this.defaultDataSource.query(`SELECT setval('${key}_id_seq', (SELECT MAX(id) FROM ${key}));`);
|
||||||
database,
|
}
|
||||||
charset,
|
}
|
||||||
port,
|
}else{
|
||||||
user: username,
|
await repository.insert(data[key]);
|
||||||
});
|
}
|
||||||
await importer
|
}catch(e){
|
||||||
.import(sqlPath)
|
|
||||||
.then(async () => {
|
|
||||||
clearInterval(t);
|
|
||||||
this.coreLogger.info(
|
|
||||||
"\x1B[36m [cool:core] midwayjs cool core init " +
|
|
||||||
module +
|
|
||||||
" database complete \x1B[0m"
|
|
||||||
);
|
|
||||||
fs.writeFileSync(lockPath, `time consuming:${second}s`);
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
clearTimeout(t);
|
|
||||||
this.coreLogger.error(
|
this.coreLogger.error(
|
||||||
"\x1B[36m [cool:core] midwayjs cool core init " +
|
"\x1B[36m [cool:core] midwayjs cool core init " +
|
||||||
module +
|
module +
|
||||||
" database err please manual import \x1B[0m"
|
" database err \x1B[0m"
|
||||||
);
|
);
|
||||||
fs.writeFileSync(lockPath, `time consuming:${second}s`);
|
continue;
|
||||||
this.coreLogger.error(err);
|
}
|
||||||
this.coreLogger.error(
|
}
|
||||||
`自动初始化模块[${module}]数据库失败,尝试手动导入数据库`
|
const endTime = new Date().getTime();
|
||||||
);
|
fs.writeFileSync(lockPath, `time consuming:${endTime-startTime}ms`);
|
||||||
});
|
this.coreLogger.info(
|
||||||
|
"\x1B[36m [cool:core] midwayjs cool core init " +
|
||||||
|
module +
|
||||||
|
" database complete \x1B[0m"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
// // sql 路径
|
||||||
|
// const sqlPath = `${modulePath}/init.sql`;
|
||||||
|
// // 延迟2秒再导入数据库
|
||||||
|
// if (fs.existsSync(sqlPath)) {
|
||||||
|
// let second = 0;
|
||||||
|
// const t = setInterval(() => {
|
||||||
|
// this.coreLogger.info(
|
||||||
|
// "\x1B[36m [cool:core] midwayjs cool core init " +
|
||||||
|
// module +
|
||||||
|
// " database... \x1B[0m"
|
||||||
|
// );
|
||||||
|
// second++;
|
||||||
|
// }, 1000);
|
||||||
|
// const { host, username, password, database, charset, port } = this
|
||||||
|
// .ormConfig?.default
|
||||||
|
// ? this.ormConfig.default
|
||||||
|
// : this.ormConfig;
|
||||||
|
// const importer = new Importer({
|
||||||
|
// host,
|
||||||
|
// password,
|
||||||
|
// database,
|
||||||
|
// charset,
|
||||||
|
// port,
|
||||||
|
// user: username,
|
||||||
|
// });
|
||||||
|
// await importer
|
||||||
|
// .import(sqlPath)
|
||||||
|
// .then(async () => {
|
||||||
|
// clearInterval(t);
|
||||||
|
// this.coreLogger.info(
|
||||||
|
// "\x1B[36m [cool:core] midwayjs cool core init " +
|
||||||
|
// module +
|
||||||
|
// " database complete \x1B[0m"
|
||||||
|
// );
|
||||||
|
// fs.writeFileSync(lockPath, `time consuming:${second}s`);
|
||||||
|
// })
|
||||||
|
// .catch((err) => {
|
||||||
|
// clearTimeout(t);
|
||||||
|
// this.coreLogger.error(
|
||||||
|
// "\x1B[36m [cool:core] midwayjs cool core init " +
|
||||||
|
// module +
|
||||||
|
// " database err please manual import \x1B[0m"
|
||||||
|
// );
|
||||||
|
// fs.writeFileSync(lockPath, `time consuming:${second}s`);
|
||||||
|
// this.coreLogger.error(err);
|
||||||
|
// this.coreLogger.error(
|
||||||
|
// `自动初始化模块[${module}]数据库失败,尝试手动导入数据库`
|
||||||
|
// );
|
||||||
|
// });
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 检查数据库版本
|
* 检查数据库版本
|
||||||
*/
|
*/
|
||||||
async checkDbVersion() {
|
// async checkDbVersion() {
|
||||||
const versions = (
|
// const versions = (
|
||||||
await this.defaultDataSource.query("SELECT VERSION() AS version")
|
// await this.defaultDataSource.query("SELECT VERSION() AS version")
|
||||||
)[0].version.split(".");
|
// )[0].version.split(".");
|
||||||
if ((versions[0] == 5 && versions[1] < 7) || versions[0] < 5) {
|
// if ((versions[0] == 5 && versions[1] < 7) || versions[0] < 5) {
|
||||||
throw new CoolCoreException(
|
// throw new CoolCoreException(
|
||||||
"数据库不满足要求:mysql>=5.7,请升级数据库版本"
|
// "数据库不满足要求:mysql>=5.7,请升级数据库版本"
|
||||||
);
|
// );
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|||||||
@ -27,6 +27,8 @@ export class CoolModuleMenu {
|
|||||||
@Inject()
|
@Inject()
|
||||||
coolEventManager: CoolEventManager;
|
coolEventManager: CoolEventManager;
|
||||||
|
|
||||||
|
datas = {};
|
||||||
|
|
||||||
async init() {
|
async init() {
|
||||||
// 是否需要导入
|
// 是否需要导入
|
||||||
if (this.coolConfig.initMenu) {
|
if (this.coolConfig.initMenu) {
|
||||||
@ -34,17 +36,19 @@ export class CoolModuleMenu {
|
|||||||
const importLockPath = path.join(
|
const importLockPath = path.join(
|
||||||
`${this.app.getBaseDir()}`,
|
`${this.app.getBaseDir()}`,
|
||||||
"..",
|
"..",
|
||||||
"lock"
|
"lock",
|
||||||
|
'menu'
|
||||||
);
|
);
|
||||||
if (!fs.existsSync(importLockPath)) {
|
if (!fs.existsSync(importLockPath)) {
|
||||||
fs.mkdirSync(importLockPath);
|
fs.mkdirSync(importLockPath, { recursive: true });
|
||||||
}
|
}
|
||||||
for (const module of modules) {
|
for (const module of modules) {
|
||||||
const lockPath = path.join(importLockPath, module + ".menu.lock");
|
const lockPath = path.join(importLockPath, module + ".menu.lock");
|
||||||
if (!fs.existsSync(lockPath)) {
|
if (!fs.existsSync(lockPath)) {
|
||||||
await this.importMenu(module, lockPath);
|
await this.importMenu(module, lockPath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
this.coolEventManager.emit("onMenuImport", this.datas);
|
||||||
this.coolEventManager.emit("onMenuInit", {});
|
this.coolEventManager.emit("onMenuInit", {});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -63,7 +67,8 @@ export class CoolModuleMenu {
|
|||||||
if (fs.existsSync(menuPath)) {
|
if (fs.existsSync(menuPath)) {
|
||||||
const data = fs.readFileSync(menuPath);
|
const data = fs.readFileSync(menuPath);
|
||||||
try {
|
try {
|
||||||
this.coolEventManager.emit("onMenuImport", module, JSON.parse(data.toString()));
|
// this.coolEventManager.emit("onMenuImport", module, JSON.parse(data.toString()));
|
||||||
|
this.datas[module] = JSON.parse(data.toString());
|
||||||
fs.writeFileSync(lockPath, data);
|
fs.writeFileSync(lockPath, data);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.coreLogger.error(error);
|
this.coreLogger.error(error);
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@cool-midway/core",
|
"name": "@cool-midway/core",
|
||||||
"version": "7.0.8",
|
"version": "7.1.0",
|
||||||
"description": "",
|
"description": "",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"typings": "index.d.ts",
|
"typings": "index.d.ts",
|
||||||
@ -40,7 +40,7 @@
|
|||||||
"jest": "^29.3.1",
|
"jest": "^29.3.1",
|
||||||
"mwts": "^1.3.0",
|
"mwts": "^1.3.0",
|
||||||
"ts-jest": "^29.0.3",
|
"ts-jest": "^29.0.3",
|
||||||
"typeorm": "^0.3.11",
|
"typeorm": "^0.3.19",
|
||||||
"typescript": "~4.9.4"
|
"typescript": "~4.9.4"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
@ -1,23 +1,41 @@
|
|||||||
import { Init, Provide, Inject, App, Config } from "@midwayjs/decorator";
|
import { App, Config, Init, Inject, Provide } from "@midwayjs/decorator";
|
||||||
|
import { Scope, ScopeEnum } from "@midwayjs/core";
|
||||||
|
import { BaseMysqlService } from "./mysql";
|
||||||
|
import { BasePgService } from "./postgres";
|
||||||
import { CoolValidateException } from "../exception/validate";
|
import { CoolValidateException } from "../exception/validate";
|
||||||
import { ERRINFO, EVENT } from "../constant/global";
|
import { ERRINFO } from "../constant/global";
|
||||||
import { Application, Context } from "@midwayjs/koa";
|
import { Application, Context } from "@midwayjs/koa";
|
||||||
import * as SqlString from "sqlstring";
|
|
||||||
import { CoolConfig } from "../interface";
|
|
||||||
import { TypeORMDataSourceManager } from "@midwayjs/typeorm";
|
import { TypeORMDataSourceManager } from "@midwayjs/typeorm";
|
||||||
import { Brackets, In, Repository, SelectQueryBuilder } from "typeorm";
|
import { Repository, SelectQueryBuilder } from "typeorm";
|
||||||
import { QueryOp } from "../decorator/controller";
|
import { QueryOp } from "../decorator/controller";
|
||||||
import * as _ from "lodash";
|
import * as _ from "lodash";
|
||||||
import { CoolEventManager } from "../event";
|
import { CoolEventManager } from "../event";
|
||||||
|
import { CoolCoreException } from "../exception/core";
|
||||||
|
import { BaseSqliteService } from "./sqlite";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 服务基类
|
* 服务基类
|
||||||
*/
|
*/
|
||||||
@Provide()
|
@Provide()
|
||||||
|
@Scope(ScopeEnum.Request, { allowDowngrade: true })
|
||||||
export abstract class BaseService {
|
export abstract class BaseService {
|
||||||
// 分页配置
|
// mysql的基类
|
||||||
@Config("cool")
|
@Inject()
|
||||||
private _coolConfig: CoolConfig;
|
baseMysqlService: BaseMysqlService;
|
||||||
|
|
||||||
|
// postgres的基类
|
||||||
|
@Inject()
|
||||||
|
basePgService: BasePgService;
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
baseSqliteService: BaseSqliteService;
|
||||||
|
|
||||||
|
// 数据库类型
|
||||||
|
@Config("typeorm.dataSource.default.type")
|
||||||
|
ormType;
|
||||||
|
|
||||||
|
// 当前服务名称
|
||||||
|
service: BaseMysqlService | BasePgService | BaseSqliteService;
|
||||||
|
|
||||||
// 模型
|
// 模型
|
||||||
protected entity: Repository<any>;
|
protected entity: Repository<any>;
|
||||||
@ -30,31 +48,42 @@ export abstract class BaseService {
|
|||||||
@Inject()
|
@Inject()
|
||||||
coolEventManager: CoolEventManager;
|
coolEventManager: CoolEventManager;
|
||||||
|
|
||||||
|
@Inject("ctx")
|
||||||
|
baseCtx: Context;
|
||||||
|
|
||||||
|
@App()
|
||||||
|
baseApp: Application;
|
||||||
|
|
||||||
|
@Init()
|
||||||
|
async init(){
|
||||||
|
const services = {
|
||||||
|
mysql: this.baseMysqlService,
|
||||||
|
postgres: this.basePgService,
|
||||||
|
sqlite: this.baseSqliteService
|
||||||
|
};
|
||||||
|
this.service = services[this.ormType];
|
||||||
|
if(!this.service) throw new CoolCoreException('暂不支持当前数据库类型');
|
||||||
|
this.sqlParams = this.service.sqlParams;
|
||||||
|
await this.service.init();
|
||||||
|
}
|
||||||
|
|
||||||
// 设置模型
|
// 设置模型
|
||||||
setEntity(entity: any) {
|
setEntity(entity: any) {
|
||||||
this.entity = entity;
|
this.entity = entity;
|
||||||
|
this.service.setEntity(entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 设置请求上下文
|
// 设置请求上下文
|
||||||
setCtx(ctx: Context) {
|
setCtx(ctx: Context) {
|
||||||
this.baseCtx = ctx;
|
this.baseCtx = ctx;
|
||||||
|
this.service.setCtx(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
@App()
|
|
||||||
baseApp: Application;
|
|
||||||
|
|
||||||
// 设置应用对象
|
// 设置应用对象
|
||||||
setApp(app: Application) {
|
setApp(app: Application) {
|
||||||
this.baseApp = app;
|
this.baseApp = app;
|
||||||
}
|
this.service.setApp(app);
|
||||||
|
|
||||||
@Inject("ctx")
|
|
||||||
baseCtx: Context;
|
|
||||||
|
|
||||||
// 初始化
|
|
||||||
@Init()
|
|
||||||
init() {
|
|
||||||
this.sqlParams = [];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -64,12 +93,7 @@ export abstract class BaseService {
|
|||||||
* @param params 参数
|
* @param params 参数
|
||||||
*/
|
*/
|
||||||
setSql(condition, sql, params) {
|
setSql(condition, sql, params) {
|
||||||
let rSql = false;
|
return this.service.setSql(condition, sql, params);
|
||||||
if (condition || (condition === 0 && condition !== "")) {
|
|
||||||
rSql = true;
|
|
||||||
this.sqlParams = this.sqlParams.concat(params);
|
|
||||||
}
|
|
||||||
return rSql ? sql : "";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -77,29 +101,15 @@ export abstract class BaseService {
|
|||||||
* @param sql
|
* @param sql
|
||||||
*/
|
*/
|
||||||
getCountSql(sql) {
|
getCountSql(sql) {
|
||||||
sql = sql
|
return this.service.getCountSql(sql);
|
||||||
.replace(new RegExp("LIMIT", "gm"), "limit ")
|
|
||||||
.replace(new RegExp("\n", "gm"), " ");
|
|
||||||
if (sql.includes("limit")) {
|
|
||||||
const sqlArr = sql.split("limit ");
|
|
||||||
sqlArr.pop();
|
|
||||||
sql = sqlArr.join("limit ");
|
|
||||||
}
|
|
||||||
return `select count(*) as count from (${sql}) a`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 参数安全性检查
|
* 参数安全性检查
|
||||||
* @param params
|
* @param params
|
||||||
*/
|
*/
|
||||||
async paramSafetyCheck(params) {
|
async paramSafetyCheck(params) {
|
||||||
const lp = params.toLowerCase();
|
return await this.service.paramSafetyCheck(params);
|
||||||
return !(
|
|
||||||
lp.indexOf("update ") > -1 ||
|
|
||||||
lp.indexOf("select ") > -1 ||
|
|
||||||
lp.indexOf("delete ") > -1 ||
|
|
||||||
lp.indexOf("insert ") > -1
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -109,16 +119,7 @@ export abstract class BaseService {
|
|||||||
* @param connectionName
|
* @param connectionName
|
||||||
*/
|
*/
|
||||||
async nativeQuery(sql, params?, connectionName?) {
|
async nativeQuery(sql, params?, connectionName?) {
|
||||||
if (_.isEmpty(params)) {
|
return await this.service.nativeQuery(sql, params, connectionName);
|
||||||
params = this.sqlParams;
|
|
||||||
}
|
|
||||||
let newParams = [];
|
|
||||||
newParams = newParams.concat(params);
|
|
||||||
this.sqlParams = [];
|
|
||||||
for (const param of newParams) {
|
|
||||||
SqlString.escape(param);
|
|
||||||
}
|
|
||||||
return await this.getOrmManager(connectionName).query(sql, newParams || []);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -126,7 +127,7 @@ export abstract class BaseService {
|
|||||||
* @param connectionName 连接名称
|
* @param connectionName 连接名称
|
||||||
*/
|
*/
|
||||||
getOrmManager(connectionName = "default") {
|
getOrmManager(connectionName = "default") {
|
||||||
return this.typeORMDataSourceManager.getDataSource(connectionName);
|
return this.service.getOrmManager(connectionName);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -141,32 +142,7 @@ export abstract class BaseService {
|
|||||||
query,
|
query,
|
||||||
autoSort = true
|
autoSort = true
|
||||||
) {
|
) {
|
||||||
const {
|
return await this.service.entityRenderPage(find, query, autoSort);
|
||||||
size = this._coolConfig.crud.pageSize,
|
|
||||||
page = 1,
|
|
||||||
order = "createTime",
|
|
||||||
sort = "desc",
|
|
||||||
isExport = false,
|
|
||||||
maxExportLimit,
|
|
||||||
} = query;
|
|
||||||
const count = await find.getCount();
|
|
||||||
let dataFind: SelectQueryBuilder<any>;
|
|
||||||
if (isExport && maxExportLimit > 0) {
|
|
||||||
dataFind = find.limit(maxExportLimit);
|
|
||||||
} else {
|
|
||||||
dataFind = find.offset((page - 1) * size).limit(size);
|
|
||||||
}
|
|
||||||
if (autoSort) {
|
|
||||||
find.addOrderBy(order, sort.toUpperCase());
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
list: await dataFind.getMany(),
|
|
||||||
pagination: {
|
|
||||||
page: parseInt(page),
|
|
||||||
size: parseInt(size),
|
|
||||||
total: count,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -177,58 +153,7 @@ export abstract class BaseService {
|
|||||||
* @param connectionName 连接名称
|
* @param connectionName 连接名称
|
||||||
*/
|
*/
|
||||||
async sqlRenderPage(sql, query, autoSort = true, connectionName?) {
|
async sqlRenderPage(sql, query, autoSort = true, connectionName?) {
|
||||||
const {
|
return await this.service.sqlRenderPage(sql, query, autoSort, connectionName);
|
||||||
size = this._coolConfig.crud.pageSize,
|
|
||||||
page = 1,
|
|
||||||
order = "createTime",
|
|
||||||
sort = "desc",
|
|
||||||
isExport = false,
|
|
||||||
maxExportLimit,
|
|
||||||
} = query;
|
|
||||||
if (order && sort && autoSort) {
|
|
||||||
if (!(await this.paramSafetyCheck(order + sort))) {
|
|
||||||
throw new CoolValidateException("非法传参~");
|
|
||||||
}
|
|
||||||
sql += ` ORDER BY ${SqlString.escapeId(order)} ${this.checkSort(sort)}`;
|
|
||||||
}
|
|
||||||
if (isExport && maxExportLimit > 0) {
|
|
||||||
this.sqlParams.push(parseInt(maxExportLimit));
|
|
||||||
sql += " LIMIT ? ";
|
|
||||||
}
|
|
||||||
if (!isExport) {
|
|
||||||
this.sqlParams.push((page - 1) * size);
|
|
||||||
this.sqlParams.push(parseInt(size));
|
|
||||||
sql += " LIMIT ?,? ";
|
|
||||||
}
|
|
||||||
|
|
||||||
let params = [];
|
|
||||||
params = params.concat(this.sqlParams);
|
|
||||||
const result = await this.nativeQuery(sql, params, connectionName);
|
|
||||||
const countResult = await this.nativeQuery(
|
|
||||||
this.getCountSql(sql),
|
|
||||||
params,
|
|
||||||
connectionName
|
|
||||||
);
|
|
||||||
return {
|
|
||||||
list: result,
|
|
||||||
pagination: {
|
|
||||||
page: parseInt(page),
|
|
||||||
size: parseInt(size),
|
|
||||||
total: parseInt(countResult[0] ? countResult[0].count : 0),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 检查排序
|
|
||||||
* @param sort 排序
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
private checkSort(sort) {
|
|
||||||
if (!["desc", "asc"].includes(sort.toLowerCase())) {
|
|
||||||
throw new CoolValidateException("sort 非法传参~");
|
|
||||||
}
|
|
||||||
return sort;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -237,17 +162,7 @@ export abstract class BaseService {
|
|||||||
* @param infoIgnoreProperty 忽略返回属性
|
* @param infoIgnoreProperty 忽略返回属性
|
||||||
*/
|
*/
|
||||||
async info(id: any, infoIgnoreProperty?: string[]) {
|
async info(id: any, infoIgnoreProperty?: string[]) {
|
||||||
if (!this.entity) throw new CoolValidateException(ERRINFO.NOENTITY);
|
return await this.service.info(id, infoIgnoreProperty);
|
||||||
if (!id) {
|
|
||||||
throw new CoolValidateException(ERRINFO.NOID);
|
|
||||||
}
|
|
||||||
const info = await this.entity.findOneBy({ id });
|
|
||||||
if (info && infoIgnoreProperty) {
|
|
||||||
for (const property of infoIgnoreProperty) {
|
|
||||||
delete info[property];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return info;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -255,16 +170,8 @@ export abstract class BaseService {
|
|||||||
* @param ids 删除的ID集合 如:[1,2,3] 或者 1,2,3
|
* @param ids 删除的ID集合 如:[1,2,3] 或者 1,2,3
|
||||||
*/
|
*/
|
||||||
async delete(ids: any) {
|
async delete(ids: any) {
|
||||||
if (!this.entity) throw new CoolValidateException(ERRINFO.NOENTITY);
|
|
||||||
await this.modifyBefore(ids, "delete");
|
await this.modifyBefore(ids, "delete");
|
||||||
if (ids instanceof String) {
|
await this.service.delete(ids);
|
||||||
ids = ids.split(",");
|
|
||||||
}
|
|
||||||
// 启动软删除发送事件
|
|
||||||
if (this._coolConfig.crud?.softDelete) {
|
|
||||||
this.softDelete(ids);
|
|
||||||
}
|
|
||||||
await this.entity.delete(ids);
|
|
||||||
await this.modifyAfter(ids, "delete");
|
await this.modifyAfter(ids, "delete");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -274,21 +181,7 @@ export abstract class BaseService {
|
|||||||
* @param entity 实体
|
* @param entity 实体
|
||||||
*/
|
*/
|
||||||
async softDelete(ids: number[], entity?: Repository<any>) {
|
async softDelete(ids: number[], entity?: Repository<any>) {
|
||||||
const data = await this.entity.find({
|
await this.service.softDelete(ids, entity);
|
||||||
where: {
|
|
||||||
id: In(ids),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
if (_.isEmpty(data)) return;
|
|
||||||
const _entity = entity ? entity : this.entity;
|
|
||||||
const params = {
|
|
||||||
data,
|
|
||||||
ctx: this.baseCtx,
|
|
||||||
entity: _entity,
|
|
||||||
};
|
|
||||||
if (data.length > 0) {
|
|
||||||
this.coolEventManager.emit(EVENT.SOFT_DELETE, params);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -297,10 +190,9 @@ export abstract class BaseService {
|
|||||||
*/
|
*/
|
||||||
async update(param: any) {
|
async update(param: any) {
|
||||||
if (!this.entity) throw new CoolValidateException(ERRINFO.NOENTITY);
|
if (!this.entity) throw new CoolValidateException(ERRINFO.NOENTITY);
|
||||||
await this.modifyBefore(param, "update");
|
|
||||||
if (!param.id && !(param instanceof Array))
|
if (!param.id && !(param instanceof Array))
|
||||||
throw new CoolValidateException(ERRINFO.NOID);
|
throw new CoolValidateException(ERRINFO.NOID);
|
||||||
await this.addOrUpdate(param, 'update');
|
await this.addOrUpdate(param,'update')
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -309,7 +201,6 @@ export abstract class BaseService {
|
|||||||
*/
|
*/
|
||||||
async add(param: any | any[]): Promise<Object> {
|
async add(param: any | any[]): Promise<Object> {
|
||||||
if (!this.entity) throw new CoolValidateException(ERRINFO.NOENTITY);
|
if (!this.entity) throw new CoolValidateException(ERRINFO.NOENTITY);
|
||||||
await this.modifyBefore(param, "add");
|
|
||||||
await this.addOrUpdate(param, 'add');
|
await this.addOrUpdate(param, 'add');
|
||||||
return {
|
return {
|
||||||
id:
|
id:
|
||||||
@ -328,34 +219,8 @@ export abstract class BaseService {
|
|||||||
* @param param 数据
|
* @param param 数据
|
||||||
*/
|
*/
|
||||||
async addOrUpdate(param: any | any[], type: 'add' | 'update' = 'add') {
|
async addOrUpdate(param: any | any[], type: 'add' | 'update' = 'add') {
|
||||||
if (!this.entity) throw new CoolValidateException(ERRINFO.NOENTITY);
|
await this.modifyBefore(param, type);
|
||||||
delete param.createTime;
|
await this.service.addOrUpdate(param, type);
|
||||||
// 判断是否是批量操作
|
|
||||||
if (param instanceof Array) {
|
|
||||||
param.forEach((item) => {
|
|
||||||
item.updateTime = new Date();
|
|
||||||
item.createTime = new Date();
|
|
||||||
});
|
|
||||||
await this.entity.save(param);
|
|
||||||
} else{
|
|
||||||
const upsert = this._coolConfig.crud?.upsert || 'normal';
|
|
||||||
if (type == 'update') {
|
|
||||||
if(upsert == 'save') {
|
|
||||||
const info = await this.entity.findOneBy({id: param.id})
|
|
||||||
param = {
|
|
||||||
...info,
|
|
||||||
...param
|
|
||||||
}
|
|
||||||
}
|
|
||||||
param.updateTime = new Date();
|
|
||||||
upsert == 'normal'? await this.entity.update(param.id, param): await this.entity.save(param);
|
|
||||||
}
|
|
||||||
if(type =='add'){
|
|
||||||
param.createTime = new Date();
|
|
||||||
param.updateTime = new Date();
|
|
||||||
upsert == 'normal'? await this.entity.insert(param): await this.entity.save(param);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
await this.modifyAfter(param, type);
|
await this.modifyAfter(param, type);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -366,9 +231,7 @@ export abstract class BaseService {
|
|||||||
* @param connectionName 连接名
|
* @param connectionName 连接名
|
||||||
*/
|
*/
|
||||||
async list(query, option, connectionName?): Promise<any> {
|
async list(query, option, connectionName?): Promise<any> {
|
||||||
if (!this.entity) throw new CoolValidateException(ERRINFO.NOENTITY);
|
return await this.service.list(query, option, connectionName);
|
||||||
const sql = await this.getOptionFind(query, option);
|
|
||||||
return this.nativeQuery(sql, [], connectionName);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -378,9 +241,7 @@ export abstract class BaseService {
|
|||||||
* @param connectionName 连接名
|
* @param connectionName 连接名
|
||||||
*/
|
*/
|
||||||
async page(query, option, connectionName?) {
|
async page(query, option, connectionName?) {
|
||||||
if (!this.entity) throw new CoolValidateException(ERRINFO.NOENTITY);
|
return await this.service.page(query, option, connectionName);
|
||||||
const sql = await this.getOptionFind(query, option);
|
|
||||||
return this.sqlRenderPage(sql, query, false, connectionName);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -388,146 +249,8 @@ export abstract class BaseService {
|
|||||||
* @param query 前端查询
|
* @param query 前端查询
|
||||||
* @param option
|
* @param option
|
||||||
*/
|
*/
|
||||||
private async getOptionFind(query, option: QueryOp) {
|
async getOptionFind(query, option: QueryOp) {
|
||||||
let { order = "createTime", sort = "desc", keyWord = "" } = query;
|
return await this.service.getOptionFind(query, option);
|
||||||
const sqlArr = ["SELECT"];
|
|
||||||
const selects = ["a.*"];
|
|
||||||
const find = this.entity.createQueryBuilder("a");
|
|
||||||
if (option) {
|
|
||||||
if (typeof option == "function") {
|
|
||||||
// @ts-ignore
|
|
||||||
option = await option(this.baseCtx, this.baseApp);
|
|
||||||
}
|
|
||||||
// 判断是否有关联查询,有的话取个别名
|
|
||||||
if (!_.isEmpty(option.join)) {
|
|
||||||
for (const item of option.join) {
|
|
||||||
selects.push(`${item.alias}.*`);
|
|
||||||
find[item.type || "leftJoin"](
|
|
||||||
item.entity,
|
|
||||||
item.alias,
|
|
||||||
item.condition
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 默认条件
|
|
||||||
if (option.where) {
|
|
||||||
const wheres =
|
|
||||||
typeof option.where == "function"
|
|
||||||
? await option.where(this.baseCtx, this.baseApp)
|
|
||||||
: option.where;
|
|
||||||
if (!_.isEmpty(wheres)) {
|
|
||||||
for (const item of wheres) {
|
|
||||||
if (
|
|
||||||
item.length == 2 ||
|
|
||||||
(item.length == 3 &&
|
|
||||||
(item[2] || (item[2] === 0 && item[2] != "")))
|
|
||||||
) {
|
|
||||||
for (const key in item[1]) {
|
|
||||||
this.sqlParams.push(item[1][key]);
|
|
||||||
}
|
|
||||||
find.andWhere(item[0], item[1]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 附加排序
|
|
||||||
if (!_.isEmpty(option.addOrderBy)) {
|
|
||||||
for (const key in option.addOrderBy) {
|
|
||||||
if (order && order == key) {
|
|
||||||
sort = option.addOrderBy[key].toUpperCase();
|
|
||||||
}
|
|
||||||
find.addOrderBy(
|
|
||||||
SqlString.escapeId(key),
|
|
||||||
this.checkSort(option.addOrderBy[key].toUpperCase())
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 关键字模糊搜索
|
|
||||||
if (keyWord || (keyWord == 0 && keyWord != "")) {
|
|
||||||
keyWord = `%${keyWord}%`;
|
|
||||||
find.andWhere(
|
|
||||||
new Brackets((qb) => {
|
|
||||||
const keyWordLikeFields = option.keyWordLikeFields;
|
|
||||||
for (let i = 0; i < option.keyWordLikeFields?.length || 0; i++) {
|
|
||||||
qb.orWhere(`${keyWordLikeFields[i]} like :keyWord`, {
|
|
||||||
keyWord,
|
|
||||||
});
|
|
||||||
this.sqlParams.push(keyWord);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
// 筛选字段
|
|
||||||
if (!_.isEmpty(option.select)) {
|
|
||||||
sqlArr.push(option.select.join(","));
|
|
||||||
find.select(option.select);
|
|
||||||
} else {
|
|
||||||
sqlArr.push(selects.join(","));
|
|
||||||
}
|
|
||||||
// 字段全匹配
|
|
||||||
if (!_.isEmpty(option.fieldEq)) {
|
|
||||||
for (let key of option.fieldEq) {
|
|
||||||
const c = {};
|
|
||||||
// 如果key有包含.的情况下操作
|
|
||||||
if(typeof key === "string" && key.includes('.')){
|
|
||||||
const keys = key.split('.');
|
|
||||||
const lastKey = keys.pop();
|
|
||||||
key = {requestParam: lastKey, column: key};
|
|
||||||
}
|
|
||||||
// 单表字段无别名的情况下操作
|
|
||||||
if (typeof key === "string") {
|
|
||||||
if (query[key] || (query[key] == 0 && query[key] == "")) {
|
|
||||||
c[key] = query[key];
|
|
||||||
const eq = query[key] instanceof Array ? "in" : "=";
|
|
||||||
if (eq === "in") {
|
|
||||||
find.andWhere(`${key} ${eq} (:${key})`, c);
|
|
||||||
} else {
|
|
||||||
find.andWhere(`${key} ${eq} :${key}`, c);
|
|
||||||
}
|
|
||||||
this.sqlParams.push(query[key]);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (
|
|
||||||
query[key.requestParam] ||
|
|
||||||
(query[key.requestParam] == 0 && query[key.requestParam] !== "")
|
|
||||||
) {
|
|
||||||
c[key.column] = query[key.requestParam];
|
|
||||||
const eq = query[key.requestParam] instanceof Array ? "in" : "=";
|
|
||||||
if (eq === "in") {
|
|
||||||
find.andWhere(`${key.column} ${eq} (:${key.column})`, c);
|
|
||||||
} else {
|
|
||||||
find.andWhere(`${key.column} ${eq} :${key.column}`, c);
|
|
||||||
}
|
|
||||||
this.sqlParams.push(query[key.requestParam]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
sqlArr.push(selects.join(","));
|
|
||||||
}
|
|
||||||
// 接口请求的排序
|
|
||||||
if (sort && order) {
|
|
||||||
const sorts = sort.toUpperCase().split(",");
|
|
||||||
const orders = order.split(",");
|
|
||||||
if (sorts.length != orders.length) {
|
|
||||||
throw new CoolValidateException(ERRINFO.SORTFIELD);
|
|
||||||
}
|
|
||||||
for (const i in sorts) {
|
|
||||||
find.addOrderBy(
|
|
||||||
SqlString.escapeId(orders[i]),
|
|
||||||
this.checkSort(sorts[i])
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (option?.extend) {
|
|
||||||
await option?.extend(find, this.baseCtx, this.baseApp);
|
|
||||||
}
|
|
||||||
const sqls = find.getSql().split("FROM");
|
|
||||||
sqlArr.push("FROM");
|
|
||||||
// 取sqls的最后一个
|
|
||||||
sqlArr.push(sqls[sqls.length - 1]);
|
|
||||||
return sqlArr.join(" ");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -547,4 +270,5 @@ export abstract class BaseService {
|
|||||||
data: any,
|
data: any,
|
||||||
type: "delete" | "update" | "add"
|
type: "delete" | "update" | "add"
|
||||||
): Promise<void> {}
|
): Promise<void> {}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
500
core/src/service/mysql.ts
Normal file
500
core/src/service/mysql.ts
Normal file
@ -0,0 +1,500 @@
|
|||||||
|
import { Init, Provide, Inject, App, Config } from "@midwayjs/decorator";
|
||||||
|
import { Scope, ScopeEnum } from "@midwayjs/core";
|
||||||
|
import { CoolValidateException } from "../exception/validate";
|
||||||
|
import { ERRINFO, EVENT } from "../constant/global";
|
||||||
|
import { Application, Context } from "@midwayjs/koa";
|
||||||
|
import * as SqlString from "sqlstring";
|
||||||
|
import { CoolConfig } from "../interface";
|
||||||
|
import { TypeORMDataSourceManager } from "@midwayjs/typeorm";
|
||||||
|
import { Brackets, In, Repository, SelectQueryBuilder } from "typeorm";
|
||||||
|
import { QueryOp } from "../decorator/controller";
|
||||||
|
import * as _ from "lodash";
|
||||||
|
import { CoolEventManager } from "../event";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 服务基类
|
||||||
|
*/
|
||||||
|
@Provide()
|
||||||
|
@Scope(ScopeEnum.Request, { allowDowngrade: true })
|
||||||
|
export abstract class BaseMysqlService {
|
||||||
|
// 分页配置
|
||||||
|
@Config("cool")
|
||||||
|
private _coolConfig: CoolConfig;
|
||||||
|
|
||||||
|
// 模型
|
||||||
|
entity: Repository<any>;
|
||||||
|
|
||||||
|
sqlParams;
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
typeORMDataSourceManager: TypeORMDataSourceManager;
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
coolEventManager: CoolEventManager;
|
||||||
|
|
||||||
|
// 设置模型
|
||||||
|
setEntity(entity: any) {
|
||||||
|
this.entity = entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置请求上下文
|
||||||
|
setCtx(ctx: Context) {
|
||||||
|
this.baseCtx = ctx;
|
||||||
|
}
|
||||||
|
|
||||||
|
@App()
|
||||||
|
baseApp: Application;
|
||||||
|
|
||||||
|
// 设置应用对象
|
||||||
|
setApp(app: Application) {
|
||||||
|
this.baseApp = app;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Inject("ctx")
|
||||||
|
baseCtx: Context;
|
||||||
|
|
||||||
|
// 初始化
|
||||||
|
@Init()
|
||||||
|
init() {
|
||||||
|
this.sqlParams = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置sql
|
||||||
|
* @param condition 条件是否成立
|
||||||
|
* @param sql sql语句
|
||||||
|
* @param params 参数
|
||||||
|
*/
|
||||||
|
setSql(condition, sql, params) {
|
||||||
|
let rSql = false;
|
||||||
|
if (condition || (condition === 0 && condition !== "")) {
|
||||||
|
rSql = true;
|
||||||
|
this.sqlParams = this.sqlParams.concat(params);
|
||||||
|
}
|
||||||
|
return rSql ? sql : "";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得查询个数的SQL
|
||||||
|
* @param sql
|
||||||
|
*/
|
||||||
|
getCountSql(sql) {
|
||||||
|
sql = sql
|
||||||
|
.replace(new RegExp("LIMIT", "gm"), "limit ")
|
||||||
|
.replace(new RegExp("\n", "gm"), " ");
|
||||||
|
if (sql.includes("limit")) {
|
||||||
|
const sqlArr = sql.split("limit ");
|
||||||
|
sqlArr.pop();
|
||||||
|
sql = sqlArr.join("limit ");
|
||||||
|
}
|
||||||
|
return `select count(*) as count from (${sql}) a`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 参数安全性检查
|
||||||
|
* @param params
|
||||||
|
*/
|
||||||
|
async paramSafetyCheck(params) {
|
||||||
|
const lp = params.toLowerCase();
|
||||||
|
return !(
|
||||||
|
lp.indexOf("update ") > -1 ||
|
||||||
|
lp.indexOf("select ") > -1 ||
|
||||||
|
lp.indexOf("delete ") > -1 ||
|
||||||
|
lp.indexOf("insert ") > -1
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 原生查询
|
||||||
|
* @param sql
|
||||||
|
* @param params
|
||||||
|
* @param connectionName
|
||||||
|
*/
|
||||||
|
async nativeQuery(sql, params?, connectionName?) {
|
||||||
|
if (_.isEmpty(params)) {
|
||||||
|
params = this.sqlParams;
|
||||||
|
}
|
||||||
|
let newParams = [];
|
||||||
|
newParams = newParams.concat(params);
|
||||||
|
this.sqlParams = [];
|
||||||
|
for (const param of newParams) {
|
||||||
|
SqlString.escape(param);
|
||||||
|
}
|
||||||
|
return await this.getOrmManager(connectionName).query(sql, newParams || []);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得ORM管理
|
||||||
|
* @param connectionName 连接名称
|
||||||
|
*/
|
||||||
|
getOrmManager(connectionName = "default") {
|
||||||
|
return this.typeORMDataSourceManager.getDataSource(connectionName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 操作entity获得分页数据,不用写sql
|
||||||
|
* @param find QueryBuilder
|
||||||
|
* @param query
|
||||||
|
* @param autoSort
|
||||||
|
* @param connectionName
|
||||||
|
*/
|
||||||
|
async entityRenderPage(
|
||||||
|
find: SelectQueryBuilder<any>,
|
||||||
|
query,
|
||||||
|
autoSort = true
|
||||||
|
) {
|
||||||
|
const {
|
||||||
|
size = this._coolConfig.crud.pageSize,
|
||||||
|
page = 1,
|
||||||
|
order = "createTime",
|
||||||
|
sort = "desc",
|
||||||
|
isExport = false,
|
||||||
|
maxExportLimit,
|
||||||
|
} = query;
|
||||||
|
const count = await find.getCount();
|
||||||
|
let dataFind: SelectQueryBuilder<any>;
|
||||||
|
if (isExport && maxExportLimit > 0) {
|
||||||
|
dataFind = find.limit(maxExportLimit);
|
||||||
|
} else {
|
||||||
|
dataFind = find.offset((page - 1) * size).limit(size);
|
||||||
|
}
|
||||||
|
if (autoSort) {
|
||||||
|
find.addOrderBy(order, sort.toUpperCase());
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
list: await dataFind.getMany(),
|
||||||
|
pagination: {
|
||||||
|
page: parseInt(page),
|
||||||
|
size: parseInt(size),
|
||||||
|
total: count,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行SQL并获得分页数据
|
||||||
|
* @param sql 执行的sql语句
|
||||||
|
* @param query 分页查询条件
|
||||||
|
* @param autoSort 是否自动排序
|
||||||
|
* @param connectionName 连接名称
|
||||||
|
*/
|
||||||
|
async sqlRenderPage(sql, query, autoSort = true, connectionName?) {
|
||||||
|
const {
|
||||||
|
size = this._coolConfig.crud.pageSize,
|
||||||
|
page = 1,
|
||||||
|
order = "createTime",
|
||||||
|
sort = "desc",
|
||||||
|
isExport = false,
|
||||||
|
maxExportLimit,
|
||||||
|
} = query;
|
||||||
|
if (order && sort && autoSort) {
|
||||||
|
if (!(await this.paramSafetyCheck(order + sort))) {
|
||||||
|
throw new CoolValidateException("非法传参~");
|
||||||
|
}
|
||||||
|
sql += ` ORDER BY ${SqlString.escapeId(order)} ${this.checkSort(sort)}`;
|
||||||
|
}
|
||||||
|
if (isExport && maxExportLimit > 0) {
|
||||||
|
this.sqlParams.push(parseInt(maxExportLimit));
|
||||||
|
sql += " LIMIT ? ";
|
||||||
|
}
|
||||||
|
if (!isExport) {
|
||||||
|
this.sqlParams.push((page - 1) * size);
|
||||||
|
this.sqlParams.push(parseInt(size));
|
||||||
|
sql += " LIMIT ?,? ";
|
||||||
|
}
|
||||||
|
|
||||||
|
let params = [];
|
||||||
|
params = params.concat(this.sqlParams);
|
||||||
|
const result = await this.nativeQuery(sql, params, connectionName);
|
||||||
|
const countResult = await this.nativeQuery(
|
||||||
|
this.getCountSql(sql),
|
||||||
|
params,
|
||||||
|
connectionName
|
||||||
|
);
|
||||||
|
return {
|
||||||
|
list: result,
|
||||||
|
pagination: {
|
||||||
|
page: parseInt(page),
|
||||||
|
size: parseInt(size),
|
||||||
|
total: parseInt(countResult[0] ? countResult[0].count : 0),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查排序
|
||||||
|
* @param sort 排序
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
checkSort(sort) {
|
||||||
|
if (!["desc", "asc"].includes(sort.toLowerCase())) {
|
||||||
|
throw new CoolValidateException("sort 非法传参~");
|
||||||
|
}
|
||||||
|
return sort;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得单个ID
|
||||||
|
* @param id ID
|
||||||
|
* @param infoIgnoreProperty 忽略返回属性
|
||||||
|
*/
|
||||||
|
async info(id: any, infoIgnoreProperty?: string[]) {
|
||||||
|
if (!this.entity) throw new CoolValidateException(ERRINFO.NOENTITY);
|
||||||
|
if (!id) {
|
||||||
|
throw new CoolValidateException(ERRINFO.NOID);
|
||||||
|
}
|
||||||
|
const info = await this.entity.findOneBy({ id });
|
||||||
|
if (info && infoIgnoreProperty) {
|
||||||
|
for (const property of infoIgnoreProperty) {
|
||||||
|
delete info[property];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除
|
||||||
|
* @param ids 删除的ID集合 如:[1,2,3] 或者 1,2,3
|
||||||
|
*/
|
||||||
|
async delete(ids: any) {
|
||||||
|
if (!this.entity) throw new CoolValidateException(ERRINFO.NOENTITY);
|
||||||
|
if (ids instanceof String) {
|
||||||
|
ids = ids.split(",");
|
||||||
|
}
|
||||||
|
// 启动软删除发送事件
|
||||||
|
if (this._coolConfig.crud?.softDelete) {
|
||||||
|
this.softDelete(ids);
|
||||||
|
}
|
||||||
|
await this.entity.delete(ids);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 软删除
|
||||||
|
* @param ids 删除的ID数组
|
||||||
|
* @param entity 实体
|
||||||
|
*/
|
||||||
|
async softDelete(ids: number[], entity?: Repository<any>) {
|
||||||
|
const data = await this.entity.find({
|
||||||
|
where: {
|
||||||
|
id: In(ids),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
if (_.isEmpty(data)) return;
|
||||||
|
const _entity = entity ? entity : this.entity;
|
||||||
|
const params = {
|
||||||
|
data,
|
||||||
|
ctx: this.baseCtx,
|
||||||
|
entity: _entity,
|
||||||
|
};
|
||||||
|
if (data.length > 0) {
|
||||||
|
this.coolEventManager.emit(EVENT.SOFT_DELETE, params);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 新增|修改
|
||||||
|
* @param param 数据
|
||||||
|
*/
|
||||||
|
async addOrUpdate(param: any | any[], type: 'add' | 'update' = 'add') {
|
||||||
|
if (!this.entity) throw new CoolValidateException(ERRINFO.NOENTITY);
|
||||||
|
delete param.createTime;
|
||||||
|
// 判断是否是批量操作
|
||||||
|
if (param instanceof Array) {
|
||||||
|
param.forEach((item) => {
|
||||||
|
item.updateTime = new Date();
|
||||||
|
item.createTime = new Date();
|
||||||
|
});
|
||||||
|
await this.entity.save(param);
|
||||||
|
} else{
|
||||||
|
const upsert = this._coolConfig.crud?.upsert || 'normal';
|
||||||
|
if (type == 'update') {
|
||||||
|
if(upsert == 'save') {
|
||||||
|
const info = await this.entity.findOneBy({id: param.id})
|
||||||
|
param = {
|
||||||
|
...info,
|
||||||
|
...param
|
||||||
|
}
|
||||||
|
}
|
||||||
|
param.updateTime = new Date();
|
||||||
|
upsert == 'normal'? await this.entity.update(param.id, param): await this.entity.save(param);
|
||||||
|
}
|
||||||
|
if(type =='add'){
|
||||||
|
param.createTime = new Date();
|
||||||
|
param.updateTime = new Date();
|
||||||
|
upsert == 'normal'? await this.entity.insert(param): await this.entity.save(param);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 非分页查询
|
||||||
|
* @param query 查询条件
|
||||||
|
* @param option 查询配置
|
||||||
|
* @param connectionName 连接名
|
||||||
|
*/
|
||||||
|
async list(query, option, connectionName?): Promise<any> {
|
||||||
|
if (!this.entity) throw new CoolValidateException(ERRINFO.NOENTITY);
|
||||||
|
const sql = await this.getOptionFind(query, option);
|
||||||
|
return this.nativeQuery(sql, [], connectionName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分页查询
|
||||||
|
* @param query 查询条件
|
||||||
|
* @param option 查询配置
|
||||||
|
* @param connectionName 连接名
|
||||||
|
*/
|
||||||
|
async page(query, option, connectionName?) {
|
||||||
|
if (!this.entity) throw new CoolValidateException(ERRINFO.NOENTITY);
|
||||||
|
const sql = await this.getOptionFind(query, option);
|
||||||
|
return this.sqlRenderPage(sql, query, false, connectionName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建查询配置
|
||||||
|
* @param query 前端查询
|
||||||
|
* @param option
|
||||||
|
*/
|
||||||
|
async getOptionFind(query, option: QueryOp) {
|
||||||
|
let { order = "createTime", sort = "desc", keyWord = "" } = query;
|
||||||
|
const sqlArr = ["SELECT"];
|
||||||
|
const selects = ["a.*"];
|
||||||
|
const find = this.entity.createQueryBuilder("a");
|
||||||
|
if (option) {
|
||||||
|
if (typeof option == "function") {
|
||||||
|
// @ts-ignore
|
||||||
|
option = await option(this.baseCtx, this.baseApp);
|
||||||
|
}
|
||||||
|
// 判断是否有关联查询,有的话取个别名
|
||||||
|
if (!_.isEmpty(option.join)) {
|
||||||
|
for (const item of option.join) {
|
||||||
|
selects.push(`${item.alias}.*`);
|
||||||
|
find[item.type || "leftJoin"](
|
||||||
|
item.entity,
|
||||||
|
item.alias,
|
||||||
|
item.condition
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 默认条件
|
||||||
|
if (option.where) {
|
||||||
|
const wheres =
|
||||||
|
typeof option.where == "function"
|
||||||
|
? await option.where(this.baseCtx, this.baseApp)
|
||||||
|
: option.where;
|
||||||
|
if (!_.isEmpty(wheres)) {
|
||||||
|
for (const item of wheres) {
|
||||||
|
if (
|
||||||
|
item.length == 2 ||
|
||||||
|
(item.length == 3 &&
|
||||||
|
(item[2] || (item[2] === 0 && item[2] != "")))
|
||||||
|
) {
|
||||||
|
for (const key in item[1]) {
|
||||||
|
this.sqlParams.push(item[1][key]);
|
||||||
|
}
|
||||||
|
find.andWhere(item[0], item[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 附加排序
|
||||||
|
if (!_.isEmpty(option.addOrderBy)) {
|
||||||
|
for (const key in option.addOrderBy) {
|
||||||
|
if (order && order == key) {
|
||||||
|
sort = option.addOrderBy[key].toUpperCase();
|
||||||
|
}
|
||||||
|
find.addOrderBy(
|
||||||
|
SqlString.escapeId(key),
|
||||||
|
this.checkSort(option.addOrderBy[key].toUpperCase())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 关键字模糊搜索
|
||||||
|
if (keyWord || (keyWord == 0 && keyWord != "")) {
|
||||||
|
keyWord = `%${keyWord}%`;
|
||||||
|
find.andWhere(
|
||||||
|
new Brackets((qb) => {
|
||||||
|
const keyWordLikeFields = option.keyWordLikeFields || [];
|
||||||
|
for (let i = 0; i < option.keyWordLikeFields?.length || 0; i++) {
|
||||||
|
qb.orWhere(`${keyWordLikeFields[i]} like :keyWord`, {
|
||||||
|
keyWord,
|
||||||
|
});
|
||||||
|
this.sqlParams.push(keyWord);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// 筛选字段
|
||||||
|
if (!_.isEmpty(option.select)) {
|
||||||
|
sqlArr.push(option.select.join(","));
|
||||||
|
find.select(option.select);
|
||||||
|
} else {
|
||||||
|
sqlArr.push(selects.join(","));
|
||||||
|
}
|
||||||
|
// 字段全匹配
|
||||||
|
if (!_.isEmpty(option.fieldEq)) {
|
||||||
|
for (let key of option.fieldEq) {
|
||||||
|
const c = {};
|
||||||
|
// 如果key有包含.的情况下操作
|
||||||
|
if(typeof key === "string" && key.includes('.')){
|
||||||
|
const keys = key.split('.');
|
||||||
|
const lastKey = keys.pop();
|
||||||
|
key = {requestParam: lastKey, column: key};
|
||||||
|
}
|
||||||
|
// 单表字段无别名的情况下操作
|
||||||
|
if (typeof key === "string") {
|
||||||
|
if (query[key] || (query[key] == 0 && query[key] == "")) {
|
||||||
|
c[key] = query[key];
|
||||||
|
const eq = query[key] instanceof Array ? "in" : "=";
|
||||||
|
if (eq === "in") {
|
||||||
|
find.andWhere(`${key} ${eq} (:${key})`, c);
|
||||||
|
} else {
|
||||||
|
find.andWhere(`${key} ${eq} :${key}`, c);
|
||||||
|
}
|
||||||
|
this.sqlParams.push(query[key]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (
|
||||||
|
query[key.requestParam] ||
|
||||||
|
(query[key.requestParam] == 0 && query[key.requestParam] !== "")
|
||||||
|
) {
|
||||||
|
c[key.column] = query[key.requestParam];
|
||||||
|
const eq = query[key.requestParam] instanceof Array ? "in" : "=";
|
||||||
|
if (eq === "in") {
|
||||||
|
find.andWhere(`${key.column} ${eq} (:${key.column})`, c);
|
||||||
|
} else {
|
||||||
|
find.andWhere(`${key.column} ${eq} :${key.column}`, c);
|
||||||
|
}
|
||||||
|
this.sqlParams.push(query[key.requestParam]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
sqlArr.push(selects.join(","));
|
||||||
|
}
|
||||||
|
// 接口请求的排序
|
||||||
|
if (sort && order) {
|
||||||
|
const sorts = sort.toUpperCase().split(",");
|
||||||
|
const orders = order.split(",");
|
||||||
|
if (sorts.length != orders.length) {
|
||||||
|
throw new CoolValidateException(ERRINFO.SORTFIELD);
|
||||||
|
}
|
||||||
|
for (const i in sorts) {
|
||||||
|
find.addOrderBy(
|
||||||
|
SqlString.escapeId(orders[i]),
|
||||||
|
this.checkSort(sorts[i])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (option?.extend) {
|
||||||
|
await option?.extend(find, this.baseCtx, this.baseApp);
|
||||||
|
}
|
||||||
|
const sqls = find.getSql().split("FROM");
|
||||||
|
sqlArr.push("FROM");
|
||||||
|
// 取sqls的最后一个
|
||||||
|
sqlArr.push(sqls[sqls.length - 1]);
|
||||||
|
return sqlArr.join(" ");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
596
core/src/service/postgres.ts
Normal file
596
core/src/service/postgres.ts
Normal file
@ -0,0 +1,596 @@
|
|||||||
|
import { Init, Provide, Inject, App, Config } from "@midwayjs/decorator";
|
||||||
|
import { CoolValidateException } from "../exception/validate";
|
||||||
|
import { ERRINFO, EVENT } from "../constant/global";
|
||||||
|
import { Application, Context } from "@midwayjs/koa";
|
||||||
|
import { Scope, ScopeEnum } from "@midwayjs/core";
|
||||||
|
import { CoolConfig } from "../interface";
|
||||||
|
import { TypeORMDataSourceManager } from "@midwayjs/typeorm";
|
||||||
|
import { Brackets, In, Repository, SelectQueryBuilder } from "typeorm";
|
||||||
|
import { QueryOp } from "../decorator/controller";
|
||||||
|
import * as _ from "lodash";
|
||||||
|
import { CoolEventManager } from "../event";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 服务基类
|
||||||
|
*/
|
||||||
|
@Provide()
|
||||||
|
@Scope(ScopeEnum.Request, { allowDowngrade: true })
|
||||||
|
export abstract class BasePgService {
|
||||||
|
// 分页配置
|
||||||
|
@Config("cool")
|
||||||
|
private _coolConfig: CoolConfig;
|
||||||
|
|
||||||
|
// 模型
|
||||||
|
entity: Repository<any>;
|
||||||
|
|
||||||
|
sqlParams;
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
typeORMDataSourceManager: TypeORMDataSourceManager;
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
coolEventManager: CoolEventManager;
|
||||||
|
|
||||||
|
// 设置模型
|
||||||
|
setEntity(entity: any) {
|
||||||
|
this.entity = entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置请求上下文
|
||||||
|
setCtx(ctx: Context) {
|
||||||
|
this.baseCtx = ctx;
|
||||||
|
}
|
||||||
|
|
||||||
|
@App()
|
||||||
|
baseApp: Application;
|
||||||
|
|
||||||
|
// 设置应用对象
|
||||||
|
setApp(app: Application) {
|
||||||
|
this.baseApp = app;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Inject("ctx")
|
||||||
|
baseCtx: Context;
|
||||||
|
|
||||||
|
// 初始化
|
||||||
|
@Init()
|
||||||
|
init() {
|
||||||
|
this.sqlParams = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置sql
|
||||||
|
* @param condition 条件是否成立
|
||||||
|
* @param sql sql语句
|
||||||
|
* @param params 参数
|
||||||
|
*/
|
||||||
|
setSql(condition, sql, params) {
|
||||||
|
let rSql = false;
|
||||||
|
if (condition || (condition === 0 && condition !== "")) {
|
||||||
|
rSql = true;
|
||||||
|
for(let i = 0; i < params.length; i++) {
|
||||||
|
const param = params[i];
|
||||||
|
if (param instanceof Array) {
|
||||||
|
// 将这个? 替换成 $1,$2,$3
|
||||||
|
const replaceStr = [];
|
||||||
|
for(let j = 0; j < param.length; j++) {
|
||||||
|
replaceStr.push('$' + (this.sqlParams.length + j + 1));
|
||||||
|
}
|
||||||
|
this.sqlParams = this.sqlParams.concat(...params);
|
||||||
|
sql = sql.replace('?', replaceStr.join(','));
|
||||||
|
} else {
|
||||||
|
sql = sql.replace('?', '$' + (this.sqlParams.length + 1));
|
||||||
|
this.sqlParams.push(param);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return rSql ? sql : "";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得查询个数的SQL
|
||||||
|
* @param sql
|
||||||
|
*/
|
||||||
|
getCountSql(sql) {
|
||||||
|
sql = sql
|
||||||
|
.replace(new RegExp("LIMIT", "gm"), "limit ")
|
||||||
|
.replace(new RegExp("\n", "gm"), " ");
|
||||||
|
if (sql.includes("limit")) {
|
||||||
|
const sqlArr = sql.split("limit ");
|
||||||
|
sqlArr.pop();
|
||||||
|
sql = sqlArr.join("limit ");
|
||||||
|
}
|
||||||
|
return `select count(*) as count from (${sql}) a`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 参数安全性检查
|
||||||
|
* @param params
|
||||||
|
*/
|
||||||
|
async paramSafetyCheck(params) {
|
||||||
|
const lp = params.toLowerCase();
|
||||||
|
return !(
|
||||||
|
lp.indexOf("update ") > -1 ||
|
||||||
|
lp.indexOf("select ") > -1 ||
|
||||||
|
lp.indexOf("delete ") > -1 ||
|
||||||
|
lp.indexOf("insert ") > -1
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 原生查询
|
||||||
|
* @param sql
|
||||||
|
* @param params
|
||||||
|
* @param connectionName
|
||||||
|
*/
|
||||||
|
async nativeQuery(sql, params?, connectionName?) {
|
||||||
|
sql = this.convertToPostgres(sql);
|
||||||
|
if (_.isEmpty(params)) {
|
||||||
|
params = this.sqlParams;
|
||||||
|
}
|
||||||
|
let newParams = [];
|
||||||
|
// sql没处理过?的情况下
|
||||||
|
if(sql.includes('?')){
|
||||||
|
for (const item of params) {
|
||||||
|
// 如果是数组,将这个? 替换成 $1,$2,$3
|
||||||
|
if (item instanceof Array) {
|
||||||
|
const replaceStr = [];
|
||||||
|
for(let i = 0; i < item.length; i++) {
|
||||||
|
replaceStr.push('$' + (newParams.length + i + 1));
|
||||||
|
}
|
||||||
|
newParams.push(...item)
|
||||||
|
sql = sql.replace('?', replaceStr.join(','));
|
||||||
|
} else {
|
||||||
|
sql = sql.replace('?', '$' + (newParams.length + 1));
|
||||||
|
newParams.push(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
newParams = params;
|
||||||
|
}
|
||||||
|
this.sqlParams = [];
|
||||||
|
return await this.getOrmManager(connectionName).query(sql, newParams || []);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得ORM管理
|
||||||
|
* @param connectionName 连接名称
|
||||||
|
*/
|
||||||
|
getOrmManager(connectionName = "default") {
|
||||||
|
return this.typeORMDataSourceManager.getDataSource(connectionName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 操作entity获得分页数据,不用写sql
|
||||||
|
* @param find QueryBuilder
|
||||||
|
* @param query
|
||||||
|
* @param autoSort
|
||||||
|
* @param connectionName
|
||||||
|
*/
|
||||||
|
async entityRenderPage(
|
||||||
|
find: SelectQueryBuilder<any>,
|
||||||
|
query,
|
||||||
|
autoSort = true
|
||||||
|
) {
|
||||||
|
const {
|
||||||
|
size = this._coolConfig.crud.pageSize,
|
||||||
|
page = 1,
|
||||||
|
order = "createTime",
|
||||||
|
sort = "desc",
|
||||||
|
isExport = false,
|
||||||
|
maxExportLimit,
|
||||||
|
} = query;
|
||||||
|
const count = await find.getCount();
|
||||||
|
let dataFind: SelectQueryBuilder<any>;
|
||||||
|
if (isExport && maxExportLimit > 0) {
|
||||||
|
dataFind = find.limit(maxExportLimit);
|
||||||
|
} else {
|
||||||
|
dataFind = find.offset((page - 1) * size).limit(size);
|
||||||
|
}
|
||||||
|
if (autoSort) {
|
||||||
|
find.addOrderBy(order, sort.toUpperCase());
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
list: await dataFind.getMany(),
|
||||||
|
pagination: {
|
||||||
|
page: parseInt(page),
|
||||||
|
size: parseInt(size),
|
||||||
|
total: count,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将mysql语句转换为postgres语句
|
||||||
|
* @param sql
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
protected convertToPostgres(sql) {
|
||||||
|
// 首先确保表名被正确引用
|
||||||
|
sql = sql.replace(/(?<!")(\b\w+\b)\.(?!\w+")/g, '"$1".');
|
||||||
|
// 然后确保字段名被正确引用
|
||||||
|
return sql.replace(/\.(\w+)(?!\w)/g, '."$1"');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询sql中的参数个数
|
||||||
|
* @param sql
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
protected countDollarSigns(sql) {
|
||||||
|
const matches = sql.match(/\$\d+/g);
|
||||||
|
return matches ? matches.length : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行SQL并获得分页数据
|
||||||
|
* @param sql 执行的sql语句
|
||||||
|
* @param query 分页查询条件
|
||||||
|
* @param autoSort 是否自动排序
|
||||||
|
* @param connectionName 连接名称
|
||||||
|
*/
|
||||||
|
async sqlRenderPage(sql, query, autoSort = true, connectionName?) {
|
||||||
|
const {
|
||||||
|
size = this._coolConfig.crud.pageSize,
|
||||||
|
page = 1,
|
||||||
|
order = "createTime",
|
||||||
|
sort = "desc",
|
||||||
|
isExport = false,
|
||||||
|
maxExportLimit,
|
||||||
|
} = query;
|
||||||
|
sql = `SELECT * FROM (${sql}) a `
|
||||||
|
if (order && sort && autoSort) {
|
||||||
|
if (!(await this.paramSafetyCheck(order + sort))) {
|
||||||
|
throw new CoolValidateException("非法传参~");
|
||||||
|
}
|
||||||
|
sql += `ORDER BY a."${order}" ${this.checkSort(sort)}`
|
||||||
|
}
|
||||||
|
let cutParams = 0;
|
||||||
|
let paramCount = this.countDollarSigns(sql);
|
||||||
|
if (isExport && maxExportLimit > 0) {
|
||||||
|
this.sqlParams.push(parseInt(maxExportLimit));
|
||||||
|
cutParams = 1;
|
||||||
|
sql += ` LIMIT $${paramCount+1}`;
|
||||||
|
}
|
||||||
|
if (!isExport) {
|
||||||
|
this.sqlParams.push(parseInt(size));
|
||||||
|
this.sqlParams.push((page - 1) * size);
|
||||||
|
cutParams = 2;
|
||||||
|
sql += ` LIMIT $${ paramCount + 1} OFFSET $${ paramCount+ 2 }`;
|
||||||
|
}
|
||||||
|
let params = [];
|
||||||
|
params = params.concat(this.sqlParams);
|
||||||
|
const result = await this.nativeQuery(sql, params, connectionName);
|
||||||
|
params = params.slice(0, -cutParams);
|
||||||
|
const countResult = await this.nativeQuery(
|
||||||
|
this.getCountSql(sql),
|
||||||
|
params,
|
||||||
|
connectionName
|
||||||
|
);
|
||||||
|
return {
|
||||||
|
list: result,
|
||||||
|
pagination: {
|
||||||
|
page: parseInt(page),
|
||||||
|
size: parseInt(size),
|
||||||
|
total: parseInt(countResult[0] ? countResult[0].count : 0),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查排序
|
||||||
|
* @param sort 排序
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
checkSort(sort) {
|
||||||
|
if (!["desc", "asc"].includes(sort.toLowerCase())) {
|
||||||
|
throw new CoolValidateException("sort 非法传参~");
|
||||||
|
}
|
||||||
|
return sort;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得单个ID
|
||||||
|
* @param id ID
|
||||||
|
* @param infoIgnoreProperty 忽略返回属性
|
||||||
|
*/
|
||||||
|
async info(id: any, infoIgnoreProperty?: string[]) {
|
||||||
|
if (!this.entity) throw new CoolValidateException(ERRINFO.NOENTITY);
|
||||||
|
if (!id) {
|
||||||
|
throw new CoolValidateException(ERRINFO.NOID);
|
||||||
|
}
|
||||||
|
const info = await this.entity.findOneBy({ id });
|
||||||
|
if (info && infoIgnoreProperty) {
|
||||||
|
for (const property of infoIgnoreProperty) {
|
||||||
|
delete info[property];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除
|
||||||
|
* @param ids 删除的ID集合 如:[1,2,3] 或者 1,2,3
|
||||||
|
*/
|
||||||
|
async delete(ids: any) {
|
||||||
|
if (!this.entity) throw new CoolValidateException(ERRINFO.NOENTITY);
|
||||||
|
if (ids instanceof String) {
|
||||||
|
ids = ids.split(",");
|
||||||
|
}
|
||||||
|
// 启动软删除发送事件
|
||||||
|
if (this._coolConfig.crud?.softDelete) {
|
||||||
|
this.softDelete(ids);
|
||||||
|
}
|
||||||
|
await this.entity.delete(ids);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 软删除
|
||||||
|
* @param ids 删除的ID数组
|
||||||
|
* @param entity 实体
|
||||||
|
*/
|
||||||
|
async softDelete(ids: number[], entity?: Repository<any>) {
|
||||||
|
const data = await this.entity.find({
|
||||||
|
where: {
|
||||||
|
id: In(ids),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
if (_.isEmpty(data)) return;
|
||||||
|
const _entity = entity ? entity : this.entity;
|
||||||
|
const params = {
|
||||||
|
data,
|
||||||
|
ctx: this.baseCtx,
|
||||||
|
entity: _entity,
|
||||||
|
};
|
||||||
|
if (data.length > 0) {
|
||||||
|
this.coolEventManager.emit(EVENT.SOFT_DELETE, params);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 新增|修改
|
||||||
|
* @param param 数据
|
||||||
|
*/
|
||||||
|
async addOrUpdate(param: any | any[], type: 'add' | 'update' = 'add') {
|
||||||
|
if (!this.entity) throw new CoolValidateException(ERRINFO.NOENTITY);
|
||||||
|
delete param.createTime;
|
||||||
|
// 判断是否是批量操作
|
||||||
|
if (param instanceof Array) {
|
||||||
|
param.forEach((item) => {
|
||||||
|
item.updateTime = new Date();
|
||||||
|
item.createTime = new Date();
|
||||||
|
});
|
||||||
|
await this.entity.save(param);
|
||||||
|
} else{
|
||||||
|
const upsert = this._coolConfig.crud?.upsert || 'normal';
|
||||||
|
if (type == 'update') {
|
||||||
|
if(upsert == 'save') {
|
||||||
|
const info = await this.entity.findOneBy({id: param.id})
|
||||||
|
param = {
|
||||||
|
...info,
|
||||||
|
...param
|
||||||
|
}
|
||||||
|
}
|
||||||
|
param.updateTime = new Date();
|
||||||
|
upsert == 'normal'? await this.entity.update(param.id, param): await this.entity.save(param);
|
||||||
|
}
|
||||||
|
if(type =='add'){
|
||||||
|
param.createTime = new Date();
|
||||||
|
param.updateTime = new Date();
|
||||||
|
upsert == 'normal'? await this.entity.insert(param): await this.entity.save(param);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 非分页查询
|
||||||
|
* @param query 查询条件
|
||||||
|
* @param option 查询配置
|
||||||
|
* @param connectionName 连接名
|
||||||
|
*/
|
||||||
|
async list(query, option, connectionName?): Promise<any> {
|
||||||
|
if (!this.entity) throw new CoolValidateException(ERRINFO.NOENTITY);
|
||||||
|
const sql = await this.getOptionFind(query, option);
|
||||||
|
return this.nativeQuery(sql, [], connectionName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分页查询
|
||||||
|
* @param query 查询条件
|
||||||
|
* @param option 查询配置
|
||||||
|
* @param connectionName 连接名
|
||||||
|
*/
|
||||||
|
async page(query, option, connectionName?) {
|
||||||
|
if (!this.entity) throw new CoolValidateException(ERRINFO.NOENTITY);
|
||||||
|
const sql = await this.getOptionFind(query, option);
|
||||||
|
return this.sqlRenderPage(sql, query, false, connectionName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建查询配置
|
||||||
|
* @param query 前端查询
|
||||||
|
* @param option
|
||||||
|
*/
|
||||||
|
async getOptionFind(query, option: QueryOp) {
|
||||||
|
let { order = "createTime", sort = "desc", keyWord = "" } = query;
|
||||||
|
const sqlArr = ["SELECT"];
|
||||||
|
const selects = ["a.*"];
|
||||||
|
const find = this.entity.createQueryBuilder("a");
|
||||||
|
if (option) {
|
||||||
|
if (typeof option == "function") {
|
||||||
|
// @ts-ignore
|
||||||
|
option = await option(this.baseCtx, this.baseApp);
|
||||||
|
}
|
||||||
|
// 判断是否有关联查询,有的话取个别名
|
||||||
|
if (!_.isEmpty(option.join)) {
|
||||||
|
for (const item of option.join) {
|
||||||
|
selects.push(`${item.alias}.*`);
|
||||||
|
find[item.type || "leftJoin"](
|
||||||
|
item.entity,
|
||||||
|
item.alias,
|
||||||
|
item.condition
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 默认条件
|
||||||
|
if (option.where) {
|
||||||
|
const wheres =
|
||||||
|
typeof option.where == "function"
|
||||||
|
? await option.where(this.baseCtx, this.baseApp)
|
||||||
|
: option.where;
|
||||||
|
if (!_.isEmpty(wheres)) {
|
||||||
|
for (const item of wheres) {
|
||||||
|
if (
|
||||||
|
item.length == 2 ||
|
||||||
|
(item.length == 3 &&
|
||||||
|
(item[2] || (item[2] === 0 && item[2] != "")))
|
||||||
|
) {
|
||||||
|
for (const key in item[1]) {
|
||||||
|
this.sqlParams.push(item[1][key]);
|
||||||
|
}
|
||||||
|
find.andWhere(item[0], item[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 附加排序
|
||||||
|
if (!_.isEmpty(option.addOrderBy)) {
|
||||||
|
for (const key in option.addOrderBy) {
|
||||||
|
if (order && order == key) {
|
||||||
|
sort = option.addOrderBy[key].toUpperCase();
|
||||||
|
}
|
||||||
|
find.addOrderBy(
|
||||||
|
`${this.matchColumn(option?.select, key)}.${key}`,
|
||||||
|
this.checkSort(option.addOrderBy[key].toUpperCase())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 关键字模糊搜索
|
||||||
|
if (keyWord || (keyWord == 0 && keyWord != "")) {
|
||||||
|
keyWord = `%${keyWord}%`;
|
||||||
|
find.andWhere(
|
||||||
|
new Brackets((qb) => {
|
||||||
|
const keyWordLikeFields = option.keyWordLikeFields || [];
|
||||||
|
for (let i = 0; i < option.keyWordLikeFields?.length || 0; i++) {
|
||||||
|
let column = keyWordLikeFields[i];
|
||||||
|
column = column.includes('.')? column: `a.${column}`;
|
||||||
|
const values = {};
|
||||||
|
values[`keyWord${i}`] = keyWord;
|
||||||
|
qb.orWhere(`${column} like :keyWord${i}`,values);
|
||||||
|
this.sqlParams.push(keyWord);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// 筛选字段
|
||||||
|
if (!_.isEmpty(option.select)) {
|
||||||
|
sqlArr.push(option.select.join(","));
|
||||||
|
find.select(option.select);
|
||||||
|
} else {
|
||||||
|
sqlArr.push(selects.join(","));
|
||||||
|
}
|
||||||
|
// 字段全匹配
|
||||||
|
if (!_.isEmpty(option.fieldEq)) {
|
||||||
|
for (let key of option.fieldEq) {
|
||||||
|
const c = {};
|
||||||
|
let column;
|
||||||
|
// 如果key有包含.的情况下操作
|
||||||
|
if(typeof key === "string" && key.includes('.')){
|
||||||
|
const keys = key.split('.');
|
||||||
|
const lastKey = keys.pop();
|
||||||
|
key = {requestParam: lastKey, column: key};
|
||||||
|
column = key
|
||||||
|
}else{
|
||||||
|
column = `a.${key}`
|
||||||
|
}
|
||||||
|
// 单表字段无别名的情况下操作
|
||||||
|
if (typeof key === "string") {
|
||||||
|
if (query[key] || (query[key] == 0 && query[key] == "")) {
|
||||||
|
c[key] = query[key];
|
||||||
|
const eq = query[key] instanceof Array ? "in" : "=";
|
||||||
|
if (eq === "in") {
|
||||||
|
find.andWhere(`${column} ${eq} (:...${key})`, c);
|
||||||
|
} else {
|
||||||
|
find.andWhere(`${column} ${eq} :${key}`, c);
|
||||||
|
}
|
||||||
|
this.sqlParams.push(query[key]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (
|
||||||
|
query[key.requestParam] ||
|
||||||
|
(query[key.requestParam] == 0 && query[key.requestParam] !== "")
|
||||||
|
) {
|
||||||
|
c[key.column] = query[key.requestParam];
|
||||||
|
const eq = query[key.requestParam] instanceof Array ? "in" : "=";
|
||||||
|
if (eq === "in") {
|
||||||
|
find.andWhere(`${key.column} ${eq} (:${key.column})`, c);
|
||||||
|
} else {
|
||||||
|
find.andWhere(`${key.column} ${eq} :${key.column}`, c);
|
||||||
|
}
|
||||||
|
this.sqlParams.push(query[key.requestParam]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
sqlArr.push(selects.join(","));
|
||||||
|
}
|
||||||
|
// 接口请求的排序
|
||||||
|
if (sort && order) {
|
||||||
|
const sorts = sort.toUpperCase().split(",");
|
||||||
|
const orders = order.split(",");
|
||||||
|
if (sorts.length != orders.length) {
|
||||||
|
throw new CoolValidateException(ERRINFO.SORTFIELD);
|
||||||
|
}
|
||||||
|
for (const i in sorts) {
|
||||||
|
find.addOrderBy(
|
||||||
|
`${this.matchColumn(option?.select, orders[i])}.${orders[i]}`,
|
||||||
|
this.checkSort(sorts[i])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (option?.extend) {
|
||||||
|
await option?.extend(find, this.baseCtx, this.baseApp);
|
||||||
|
}
|
||||||
|
const sqls = find.getSql().split("FROM");
|
||||||
|
sqlArr.push("FROM");
|
||||||
|
// 取sqls的最后一个
|
||||||
|
sqlArr.push(sqls[sqls.length - 1]);
|
||||||
|
return sqlArr.join(" ");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 筛选的字段匹配
|
||||||
|
* @param select 筛选的字段
|
||||||
|
* @param field 字段
|
||||||
|
* @returns 字段在哪个表中
|
||||||
|
*/
|
||||||
|
protected matchColumn(select: string[] = [], field: string) {
|
||||||
|
for (const column of select) {
|
||||||
|
// 检查字段是否有别名,考虑 'AS' 关键字的不同大小写形式
|
||||||
|
const aliasPattern = new RegExp(`\\b\\w+\\s+as\\s+${field}\\b`, 'i');
|
||||||
|
const aliasMatch = column.match(aliasPattern);
|
||||||
|
if (aliasMatch) {
|
||||||
|
// 提取别名前的字段和表名
|
||||||
|
const fieldPattern = new RegExp(`(\\w+)\\.(\\w+)\\s+as\\s+${field}`, 'i');
|
||||||
|
const fieldMatch = column.match(fieldPattern);
|
||||||
|
if (fieldMatch) {
|
||||||
|
// 返回匹配到的表名
|
||||||
|
return fieldMatch[1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查字段是否直接在选择列表中
|
||||||
|
const fieldPattern = new RegExp(`\\b(\\w+)\\.${field}\\b`, 'i');
|
||||||
|
const fieldMatch = column.match(fieldPattern);
|
||||||
|
if (fieldMatch) {
|
||||||
|
// 如果直接匹配到字段,返回字段所属的表名
|
||||||
|
return fieldMatch[1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果没有匹配到任何特定的表或别名,返回默认的 'a' 表
|
||||||
|
return 'a';
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
530
core/src/service/sqlite.ts
Normal file
530
core/src/service/sqlite.ts
Normal file
@ -0,0 +1,530 @@
|
|||||||
|
import { Init, Provide, Inject, App, Config } from "@midwayjs/decorator";
|
||||||
|
import { Scope, ScopeEnum } from "@midwayjs/core";
|
||||||
|
import { CoolValidateException } from "../exception/validate";
|
||||||
|
import { ERRINFO, EVENT } from "../constant/global";
|
||||||
|
import { Application, Context } from "@midwayjs/koa";
|
||||||
|
import * as SqlString from "sqlstring";
|
||||||
|
import { CoolConfig } from "../interface";
|
||||||
|
import { TypeORMDataSourceManager } from "@midwayjs/typeorm";
|
||||||
|
import { Brackets, In, Repository, SelectQueryBuilder } from "typeorm";
|
||||||
|
import { QueryOp } from "../decorator/controller";
|
||||||
|
import * as _ from "lodash";
|
||||||
|
import { CoolEventManager } from "../event";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 服务基类
|
||||||
|
*/
|
||||||
|
@Provide()
|
||||||
|
@Scope(ScopeEnum.Request, { allowDowngrade: true })
|
||||||
|
export abstract class BaseSqliteService {
|
||||||
|
// 分页配置
|
||||||
|
@Config("cool")
|
||||||
|
private _coolConfig: CoolConfig;
|
||||||
|
|
||||||
|
// 模型
|
||||||
|
entity: Repository<any>;
|
||||||
|
|
||||||
|
sqlParams;
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
typeORMDataSourceManager: TypeORMDataSourceManager;
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
coolEventManager: CoolEventManager;
|
||||||
|
|
||||||
|
// 设置模型
|
||||||
|
setEntity(entity: any) {
|
||||||
|
this.entity = entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置请求上下文
|
||||||
|
setCtx(ctx: Context) {
|
||||||
|
this.baseCtx = ctx;
|
||||||
|
}
|
||||||
|
|
||||||
|
@App()
|
||||||
|
baseApp: Application;
|
||||||
|
|
||||||
|
// 设置应用对象
|
||||||
|
setApp(app: Application) {
|
||||||
|
this.baseApp = app;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Inject("ctx")
|
||||||
|
baseCtx: Context;
|
||||||
|
|
||||||
|
// 初始化
|
||||||
|
@Init()
|
||||||
|
init() {
|
||||||
|
this.sqlParams = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置sql
|
||||||
|
* @param condition 条件是否成立
|
||||||
|
* @param sql sql语句
|
||||||
|
* @param params 参数
|
||||||
|
*/
|
||||||
|
setSql(condition, sql, params) {
|
||||||
|
let rSql = false;
|
||||||
|
if (condition || (condition === 0 && condition !== "")) {
|
||||||
|
rSql = true;
|
||||||
|
for(let i = 0; i < params.length; i++) {
|
||||||
|
const param = params[i];
|
||||||
|
if (param instanceof Array) {
|
||||||
|
// 将这个? 替换成 $1,$2,$3
|
||||||
|
const replaceStr = [];
|
||||||
|
for(let j = 0; j < param.length; j++) {
|
||||||
|
replaceStr.push('$' + (this.sqlParams.length + j + 1));
|
||||||
|
}
|
||||||
|
this.sqlParams = this.sqlParams.concat(...params);
|
||||||
|
sql = sql.replace('?', replaceStr.join(','));
|
||||||
|
} else {
|
||||||
|
sql = sql.replace('?', '$' + (this.sqlParams.length + 1));
|
||||||
|
this.sqlParams.push(param);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (rSql ? sql : "").replace(/\$\d+/g, '?');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得查询个数的SQL
|
||||||
|
* @param sql
|
||||||
|
*/
|
||||||
|
getCountSql(sql) {
|
||||||
|
sql = sql
|
||||||
|
.replace(new RegExp("LIMIT", "gm"), "limit ")
|
||||||
|
.replace(new RegExp("\n", "gm"), " ");
|
||||||
|
if (sql.includes("limit")) {
|
||||||
|
const sqlArr = sql.split("limit ");
|
||||||
|
sqlArr.pop();
|
||||||
|
sql = sqlArr.join("limit ");
|
||||||
|
}
|
||||||
|
return `select count(*) as count from (${sql}) a`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 参数安全性检查
|
||||||
|
* @param params
|
||||||
|
*/
|
||||||
|
async paramSafetyCheck(params) {
|
||||||
|
const lp = params.toLowerCase();
|
||||||
|
return !(
|
||||||
|
lp.indexOf("update ") > -1 ||
|
||||||
|
lp.indexOf("select ") > -1 ||
|
||||||
|
lp.indexOf("delete ") > -1 ||
|
||||||
|
lp.indexOf("insert ") > -1
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 原生查询
|
||||||
|
* @param sql
|
||||||
|
* @param params
|
||||||
|
* @param connectionName
|
||||||
|
*/
|
||||||
|
async nativeQuery(sql, params?, connectionName?) {
|
||||||
|
if (_.isEmpty(params)) {
|
||||||
|
params = this.sqlParams;
|
||||||
|
}
|
||||||
|
let newParams = [];
|
||||||
|
// sql没处理过?的情况下
|
||||||
|
for (const item of params) {
|
||||||
|
// 如果是数组,将这个? 替换成 $1,$2,$3
|
||||||
|
if (item instanceof Array) {
|
||||||
|
const replaceStr = [];
|
||||||
|
for(let i = 0; i < item.length; i++) {
|
||||||
|
replaceStr.push('$' + (newParams.length + i + 1));
|
||||||
|
}
|
||||||
|
newParams.push(...item)
|
||||||
|
sql = sql.replace('?', replaceStr.join(','));
|
||||||
|
} else {
|
||||||
|
sql = sql.replace('?', '$' + (newParams.length + 1));
|
||||||
|
newParams.push(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.sqlParams = [];
|
||||||
|
return await this.getOrmManager(connectionName).query(sql.replace(/\$\d+/g, '?'), newParams || []);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得ORM管理
|
||||||
|
* @param connectionName 连接名称
|
||||||
|
*/
|
||||||
|
getOrmManager(connectionName = "default") {
|
||||||
|
return this.typeORMDataSourceManager.getDataSource(connectionName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 操作entity获得分页数据,不用写sql
|
||||||
|
* @param find QueryBuilder
|
||||||
|
* @param query
|
||||||
|
* @param autoSort
|
||||||
|
* @param connectionName
|
||||||
|
*/
|
||||||
|
async entityRenderPage(
|
||||||
|
find: SelectQueryBuilder<any>,
|
||||||
|
query,
|
||||||
|
autoSort = true
|
||||||
|
) {
|
||||||
|
const {
|
||||||
|
size = this._coolConfig.crud.pageSize,
|
||||||
|
page = 1,
|
||||||
|
order = "createTime",
|
||||||
|
sort = "desc",
|
||||||
|
isExport = false,
|
||||||
|
maxExportLimit,
|
||||||
|
} = query;
|
||||||
|
const count = await find.getCount();
|
||||||
|
let dataFind: SelectQueryBuilder<any>;
|
||||||
|
if (isExport && maxExportLimit > 0) {
|
||||||
|
dataFind = find.limit(maxExportLimit);
|
||||||
|
} else {
|
||||||
|
dataFind = find.offset((page - 1) * size).limit(size);
|
||||||
|
}
|
||||||
|
if (autoSort) {
|
||||||
|
find.addOrderBy(order, sort.toUpperCase());
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
list: await dataFind.getMany(),
|
||||||
|
pagination: {
|
||||||
|
page: parseInt(page),
|
||||||
|
size: parseInt(size),
|
||||||
|
total: count,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行SQL并获得分页数据
|
||||||
|
* @param sql 执行的sql语句
|
||||||
|
* @param query 分页查询条件
|
||||||
|
* @param autoSort 是否自动排序
|
||||||
|
* @param connectionName 连接名称
|
||||||
|
*/
|
||||||
|
async sqlRenderPage(sql, query, autoSort = true, connectionName?) {
|
||||||
|
const {
|
||||||
|
size = this._coolConfig.crud.pageSize,
|
||||||
|
page = 1,
|
||||||
|
order = "createTime",
|
||||||
|
sort = "desc",
|
||||||
|
isExport = false,
|
||||||
|
maxExportLimit,
|
||||||
|
} = query;
|
||||||
|
sql = `SELECT * FROM (${sql}) a`;
|
||||||
|
if (order && sort && autoSort) {
|
||||||
|
if (!(await this.paramSafetyCheck(order + sort))) {
|
||||||
|
throw new CoolValidateException("非法传参~");
|
||||||
|
}
|
||||||
|
sql += ` ORDER BY a.${SqlString.escapeId(order)} ${this.checkSort(sort)}`;
|
||||||
|
}
|
||||||
|
let cutParams = 0;
|
||||||
|
if (isExport && maxExportLimit > 0) {
|
||||||
|
this.sqlParams.push(parseInt(maxExportLimit));
|
||||||
|
cutParams = 1;
|
||||||
|
sql += " LIMIT ? ";
|
||||||
|
}
|
||||||
|
if (!isExport) {
|
||||||
|
this.sqlParams.push((page - 1) * size);
|
||||||
|
this.sqlParams.push(parseInt(size));
|
||||||
|
cutParams = 2;
|
||||||
|
sql += " LIMIT ?,? ";
|
||||||
|
}
|
||||||
|
|
||||||
|
let params = [];
|
||||||
|
params = params.concat(this.sqlParams);
|
||||||
|
const result = await this.nativeQuery(sql, params, connectionName);
|
||||||
|
params = params.slice(0, -cutParams);
|
||||||
|
const countResult = await this.nativeQuery(
|
||||||
|
this.getCountSql(sql),
|
||||||
|
params,
|
||||||
|
connectionName
|
||||||
|
);
|
||||||
|
return {
|
||||||
|
list: result,
|
||||||
|
pagination: {
|
||||||
|
page: parseInt(page),
|
||||||
|
size: parseInt(size),
|
||||||
|
total: parseInt(countResult[0] ? countResult[0].count : 0),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查排序
|
||||||
|
* @param sort 排序
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
checkSort(sort) {
|
||||||
|
if (!["desc", "asc"].includes(sort.toLowerCase())) {
|
||||||
|
throw new CoolValidateException("sort 非法传参~");
|
||||||
|
}
|
||||||
|
return sort;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得单个ID
|
||||||
|
* @param id ID
|
||||||
|
* @param infoIgnoreProperty 忽略返回属性
|
||||||
|
*/
|
||||||
|
async info(id: any, infoIgnoreProperty?: string[]) {
|
||||||
|
if (!this.entity) throw new CoolValidateException(ERRINFO.NOENTITY);
|
||||||
|
if (!id) {
|
||||||
|
throw new CoolValidateException(ERRINFO.NOID);
|
||||||
|
}
|
||||||
|
const info = await this.entity.findOneBy({ id });
|
||||||
|
if (info && infoIgnoreProperty) {
|
||||||
|
for (const property of infoIgnoreProperty) {
|
||||||
|
delete info[property];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除
|
||||||
|
* @param ids 删除的ID集合 如:[1,2,3] 或者 1,2,3
|
||||||
|
*/
|
||||||
|
async delete(ids: any) {
|
||||||
|
if (!this.entity) throw new CoolValidateException(ERRINFO.NOENTITY);
|
||||||
|
if (ids instanceof String) {
|
||||||
|
ids = ids.split(",");
|
||||||
|
}
|
||||||
|
// 启动软删除发送事件
|
||||||
|
if (this._coolConfig.crud?.softDelete) {
|
||||||
|
this.softDelete(ids);
|
||||||
|
}
|
||||||
|
await this.entity.delete(ids);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 软删除
|
||||||
|
* @param ids 删除的ID数组
|
||||||
|
* @param entity 实体
|
||||||
|
*/
|
||||||
|
async softDelete(ids: number[], entity?: Repository<any>) {
|
||||||
|
const data = await this.entity.find({
|
||||||
|
where: {
|
||||||
|
id: In(ids),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
if (_.isEmpty(data)) return;
|
||||||
|
const _entity = entity ? entity : this.entity;
|
||||||
|
const params = {
|
||||||
|
data,
|
||||||
|
ctx: this.baseCtx,
|
||||||
|
entity: _entity,
|
||||||
|
};
|
||||||
|
if (data.length > 0) {
|
||||||
|
this.coolEventManager.emit(EVENT.SOFT_DELETE, params);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 新增|修改
|
||||||
|
* @param param 数据
|
||||||
|
*/
|
||||||
|
async addOrUpdate(param: any | any[], type: 'add' | 'update' = 'add') {
|
||||||
|
if (!this.entity) throw new CoolValidateException(ERRINFO.NOENTITY);
|
||||||
|
delete param.createTime;
|
||||||
|
// 判断是否是批量操作
|
||||||
|
if (param instanceof Array) {
|
||||||
|
param.forEach((item) => {
|
||||||
|
item.updateTime = new Date();
|
||||||
|
item.createTime = new Date();
|
||||||
|
});
|
||||||
|
await this.entity.save(param);
|
||||||
|
} else{
|
||||||
|
const upsert = this._coolConfig.crud?.upsert || 'normal';
|
||||||
|
if (type == 'update') {
|
||||||
|
if(upsert == 'save') {
|
||||||
|
const info = await this.entity.findOneBy({id: param.id})
|
||||||
|
param = {
|
||||||
|
...info,
|
||||||
|
...param
|
||||||
|
}
|
||||||
|
}
|
||||||
|
param.updateTime = new Date();
|
||||||
|
upsert == 'normal'? await this.entity.update(param.id, param): await this.entity.save(param);
|
||||||
|
}
|
||||||
|
if(type =='add'){
|
||||||
|
param.createTime = new Date();
|
||||||
|
param.updateTime = new Date();
|
||||||
|
upsert == 'normal'? await this.entity.insert(param): await this.entity.save(param);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 非分页查询
|
||||||
|
* @param query 查询条件
|
||||||
|
* @param option 查询配置
|
||||||
|
* @param connectionName 连接名
|
||||||
|
*/
|
||||||
|
async list(query, option, connectionName?): Promise<any> {
|
||||||
|
if (!this.entity) throw new CoolValidateException(ERRINFO.NOENTITY);
|
||||||
|
const sql = await this.getOptionFind(query, option);
|
||||||
|
return this.nativeQuery(sql, [], connectionName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分页查询
|
||||||
|
* @param query 查询条件
|
||||||
|
* @param option 查询配置
|
||||||
|
* @param connectionName 连接名
|
||||||
|
*/
|
||||||
|
async page(query, option, connectionName?) {
|
||||||
|
if (!this.entity) throw new CoolValidateException(ERRINFO.NOENTITY);
|
||||||
|
const sql = await this.getOptionFind(query, option);
|
||||||
|
return this.sqlRenderPage(sql, query, false, connectionName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建查询配置
|
||||||
|
* @param query 前端查询
|
||||||
|
* @param option
|
||||||
|
*/
|
||||||
|
async getOptionFind(query, option: QueryOp) {
|
||||||
|
let { order = "createTime", sort = "desc", keyWord = "" } = query;
|
||||||
|
const sqlArr = ["SELECT"];
|
||||||
|
const selects = ["a.*"];
|
||||||
|
const find = this.entity.createQueryBuilder("a");
|
||||||
|
if (option) {
|
||||||
|
if (typeof option == "function") {
|
||||||
|
// @ts-ignore
|
||||||
|
option = await option(this.baseCtx, this.baseApp);
|
||||||
|
}
|
||||||
|
// 判断是否有关联查询,有的话取个别名
|
||||||
|
if (!_.isEmpty(option.join)) {
|
||||||
|
for (const item of option.join) {
|
||||||
|
selects.push(`${item.alias}.*`);
|
||||||
|
find[item.type || "leftJoin"](
|
||||||
|
item.entity,
|
||||||
|
item.alias,
|
||||||
|
item.condition
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 默认条件
|
||||||
|
if (option.where) {
|
||||||
|
const wheres =
|
||||||
|
typeof option.where == "function"
|
||||||
|
? await option.where(this.baseCtx, this.baseApp)
|
||||||
|
: option.where;
|
||||||
|
if (!_.isEmpty(wheres)) {
|
||||||
|
for (const item of wheres) {
|
||||||
|
if (
|
||||||
|
item.length == 2 ||
|
||||||
|
(item.length == 3 &&
|
||||||
|
(item[2] || (item[2] === 0 && item[2] != "")))
|
||||||
|
) {
|
||||||
|
for (const key in item[1]) {
|
||||||
|
this.sqlParams.push(item[1][key]);
|
||||||
|
}
|
||||||
|
find.andWhere(item[0], item[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 附加排序
|
||||||
|
if (!_.isEmpty(option.addOrderBy)) {
|
||||||
|
for (const key in option.addOrderBy) {
|
||||||
|
if (order && order == key) {
|
||||||
|
sort = option.addOrderBy[key].toUpperCase();
|
||||||
|
}
|
||||||
|
find.addOrderBy(
|
||||||
|
SqlString.escapeId(key),
|
||||||
|
this.checkSort(option.addOrderBy[key].toUpperCase())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 关键字模糊搜索
|
||||||
|
if (keyWord || (keyWord == 0 && keyWord != "")) {
|
||||||
|
keyWord = `%${keyWord}%`;
|
||||||
|
find.andWhere(
|
||||||
|
new Brackets((qb) => {
|
||||||
|
const keyWordLikeFields = option.keyWordLikeFields || [];
|
||||||
|
for (let i = 0; i < option.keyWordLikeFields?.length || 0; i++) {
|
||||||
|
qb.orWhere(`${keyWordLikeFields[i]} like :keyWord`, {
|
||||||
|
keyWord,
|
||||||
|
});
|
||||||
|
this.sqlParams.push(keyWord);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// 筛选字段
|
||||||
|
if (!_.isEmpty(option.select)) {
|
||||||
|
sqlArr.push(option.select.join(","));
|
||||||
|
find.select(option.select);
|
||||||
|
} else {
|
||||||
|
sqlArr.push(selects.join(","));
|
||||||
|
}
|
||||||
|
// 字段全匹配
|
||||||
|
if (!_.isEmpty(option.fieldEq)) {
|
||||||
|
for (let key of option.fieldEq) {
|
||||||
|
const c = {};
|
||||||
|
// 如果key有包含.的情况下操作
|
||||||
|
if(typeof key === "string" && key.includes('.')){
|
||||||
|
const keys = key.split('.');
|
||||||
|
const lastKey = keys.pop();
|
||||||
|
key = {requestParam: lastKey, column: key};
|
||||||
|
}
|
||||||
|
// 单表字段无别名的情况下操作
|
||||||
|
if (typeof key === "string") {
|
||||||
|
if (query[key] || (query[key] == 0 && query[key] == "")) {
|
||||||
|
c[key] = query[key];
|
||||||
|
const eq = query[key] instanceof Array ? "in" : "=";
|
||||||
|
if (eq === "in") {
|
||||||
|
find.andWhere(`${key} ${eq} (:${key})`, c);
|
||||||
|
} else {
|
||||||
|
find.andWhere(`${key} ${eq} :${key}`, c);
|
||||||
|
}
|
||||||
|
// this.sqlParams.push(query[key]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (
|
||||||
|
query[key.requestParam] ||
|
||||||
|
(query[key.requestParam] == 0 && query[key.requestParam] !== "")
|
||||||
|
) {
|
||||||
|
c[key.column] = query[key.requestParam];
|
||||||
|
const eq = query[key.requestParam] instanceof Array ? "in" : "=";
|
||||||
|
if (eq === "in") {
|
||||||
|
find.andWhere(`${key.column} ${eq} (:${key.column})`, c);
|
||||||
|
} else {
|
||||||
|
find.andWhere(`${key.column} ${eq} :${key.column}`, c);
|
||||||
|
}
|
||||||
|
// this.sqlParams.push(query[key.requestParam]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
sqlArr.push(selects.join(","));
|
||||||
|
}
|
||||||
|
// 接口请求的排序
|
||||||
|
if (sort && order) {
|
||||||
|
const sorts = sort.toUpperCase().split(",");
|
||||||
|
const orders = order.split(",");
|
||||||
|
if (sorts.length != orders.length) {
|
||||||
|
throw new CoolValidateException(ERRINFO.SORTFIELD);
|
||||||
|
}
|
||||||
|
for (const i in sorts) {
|
||||||
|
find.addOrderBy(
|
||||||
|
SqlString.escapeId(orders[i]),
|
||||||
|
this.checkSort(sorts[i])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (option?.extend) {
|
||||||
|
await option?.extend(find, this.baseCtx, this.baseApp);
|
||||||
|
}
|
||||||
|
const sqls = find.getSql().split("FROM");
|
||||||
|
sqlArr.push("FROM");
|
||||||
|
// 取sqls的最后一个
|
||||||
|
sqlArr.push(sqls[sqls.length - 1]);
|
||||||
|
return sqlArr.join(" ");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user