mirror of
https://github.com/cool-team-official/cool-admin-midway-packages.git
synced 2026-03-17 19:23:30 +00:00
清空仓库
This commit is contained in:
parent
0623782867
commit
48aa401553
4
.gitattributes
vendored
4
.gitattributes
vendored
@ -1,4 +0,0 @@
|
|||||||
*.js text eol=lf
|
|
||||||
*.json text eol=lf
|
|
||||||
*.ts text eol=lf
|
|
||||||
*.code-snippets text eol=lf
|
|
||||||
19
.gitignore
vendored
19
.gitignore
vendored
@ -1,19 +0,0 @@
|
|||||||
logs/
|
|
||||||
npm-debug.log
|
|
||||||
yarn-error.log
|
|
||||||
node_modules/
|
|
||||||
package-lock.json
|
|
||||||
yarn.lock
|
|
||||||
coverage/
|
|
||||||
dist/
|
|
||||||
.idea/
|
|
||||||
run/
|
|
||||||
.DS_Store
|
|
||||||
launch.json
|
|
||||||
*.sw*
|
|
||||||
*.un~
|
|
||||||
.tsbuildinfo
|
|
||||||
.tsbuildinfo.*
|
|
||||||
data/*
|
|
||||||
pnpm-lock.yaml
|
|
||||||
public/uploads/*
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
# cool-admin-midway-packages
|
|
||||||
cool-admin midway 后端核心包
|
|
||||||
|
|
||||||
- 为了不和src代码相互影响,7.0的核心依赖包单独成一个项目
|
|
||||||
|
|
||||||
- 7.0之前的在cool-admin-midway这个项目的packages目录下
|
|
||||||
@ -1,11 +0,0 @@
|
|||||||
# 🎨 editorconfig.org
|
|
||||||
|
|
||||||
root = true
|
|
||||||
|
|
||||||
[*]
|
|
||||||
charset = utf-8
|
|
||||||
end_of_line = lf
|
|
||||||
indent_style = space
|
|
||||||
indent_size = 2
|
|
||||||
trim_trailing_whitespace = true
|
|
||||||
insert_final_newline = true
|
|
||||||
@ -1,20 +0,0 @@
|
|||||||
{
|
|
||||||
"extends": "./node_modules/mwts/",
|
|
||||||
"ignorePatterns": ["node_modules", "dist", "test", "jest.config.js", "typings"],
|
|
||||||
"env": {
|
|
||||||
"jest": true
|
|
||||||
},
|
|
||||||
"rules": {
|
|
||||||
"@typescript-eslint/explicit-module-boundary-types": "off",
|
|
||||||
"@typescript-eslint/no-unused-vars": "off",
|
|
||||||
"@typescript-eslint/ban-ts-comment": "off",
|
|
||||||
"node/no-extraneous-import": "off",
|
|
||||||
"no-empty": "off",
|
|
||||||
"node/no-extraneous-require": "off",
|
|
||||||
"eqeqeq": "off",
|
|
||||||
"node/no-unsupported-features/node-builtins": "off",
|
|
||||||
"@typescript-eslint/ban-types": "off",
|
|
||||||
"no-control-regex": "off",
|
|
||||||
"prefer-const": "off"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
15
cloud/.gitignore
vendored
15
cloud/.gitignore
vendored
@ -1,15 +0,0 @@
|
|||||||
logs/
|
|
||||||
npm-debug.log
|
|
||||||
yarn-error.log
|
|
||||||
node_modules/
|
|
||||||
package-lock.json
|
|
||||||
yarn.lock
|
|
||||||
coverage/
|
|
||||||
dist/
|
|
||||||
.idea/
|
|
||||||
run/
|
|
||||||
.DS_Store
|
|
||||||
*.sw*
|
|
||||||
*.un~
|
|
||||||
.tsbuildinfo
|
|
||||||
.tsbuildinfo.*
|
|
||||||
@ -1,3 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
...require('mwts/.prettierrc.json')
|
|
||||||
}
|
|
||||||
@ -1,21 +0,0 @@
|
|||||||
MIT License
|
|
||||||
|
|
||||||
Copyright (c) 2013 - Now midwayjs
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
||||||
10
cloud/index.d.ts
vendored
10
cloud/index.d.ts
vendored
@ -1,10 +0,0 @@
|
|||||||
export * from './dist/index';
|
|
||||||
|
|
||||||
declare module '@midwayjs/core/dist/interface' {
|
|
||||||
interface MidwayConfig {
|
|
||||||
book?: PowerPartial<{
|
|
||||||
a: number;
|
|
||||||
b: string;
|
|
||||||
}>;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,7 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
preset: 'ts-jest',
|
|
||||||
testEnvironment: 'node',
|
|
||||||
testPathIgnorePatterns: ['<rootDir>/test/fixtures'],
|
|
||||||
coveragePathIgnorePatterns: ['<rootDir>/test/'],
|
|
||||||
setupFilesAfterEnv: ['./jest.setup.js']
|
|
||||||
};
|
|
||||||
@ -1 +0,0 @@
|
|||||||
jest.setTimeout(30000);
|
|
||||||
@ -1,49 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "@cool-midway/cloud",
|
|
||||||
"version": "7.1.0",
|
|
||||||
"description": "",
|
|
||||||
"main": "dist/index.js",
|
|
||||||
"typings": "index.d.ts",
|
|
||||||
"scripts": {
|
|
||||||
"build": "cross-env midway-bin build -c",
|
|
||||||
"cov": "cross-env midway-bin cov --ts",
|
|
||||||
"lint": "mwts check",
|
|
||||||
"lint:fix": "mwts fix"
|
|
||||||
},
|
|
||||||
"keywords": [
|
|
||||||
"cool",
|
|
||||||
"cool-admin",
|
|
||||||
"cooljs"
|
|
||||||
],
|
|
||||||
"author": "COOL",
|
|
||||||
"files": [
|
|
||||||
"dist/**/*.js",
|
|
||||||
"dist/**/*.d.ts",
|
|
||||||
"index.d.ts"
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
|
||||||
"repository": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "https://cool-js.com"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"@cool-midway/core": "^7.1.19",
|
|
||||||
"@midwayjs/cli": "^2.0.0",
|
|
||||||
"@midwayjs/core": "^3.16.0",
|
|
||||||
"@midwayjs/decorator": "^3.9.0",
|
|
||||||
"@midwayjs/mock": "^3.9.0",
|
|
||||||
"@midwayjs/typeorm": "^3.9.0",
|
|
||||||
"@types/jest": "^29.2.4",
|
|
||||||
"@types/node": "^18.11.15",
|
|
||||||
"cross-env": "^7.0.3",
|
|
||||||
"jest": "^29.3.1",
|
|
||||||
"lodash": "^4.17.21",
|
|
||||||
"mwts": "^1.3.0",
|
|
||||||
"ts-jest": "^29.0.3",
|
|
||||||
"typeorm": "^0.3.11",
|
|
||||||
"typescript": "^4.9.4"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"sqlstring": "^2.3.3"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,21 +0,0 @@
|
|||||||
MIT License
|
|
||||||
|
|
||||||
Copyright (c) 2013 - Now midwayjs
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
||||||
@ -1,27 +0,0 @@
|
|||||||
### COOL-ADMIN
|
|
||||||
|
|
||||||
cool-admin一个很酷的后台权限管理系统,开源免费,模块化、插件化、极速开发CRUD,方便快速构建迭代后台管理系统
|
|
||||||
|
|
||||||
大数据、微服务、AI编码快速开发!!!
|
|
||||||
|
|
||||||
|
|
||||||
### 技术栈
|
|
||||||
|
|
||||||
- 后端:node.js midway.js koa.js mysql typescript
|
|
||||||
- 前端:vue.js element-plus jsx pinia vue-router
|
|
||||||
|
|
||||||
### 官网
|
|
||||||
|
|
||||||
[https://cool-js.com](https://cool-js.com)
|
|
||||||
|
|
||||||
|
|
||||||
### 演示地址
|
|
||||||
|
|
||||||
[https://show.cool-admin.com](https://show.cool-admin.com)
|
|
||||||
|
|
||||||
- 账户:admin
|
|
||||||
- 密码:123456
|
|
||||||
|
|
||||||
### 项目地址
|
|
||||||
|
|
||||||
[https://github.com/cool-team-official/cool-admin-midway](https://github.com/cool-team-official/cool-admin-midway)
|
|
||||||
@ -1,4 +0,0 @@
|
|||||||
/**
|
|
||||||
* cool的配置
|
|
||||||
*/
|
|
||||||
export default {};
|
|
||||||
@ -1,22 +0,0 @@
|
|||||||
import { ILifeCycle, ILogger, IMidwayContainer, Logger } from '@midwayjs/core';
|
|
||||||
import { Configuration } from '@midwayjs/decorator';
|
|
||||||
import * as DefaultConfig from './config/config.default';
|
|
||||||
import { CoolCloudDb } from './db';
|
|
||||||
|
|
||||||
@Configuration({
|
|
||||||
namespace: 'cloud',
|
|
||||||
importConfigs: [
|
|
||||||
{
|
|
||||||
default: DefaultConfig,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
})
|
|
||||||
export class CoolCloudConfiguration implements ILifeCycle {
|
|
||||||
@Logger()
|
|
||||||
coreLogger: ILogger;
|
|
||||||
|
|
||||||
async onReady(container: IMidwayContainer) {
|
|
||||||
await container.getAsync(CoolCloudDb);
|
|
||||||
this.coreLogger.info('\x1B[36m [cool:cloud] ready \x1B[0m');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,131 +0,0 @@
|
|||||||
import { CoolCommException } from '@cool-midway/core';
|
|
||||||
import { CoolDataSource } from './source';
|
|
||||||
import {
|
|
||||||
ALL,
|
|
||||||
Config,
|
|
||||||
ILogger,
|
|
||||||
Init,
|
|
||||||
Logger,
|
|
||||||
Provide,
|
|
||||||
Scope,
|
|
||||||
ScopeEnum,
|
|
||||||
} from '@midwayjs/core';
|
|
||||||
import { Repository } from 'typeorm';
|
|
||||||
import * as ts from 'typescript';
|
|
||||||
import * as _ from 'lodash';
|
|
||||||
/**
|
|
||||||
* 数据库
|
|
||||||
*/
|
|
||||||
@Provide()
|
|
||||||
@Scope(ScopeEnum.Singleton)
|
|
||||||
export class CoolCloudDb {
|
|
||||||
@Logger()
|
|
||||||
coreLogger: ILogger;
|
|
||||||
|
|
||||||
coolDataSource: CoolDataSource;
|
|
||||||
|
|
||||||
@Config(ALL)
|
|
||||||
config;
|
|
||||||
|
|
||||||
@Init()
|
|
||||||
async init() {
|
|
||||||
const config = this.config.typeorm.dataSource.default;
|
|
||||||
if (!config) {
|
|
||||||
throw new CoolCommException('未配置数据库default信息');
|
|
||||||
}
|
|
||||||
this.coolDataSource = new CoolDataSource({
|
|
||||||
...this.config.typeorm.dataSource.default,
|
|
||||||
entities: [],
|
|
||||||
});
|
|
||||||
// 连接数据库
|
|
||||||
await this.coolDataSource.initialize();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获得数据库操作实例
|
|
||||||
* @param tableClass 表类
|
|
||||||
* @param appId 应用ID
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
getRepository(tableClass: string, appId = 'CLOUD'): Repository<any> {
|
|
||||||
return this.coolDataSource.getRepository(`${tableClass}${appId}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 创建表
|
|
||||||
* @param table 表结构,元函数,字符串
|
|
||||||
* @param appId 应用ID,确保每个应用的数据隔离
|
|
||||||
* @param synchronize 是否同步表结构
|
|
||||||
*/
|
|
||||||
async createTable(table: string, synchronize = false, appId = 'CLOUD') {
|
|
||||||
if (!table || !appId) {
|
|
||||||
throw new CoolCommException('table、appId不能为空');
|
|
||||||
}
|
|
||||||
const { newCode, className } = this.parseCode(table, appId);
|
|
||||||
const entities = this.coolDataSource.options.entities;
|
|
||||||
// @ts-ignore
|
|
||||||
this.coolDataSource.options.entities = _.dropWhile(entities, {
|
|
||||||
name: className,
|
|
||||||
});
|
|
||||||
const code = ts.transpile(
|
|
||||||
`${newCode}
|
|
||||||
this.coolDataSource.options.entities.push(${className})
|
|
||||||
|
|
||||||
this.coolDataSource.buildMetadatas().then(() => {
|
|
||||||
if(synchronize){
|
|
||||||
this.coolDataSource.synchronize();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
`,
|
|
||||||
{
|
|
||||||
emitDecoratorMetadata: true,
|
|
||||||
module: ts.ModuleKind.CommonJS,
|
|
||||||
target: ts.ScriptTarget.ES2018,
|
|
||||||
removeComments: true,
|
|
||||||
experimentalDecorators: true,
|
|
||||||
noImplicitThis: true,
|
|
||||||
noUnusedLocals: true,
|
|
||||||
stripInternal: true,
|
|
||||||
skipLibCheck: true,
|
|
||||||
pretty: true,
|
|
||||||
declaration: true,
|
|
||||||
noImplicitAny: false,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
eval(code);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 根据字符串查找并生成一个跟appId相关的类名
|
|
||||||
* @param code 代码
|
|
||||||
* @param appId
|
|
||||||
*/
|
|
||||||
parseCode(code: string, appId = 'CLOUD') {
|
|
||||||
try {
|
|
||||||
const oldClassName = code
|
|
||||||
.match('class(.*)extends')[1]
|
|
||||||
.replace(/\s*/g, '');
|
|
||||||
const oldTableStart = code.indexOf('@Entity(');
|
|
||||||
const oldTableEnd = code.indexOf(')');
|
|
||||||
|
|
||||||
const oldTableName = code
|
|
||||||
.substring(oldTableStart + 9, oldTableEnd - 1)
|
|
||||||
.replace(/\s*/g, '')
|
|
||||||
// eslint-disable-next-line no-useless-escape
|
|
||||||
.replace(/\"/g, '')
|
|
||||||
// eslint-disable-next-line no-useless-escape
|
|
||||||
.replace(/\'/g, '');
|
|
||||||
const className = `${oldClassName}${appId}`;
|
|
||||||
return {
|
|
||||||
newCode: code
|
|
||||||
.replace(oldClassName, className)
|
|
||||||
.replace(oldTableName, `func_${oldTableName}`),
|
|
||||||
className,
|
|
||||||
tableName: `func_${oldTableName}`,
|
|
||||||
};
|
|
||||||
} catch (err) {
|
|
||||||
this.coreLogger.error(err);
|
|
||||||
throw new CoolCommException('代码结构不正确,请检查');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,10 +0,0 @@
|
|||||||
import { DataSource } from 'typeorm';
|
|
||||||
|
|
||||||
export class CoolDataSource extends DataSource {
|
|
||||||
/**
|
|
||||||
* 重新构造元数据
|
|
||||||
*/
|
|
||||||
async buildMetadatas() {
|
|
||||||
await super.buildMetadatas();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,525 +0,0 @@
|
|||||||
import { CloudReq } from './../interface';
|
|
||||||
import { IMidwayApplication } from '@midwayjs/core';
|
|
||||||
import {
|
|
||||||
CoolConfig,
|
|
||||||
CoolEventManager,
|
|
||||||
CoolValidateException,
|
|
||||||
CurdOption,
|
|
||||||
ERRINFO,
|
|
||||||
EVENT,
|
|
||||||
} from '@cool-midway/core';
|
|
||||||
import { Brackets, In, Repository, SelectQueryBuilder } from 'typeorm';
|
|
||||||
import { CoolCloudDb } from '../db';
|
|
||||||
import * as _ from 'lodash';
|
|
||||||
import * as SqlString from 'sqlstring';
|
|
||||||
|
|
||||||
export abstract class CloudCrud {
|
|
||||||
ctx;
|
|
||||||
|
|
||||||
curdOption: CurdOption;
|
|
||||||
|
|
||||||
coolCloudDb: CoolCloudDb;
|
|
||||||
|
|
||||||
coolConfig: CoolConfig;
|
|
||||||
|
|
||||||
entity: Repository<any>;
|
|
||||||
|
|
||||||
app: IMidwayApplication;
|
|
||||||
|
|
||||||
req: CloudReq;
|
|
||||||
|
|
||||||
coolEventManager: CoolEventManager;
|
|
||||||
|
|
||||||
protected sqlParams;
|
|
||||||
|
|
||||||
setCurdOption(curdOption: CurdOption) {
|
|
||||||
this.curdOption = curdOption;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 设置实体
|
|
||||||
* @param entityModel
|
|
||||||
*/
|
|
||||||
async setEntity() {
|
|
||||||
this.entity = this.coolCloudDb.getRepository(
|
|
||||||
this.curdOption.entity,
|
|
||||||
'CLOUD'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract main(req: CloudReq): Promise<void>;
|
|
||||||
|
|
||||||
async init(req: CloudReq) {
|
|
||||||
this.sqlParams = [];
|
|
||||||
// 执行主函数
|
|
||||||
await this.main(req);
|
|
||||||
// 操作之前
|
|
||||||
await this.before();
|
|
||||||
// // 设置实体
|
|
||||||
await this.setEntity();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 参数安全性检查
|
|
||||||
* @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 query 查询条件
|
|
||||||
* @param option 查询配置
|
|
||||||
*/
|
|
||||||
async list(query): Promise<any> {
|
|
||||||
if (!this.entity) throw new CoolValidateException(ERRINFO.NOENTITY);
|
|
||||||
const sql = await this.getOptionFind(query, this.curdOption.listQueryOp);
|
|
||||||
return this.nativeQuery(sql, []);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 执行SQL并获得分页数据
|
|
||||||
* @param sql 执行的sql语句
|
|
||||||
* @param query 分页查询条件
|
|
||||||
* @param autoSort 是否自动排序
|
|
||||||
*/
|
|
||||||
async sqlRenderPage(sql, query, autoSort = true) {
|
|
||||||
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);
|
|
||||||
const countResult = await this.nativeQuery(this.getCountSql(sql), params);
|
|
||||||
return {
|
|
||||||
list: result,
|
|
||||||
pagination: {
|
|
||||||
page: parseInt(page),
|
|
||||||
size: parseInt(size),
|
|
||||||
total: parseInt(countResult[0] ? countResult[0].count : 0),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 分页查询
|
|
||||||
* @param connectionName 连接名
|
|
||||||
*/
|
|
||||||
async page(query) {
|
|
||||||
if (!this.entity) throw new CoolValidateException(ERRINFO.NOENTITY);
|
|
||||||
const sql = await this.getOptionFind(query, this.curdOption.pageQueryOp);
|
|
||||||
return this.sqlRenderPage(sql, query, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获得查询个数的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`;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 操作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.getRawMany(),
|
|
||||||
pagination: {
|
|
||||||
page: parseInt(page),
|
|
||||||
size: parseInt(size),
|
|
||||||
total: count,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 检查排序
|
|
||||||
* @param sort 排序
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
private checkSort(sort) {
|
|
||||||
if (!['desc', 'asc'].includes(sort.toLowerCase())) {
|
|
||||||
throw new CoolValidateException('sort 非法传参~');
|
|
||||||
}
|
|
||||||
return sort;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 原生查询
|
|
||||||
* @param sql
|
|
||||||
* @param params
|
|
||||||
*/
|
|
||||||
async nativeQuery(sql, params?) {
|
|
||||||
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().query(sql, newParams || []);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获得ORM管理
|
|
||||||
* @param connectionName 连接名称
|
|
||||||
*/
|
|
||||||
getOrmManager() {
|
|
||||||
return this.coolCloudDb.coolDataSource;
|
|
||||||
}
|
|
||||||
|
|
||||||
private async before() {
|
|
||||||
if (!this.curdOption?.before) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
await this.curdOption.before(this.ctx, this.app);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 插入参数值
|
|
||||||
* @param curdOption 配置
|
|
||||||
*/
|
|
||||||
private async insertParam(param) {
|
|
||||||
if (!this.curdOption?.insertParam) {
|
|
||||||
return param;
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
...param,
|
|
||||||
...(await this.curdOption.insertParam(this.ctx, this.app)),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 新增|修改|删除 之后的操作
|
|
||||||
* @param data 对应数据
|
|
||||||
*/
|
|
||||||
async modifyAfter(
|
|
||||||
data: any,
|
|
||||||
type: 'delete' | 'update' | 'add'
|
|
||||||
): Promise<void> {}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 新增|修改|删除 之前的操作
|
|
||||||
* @param data 对应数据
|
|
||||||
*/
|
|
||||||
async modifyBefore(
|
|
||||||
data: any,
|
|
||||||
type: 'delete' | 'update' | 'add'
|
|
||||||
): Promise<void> {}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 新增
|
|
||||||
* @param param
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
async add(param) {
|
|
||||||
param = await this.insertParam(param);
|
|
||||||
if (!this.entity) throw new CoolValidateException(ERRINFO.NOENTITY);
|
|
||||||
await this.modifyBefore(param, 'add');
|
|
||||||
await this.addOrUpdate(param);
|
|
||||||
await this.modifyAfter(param, 'add');
|
|
||||||
return {
|
|
||||||
id:
|
|
||||||
param instanceof Array
|
|
||||||
? param.map(e => {
|
|
||||||
return e.id ? e.id : e._id;
|
|
||||||
})
|
|
||||||
: param.id
|
|
||||||
? param.id
|
|
||||||
: param._id,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 新增|修改
|
|
||||||
* @param param 数据
|
|
||||||
*/
|
|
||||||
async addOrUpdate(param: any | any[]) {
|
|
||||||
if (!this.entity) throw new CoolValidateException(ERRINFO.NOENTITY);
|
|
||||||
delete param.createTime;
|
|
||||||
if (param.id) {
|
|
||||||
param.updateTime = new Date();
|
|
||||||
await this.entity.update(param.id, param);
|
|
||||||
} else {
|
|
||||||
param.createTime = new Date();
|
|
||||||
param.updateTime = new Date();
|
|
||||||
await this.entity.insert(param);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 删除
|
|
||||||
* @param ids 删除的ID集合 如:[1,2,3] 或者 1,2,3
|
|
||||||
*/
|
|
||||||
async delete(ids: any) {
|
|
||||||
if (!this.entity) throw new CoolValidateException(ERRINFO.NOENTITY);
|
|
||||||
await this.modifyBefore(ids, 'delete');
|
|
||||||
if (ids instanceof String) {
|
|
||||||
ids = ids.split(',');
|
|
||||||
}
|
|
||||||
if (this.coolConfig.crud?.softDelete) {
|
|
||||||
this.softDelete(ids);
|
|
||||||
}
|
|
||||||
await this.entity.delete(ids);
|
|
||||||
await this.modifyAfter(ids, 'delete');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 软删除
|
|
||||||
* @param ids 删除的ID数组
|
|
||||||
* @param entity 实体
|
|
||||||
*/
|
|
||||||
async softDelete(ids: string[], entity?: Repository<any>, userId?: string) {
|
|
||||||
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.ctx,
|
|
||||||
entity: _entity,
|
|
||||||
};
|
|
||||||
if (data.length > 0) {
|
|
||||||
this.coolEventManager.emit(EVENT.SOFT_DELETE, params);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 修改
|
|
||||||
* @param param 数据
|
|
||||||
*/
|
|
||||||
async update(param: any) {
|
|
||||||
if (!this.entity) throw new CoolValidateException(ERRINFO.NOENTITY);
|
|
||||||
await this.modifyBefore(param, 'update');
|
|
||||||
if (!param.id && !(param instanceof Array))
|
|
||||||
throw new CoolValidateException(ERRINFO.NOID);
|
|
||||||
await this.addOrUpdate(param);
|
|
||||||
await this.modifyAfter(param, 'update');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获得单个ID
|
|
||||||
* @param id ID
|
|
||||||
*/
|
|
||||||
async info(id: any): Promise<any> {
|
|
||||||
if (!this.entity) throw new CoolValidateException(ERRINFO.NOENTITY);
|
|
||||||
if (!id) {
|
|
||||||
throw new CoolValidateException(ERRINFO.NOID);
|
|
||||||
}
|
|
||||||
const info = await this.entity.findBy({ id });
|
|
||||||
if (info && this.curdOption?.infoIgnoreProperty) {
|
|
||||||
for (const property of this.curdOption?.infoIgnoreProperty) {
|
|
||||||
delete info[property];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return info;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 构建查询配置
|
|
||||||
* @param query 前端查询
|
|
||||||
* @param option
|
|
||||||
*/
|
|
||||||
private async getOptionFind(query, option) {
|
|
||||||
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.ctx, this.app)
|
|
||||||
: 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 (const key of option.fieldEq) {
|
|
||||||
const c = {};
|
|
||||||
// 单表字段无别名的情况下操作
|
|
||||||
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.ctx, this.app);
|
|
||||||
}
|
|
||||||
const sqls = find.getSql().split('FROM');
|
|
||||||
sqlArr.push('FROM');
|
|
||||||
sqlArr.push(sqls[1]);
|
|
||||||
return sqlArr.join(' ');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,17 +0,0 @@
|
|||||||
import { Provide, Scope, ScopeEnum } from '@midwayjs/core';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 云函数
|
|
||||||
*/
|
|
||||||
@Provide()
|
|
||||||
@Scope(ScopeEnum.Singleton)
|
|
||||||
export class CoolCloudFunc {
|
|
||||||
/**
|
|
||||||
* 获得类名
|
|
||||||
* @param code
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
getClassName(code: string) {
|
|
||||||
return code.match('class(.*)extends')[1].replace(/\s*/g, '');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,10 +0,0 @@
|
|||||||
export { CoolCloudConfiguration as Configuration } from './configuration';
|
|
||||||
|
|
||||||
export * from './interface';
|
|
||||||
|
|
||||||
// 云数据库
|
|
||||||
export * from './db/index';
|
|
||||||
|
|
||||||
// 云函数
|
|
||||||
export * from './func/index';
|
|
||||||
export * from './func/crud';
|
|
||||||
@ -1,11 +0,0 @@
|
|||||||
/**
|
|
||||||
* 云函数请求
|
|
||||||
*/
|
|
||||||
export interface CloudReq {
|
|
||||||
// 云函数名称
|
|
||||||
name: string;
|
|
||||||
// 请求参数
|
|
||||||
params: any;
|
|
||||||
// 调用方法
|
|
||||||
method: string;
|
|
||||||
}
|
|
||||||
@ -1,45 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "@cool-midway/cloud",
|
|
||||||
"version": "7.1.0",
|
|
||||||
"description": "",
|
|
||||||
"main": "index.js",
|
|
||||||
"typings": "index.d.ts",
|
|
||||||
"scripts": {
|
|
||||||
"build": "cross-env midway-bin build -c",
|
|
||||||
"cov": "cross-env midway-bin cov --ts",
|
|
||||||
"lint": "mwts check",
|
|
||||||
"lint:fix": "mwts fix"
|
|
||||||
},
|
|
||||||
"keywords": ["cool","cool-admin","cooljs"],
|
|
||||||
"author": "COOL",
|
|
||||||
"files": [
|
|
||||||
"**/*.js",
|
|
||||||
"**/*.d.ts",
|
|
||||||
"index.d.ts"
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
|
||||||
"repository": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "https://cool-js.com"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"@cool-midway/core": "^7.1.19",
|
|
||||||
"@midwayjs/cli": "^2.0.0",
|
|
||||||
"@midwayjs/core": "^3.16.0",
|
|
||||||
"@midwayjs/decorator": "^3.9.0",
|
|
||||||
"@midwayjs/mock": "^3.9.0",
|
|
||||||
"@midwayjs/typeorm": "^3.9.0",
|
|
||||||
"@types/jest": "^29.2.4",
|
|
||||||
"@types/node": "^18.11.15",
|
|
||||||
"cross-env": "^7.0.3",
|
|
||||||
"jest": "^29.3.1",
|
|
||||||
"lodash": "^4.17.21",
|
|
||||||
"mwts": "^1.3.0",
|
|
||||||
"ts-jest": "^29.0.3",
|
|
||||||
"typeorm": "^0.3.11",
|
|
||||||
"typescript": "^4.9.4"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"sqlstring": "^2.3.3"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1 +0,0 @@
|
|||||||
export class CoolCloudUtil {}
|
|
||||||
@ -1,25 +0,0 @@
|
|||||||
{
|
|
||||||
"compileOnSave": true,
|
|
||||||
"compilerOptions": {
|
|
||||||
"target": "es2018",
|
|
||||||
"module": "commonjs",
|
|
||||||
"moduleResolution": "node",
|
|
||||||
"experimentalDecorators": true,
|
|
||||||
"emitDecoratorMetadata": true,
|
|
||||||
"inlineSourceMap":false,
|
|
||||||
"noImplicitThis": true,
|
|
||||||
"noUnusedLocals": true,
|
|
||||||
"stripInternal": true,
|
|
||||||
"skipLibCheck": true,
|
|
||||||
"noImplicitReturns": false,
|
|
||||||
"pretty": true,
|
|
||||||
"declaration": true,
|
|
||||||
"forceConsistentCasingInFileNames": true,
|
|
||||||
"outDir": "dist"
|
|
||||||
},
|
|
||||||
"exclude": [
|
|
||||||
"dist",
|
|
||||||
"node_modules",
|
|
||||||
"test"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
21
core/LICENSE
21
core/LICENSE
@ -1,21 +0,0 @@
|
|||||||
MIT License
|
|
||||||
|
|
||||||
Copyright (c) 2013 - Now midwayjs
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
||||||
@ -1,3 +0,0 @@
|
|||||||
# cool-admin
|
|
||||||
|
|
||||||
https://cool-js.com
|
|
||||||
@ -1,11 +0,0 @@
|
|||||||
# 🎨 editorconfig.org
|
|
||||||
|
|
||||||
root = true
|
|
||||||
|
|
||||||
[*]
|
|
||||||
charset = utf-8
|
|
||||||
end_of_line = lf
|
|
||||||
indent_style = space
|
|
||||||
indent_size = 2
|
|
||||||
trim_trailing_whitespace = true
|
|
||||||
insert_final_newline = true
|
|
||||||
@ -1,7 +0,0 @@
|
|||||||
{
|
|
||||||
"extends": "./node_modules/mwts/",
|
|
||||||
"ignorePatterns": ["node_modules", "dist", "test", "jest.config.js", "typings"],
|
|
||||||
"env": {
|
|
||||||
"jest": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,15 +0,0 @@
|
|||||||
logs/
|
|
||||||
npm-debug.log
|
|
||||||
yarn-error.log
|
|
||||||
node_modules/
|
|
||||||
package-lock.json
|
|
||||||
yarn.lock
|
|
||||||
coverage/
|
|
||||||
dist/
|
|
||||||
.idea/
|
|
||||||
run/
|
|
||||||
.DS_Store
|
|
||||||
*.sw*
|
|
||||||
*.un~
|
|
||||||
.tsbuildinfo
|
|
||||||
.tsbuildinfo.*
|
|
||||||
@ -1,3 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
...require('mwts/.prettierrc.json')
|
|
||||||
}
|
|
||||||
10
core/index.d.ts
vendored
10
core/index.d.ts
vendored
@ -1,10 +0,0 @@
|
|||||||
export * from './dist/index';
|
|
||||||
|
|
||||||
declare module '@midwayjs/core/dist/interface' {
|
|
||||||
interface MidwayConfig {
|
|
||||||
book?: PowerPartial<{
|
|
||||||
a: number;
|
|
||||||
b: string;
|
|
||||||
}>;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,7 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
preset: 'ts-jest',
|
|
||||||
testEnvironment: 'node',
|
|
||||||
testPathIgnorePatterns: ['<rootDir>/test/fixtures'],
|
|
||||||
coveragePathIgnorePatterns: ['<rootDir>/test/'],
|
|
||||||
setupFilesAfterEnv: ['./jest.setup.js']
|
|
||||||
};
|
|
||||||
@ -1 +0,0 @@
|
|||||||
jest.setTimeout(30000);
|
|
||||||
@ -1,64 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "@cool-midway/core",
|
|
||||||
"version": "7.1.25",
|
|
||||||
"description": "",
|
|
||||||
"main": "dist/index.js",
|
|
||||||
"typings": "index.d.ts",
|
|
||||||
"scripts": {
|
|
||||||
"build": "cross-env midway-bin build -c",
|
|
||||||
"cov": "cross-env midway-bin cov --ts",
|
|
||||||
"lint": "mwts check",
|
|
||||||
"lint:fix": "mwts fix"
|
|
||||||
},
|
|
||||||
"keywords": [
|
|
||||||
"cool",
|
|
||||||
"cool-admin",
|
|
||||||
"cooljs"
|
|
||||||
],
|
|
||||||
"author": "COOL",
|
|
||||||
"files": [
|
|
||||||
"dist/**/*.js",
|
|
||||||
"dist/**/*.d.ts",
|
|
||||||
"index.d.ts"
|
|
||||||
],
|
|
||||||
"readme": "README.md",
|
|
||||||
"license": "MIT",
|
|
||||||
"repository": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "https://cool-js.com"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"@midwayjs/cli": "2.1.1",
|
|
||||||
"@midwayjs/core": "^3.15.0",
|
|
||||||
"@midwayjs/decorator": "^3.15.0",
|
|
||||||
"@midwayjs/koa": "^3.15.2",
|
|
||||||
"@midwayjs/mock": "^3.15.2",
|
|
||||||
"@midwayjs/typeorm": "^3.15.2",
|
|
||||||
"@types/download": "^8.0.5",
|
|
||||||
"@types/jest": "^29.5.12",
|
|
||||||
"@types/node": "^20.11.30",
|
|
||||||
"aedes": "^0.51.0",
|
|
||||||
"cross-env": "^7.0.3",
|
|
||||||
"jest": "^29.7.0",
|
|
||||||
"mwts": "^1.3.0",
|
|
||||||
"ts-jest": "^29.1.2",
|
|
||||||
"typeorm": "^0.3.20",
|
|
||||||
"typescript": "~5.4.2"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"@cool-midway/cache-manager-fs-hash": "^7.0.0",
|
|
||||||
"@midwayjs/cache": "^3.14.0",
|
|
||||||
"@midwayjs/cache-manager": "^3.15.2",
|
|
||||||
"axios": "^1.6.8",
|
|
||||||
"decompress": "^4.2.1",
|
|
||||||
"download": "^8.0.0",
|
|
||||||
"jsonwebtoken": "^9.0.2",
|
|
||||||
"lodash": "^4.17.21",
|
|
||||||
"md5": "^2.3.0",
|
|
||||||
"moment": "^2.30.1",
|
|
||||||
"pm2": "^5.3.1",
|
|
||||||
"sqlstring": "^2.3.3",
|
|
||||||
"uuid": "^9.0.1",
|
|
||||||
"ws": "^8.16.0"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,21 +0,0 @@
|
|||||||
MIT License
|
|
||||||
|
|
||||||
Copyright (c) 2013 - Now midwayjs
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
||||||
@ -1,27 +0,0 @@
|
|||||||
### COOL-ADMIN
|
|
||||||
|
|
||||||
cool-admin一个很酷的后台权限管理系统,开源免费,模块化、插件化、极速开发CRUD,方便快速构建迭代后台管理系统
|
|
||||||
|
|
||||||
大数据、微服务、AI编码快速开发!!!
|
|
||||||
|
|
||||||
|
|
||||||
### 技术栈
|
|
||||||
|
|
||||||
- 后端:node.js midway.js koa.js mysql typescript
|
|
||||||
- 前端:vue.js element-plus jsx pinia vue-router
|
|
||||||
|
|
||||||
### 官网
|
|
||||||
|
|
||||||
[https://cool-js.com](https://cool-js.com)
|
|
||||||
|
|
||||||
|
|
||||||
### 演示地址
|
|
||||||
|
|
||||||
[https://show.cool-admin.com](https://show.cool-admin.com)
|
|
||||||
|
|
||||||
- 账户:admin
|
|
||||||
- 密码:123456
|
|
||||||
|
|
||||||
### 项目地址
|
|
||||||
|
|
||||||
[https://github.com/cool-team-official/cool-admin-midway](https://github.com/cool-team-official/cool-admin-midway)
|
|
||||||
64
core/src/cache/store.ts
vendored
64
core/src/cache/store.ts
vendored
@ -1,64 +0,0 @@
|
|||||||
import * as FsStore from "@cool-midway/cache-manager-fs-hash";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* cool 基于磁盘的缓存
|
|
||||||
*/
|
|
||||||
class FsCacheStore {
|
|
||||||
options: any;
|
|
||||||
|
|
||||||
store: FsStore;
|
|
||||||
|
|
||||||
constructor(options = {}) {
|
|
||||||
options = {
|
|
||||||
...options,
|
|
||||||
path: "cache",
|
|
||||||
ttl: -1,
|
|
||||||
};
|
|
||||||
this.options = options;
|
|
||||||
this.store = FsStore.create(options);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获得
|
|
||||||
* @param key
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
async get<T>(key: string): Promise<T> {
|
|
||||||
return await this.store.get(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 设置
|
|
||||||
* @param key
|
|
||||||
* @param value
|
|
||||||
* @param ttl
|
|
||||||
*/
|
|
||||||
async set<T>(key: string, value: T, ttl: number): Promise<void> {
|
|
||||||
let t = ttl ? ttl : this.options.ttl;
|
|
||||||
if (t > 0) {
|
|
||||||
t = t / 1000;
|
|
||||||
}
|
|
||||||
await this.store.set(key, value, {
|
|
||||||
ttl: t,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 删除
|
|
||||||
* @param key
|
|
||||||
*/
|
|
||||||
async del(key: string): Promise<void> {
|
|
||||||
await this.store.del(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 重置
|
|
||||||
*/
|
|
||||||
async reset(): Promise<void> {
|
|
||||||
await this.store.reset();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const CoolCacheStore = function (options = {}) {
|
|
||||||
return new FsCacheStore(options);
|
|
||||||
};
|
|
||||||
@ -1,22 +0,0 @@
|
|||||||
import { CoolConfig } from "../interface";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* cool的配置
|
|
||||||
*/
|
|
||||||
export default {
|
|
||||||
cool: {
|
|
||||||
// 是否自动导入数据库
|
|
||||||
initDB: false,
|
|
||||||
// 是否自动导入模块菜单
|
|
||||||
initMenu: true,
|
|
||||||
// 判断是否初始化的方式
|
|
||||||
initJudge: "file",
|
|
||||||
// crud配置
|
|
||||||
crud: {
|
|
||||||
// 软删除
|
|
||||||
softDelete: true,
|
|
||||||
// 分页查询每页条数
|
|
||||||
pageSize: 15,
|
|
||||||
},
|
|
||||||
} as CoolConfig,
|
|
||||||
};
|
|
||||||
@ -1,79 +0,0 @@
|
|||||||
import {
|
|
||||||
App,
|
|
||||||
Context,
|
|
||||||
ILifeCycle,
|
|
||||||
ILogger,
|
|
||||||
IMidwayBaseApplication,
|
|
||||||
IMidwayContainer,
|
|
||||||
Inject,
|
|
||||||
Logger,
|
|
||||||
} from "@midwayjs/core";
|
|
||||||
import { Configuration } from "@midwayjs/decorator";
|
|
||||||
import * as DefaultConfig from "./config/config.default";
|
|
||||||
import { CoolExceptionFilter } from "./exception/filter";
|
|
||||||
import { FuncUtil } from "./util/func";
|
|
||||||
import * as koa from "@midwayjs/koa";
|
|
||||||
import { CoolModuleConfig } from "./module/config";
|
|
||||||
import { CoolModuleImport } from "./module/import";
|
|
||||||
import { CoolEventManager } from "./event";
|
|
||||||
import { CoolEps } from "./rest/eps";
|
|
||||||
import { CoolDecorator } from "./decorator";
|
|
||||||
import * as cache from "@midwayjs/cache-manager";
|
|
||||||
import * as _cache from "@midwayjs/cache";
|
|
||||||
|
|
||||||
@Configuration({
|
|
||||||
namespace: "cool",
|
|
||||||
imports: [_cache, cache],
|
|
||||||
importConfigs: [
|
|
||||||
{
|
|
||||||
default: DefaultConfig,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
})
|
|
||||||
export class CoolConfiguration implements ILifeCycle {
|
|
||||||
@Logger()
|
|
||||||
coreLogger: ILogger;
|
|
||||||
|
|
||||||
@App()
|
|
||||||
app: koa.Application;
|
|
||||||
|
|
||||||
@Inject()
|
|
||||||
coolEventManager: CoolEventManager;
|
|
||||||
|
|
||||||
async onReady(container: IMidwayContainer) {
|
|
||||||
this.coolEventManager.emit("onReady");
|
|
||||||
// 处理模块配置
|
|
||||||
await container.getAsync(CoolModuleConfig);
|
|
||||||
// 常用函数处理
|
|
||||||
await container.getAsync(FuncUtil);
|
|
||||||
// 异常处理
|
|
||||||
this.app.useFilter([CoolExceptionFilter]);
|
|
||||||
// 装饰器
|
|
||||||
await container.getAsync(CoolDecorator);
|
|
||||||
|
|
||||||
// 缓存设置为全局
|
|
||||||
// global["COOL-CACHE"] = await container.getAsync(CacheManager);
|
|
||||||
// // 清除 location
|
|
||||||
// setTimeout(() => {
|
|
||||||
// location.clean();
|
|
||||||
// this.coreLogger.info("\x1B[36m [cool:core] location clean \x1B[0m");
|
|
||||||
// }, 10000);
|
|
||||||
}
|
|
||||||
|
|
||||||
async onConfigLoad(
|
|
||||||
container: IMidwayContainer,
|
|
||||||
mainApp?: IMidwayBaseApplication<Context>
|
|
||||||
) {}
|
|
||||||
|
|
||||||
async onServerReady(container: IMidwayContainer) {
|
|
||||||
// 事件
|
|
||||||
await (await container.getAsync(CoolEventManager)).init();
|
|
||||||
// 导入模块数据
|
|
||||||
(await container.getAsync(CoolModuleImport)).init();
|
|
||||||
// 实体与路径
|
|
||||||
const eps: CoolEps = await container.getAsync(CoolEps);
|
|
||||||
eps.init();
|
|
||||||
this.coolEventManager.emit("onServerReady");
|
|
||||||
// location.clean();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,84 +0,0 @@
|
|||||||
/**
|
|
||||||
* 返回码
|
|
||||||
*/
|
|
||||||
export enum RESCODE {
|
|
||||||
// 成功
|
|
||||||
SUCCESS = 1000,
|
|
||||||
// 失败
|
|
||||||
COMMFAIL = 1001,
|
|
||||||
// 参数验证失败
|
|
||||||
VALIDATEFAIL = 1002,
|
|
||||||
// 参数验证失败
|
|
||||||
COREFAIL = 1003,
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 返回信息
|
|
||||||
*/
|
|
||||||
export enum RESMESSAGE {
|
|
||||||
// 成功
|
|
||||||
SUCCESS = "success",
|
|
||||||
// 失败
|
|
||||||
COMMFAIL = "comm fail",
|
|
||||||
// 参数验证失败
|
|
||||||
VALIDATEFAIL = "validate fail",
|
|
||||||
// 核心异常
|
|
||||||
COREFAIL = "core fail",
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 错误提示
|
|
||||||
*/
|
|
||||||
export enum ERRINFO {
|
|
||||||
NOENTITY = "未设置操作实体",
|
|
||||||
NOID = "查询参数[id]不存在",
|
|
||||||
SORTFIELD = "排序参数不正确",
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 事件
|
|
||||||
*/
|
|
||||||
export enum EVENT {
|
|
||||||
// 软删除
|
|
||||||
SOFT_DELETE = "onSoftDelete",
|
|
||||||
// 服务成功启动
|
|
||||||
SERVER_READY = "onServerReady",
|
|
||||||
// 服务就绪
|
|
||||||
READY = "onReady",
|
|
||||||
// ES 数据改变
|
|
||||||
ES_DATA_CHANGE = "esDataChange",
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
export class GlobalConfig {
|
|
||||||
private static instance: GlobalConfig;
|
|
||||||
|
|
||||||
RESCODE = {
|
|
||||||
SUCCESS: 1000,
|
|
||||||
COMMFAIL: 1001,
|
|
||||||
VALIDATEFAIL: 1002,
|
|
||||||
COREFAIL: 1003,
|
|
||||||
};
|
|
||||||
|
|
||||||
RESMESSAGE = {
|
|
||||||
SUCCESS: "success",
|
|
||||||
COMMFAIL: "comm fail",
|
|
||||||
VALIDATEFAIL: "validate fail",
|
|
||||||
COREFAIL: "core fail",
|
|
||||||
};
|
|
||||||
|
|
||||||
// ... 其他的配置 ...
|
|
||||||
|
|
||||||
private constructor() {}
|
|
||||||
|
|
||||||
static getInstance(): GlobalConfig {
|
|
||||||
if (!GlobalConfig.instance) {
|
|
||||||
GlobalConfig.instance = new GlobalConfig();
|
|
||||||
}
|
|
||||||
return GlobalConfig.instance;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -1,220 +0,0 @@
|
|||||||
import {
|
|
||||||
App,
|
|
||||||
CONTROLLER_KEY,
|
|
||||||
getClassMetadata,
|
|
||||||
Init,
|
|
||||||
Inject,
|
|
||||||
Provide,
|
|
||||||
} from "@midwayjs/decorator";
|
|
||||||
import { GlobalConfig } from "../constant/global";
|
|
||||||
import { ControllerOption, CurdOption } from "../decorator/controller";
|
|
||||||
import { BaseService } from "../service/base";
|
|
||||||
import { IMidwayApplication } from "@midwayjs/core";
|
|
||||||
import { Context } from "@midwayjs/koa";
|
|
||||||
import { TypeORMDataSourceManager } from "@midwayjs/typeorm";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 控制器基类
|
|
||||||
*/
|
|
||||||
@Provide()
|
|
||||||
export abstract class BaseController {
|
|
||||||
@Inject("ctx")
|
|
||||||
baseCtx: Context;
|
|
||||||
|
|
||||||
@Inject()
|
|
||||||
service: BaseService;
|
|
||||||
|
|
||||||
@App()
|
|
||||||
baseApp: IMidwayApplication;
|
|
||||||
|
|
||||||
curdOption: CurdOption;
|
|
||||||
|
|
||||||
@Inject()
|
|
||||||
typeORMDataSourceManager: TypeORMDataSourceManager;
|
|
||||||
|
|
||||||
connectionName;
|
|
||||||
|
|
||||||
@Init()
|
|
||||||
async init() {
|
|
||||||
const option: ControllerOption = getClassMetadata(CONTROLLER_KEY, this);
|
|
||||||
const curdOption: CurdOption = option.curdOption;
|
|
||||||
this.curdOption = curdOption;
|
|
||||||
if (!this.curdOption) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// 操作之前
|
|
||||||
await this.before(curdOption);
|
|
||||||
// 设置service
|
|
||||||
await this.setService(curdOption);
|
|
||||||
// 设置实体
|
|
||||||
await this.setEntity(curdOption);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async before(curdOption: CurdOption) {
|
|
||||||
if (!curdOption?.before) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
await curdOption.before(this.baseCtx, this.baseApp);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 插入参数值
|
|
||||||
* @param curdOption 配置
|
|
||||||
*/
|
|
||||||
private async insertParam(curdOption: CurdOption) {
|
|
||||||
if (!curdOption?.insertParam) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const body = this.baseCtx.request.body;
|
|
||||||
if (body) {
|
|
||||||
// 判断body是否是数组
|
|
||||||
if (Array.isArray(body)) {
|
|
||||||
for (let i = 0; i < body.length; i++) {
|
|
||||||
body[i] = {
|
|
||||||
...body[i],
|
|
||||||
...(await curdOption.insertParam(this.baseCtx, this.baseApp)),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
this.baseCtx.request.body = body;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.baseCtx.request.body = {
|
|
||||||
// @ts-ignore
|
|
||||||
...this.baseCtx.request.body,
|
|
||||||
...(await curdOption.insertParam(this.baseCtx, this.baseApp)),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 设置实体
|
|
||||||
* @param curdOption 配置
|
|
||||||
*/
|
|
||||||
private async setEntity(curdOption: CurdOption) {
|
|
||||||
const entity = curdOption?.entity;
|
|
||||||
if (entity) {
|
|
||||||
const dataSourceName =
|
|
||||||
this.typeORMDataSourceManager.getDataSourceNameByModel(entity);
|
|
||||||
this.connectionName = dataSourceName;
|
|
||||||
let entityModel = this.typeORMDataSourceManager
|
|
||||||
.getDataSource(dataSourceName)
|
|
||||||
.getRepository(entity);
|
|
||||||
this.service.setEntity(entityModel);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 设置service
|
|
||||||
* @param curdOption
|
|
||||||
*/
|
|
||||||
private async setService(curdOption: CurdOption) {
|
|
||||||
if (curdOption.service) {
|
|
||||||
this.service = await this.baseCtx.requestContext.getAsync(
|
|
||||||
curdOption.service
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 新增
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
async add() {
|
|
||||||
// 插入参数
|
|
||||||
await this.insertParam(this.curdOption);
|
|
||||||
const { body } = this.baseCtx.request;
|
|
||||||
return this.ok(await this.service.add(body));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 删除
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
async delete() {
|
|
||||||
// @ts-ignore
|
|
||||||
const { ids } = this.baseCtx.request.body;
|
|
||||||
return this.ok(await this.service.delete(ids));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 更新
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
async update() {
|
|
||||||
const { body } = this.baseCtx.request;
|
|
||||||
return this.ok(await this.service.update(body));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 分页查询
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
async page() {
|
|
||||||
const { body } = this.baseCtx.request;
|
|
||||||
return this.ok(
|
|
||||||
await this.service.page(
|
|
||||||
body,
|
|
||||||
this.curdOption.pageQueryOp,
|
|
||||||
this.connectionName
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 列表查询
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
async list() {
|
|
||||||
const { body } = this.baseCtx.request;
|
|
||||||
return this.ok(
|
|
||||||
await this.service.list(
|
|
||||||
body,
|
|
||||||
this.curdOption.listQueryOp,
|
|
||||||
this.connectionName
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 根据ID查询信息
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
async info() {
|
|
||||||
const { id } = this.baseCtx.query;
|
|
||||||
return this.ok(
|
|
||||||
await this.service.info(id, this.curdOption.infoIgnoreProperty)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 成功返回
|
|
||||||
* @param data 返回数据
|
|
||||||
*/
|
|
||||||
ok(data?: any) {
|
|
||||||
const { RESCODE, RESMESSAGE } = GlobalConfig.getInstance();
|
|
||||||
const res = {
|
|
||||||
code: RESCODE.SUCCESS,
|
|
||||||
message: RESMESSAGE.SUCCESS,
|
|
||||||
};
|
|
||||||
if (data || data == 0) {
|
|
||||||
res["data"] = data;
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 失败返回
|
|
||||||
* @param message
|
|
||||||
*/
|
|
||||||
fail(message?: string, code?: number) {
|
|
||||||
const { RESCODE, RESMESSAGE } = GlobalConfig.getInstance();
|
|
||||||
return {
|
|
||||||
code: code ? code : RESCODE.COMMFAIL,
|
|
||||||
message: message
|
|
||||||
? message
|
|
||||||
: code == RESCODE.VALIDATEFAIL
|
|
||||||
? RESMESSAGE.VALIDATEFAIL
|
|
||||||
: RESMESSAGE.COMMFAIL,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,8 +0,0 @@
|
|||||||
import { createCustomMethodDecorator } from "@midwayjs/decorator";
|
|
||||||
|
|
||||||
// 装饰器内部的唯一 id
|
|
||||||
export const COOL_CACHE = "decorator:cool_cache";
|
|
||||||
|
|
||||||
export function CoolCache(ttl?: number): MethodDecorator {
|
|
||||||
return createCustomMethodDecorator(COOL_CACHE, ttl);
|
|
||||||
}
|
|
||||||
@ -1,212 +0,0 @@
|
|||||||
import { ModuleConfig } from "./../interface";
|
|
||||||
import {
|
|
||||||
Scope,
|
|
||||||
ScopeEnum,
|
|
||||||
saveClassMetadata,
|
|
||||||
saveModule,
|
|
||||||
CONTROLLER_KEY,
|
|
||||||
MiddlewareParamArray,
|
|
||||||
WEB_ROUTER_KEY,
|
|
||||||
attachClassMetadata,
|
|
||||||
} from "@midwayjs/decorator";
|
|
||||||
import * as fs from "fs";
|
|
||||||
import * as _ from "lodash";
|
|
||||||
import location from "../util/location";
|
|
||||||
|
|
||||||
export type ApiTypes = "add" | "delete" | "update" | "page" | "info" | "list";
|
|
||||||
// Crud配置
|
|
||||||
|
|
||||||
export interface CurdOption {
|
|
||||||
// 路由前缀,不配置默认是按Controller下的文件夹路径
|
|
||||||
prefix?: string;
|
|
||||||
// curd api接口
|
|
||||||
api: ApiTypes[];
|
|
||||||
// 分页查询配置
|
|
||||||
pageQueryOp?: QueryOp | Function;
|
|
||||||
// 非分页查询配置
|
|
||||||
listQueryOp?: QueryOp | Function;
|
|
||||||
// 插入参数
|
|
||||||
insertParam?: Function;
|
|
||||||
// 操作之前
|
|
||||||
before?: Function;
|
|
||||||
// info 忽略返回属性
|
|
||||||
infoIgnoreProperty?: string[];
|
|
||||||
// 实体
|
|
||||||
entity: any;
|
|
||||||
// 服务
|
|
||||||
service?: any;
|
|
||||||
// api标签
|
|
||||||
urlTag?: {
|
|
||||||
name: "ignoreToken" | string;
|
|
||||||
url: ApiTypes[];
|
|
||||||
};
|
|
||||||
}
|
|
||||||
export interface JoinOp {
|
|
||||||
// 实体
|
|
||||||
entity: any;
|
|
||||||
// 别名
|
|
||||||
alias: string;
|
|
||||||
// 关联条件
|
|
||||||
condition: string;
|
|
||||||
// 关联类型
|
|
||||||
type?: "innerJoin" | "leftJoin";
|
|
||||||
}
|
|
||||||
|
|
||||||
// 字段匹配
|
|
||||||
export interface FieldEq {
|
|
||||||
// 字段
|
|
||||||
column: string;
|
|
||||||
// 请求参数
|
|
||||||
requestParam: string;
|
|
||||||
}
|
|
||||||
// 查询配置
|
|
||||||
export interface QueryOp {
|
|
||||||
// 需要模糊查询的字段
|
|
||||||
keyWordLikeFields?: string[];
|
|
||||||
// 查询条件
|
|
||||||
where?: Function;
|
|
||||||
// 查询字段
|
|
||||||
select?: string[];
|
|
||||||
// 字段相等
|
|
||||||
fieldEq?: string[] | FieldEq[] | (string | FieldEq)[];
|
|
||||||
// 添加排序条件
|
|
||||||
addOrderBy?: {};
|
|
||||||
// 关联配置
|
|
||||||
join?: JoinOp[];
|
|
||||||
// 其他条件
|
|
||||||
extend?: Function;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Controller 配置
|
|
||||||
export interface ControllerOption {
|
|
||||||
// crud配置 如果是字符串则为路由前缀,不配置默认是按Controller下的文件夹路径
|
|
||||||
curdOption?: CurdOption & string;
|
|
||||||
// 路由配置
|
|
||||||
routerOptions?: {
|
|
||||||
// 是否敏感
|
|
||||||
sensitive?: boolean;
|
|
||||||
// 路由中间件
|
|
||||||
middleware?: MiddlewareParamArray;
|
|
||||||
// 别名
|
|
||||||
alias?: string[];
|
|
||||||
// 描述
|
|
||||||
description?: string;
|
|
||||||
// 标签名称
|
|
||||||
tagName?: string;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// 路由配置
|
|
||||||
export interface RouterOptions {
|
|
||||||
sensitive?: boolean;
|
|
||||||
middleware?: MiddlewareParamArray;
|
|
||||||
description?: string;
|
|
||||||
tagName?: string;
|
|
||||||
ignoreGlobalPrefix?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
// COOL的装饰器
|
|
||||||
export function CoolController(
|
|
||||||
curdOption?: CurdOption | string | RouterOptions,
|
|
||||||
routerOptions: RouterOptions = { middleware: [], sensitive: true }
|
|
||||||
): ClassDecorator {
|
|
||||||
return (target: any) => {
|
|
||||||
// 将装饰的类,绑定到该装饰器,用于后续能获取到 class
|
|
||||||
saveModule(CONTROLLER_KEY, target);
|
|
||||||
let prefix;
|
|
||||||
if (curdOption) {
|
|
||||||
// 判断 curdOption 的类型
|
|
||||||
if (typeof curdOption === "string") {
|
|
||||||
prefix = curdOption;
|
|
||||||
} else if (curdOption && "api" in curdOption) {
|
|
||||||
// curdOption 是 CurdOption 类型
|
|
||||||
prefix = curdOption.prefix || "";
|
|
||||||
} else {
|
|
||||||
// curdOption 是 RouterOptions 类型 合并到 routerOptions
|
|
||||||
routerOptions = { ...curdOption, ...routerOptions };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 如果不存在路由前缀,那么自动根据当前文件夹路径
|
|
||||||
location.scriptPath(target).then(async (res: any) => {
|
|
||||||
const pathSps = res.path.split(".");
|
|
||||||
const paths = pathSps[pathSps.length - 2].split(/[/\\]/);
|
|
||||||
const pathArr = [];
|
|
||||||
let module = null;
|
|
||||||
for (const path of paths.reverse()) {
|
|
||||||
if (path != "controller" && !module) {
|
|
||||||
pathArr.push(path);
|
|
||||||
}
|
|
||||||
if (path == "controller" && !paths.includes("modules")) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (path == "controller" && paths.includes("modules")) {
|
|
||||||
module = "ready";
|
|
||||||
}
|
|
||||||
if (module && path != "controller") {
|
|
||||||
module = `${path}`;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (module) {
|
|
||||||
pathArr.reverse();
|
|
||||||
pathArr.splice(1, 0, module);
|
|
||||||
// 追加模块中间件
|
|
||||||
let path = `${res.path.split(new RegExp(`modules[/\\\\]${module}`))[0]}modules/${module}/config.${_.endsWith(res.path, "ts") ? "ts" : "js"}`;
|
|
||||||
if (fs.existsSync(path)) {
|
|
||||||
const config: ModuleConfig = require(path).default();
|
|
||||||
routerOptions.middleware = (config.middlewares || []).concat(
|
|
||||||
routerOptions.middleware || []
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!prefix) {
|
|
||||||
prefix = `/${pathArr.join("/")}`;
|
|
||||||
}
|
|
||||||
saveMetadata(prefix, routerOptions, target, curdOption, module);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export const apiDesc = {
|
|
||||||
add: "新增",
|
|
||||||
delete: "删除",
|
|
||||||
update: "修改",
|
|
||||||
page: "分页查询",
|
|
||||||
list: "列表查询",
|
|
||||||
info: "单个信息",
|
|
||||||
};
|
|
||||||
|
|
||||||
// 保存一些元数据信息,任意你希望存的东西
|
|
||||||
function saveMetadata(prefix, routerOptions, target, curdOption, module) {
|
|
||||||
if (module && !routerOptions.tagName) {
|
|
||||||
routerOptions = routerOptions || {};
|
|
||||||
routerOptions.tagName = module;
|
|
||||||
}
|
|
||||||
saveClassMetadata(
|
|
||||||
CONTROLLER_KEY,
|
|
||||||
{
|
|
||||||
prefix,
|
|
||||||
routerOptions,
|
|
||||||
curdOption,
|
|
||||||
module,
|
|
||||||
} as ControllerOption,
|
|
||||||
target
|
|
||||||
);
|
|
||||||
// 追加CRUD路由
|
|
||||||
if (!_.isEmpty(curdOption?.api)) {
|
|
||||||
curdOption?.api.forEach((path) => {
|
|
||||||
attachClassMetadata(
|
|
||||||
WEB_ROUTER_KEY,
|
|
||||||
{
|
|
||||||
path: `/${path}`,
|
|
||||||
requestMethod: path == "info" ? "get" : "post",
|
|
||||||
method: path,
|
|
||||||
summary: apiDesc[path],
|
|
||||||
description: "",
|
|
||||||
},
|
|
||||||
target
|
|
||||||
);
|
|
||||||
});
|
|
||||||
Scope(ScopeEnum.Request)(target);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,54 +0,0 @@
|
|||||||
import {
|
|
||||||
saveClassMetadata,
|
|
||||||
saveModule,
|
|
||||||
attachClassMetadata,
|
|
||||||
} from "@midwayjs/decorator";
|
|
||||||
import { Scope, ScopeEnum } from "@midwayjs/core";
|
|
||||||
|
|
||||||
export const COOL_CLS_EVENT_KEY = "decorator:cool:cls:event";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 事件配置
|
|
||||||
*/
|
|
||||||
export interface CoolEventOptions {
|
|
||||||
/** 是否全局 */
|
|
||||||
isGlobal: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 事件
|
|
||||||
* @param options
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
export function CoolEvent(options = {} as CoolEventOptions): ClassDecorator {
|
|
||||||
return (target: any) => {
|
|
||||||
// 将装饰的类,绑定到该装饰器,用于后续能获取到 class
|
|
||||||
saveModule(COOL_CLS_EVENT_KEY, target);
|
|
||||||
// 保存一些元数据信息,任意你希望存的东西
|
|
||||||
saveClassMetadata(COOL_CLS_EVENT_KEY, options, target);
|
|
||||||
// 指定 IoC 容器创建实例的作用域,这里注册为请求作用域,这样能取到 ctx
|
|
||||||
Scope(ScopeEnum.Singleton)(target);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export const COOL_EVENT_KEY = "decorator:cool:event";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 事件
|
|
||||||
* @param eventName
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
export function Event(eventName?: string): MethodDecorator {
|
|
||||||
return (target: any, propertyKey: string, descriptor: PropertyDescriptor) => {
|
|
||||||
// 将装饰的类,绑定到该装饰器,用于后续能获取到 class
|
|
||||||
attachClassMetadata(
|
|
||||||
COOL_EVENT_KEY,
|
|
||||||
{
|
|
||||||
eventName,
|
|
||||||
propertyKey,
|
|
||||||
descriptor,
|
|
||||||
},
|
|
||||||
target
|
|
||||||
);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@ -1,112 +0,0 @@
|
|||||||
import { COOL_CACHE } from "./cache";
|
|
||||||
import { CachingFactory, MidwayCache } from "@midwayjs/cache-manager";
|
|
||||||
import {
|
|
||||||
Init,
|
|
||||||
Inject,
|
|
||||||
InjectClient,
|
|
||||||
JoinPoint,
|
|
||||||
MidwayDecoratorService,
|
|
||||||
Provide,
|
|
||||||
Scope,
|
|
||||||
ScopeEnum,
|
|
||||||
} from "@midwayjs/core";
|
|
||||||
import { TypeORMDataSourceManager } from "@midwayjs/typeorm";
|
|
||||||
import { CoolCommException } from "../exception/comm";
|
|
||||||
import { COOL_TRANSACTION, TransactionOptions } from "./transaction";
|
|
||||||
import * as md5 from "md5";
|
|
||||||
import { CoolUrlTagData } from "../tag/data";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 装饰器
|
|
||||||
*/
|
|
||||||
@Provide()
|
|
||||||
@Scope(ScopeEnum.Singleton)
|
|
||||||
export class CoolDecorator {
|
|
||||||
@Inject()
|
|
||||||
typeORMDataSourceManager: TypeORMDataSourceManager;
|
|
||||||
|
|
||||||
@Inject()
|
|
||||||
decoratorService: MidwayDecoratorService;
|
|
||||||
|
|
||||||
@InjectClient(CachingFactory, "default")
|
|
||||||
midwayCache: MidwayCache;
|
|
||||||
|
|
||||||
@Inject()
|
|
||||||
coolUrlTagData: CoolUrlTagData;
|
|
||||||
|
|
||||||
@Init()
|
|
||||||
async init() {
|
|
||||||
// 事务
|
|
||||||
await this.transaction();
|
|
||||||
// 缓存
|
|
||||||
await this.cache();
|
|
||||||
// URL标签
|
|
||||||
await this.coolUrlTagData.init();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 缓存
|
|
||||||
*/
|
|
||||||
async cache() {
|
|
||||||
this.decoratorService.registerMethodHandler(COOL_CACHE, (options) => {
|
|
||||||
return {
|
|
||||||
around: async (joinPoint: JoinPoint) => {
|
|
||||||
const key = md5(
|
|
||||||
joinPoint.target.constructor.name +
|
|
||||||
joinPoint.methodName +
|
|
||||||
JSON.stringify(joinPoint.args)
|
|
||||||
);
|
|
||||||
// 缓存有数据就返回
|
|
||||||
let data: any = await this.midwayCache.get(key);
|
|
||||||
if (data) {
|
|
||||||
return JSON.parse(data);
|
|
||||||
} else {
|
|
||||||
// 执行原始方法
|
|
||||||
data = await joinPoint.proceed(...joinPoint.args);
|
|
||||||
await this.midwayCache.set(
|
|
||||||
key,
|
|
||||||
JSON.stringify(data),
|
|
||||||
options.metadata
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return data;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 事务
|
|
||||||
*/
|
|
||||||
async transaction() {
|
|
||||||
this.decoratorService.registerMethodHandler(COOL_TRANSACTION, (options) => {
|
|
||||||
return {
|
|
||||||
around: async (joinPoint: JoinPoint) => {
|
|
||||||
const option: TransactionOptions = options.metadata;
|
|
||||||
const dataSource = this.typeORMDataSourceManager.getDataSource(
|
|
||||||
option?.connectionName || "default"
|
|
||||||
);
|
|
||||||
const queryRunner = dataSource.createQueryRunner();
|
|
||||||
await queryRunner.connect();
|
|
||||||
if (option && option.isolation) {
|
|
||||||
await queryRunner.startTransaction(option.isolation);
|
|
||||||
} else {
|
|
||||||
await queryRunner.startTransaction();
|
|
||||||
}
|
|
||||||
let data;
|
|
||||||
try {
|
|
||||||
joinPoint.args.push(queryRunner);
|
|
||||||
data = await joinPoint.proceed(...joinPoint.args);
|
|
||||||
await queryRunner.commitTransaction();
|
|
||||||
} catch (error) {
|
|
||||||
await queryRunner.rollbackTransaction();
|
|
||||||
throw new CoolCommException(error.message);
|
|
||||||
} finally {
|
|
||||||
await queryRunner.release();
|
|
||||||
}
|
|
||||||
return data;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,50 +0,0 @@
|
|||||||
import { saveClassMetadata, savePropertyDataToClass, saveModule } from "@midwayjs/decorator";
|
|
||||||
|
|
||||||
export const COOL_URL_TAG_KEY = "decorator:cool:url:tag";
|
|
||||||
|
|
||||||
export const COOL_METHOD_TAG_KEY = "decorator:cool:method:tag";
|
|
||||||
|
|
||||||
export enum TagTypes {
|
|
||||||
IGNORE_TOKEN = "ignoreToken",
|
|
||||||
IGNORE_SIGN = "ignoreSign",
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface CoolUrlTagConfig {
|
|
||||||
key: TagTypes | string;
|
|
||||||
value?: string[];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 打标记
|
|
||||||
* @param data
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
export function CoolUrlTag(data?: CoolUrlTagConfig): ClassDecorator {
|
|
||||||
return (target: any) => {
|
|
||||||
// 将装饰的类,绑定到该装饰器,用于后续能获取到 class
|
|
||||||
saveModule(COOL_URL_TAG_KEY, target);
|
|
||||||
// 保存一些元数据信息,任意你希望存的东西
|
|
||||||
saveClassMetadata(COOL_URL_TAG_KEY, data, target);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 方法打标记
|
|
||||||
* @param data
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
export function CoolTag(tag: TagTypes | string): MethodDecorator {
|
|
||||||
return (target, key, descriptor: PropertyDescriptor) => {
|
|
||||||
savePropertyDataToClass(
|
|
||||||
COOL_METHOD_TAG_KEY,
|
|
||||||
{
|
|
||||||
key,
|
|
||||||
tag
|
|
||||||
},
|
|
||||||
target,
|
|
||||||
key
|
|
||||||
);
|
|
||||||
return descriptor;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@ -1,19 +0,0 @@
|
|||||||
import { createCustomMethodDecorator } from "@midwayjs/decorator";
|
|
||||||
|
|
||||||
type IsolationLevel =
|
|
||||||
| "READ UNCOMMITTED"
|
|
||||||
| "READ COMMITTED"
|
|
||||||
| "REPEATABLE READ"
|
|
||||||
| "SERIALIZABLE";
|
|
||||||
|
|
||||||
export interface TransactionOptions {
|
|
||||||
connectionName?: string;
|
|
||||||
isolation?: IsolationLevel;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 装饰器内部的唯一 id
|
|
||||||
export const COOL_TRANSACTION = "decorator:cool_transaction";
|
|
||||||
|
|
||||||
export function CoolTransaction(option?: TransactionOptions): MethodDecorator {
|
|
||||||
return createCustomMethodDecorator(COOL_TRANSACTION, option);
|
|
||||||
}
|
|
||||||
@ -1,26 +0,0 @@
|
|||||||
import {
|
|
||||||
Index,
|
|
||||||
UpdateDateColumn,
|
|
||||||
CreateDateColumn,
|
|
||||||
PrimaryGeneratedColumn,
|
|
||||||
} from "typeorm";
|
|
||||||
import { CoolBaseEntity } from "./typeorm";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 模型基类
|
|
||||||
*/
|
|
||||||
export abstract class BaseEntity extends CoolBaseEntity {
|
|
||||||
// 默认自增
|
|
||||||
@PrimaryGeneratedColumn("increment", {
|
|
||||||
comment: "ID",
|
|
||||||
})
|
|
||||||
id: number;
|
|
||||||
|
|
||||||
@Index()
|
|
||||||
@CreateDateColumn({ comment: "创建时间" })
|
|
||||||
createTime: Date;
|
|
||||||
|
|
||||||
@Index()
|
|
||||||
@UpdateDateColumn({ comment: "更新时间" })
|
|
||||||
updateTime: Date;
|
|
||||||
}
|
|
||||||
@ -1,25 +0,0 @@
|
|||||||
import {
|
|
||||||
Index,
|
|
||||||
UpdateDateColumn,
|
|
||||||
CreateDateColumn,
|
|
||||||
// @ts-ignore
|
|
||||||
ObjectID,
|
|
||||||
ObjectIdColumn,
|
|
||||||
} from "typeorm";
|
|
||||||
import { CoolBaseEntity } from "./typeorm";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 模型基类
|
|
||||||
*/
|
|
||||||
export abstract class BaseMongoEntity extends CoolBaseEntity {
|
|
||||||
@ObjectIdColumn({ comment: "id" })
|
|
||||||
id: ObjectID;
|
|
||||||
|
|
||||||
@Index()
|
|
||||||
@CreateDateColumn({ comment: "创建时间" })
|
|
||||||
createTime: Date;
|
|
||||||
|
|
||||||
@Index()
|
|
||||||
@UpdateDateColumn({ comment: "更新时间" })
|
|
||||||
updateTime: Date;
|
|
||||||
}
|
|
||||||
@ -1,3 +0,0 @@
|
|||||||
import { BaseEntity } from "typeorm";
|
|
||||||
|
|
||||||
export abstract class CoolBaseEntity extends BaseEntity {}
|
|
||||||
@ -1,190 +0,0 @@
|
|||||||
import {
|
|
||||||
App,
|
|
||||||
getClassMetadata,
|
|
||||||
listModule,
|
|
||||||
Provide,
|
|
||||||
} from "@midwayjs/decorator";
|
|
||||||
import * as Events from "events";
|
|
||||||
import { Scope, ScopeEnum, IMidwayApplication, Config } from "@midwayjs/core";
|
|
||||||
import { COOL_CLS_EVENT_KEY, COOL_EVENT_KEY } from "../decorator/event";
|
|
||||||
import * as pm2 from "pm2";
|
|
||||||
import * as _ from "lodash";
|
|
||||||
|
|
||||||
export const COOL_EVENT_MESSAGE = "cool:event:message";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 事件
|
|
||||||
*/
|
|
||||||
@Provide()
|
|
||||||
@Scope(ScopeEnum.Singleton)
|
|
||||||
export class CoolEventManager extends Events {
|
|
||||||
@App()
|
|
||||||
app: IMidwayApplication;
|
|
||||||
|
|
||||||
@Config("keys")
|
|
||||||
keys: string;
|
|
||||||
|
|
||||||
// 事件数据 某个事件对应的模块对应的方法
|
|
||||||
eventData = {} as {
|
|
||||||
[key: string]: {
|
|
||||||
module: any;
|
|
||||||
method: string;
|
|
||||||
}[];
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 初始化
|
|
||||||
*/
|
|
||||||
async init() {
|
|
||||||
const eventModules = listModule(COOL_CLS_EVENT_KEY);
|
|
||||||
for (const module of eventModules) {
|
|
||||||
await this.handlerEvent(module);
|
|
||||||
}
|
|
||||||
await this.commEvent();
|
|
||||||
await this.globalEvent();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 发送事件
|
|
||||||
* @param event
|
|
||||||
* @param args
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
emit(event: string | symbol, ...args: any[]): boolean {
|
|
||||||
return super.emit(COOL_EVENT_MESSAGE, {
|
|
||||||
type: COOL_EVENT_MESSAGE,
|
|
||||||
data: {
|
|
||||||
event,
|
|
||||||
args,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 发送全局事件
|
|
||||||
* @param event 事件
|
|
||||||
* @param random 是否随机一个
|
|
||||||
* @param args 参数
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
async globalEmit(event: string, random: boolean = false, ...args) {
|
|
||||||
// 如果是本地运行还是转普通模式
|
|
||||||
if (this.app.getEnv() === "local") {
|
|
||||||
this.emit(event, ...args);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
pm2.connect(() => {
|
|
||||||
pm2.list((err, list) => {
|
|
||||||
const ps = list.map((e) => {
|
|
||||||
return {
|
|
||||||
id: e.pm_id,
|
|
||||||
name: e.name,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
// random 为 true 时随机发给同名称的一个进程
|
|
||||||
if (random) {
|
|
||||||
// 按名称分组
|
|
||||||
const group = _.groupBy(ps, "name");
|
|
||||||
const names = Object.keys(group);
|
|
||||||
// 遍历名称
|
|
||||||
names.forEach((name) => {
|
|
||||||
const pss = group[name];
|
|
||||||
// 随机一个
|
|
||||||
const index = _.random(0, pss.length - 1);
|
|
||||||
const ps = pss[index];
|
|
||||||
// 发给这个进程
|
|
||||||
// @ts-ignore
|
|
||||||
pm2.sendDataToProcessId(
|
|
||||||
{
|
|
||||||
type: "process:msg",
|
|
||||||
data: {
|
|
||||||
type: `${COOL_EVENT_MESSAGE}@${this.keys}`,
|
|
||||||
event,
|
|
||||||
args,
|
|
||||||
},
|
|
||||||
id: ps.id,
|
|
||||||
topic: "cool:event:topic",
|
|
||||||
},
|
|
||||||
(err, res) => {}
|
|
||||||
);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
// 发给所有进程
|
|
||||||
ps.forEach((e) => {
|
|
||||||
// @ts-ignore
|
|
||||||
pm2.sendDataToProcessId(
|
|
||||||
{
|
|
||||||
type: "process:msg",
|
|
||||||
data: {
|
|
||||||
type: `${COOL_EVENT_MESSAGE}@${this.keys}`,
|
|
||||||
event,
|
|
||||||
args,
|
|
||||||
},
|
|
||||||
id: e.id,
|
|
||||||
topic: "cool:event:topic",
|
|
||||||
},
|
|
||||||
(err, res) => {}
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 处理事件
|
|
||||||
* @param module
|
|
||||||
*/
|
|
||||||
async handlerEvent(module) {
|
|
||||||
const events = getClassMetadata(COOL_EVENT_KEY, module);
|
|
||||||
for (const event of events) {
|
|
||||||
const listen = event.eventName ? event.eventName : event.propertyKey;
|
|
||||||
if (!this.eventData[listen]) {
|
|
||||||
this.eventData[listen] = [];
|
|
||||||
}
|
|
||||||
this.eventData[listen].push({
|
|
||||||
module,
|
|
||||||
method: event.propertyKey,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 全局事件
|
|
||||||
*/
|
|
||||||
async globalEvent() {
|
|
||||||
process.on("message", async (message: any) => {
|
|
||||||
const data = message?.data;
|
|
||||||
if (!data) return;
|
|
||||||
if (data.type != `${COOL_EVENT_MESSAGE}@${this.keys}`) return;
|
|
||||||
await this.doAction(message);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 普通事件
|
|
||||||
*/
|
|
||||||
async commEvent() {
|
|
||||||
this.on(COOL_EVENT_MESSAGE, async (message: any) => {
|
|
||||||
await this.doAction(message);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 执行事件
|
|
||||||
* @param message
|
|
||||||
*/
|
|
||||||
async doAction(message) {
|
|
||||||
const data = message.data;
|
|
||||||
const method = data.event;
|
|
||||||
const args = data.args;
|
|
||||||
if (this.eventData[method]) {
|
|
||||||
for (const event of this.eventData[method]) {
|
|
||||||
const moduleInstance = await this.app
|
|
||||||
.getApplicationContext()
|
|
||||||
.getAsync(event.module);
|
|
||||||
moduleInstance[event.method](...args);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,13 +0,0 @@
|
|||||||
/**
|
|
||||||
* 异常基类
|
|
||||||
*/
|
|
||||||
export class BaseException extends Error {
|
|
||||||
status: number;
|
|
||||||
|
|
||||||
constructor(name: string, code: number, message: string) {
|
|
||||||
super(message);
|
|
||||||
|
|
||||||
this.name = name;
|
|
||||||
this.status = code;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,16 +0,0 @@
|
|||||||
import { GlobalConfig } from '../constant/global';
|
|
||||||
import { BaseException } from './base';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 通用异常
|
|
||||||
*/
|
|
||||||
export class CoolCommException extends BaseException {
|
|
||||||
constructor(message: string) {
|
|
||||||
const { RESCODE, RESMESSAGE } = GlobalConfig.getInstance();
|
|
||||||
super(
|
|
||||||
'CoolCommException',
|
|
||||||
RESCODE.COMMFAIL,
|
|
||||||
message ? message : RESMESSAGE.COMMFAIL
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,16 +0,0 @@
|
|||||||
import { GlobalConfig } from '../constant/global';
|
|
||||||
import { BaseException } from './base';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 核心异常
|
|
||||||
*/
|
|
||||||
export class CoolCoreException extends BaseException {
|
|
||||||
constructor(message: string) {
|
|
||||||
const { RESCODE, RESMESSAGE } = GlobalConfig.getInstance();
|
|
||||||
super(
|
|
||||||
'CoolCoreException',
|
|
||||||
RESCODE.COREFAIL,
|
|
||||||
message ? message : RESMESSAGE.COREFAIL
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,21 +0,0 @@
|
|||||||
import { ILogger } from '@midwayjs/core';
|
|
||||||
import { Catch, Logger } from '@midwayjs/decorator';
|
|
||||||
import { GlobalConfig } from '../constant/global';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 全局异常处理
|
|
||||||
*/
|
|
||||||
@Catch()
|
|
||||||
export class CoolExceptionFilter {
|
|
||||||
@Logger()
|
|
||||||
coreLogger: ILogger;
|
|
||||||
|
|
||||||
async catch(err) {
|
|
||||||
const { RESCODE } = GlobalConfig.getInstance();
|
|
||||||
this.coreLogger.error(err);
|
|
||||||
return {
|
|
||||||
code: err.status || RESCODE.COMMFAIL,
|
|
||||||
message: err.message,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,16 +0,0 @@
|
|||||||
import { GlobalConfig } from '../constant/global';
|
|
||||||
import { BaseException } from './base';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 校验异常
|
|
||||||
*/
|
|
||||||
export class CoolValidateException extends BaseException {
|
|
||||||
constructor(message: string) {
|
|
||||||
const { RESCODE, RESMESSAGE } = GlobalConfig.getInstance();
|
|
||||||
super(
|
|
||||||
'CoolValidateException',
|
|
||||||
RESCODE.VALIDATEFAIL,
|
|
||||||
message ? message : RESMESSAGE.VALIDATEFAIL
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,52 +0,0 @@
|
|||||||
export { CoolConfiguration as Configuration } from "./configuration";
|
|
||||||
|
|
||||||
// 异常处理
|
|
||||||
export * from "./exception/filter";
|
|
||||||
export * from "./exception/core";
|
|
||||||
export * from "./exception/base";
|
|
||||||
export * from "./exception/comm";
|
|
||||||
export * from "./exception/validate";
|
|
||||||
|
|
||||||
// cache
|
|
||||||
export * from "./cache/store";
|
|
||||||
|
|
||||||
// entity
|
|
||||||
export * from "./entity/base";
|
|
||||||
export * from "./entity/typeorm";
|
|
||||||
export * from "./entity/mongo";
|
|
||||||
|
|
||||||
// service
|
|
||||||
export * from "./service/base";
|
|
||||||
export * from "./service/mysql";
|
|
||||||
export * from "./service/postgres";
|
|
||||||
export * from "./service/sqlite";
|
|
||||||
|
|
||||||
// controller
|
|
||||||
export * from "./controller/base";
|
|
||||||
|
|
||||||
// 事件
|
|
||||||
export * from "./event/index";
|
|
||||||
|
|
||||||
// 装饰器
|
|
||||||
export * from "./decorator/controller";
|
|
||||||
export * from "./decorator/cache";
|
|
||||||
export * from "./decorator/event";
|
|
||||||
export * from "./decorator/transaction";
|
|
||||||
export * from "./decorator/tag";
|
|
||||||
export * from "./decorator/index";
|
|
||||||
|
|
||||||
// rest
|
|
||||||
export * from "./rest/eps";
|
|
||||||
|
|
||||||
// tag
|
|
||||||
export * from "./tag/data";
|
|
||||||
|
|
||||||
// 模块
|
|
||||||
export * from "./module/config";
|
|
||||||
export * from "./module/import";
|
|
||||||
export * from "./module/menu";
|
|
||||||
|
|
||||||
// 其他
|
|
||||||
export * from "./interface";
|
|
||||||
export * from "./util/func";
|
|
||||||
export * from "./constant/global";
|
|
||||||
@ -1,447 +0,0 @@
|
|||||||
import { MiddlewareParamArray } from "@midwayjs/core";
|
|
||||||
import { AedesOptions } from "aedes";
|
|
||||||
// @ts-ignore
|
|
||||||
import { PublishPacket } from "packet";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 模块配置
|
|
||||||
*/
|
|
||||||
export interface ModuleConfig {
|
|
||||||
/** 名称 */
|
|
||||||
name: string;
|
|
||||||
/** 描述 */
|
|
||||||
description: string;
|
|
||||||
/** 模块中间件 */
|
|
||||||
middlewares?: MiddlewareParamArray;
|
|
||||||
/** 全局中间件 */
|
|
||||||
globalMiddlewares?: MiddlewareParamArray;
|
|
||||||
/** 模块加载顺序,默认为0,值越大越优先加载 */
|
|
||||||
order?: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface CoolConfig {
|
|
||||||
/** 短信 */
|
|
||||||
sms?: CoolSmsConfig;
|
|
||||||
/** 是否自动导入数据库 */
|
|
||||||
initDB?: boolean;
|
|
||||||
/** Eps */
|
|
||||||
eps?: boolean;
|
|
||||||
/** 是否自动导入模块菜单 */
|
|
||||||
initMenu?: boolean;
|
|
||||||
/** 判断是否初始化的方式 */
|
|
||||||
initJudge: "file" | "db";
|
|
||||||
// 实体配置
|
|
||||||
// entity?: {
|
|
||||||
// primaryType: "uuid" | "increment" | "rowid" | "identity";
|
|
||||||
// };
|
|
||||||
/** crud配置 */
|
|
||||||
crud?: {
|
|
||||||
/** 软删除 */
|
|
||||||
softDelete: boolean;
|
|
||||||
/** 分页查询每页条数 */
|
|
||||||
pageSize: number;
|
|
||||||
/** 插入方式 */
|
|
||||||
upsert: "normal" | "save";
|
|
||||||
// 多租户
|
|
||||||
// tenant: boolean;
|
|
||||||
};
|
|
||||||
/** elasticsearch配置 */
|
|
||||||
es?: {
|
|
||||||
nodes: string[];
|
|
||||||
options?: any;
|
|
||||||
};
|
|
||||||
/** pay */
|
|
||||||
pay?: {
|
|
||||||
/** 微信支付 */
|
|
||||||
wx?: CoolWxPayConfig;
|
|
||||||
/** 支付宝支付 */
|
|
||||||
ali?: CoolAliPayConfig;
|
|
||||||
};
|
|
||||||
/** rpc */
|
|
||||||
rpc?: CoolRpcConfig;
|
|
||||||
/** redis */
|
|
||||||
redis?: RedisConfig | RedisConfig[];
|
|
||||||
/** 文件上传 */
|
|
||||||
file?: {
|
|
||||||
/** 上传模式 */
|
|
||||||
mode: MODETYPE;
|
|
||||||
/** 本地上传 文件地址前缀 */
|
|
||||||
domain?: string;
|
|
||||||
/** oss */
|
|
||||||
oss?: OSSConfig;
|
|
||||||
/** cos */
|
|
||||||
cos?: COSConfig;
|
|
||||||
/** qiniu */
|
|
||||||
qiniu?: QINIUConfig;
|
|
||||||
/** aws */
|
|
||||||
aws: AWSConfig;
|
|
||||||
};
|
|
||||||
/** IOT 配置 */
|
|
||||||
iot?: CoolIotConfig;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface CoolRpcConfig {
|
|
||||||
/** 服务名称 */
|
|
||||||
name: string;
|
|
||||||
/** redis */
|
|
||||||
redis: RedisConfig & RedisConfig[] & unknown;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface RedisConfig {
|
|
||||||
/** host */
|
|
||||||
host: string;
|
|
||||||
/** password */
|
|
||||||
password: string;
|
|
||||||
/** port */
|
|
||||||
port: number;
|
|
||||||
/** db */
|
|
||||||
db: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 模式
|
|
||||||
export enum MODETYPE {
|
|
||||||
/** 本地 */
|
|
||||||
LOCAL = "local",
|
|
||||||
/** 云存储 */
|
|
||||||
CLOUD = "cloud",
|
|
||||||
/** 其他 */
|
|
||||||
OTHER = "other",
|
|
||||||
}
|
|
||||||
|
|
||||||
export enum CLOUDTYPE {
|
|
||||||
/** 阿里云存储 */
|
|
||||||
OSS = "oss",
|
|
||||||
/** 腾讯云存储 */
|
|
||||||
COS = "cos",
|
|
||||||
/** 七牛云存储 */
|
|
||||||
QINIU = "qiniu",
|
|
||||||
/** AWS S3 */
|
|
||||||
AWS = "aws",
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 上传模式
|
|
||||||
*/
|
|
||||||
export interface Mode {
|
|
||||||
/** 模式 */
|
|
||||||
mode: MODETYPE;
|
|
||||||
/** 类型 */
|
|
||||||
type: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 模块配置
|
|
||||||
*/
|
|
||||||
export interface CoolFileConfig {
|
|
||||||
/** 上传模式 */
|
|
||||||
mode: MODETYPE;
|
|
||||||
/** 阿里云oss 配置 */
|
|
||||||
oss: OSSConfig;
|
|
||||||
/** 腾讯云 cos配置 */
|
|
||||||
cos: COSConfig;
|
|
||||||
/** 七牛云 配置 */
|
|
||||||
qiniu: QINIUConfig;
|
|
||||||
/** AWS s3 配置 */
|
|
||||||
aws: AWSConfig;
|
|
||||||
/** 文件前缀 */
|
|
||||||
domain: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* OSS 配置
|
|
||||||
*/
|
|
||||||
export interface OSSConfig {
|
|
||||||
/** 阿里云accessKeyId */
|
|
||||||
accessKeyId: string;
|
|
||||||
/** 阿里云accessKeySecret */
|
|
||||||
accessKeySecret: string;
|
|
||||||
/** 阿里云oss的bucket */
|
|
||||||
bucket: string;
|
|
||||||
/** 阿里云oss的endpoint */
|
|
||||||
endpoint: string;
|
|
||||||
/** 阿里云oss的timeout */
|
|
||||||
timeout: string;
|
|
||||||
/** 签名失效时间,毫秒 */
|
|
||||||
expAfter?: number;
|
|
||||||
/** 文件最大的 size */
|
|
||||||
maxSize?: number;
|
|
||||||
// host
|
|
||||||
host?: string;
|
|
||||||
// 阿里云oss的公网访问地址
|
|
||||||
publicDomain?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* COS 配置
|
|
||||||
*/
|
|
||||||
export interface COSConfig {
|
|
||||||
/** 腾讯云accessKeyId */
|
|
||||||
accessKeyId: string;
|
|
||||||
/** 腾讯云accessKeySecret */
|
|
||||||
accessKeySecret: string;
|
|
||||||
/** 腾讯云cos的bucket */
|
|
||||||
bucket: string;
|
|
||||||
/** 腾讯云cos的区域 */
|
|
||||||
region: string;
|
|
||||||
/** 腾讯云cos的公网访问地址 */
|
|
||||||
publicDomain: string;
|
|
||||||
/** 上传持续时间 */
|
|
||||||
durationSeconds?: number;
|
|
||||||
/** 允许操作(上传)的对象前缀 */
|
|
||||||
allowPrefix?: string;
|
|
||||||
/** 密钥的权限列表 */
|
|
||||||
allowActions?: string[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface QINIUConfig {
|
|
||||||
/** 七牛云accessKeyId */
|
|
||||||
accessKeyId: string;
|
|
||||||
/** 七牛云accessKeySecret */
|
|
||||||
accessKeySecret: string;
|
|
||||||
/** 七牛云cos的bucket */
|
|
||||||
bucket: string;
|
|
||||||
/** 七牛云cos的区域 */
|
|
||||||
region: string;
|
|
||||||
/** 七牛云cos的公网访问地址 */
|
|
||||||
publicDomain: string;
|
|
||||||
/** 上传地址 */
|
|
||||||
uploadUrl?: string;
|
|
||||||
/** 上传fileKey */
|
|
||||||
fileKey?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface AWSConfig {
|
|
||||||
/** accessKeyId */
|
|
||||||
accessKeyId: string;
|
|
||||||
/** secretAccessKey */
|
|
||||||
secretAccessKey: string;
|
|
||||||
/** bucket */
|
|
||||||
bucket: string;
|
|
||||||
/** region */
|
|
||||||
region: string;
|
|
||||||
/** fields */
|
|
||||||
fields?: any;
|
|
||||||
/** conditions */
|
|
||||||
conditions?: any[];
|
|
||||||
/** expires */
|
|
||||||
expires?: number;
|
|
||||||
/** publicDomain */
|
|
||||||
publicDomain?: string;
|
|
||||||
/** forcePathStyle */
|
|
||||||
forcePathStyle?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 微信支付配置
|
|
||||||
*/
|
|
||||||
export interface CoolWxPayConfig {
|
|
||||||
/** 直连商户申请的公众号或移动应用appid。 */
|
|
||||||
appid: string;
|
|
||||||
/** 商户号 */
|
|
||||||
mchid: string;
|
|
||||||
/** 可选参数 证书序列号 */
|
|
||||||
serial_no?: string;
|
|
||||||
/** 回调链接 */
|
|
||||||
notify_url: string;
|
|
||||||
/** 公钥 */
|
|
||||||
publicKey: Buffer;
|
|
||||||
/** 私钥 */
|
|
||||||
privateKey: Buffer;
|
|
||||||
/** 可选参数 认证类型,目前为WECHATPAY2-SHA256-RSA2048 */
|
|
||||||
authType?: string;
|
|
||||||
/** 可选参数 User-Agent */
|
|
||||||
userAgent?: string;
|
|
||||||
/** 可选参数 APIv3密钥 */
|
|
||||||
key?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 支付宝支付配置
|
|
||||||
*/
|
|
||||||
export interface CoolAliPayConfig {
|
|
||||||
/** 支付回调地址 */
|
|
||||||
notifyUrl: string;
|
|
||||||
/** 应用ID */
|
|
||||||
appId: string;
|
|
||||||
/**
|
|
||||||
* 应用私钥字符串
|
|
||||||
* RSA签名验签工具:https://docs.open.alipay.com/291/106097)
|
|
||||||
* 密钥格式一栏请选择 “PKCS1(非JAVA适用)”
|
|
||||||
*/
|
|
||||||
privateKey: string;
|
|
||||||
/** 签名类型 */
|
|
||||||
signType?: "RSA2" | "RSA";
|
|
||||||
/** 支付宝公钥(需要对返回值做验签时候必填) */
|
|
||||||
alipayPublicKey?: string;
|
|
||||||
/** 网关 */
|
|
||||||
gateway?: string;
|
|
||||||
/** 网关超时时间(单位毫秒,默认 5s) */
|
|
||||||
timeout?: number;
|
|
||||||
/** 是否把网关返回的下划线 key 转换为驼峰写法 */
|
|
||||||
camelcase?: boolean;
|
|
||||||
/** 编码(只支持 utf-8) */
|
|
||||||
charset?: "utf-8";
|
|
||||||
/** api版本 */
|
|
||||||
version?: "1.0";
|
|
||||||
urllib?: any;
|
|
||||||
/** 指定private key类型, 默认: PKCS1, PKCS8: PRIVATE KEY, PKCS1: RSA PRIVATE KEY */
|
|
||||||
keyType?: "PKCS1" | "PKCS8";
|
|
||||||
/** 应用公钥证书文件路径 */
|
|
||||||
appCertPath?: string;
|
|
||||||
/** 应用公钥证书文件内容 */
|
|
||||||
appCertContent?: string | Buffer;
|
|
||||||
/** 应用公钥证书sn */
|
|
||||||
appCertSn?: string;
|
|
||||||
/** 支付宝根证书文件路径 */
|
|
||||||
alipayRootCertPath?: string;
|
|
||||||
/** 支付宝根证书文件内容 */
|
|
||||||
alipayRootCertContent?: string | Buffer;
|
|
||||||
/** 支付宝根证书sn */
|
|
||||||
alipayRootCertSn?: string;
|
|
||||||
/** 支付宝公钥证书文件路径 */
|
|
||||||
alipayPublicCertPath?: string;
|
|
||||||
/** 支付宝公钥证书文件内容 */
|
|
||||||
alipayPublicCertContent?: string | Buffer;
|
|
||||||
/** 支付宝公钥证书sn */
|
|
||||||
alipayCertSn?: string;
|
|
||||||
/** AES密钥,调用AES加解密相关接口时需要 */
|
|
||||||
encryptKey?: string;
|
|
||||||
/** 服务器地址 */
|
|
||||||
wsServiceUrl?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* IOT配置
|
|
||||||
*/
|
|
||||||
export interface CoolIotConfig {
|
|
||||||
/** MQTT服务端口 */
|
|
||||||
port: number;
|
|
||||||
/** MQTT Websocket服务端口 */
|
|
||||||
wsPort: number;
|
|
||||||
/** redis 配置 mqtt cluster下必须要配置 */
|
|
||||||
redis?: {
|
|
||||||
/** host */
|
|
||||||
host: string;
|
|
||||||
/** port */
|
|
||||||
port: number;
|
|
||||||
/** password */
|
|
||||||
password: string;
|
|
||||||
/** db */
|
|
||||||
db: number;
|
|
||||||
};
|
|
||||||
/** 发布消息配置 */
|
|
||||||
publish?: PublishPacket;
|
|
||||||
/** 认证 */
|
|
||||||
auth?: {
|
|
||||||
/** 用户 */
|
|
||||||
username: string;
|
|
||||||
/** 密码 */
|
|
||||||
password: string;
|
|
||||||
};
|
|
||||||
/** 服务配置 */
|
|
||||||
serve?: AedesOptions;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface CoolSmsConfig {
|
|
||||||
/**
|
|
||||||
* 阿里云短信配置
|
|
||||||
*/
|
|
||||||
ali: CoolSmsAliConfig;
|
|
||||||
/**
|
|
||||||
* 腾讯云短信配置
|
|
||||||
*/
|
|
||||||
tx: CoolSmsTxConfig;
|
|
||||||
/**
|
|
||||||
* 云片短信配置
|
|
||||||
*/
|
|
||||||
yp: CoolSmsYpConfig;
|
|
||||||
/**
|
|
||||||
* aws短信配置
|
|
||||||
*/
|
|
||||||
aws: CoolSmsAwsConfig;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface CoolSmsAwsConfig {
|
|
||||||
/**
|
|
||||||
* 区域
|
|
||||||
*/
|
|
||||||
region: string;
|
|
||||||
/**
|
|
||||||
* accessKeyId
|
|
||||||
*/
|
|
||||||
accessKeyId: string;
|
|
||||||
/**
|
|
||||||
* secretAccessKey
|
|
||||||
*/
|
|
||||||
secretAccessKey: string;
|
|
||||||
/**
|
|
||||||
* 扩展配置
|
|
||||||
*/
|
|
||||||
extend?: any;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 阿里云配置
|
|
||||||
*/
|
|
||||||
export interface CoolSmsAliConfig {
|
|
||||||
/**
|
|
||||||
* 阿里云accessKeyId
|
|
||||||
*/
|
|
||||||
accessKeyId: string;
|
|
||||||
/**
|
|
||||||
* 阿里云accessKeySecret
|
|
||||||
*/
|
|
||||||
accessKeySecret: string;
|
|
||||||
/**
|
|
||||||
* 签名,非必填,调用时可以传入
|
|
||||||
*/
|
|
||||||
signName?: string;
|
|
||||||
/**
|
|
||||||
* 模板,非必填,调用时可以传入
|
|
||||||
*/
|
|
||||||
template?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 腾讯云配置
|
|
||||||
*/
|
|
||||||
export interface CoolSmsTxConfig {
|
|
||||||
/**
|
|
||||||
* 应用ID
|
|
||||||
*/
|
|
||||||
appId: string;
|
|
||||||
/**
|
|
||||||
* 腾讯云secretId
|
|
||||||
*/
|
|
||||||
secretId: string;
|
|
||||||
/**
|
|
||||||
* 腾讯云secretKey
|
|
||||||
*/
|
|
||||||
secretKey: string;
|
|
||||||
/**
|
|
||||||
* 签名,非必填,调用时可以传入
|
|
||||||
*/
|
|
||||||
signName?: string;
|
|
||||||
/**
|
|
||||||
* 模板,非必填,调用时可以传入
|
|
||||||
*/
|
|
||||||
template?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 云片短信配置
|
|
||||||
*/
|
|
||||||
export interface CoolSmsYpConfig {
|
|
||||||
/**
|
|
||||||
* 云片apikey
|
|
||||||
*/
|
|
||||||
apikey: string;
|
|
||||||
/**
|
|
||||||
* 签名,非必填,调用时可以传入
|
|
||||||
*/
|
|
||||||
signName?: string;
|
|
||||||
/**
|
|
||||||
* 模板,非必填,调用时可以传入
|
|
||||||
*/
|
|
||||||
template?: string;
|
|
||||||
}
|
|
||||||
@ -1,100 +0,0 @@
|
|||||||
import { IMidwayApplication } from "@midwayjs/core";
|
|
||||||
import {
|
|
||||||
ALL,
|
|
||||||
App,
|
|
||||||
Config,
|
|
||||||
Init,
|
|
||||||
Provide,
|
|
||||||
Scope,
|
|
||||||
ScopeEnum,
|
|
||||||
} from "@midwayjs/decorator";
|
|
||||||
import * as fs from "fs";
|
|
||||||
import { CoolCoreException } from "../exception/core";
|
|
||||||
import { ModuleConfig } from "../interface";
|
|
||||||
import * as _ from "lodash";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 模块配置
|
|
||||||
*/
|
|
||||||
@Provide()
|
|
||||||
@Scope(ScopeEnum.Singleton)
|
|
||||||
export class CoolModuleConfig {
|
|
||||||
@App()
|
|
||||||
app: IMidwayApplication;
|
|
||||||
|
|
||||||
@Config(ALL)
|
|
||||||
allConfig;
|
|
||||||
|
|
||||||
modules;
|
|
||||||
|
|
||||||
@Init()
|
|
||||||
async init() {
|
|
||||||
let modules = [];
|
|
||||||
// 模块路径
|
|
||||||
const moduleBasePath = `${this.app.getBaseDir()}/modules/`;
|
|
||||||
if (!fs.existsSync(moduleBasePath)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!this.allConfig["module"]) {
|
|
||||||
this.allConfig["module"] = {};
|
|
||||||
}
|
|
||||||
// 全局中间件
|
|
||||||
const globalMiddlewareArr = [];
|
|
||||||
for (const module of fs.readdirSync(moduleBasePath)) {
|
|
||||||
const modulePath = `${moduleBasePath}/${module}`;
|
|
||||||
const dirStats = fs.statSync(modulePath);
|
|
||||||
if (dirStats.isDirectory()) {
|
|
||||||
const configPath = fs.existsSync(`${modulePath}/config.ts`)
|
|
||||||
? `${modulePath}/config.ts`
|
|
||||||
: `${modulePath}/config.js`;
|
|
||||||
if (fs.existsSync(configPath)) {
|
|
||||||
const moduleConfig: ModuleConfig = require(configPath).default({
|
|
||||||
app: this.app,
|
|
||||||
env: this.app.getEnv(),
|
|
||||||
});
|
|
||||||
modules.push({
|
|
||||||
order: moduleConfig.order || 0,
|
|
||||||
module: module,
|
|
||||||
});
|
|
||||||
await this.moduleConfig(module, moduleConfig);
|
|
||||||
// 处理全局中间件
|
|
||||||
if (!_.isEmpty(moduleConfig.globalMiddlewares)) {
|
|
||||||
globalMiddlewareArr.push({
|
|
||||||
order: moduleConfig.order || 0,
|
|
||||||
data: moduleConfig.globalMiddlewares,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
throw new CoolCoreException(`模块【${module}】缺少config.ts配置文件`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.modules = _.orderBy(modules, ["order"], ["desc"]).map((e) => {
|
|
||||||
return e.module;
|
|
||||||
});
|
|
||||||
await this.globalMiddlewareArr(globalMiddlewareArr);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 模块配置
|
|
||||||
* @param module 模块
|
|
||||||
* @param config 配置
|
|
||||||
*/
|
|
||||||
async moduleConfig(module, config) {
|
|
||||||
// 追加配置
|
|
||||||
this.allConfig["module"][module] = config;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 全局中间件
|
|
||||||
* @param middleware 中间件
|
|
||||||
*/
|
|
||||||
async globalMiddlewareArr(middlewares: any[]) {
|
|
||||||
middlewares = _.orderBy(middlewares, ["order"], ["desc"]);
|
|
||||||
for (const middleware of middlewares) {
|
|
||||||
for (const item of middleware.data) {
|
|
||||||
this.app.getMiddleware().insertLast(item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,260 +0,0 @@
|
|||||||
import { ILogger, IMidwayApplication, Scope, ScopeEnum } from "@midwayjs/core";
|
|
||||||
import { App, Config, Inject, Logger, Provide } from "@midwayjs/decorator";
|
|
||||||
import { InjectDataSource, TypeORMDataSourceManager } from "@midwayjs/typeorm";
|
|
||||||
import * as fs from "fs";
|
|
||||||
import * as _ from "lodash";
|
|
||||||
import * as path from "path";
|
|
||||||
import { DataSource, Equal } from "typeorm";
|
|
||||||
import { CoolEventManager } from "../event";
|
|
||||||
import { CoolModuleConfig } from "./config";
|
|
||||||
import { CoolModuleMenu } from "./menu";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 模块sql
|
|
||||||
*/
|
|
||||||
@Provide()
|
|
||||||
@Scope(ScopeEnum.Singleton)
|
|
||||||
export class CoolModuleImport {
|
|
||||||
@Config("typeorm.dataSource")
|
|
||||||
ormConfig;
|
|
||||||
|
|
||||||
@InjectDataSource("default")
|
|
||||||
defaultDataSource: DataSource;
|
|
||||||
|
|
||||||
@Inject()
|
|
||||||
typeORMDataSourceManager: TypeORMDataSourceManager;
|
|
||||||
|
|
||||||
@Config("cool")
|
|
||||||
coolConfig;
|
|
||||||
|
|
||||||
@Logger()
|
|
||||||
coreLogger: ILogger;
|
|
||||||
|
|
||||||
@Inject()
|
|
||||||
coolModuleConfig: CoolModuleConfig;
|
|
||||||
|
|
||||||
@Inject()
|
|
||||||
coolEventManager: CoolEventManager;
|
|
||||||
|
|
||||||
@App()
|
|
||||||
app: IMidwayApplication;
|
|
||||||
|
|
||||||
@Inject()
|
|
||||||
coolModuleMenu: CoolModuleMenu;
|
|
||||||
|
|
||||||
initJudge: "file" | "db";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 初始化
|
|
||||||
*/
|
|
||||||
async init() {
|
|
||||||
this.initJudge = this.coolConfig.initJudge;
|
|
||||||
if (!this.initJudge) {
|
|
||||||
this.initJudge = "file";
|
|
||||||
}
|
|
||||||
// 是否需要导入
|
|
||||||
if (this.coolConfig.initDB) {
|
|
||||||
const modules = this.coolModuleConfig.modules;
|
|
||||||
const metadatas = await this.getDbMetadatas();
|
|
||||||
setTimeout(async () => {
|
|
||||||
for (const module of modules) {
|
|
||||||
if (this.initJudge == "file") {
|
|
||||||
const { exist, lockPath } = this.checkFileExist(module);
|
|
||||||
if (!exist) {
|
|
||||||
await this.initDataBase(module, metadatas, lockPath);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (this.initJudge == "db") {
|
|
||||||
const exist = await this.checkDbExist(module, metadatas);
|
|
||||||
if (!exist) {
|
|
||||||
await this.initDataBase(module, metadatas);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.coolEventManager.emit("onDBInit", {});
|
|
||||||
this.coolModuleMenu.init();
|
|
||||||
}, 2000);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取数据库元数据
|
|
||||||
*/
|
|
||||||
async getDbMetadatas() {
|
|
||||||
// 获得所有的实体
|
|
||||||
const entityMetadatas = this.defaultDataSource.entityMetadatas;
|
|
||||||
const metadatas = _.mapValues(
|
|
||||||
_.keyBy(entityMetadatas, "tableName"),
|
|
||||||
"target"
|
|
||||||
);
|
|
||||||
return metadatas;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 检查数据是否存在
|
|
||||||
* @param module
|
|
||||||
* @param metadatas
|
|
||||||
*/
|
|
||||||
async checkDbExist(module: string, metadatas) {
|
|
||||||
const cKey = `init_db_${module}`;
|
|
||||||
const repository = this.defaultDataSource.getRepository(
|
|
||||||
metadatas["base_sys_conf"]
|
|
||||||
);
|
|
||||||
const data = await repository.findOneBy({ cKey: Equal(cKey) });
|
|
||||||
return !!data;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 检查文件是否存在
|
|
||||||
* @param module
|
|
||||||
*/
|
|
||||||
checkFileExist(module: string) {
|
|
||||||
const importLockPath = path.join(
|
|
||||||
`${this.app.getBaseDir()}`,
|
|
||||||
"..",
|
|
||||||
"lock",
|
|
||||||
"db"
|
|
||||||
);
|
|
||||||
if (!fs.existsSync(importLockPath)) {
|
|
||||||
fs.mkdirSync(importLockPath, { recursive: true });
|
|
||||||
}
|
|
||||||
const lockPath = path.join(importLockPath, module + ".db.lock");
|
|
||||||
return {
|
|
||||||
exist: fs.existsSync(lockPath),
|
|
||||||
lockPath,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 导入数据库
|
|
||||||
* @param module
|
|
||||||
* @param lockPath 锁定导入
|
|
||||||
*/
|
|
||||||
async initDataBase(module: string, metadatas, lockPath?: string) {
|
|
||||||
// 计算耗时
|
|
||||||
const startTime = new Date().getTime();
|
|
||||||
// 模块路径
|
|
||||||
const modulePath = `${this.app.getBaseDir()}/modules/${module}`;
|
|
||||||
// 数据路径
|
|
||||||
const dataPath = `${modulePath}/db.json`;
|
|
||||||
// 判断文件是否存在
|
|
||||||
if (fs.existsSync(dataPath)) {
|
|
||||||
// 读取数据
|
|
||||||
const data = JSON.parse(fs.readFileSync(dataPath).toString() || "{}");
|
|
||||||
// 导入数据
|
|
||||||
for (const key in data) {
|
|
||||||
try {
|
|
||||||
for (const item of data[key]) {
|
|
||||||
await this.importData(metadatas, item, key);
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
this.coreLogger.error(
|
|
||||||
"\x1B[36m [cool:core] midwayjs cool core init " +
|
|
||||||
module +
|
|
||||||
" database err \x1B[0m"
|
|
||||||
);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const endTime = new Date().getTime();
|
|
||||||
await this.lockImportData(
|
|
||||||
module,
|
|
||||||
metadatas,
|
|
||||||
lockPath,
|
|
||||||
endTime - startTime
|
|
||||||
);
|
|
||||||
this.coreLogger.info(
|
|
||||||
"\x1B[36m [cool:core] midwayjs cool core init " +
|
|
||||||
module +
|
|
||||||
" database complete \x1B[0m"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 锁定导入
|
|
||||||
* @param module
|
|
||||||
* @param metadatas
|
|
||||||
* @param lockPath
|
|
||||||
* @param time
|
|
||||||
*/
|
|
||||||
async lockImportData(
|
|
||||||
module: string,
|
|
||||||
metadatas,
|
|
||||||
lockPath: string,
|
|
||||||
time: number
|
|
||||||
) {
|
|
||||||
if (this.initJudge == "file") {
|
|
||||||
fs.writeFileSync(lockPath, `time consuming:${time}ms`);
|
|
||||||
}
|
|
||||||
if (this.initJudge == "db") {
|
|
||||||
const repository = this.defaultDataSource.getRepository(
|
|
||||||
metadatas["base_sys_conf"]
|
|
||||||
);
|
|
||||||
if (this.ormConfig.default.type == "postgres") {
|
|
||||||
await repository.save(
|
|
||||||
repository.create({
|
|
||||||
cKey: `init_db_${module}`,
|
|
||||||
cValue: `time consuming:${time}ms`,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
await repository.insert({
|
|
||||||
cKey: `init_db_${module}`,
|
|
||||||
cValue: `time consuming:${time}ms`,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 导入数据
|
|
||||||
* @param metadatas
|
|
||||||
* @param datas
|
|
||||||
* @param tableName
|
|
||||||
*/
|
|
||||||
async importData(
|
|
||||||
metadatas: any[],
|
|
||||||
item: any,
|
|
||||||
tableName: string,
|
|
||||||
parentItem: any = null
|
|
||||||
) {
|
|
||||||
const repository = this.defaultDataSource.getRepository(
|
|
||||||
metadatas[tableName]
|
|
||||||
);
|
|
||||||
// 处理当前项中的引用
|
|
||||||
if (parentItem) {
|
|
||||||
for (const key in item) {
|
|
||||||
if (typeof item[key] === "string" && item[key].startsWith("@")) {
|
|
||||||
const parentKey = item[key].substring(1); // 移除"@"符号
|
|
||||||
if (parentItem.hasOwnProperty(parentKey)) {
|
|
||||||
item[key] = parentItem[parentKey];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 插入当前项到数据库
|
|
||||||
let insertedItem;
|
|
||||||
if (this.ormConfig.default.type == "postgres") {
|
|
||||||
insertedItem = await repository.save(repository.create(item));
|
|
||||||
if (item.id) {
|
|
||||||
await repository.update(insertedItem.id, { id: item.id });
|
|
||||||
await this.defaultDataSource.query(
|
|
||||||
`SELECT setval('${tableName}_id_seq', (SELECT MAX(id) FROM ${tableName}));`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
insertedItem = await repository.insert(item);
|
|
||||||
}
|
|
||||||
// 递归处理@childDatas
|
|
||||||
if (!_.isEmpty(item["@childDatas"])) {
|
|
||||||
const childDatas = item["@childDatas"];
|
|
||||||
delete item["@childDatas"];
|
|
||||||
for (const childKey in childDatas) {
|
|
||||||
for (const childItem of childDatas[childKey]) {
|
|
||||||
await this.importData(metadatas, childItem, childKey, item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,186 +0,0 @@
|
|||||||
import {
|
|
||||||
App,
|
|
||||||
Config,
|
|
||||||
ILogger,
|
|
||||||
IMidwayApplication,
|
|
||||||
Inject,
|
|
||||||
Logger,
|
|
||||||
Provide,
|
|
||||||
Scope,
|
|
||||||
ScopeEnum,
|
|
||||||
} from "@midwayjs/core";
|
|
||||||
import { InjectDataSource, TypeORMDataSourceManager } from "@midwayjs/typeorm";
|
|
||||||
import * as fs from "fs";
|
|
||||||
import * as _ from "lodash";
|
|
||||||
import * as path from "path";
|
|
||||||
import { DataSource, Equal } from "typeorm";
|
|
||||||
import { CoolEventManager } from "../event";
|
|
||||||
import { CoolConfig } from "../interface";
|
|
||||||
import { CoolModuleConfig } from "./config";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 菜单
|
|
||||||
*/
|
|
||||||
@Provide()
|
|
||||||
@Scope(ScopeEnum.Singleton)
|
|
||||||
export class CoolModuleMenu {
|
|
||||||
@Inject()
|
|
||||||
coolModuleConfig: CoolModuleConfig;
|
|
||||||
|
|
||||||
@Config("cool")
|
|
||||||
coolConfig: CoolConfig;
|
|
||||||
|
|
||||||
@App()
|
|
||||||
app: IMidwayApplication;
|
|
||||||
|
|
||||||
@Logger()
|
|
||||||
coreLogger: ILogger;
|
|
||||||
|
|
||||||
@Inject()
|
|
||||||
coolEventManager: CoolEventManager;
|
|
||||||
|
|
||||||
initJudge: "file" | "db";
|
|
||||||
|
|
||||||
@Config("typeorm.dataSource")
|
|
||||||
ormConfig;
|
|
||||||
|
|
||||||
@InjectDataSource("default")
|
|
||||||
defaultDataSource: DataSource;
|
|
||||||
|
|
||||||
@Inject()
|
|
||||||
typeORMDataSourceManager: TypeORMDataSourceManager;
|
|
||||||
|
|
||||||
datas = {};
|
|
||||||
|
|
||||||
async init() {
|
|
||||||
this.initJudge = this.coolConfig.initJudge;
|
|
||||||
if (!this.initJudge) {
|
|
||||||
this.initJudge = "file";
|
|
||||||
}
|
|
||||||
// 是否需要导入
|
|
||||||
if (this.coolConfig.initMenu) {
|
|
||||||
const modules = this.coolModuleConfig.modules;
|
|
||||||
const metadatas = await this.getDbMetadatas();
|
|
||||||
for (const module of modules) {
|
|
||||||
if (this.initJudge == "file") {
|
|
||||||
const { exist, lockPath } = this.checkFileExist(module);
|
|
||||||
if (!exist) {
|
|
||||||
await this.importMenu(module, metadatas, lockPath);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (this.initJudge == "db") {
|
|
||||||
const exist = await this.checkDbExist(module, metadatas);
|
|
||||||
if (!exist) {
|
|
||||||
await this.importMenu(module, metadatas);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.coolEventManager.emit("onMenuImport", this.datas);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 导入菜单
|
|
||||||
* @param module
|
|
||||||
* @param lockPath
|
|
||||||
*/
|
|
||||||
async importMenu(module: string, metadatas, lockPath?: string) {
|
|
||||||
// 模块路径
|
|
||||||
const modulePath = `${this.app.getBaseDir()}/modules/${module}`;
|
|
||||||
// json 路径
|
|
||||||
const menuPath = `${modulePath}/menu.json`;
|
|
||||||
// 导入
|
|
||||||
if (fs.existsSync(menuPath)) {
|
|
||||||
const data = fs.readFileSync(menuPath);
|
|
||||||
try {
|
|
||||||
// this.coolEventManager.emit("onMenuImport", module, JSON.parse(data.toString()));
|
|
||||||
this.datas[module] = JSON.parse(data.toString());
|
|
||||||
await this.lockImportData(module, metadatas, lockPath);
|
|
||||||
} catch (error) {
|
|
||||||
this.coreLogger.error(error);
|
|
||||||
this.coreLogger.error(
|
|
||||||
`自动初始化模块[${module}]菜单失败,请检查对应的数据结构是否正确`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取数据库元数据
|
|
||||||
*/
|
|
||||||
async getDbMetadatas() {
|
|
||||||
// 获得所有的实体
|
|
||||||
const entityMetadatas = this.defaultDataSource.entityMetadatas;
|
|
||||||
const metadatas = _.mapValues(
|
|
||||||
_.keyBy(entityMetadatas, "tableName"),
|
|
||||||
"target"
|
|
||||||
);
|
|
||||||
return metadatas;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 检查数据是否存在
|
|
||||||
* @param module
|
|
||||||
* @param metadatas
|
|
||||||
*/
|
|
||||||
async checkDbExist(module: string, metadatas) {
|
|
||||||
const cKey = `init_menu_${module}`;
|
|
||||||
const repository = this.defaultDataSource.getRepository(
|
|
||||||
metadatas["base_sys_conf"]
|
|
||||||
);
|
|
||||||
const data = await repository.findOneBy({ cKey: Equal(cKey) });
|
|
||||||
return !!data;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 检查文件是否存在
|
|
||||||
* @param module
|
|
||||||
*/
|
|
||||||
checkFileExist(module: string) {
|
|
||||||
const importLockPath = path.join(
|
|
||||||
`${this.app.getBaseDir()}`,
|
|
||||||
"..",
|
|
||||||
"lock",
|
|
||||||
"menu"
|
|
||||||
);
|
|
||||||
if (!fs.existsSync(importLockPath)) {
|
|
||||||
fs.mkdirSync(importLockPath, { recursive: true });
|
|
||||||
}
|
|
||||||
const lockPath = path.join(importLockPath, module + ".menu.lock");
|
|
||||||
return {
|
|
||||||
exist: fs.existsSync(lockPath),
|
|
||||||
lockPath,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 锁定导入
|
|
||||||
* @param module
|
|
||||||
* @param metadatas
|
|
||||||
* @param lockPath
|
|
||||||
* @param time
|
|
||||||
*/
|
|
||||||
async lockImportData(module: string, metadatas, lockPath: string) {
|
|
||||||
if (this.initJudge == "file") {
|
|
||||||
fs.writeFileSync(lockPath, `success`);
|
|
||||||
}
|
|
||||||
if (this.initJudge == "db") {
|
|
||||||
const repository = this.defaultDataSource.getRepository(
|
|
||||||
metadatas["base_sys_conf"]
|
|
||||||
);
|
|
||||||
if (this.ormConfig.default.type == "postgres") {
|
|
||||||
await repository.save(
|
|
||||||
repository.create({
|
|
||||||
cKey: `init_menu_${module}`,
|
|
||||||
cValue: `success`,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
await repository.insert({
|
|
||||||
cKey: `init_menu_${module}`,
|
|
||||||
cValue: `success`,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,62 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "@cool-midway/core",
|
|
||||||
"version": "7.1.25",
|
|
||||||
"description": "",
|
|
||||||
"main": "index.js",
|
|
||||||
"typings": "index.d.ts",
|
|
||||||
"scripts": {
|
|
||||||
"build": "cross-env midway-bin build -c",
|
|
||||||
"cov": "cross-env midway-bin cov --ts",
|
|
||||||
"lint": "mwts check",
|
|
||||||
"lint:fix": "mwts fix"
|
|
||||||
},
|
|
||||||
"keywords": [
|
|
||||||
"cool",
|
|
||||||
"cool-admin",
|
|
||||||
"cooljs"
|
|
||||||
],
|
|
||||||
"readme": "README.md",
|
|
||||||
"author": "COOL",
|
|
||||||
"files": [
|
|
||||||
"**/*.js",
|
|
||||||
"**/*.d.ts",
|
|
||||||
"index.d.ts"
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
|
||||||
"repository": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "https://cool-js.com"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"@midwayjs/cli": "1.3.21",
|
|
||||||
"@midwayjs/core": "^3.9.0",
|
|
||||||
"@midwayjs/decorator": "^3.9.0",
|
|
||||||
"@midwayjs/koa": "^3.9.0",
|
|
||||||
"@midwayjs/mock": "^3.9.0",
|
|
||||||
"@midwayjs/typeorm": "^3.9.0",
|
|
||||||
"@types/jest": "^29.2.4",
|
|
||||||
"@types/node": "^18.11.15",
|
|
||||||
"cross-env": "^7.0.3",
|
|
||||||
"jest": "^29.3.1",
|
|
||||||
"mwts": "^1.3.0",
|
|
||||||
"ts-jest": "^29.0.3",
|
|
||||||
"typeorm": "^0.3.19",
|
|
||||||
"typescript": "~4.9.4"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"@midwayjs/cache": "^3.14.0",
|
|
||||||
"@midwayjs/cache-manager": "^3.15.0",
|
|
||||||
"@cool-midway/cache-manager-fs-hash": "^7.0.0",
|
|
||||||
"axios": "^1.6.5",
|
|
||||||
"decompress": "^4.2.1",
|
|
||||||
"download": "^8.0.0",
|
|
||||||
"jsonwebtoken": "^9.0.2",
|
|
||||||
"lodash": "^4.17.21",
|
|
||||||
"md5": "^2.3.0",
|
|
||||||
"moment": "^2.30.1",
|
|
||||||
"sqlstring": "^2.3.3",
|
|
||||||
"uuid": "^9.0.1",
|
|
||||||
"ws": "^8.16.0",
|
|
||||||
"pm2": "^5.3.1"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,169 +0,0 @@
|
|||||||
import {
|
|
||||||
CONTROLLER_KEY,
|
|
||||||
getClassMetadata,
|
|
||||||
listModule,
|
|
||||||
Provide,
|
|
||||||
} from "@midwayjs/decorator";
|
|
||||||
import * as _ from "lodash";
|
|
||||||
import {
|
|
||||||
Scope,
|
|
||||||
ScopeEnum,
|
|
||||||
Config,
|
|
||||||
Inject,
|
|
||||||
MidwayWebRouterService,
|
|
||||||
} from "@midwayjs/core";
|
|
||||||
import { TypeORMDataSourceManager } from "@midwayjs/typeorm";
|
|
||||||
import { CoolUrlTagData } from "../tag/data";
|
|
||||||
import { TagTypes } from "../decorator/tag";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 实体路径
|
|
||||||
*/
|
|
||||||
@Provide()
|
|
||||||
@Scope(ScopeEnum.Singleton)
|
|
||||||
export class CoolEps {
|
|
||||||
admin = {};
|
|
||||||
|
|
||||||
app = {};
|
|
||||||
|
|
||||||
module = {};
|
|
||||||
|
|
||||||
@Inject()
|
|
||||||
midwayWebRouterService: MidwayWebRouterService;
|
|
||||||
|
|
||||||
@Inject()
|
|
||||||
typeORMDataSourceManager: TypeORMDataSourceManager;
|
|
||||||
|
|
||||||
@Config("cool.eps")
|
|
||||||
epsConfig: boolean;
|
|
||||||
|
|
||||||
@Config("module")
|
|
||||||
moduleConfig: any;
|
|
||||||
|
|
||||||
@Inject()
|
|
||||||
coolUrlTagData: CoolUrlTagData;
|
|
||||||
|
|
||||||
// @Init()
|
|
||||||
async init() {
|
|
||||||
if (!this.epsConfig) return;
|
|
||||||
const entitys = await this.entity();
|
|
||||||
const controllers = await this.controller();
|
|
||||||
const routers = await this.router();
|
|
||||||
await this.modules();
|
|
||||||
const adminArr = [];
|
|
||||||
const appArr = [];
|
|
||||||
for (const controller of controllers) {
|
|
||||||
const { prefix, module, curdOption, routerOptions } = controller;
|
|
||||||
const name = curdOption?.entity?.name;
|
|
||||||
(_.startsWith(prefix, "/admin/") ? adminArr : appArr).push({
|
|
||||||
module,
|
|
||||||
info: {
|
|
||||||
type: {
|
|
||||||
name: prefix.split("/").pop(),
|
|
||||||
description: routerOptions?.description || "",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
api: routers[prefix],
|
|
||||||
name,
|
|
||||||
columns: entitys[name] || [],
|
|
||||||
prefix,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
this.admin = _.groupBy(adminArr, "module");
|
|
||||||
this.app = _.groupBy(appArr, "module");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 模块信息
|
|
||||||
* @param module
|
|
||||||
*/
|
|
||||||
async modules(module?: string) {
|
|
||||||
for (const key in this.moduleConfig) {
|
|
||||||
const config = this.moduleConfig[key];
|
|
||||||
this.module[key] = {
|
|
||||||
name: config.name,
|
|
||||||
description: config.description,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return module ? this.module[module] : this.module;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 所有controller
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
async controller() {
|
|
||||||
const result = [];
|
|
||||||
const controllers = listModule(CONTROLLER_KEY);
|
|
||||||
for (const controller of controllers) {
|
|
||||||
result.push(getClassMetadata(CONTROLLER_KEY, controller));
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 所有路由
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
async router() {
|
|
||||||
let ignoreUrls: string[] = this.coolUrlTagData.byKey(TagTypes.IGNORE_TOKEN);
|
|
||||||
if (_.isEmpty(ignoreUrls)) {
|
|
||||||
ignoreUrls = [];
|
|
||||||
}
|
|
||||||
return _.groupBy(
|
|
||||||
(await this.midwayWebRouterService.getFlattenRouterTable()).map(
|
|
||||||
(item) => {
|
|
||||||
return {
|
|
||||||
method: item.requestMethod,
|
|
||||||
path: item.url,
|
|
||||||
summary: item.summary,
|
|
||||||
dts: {},
|
|
||||||
tag: "",
|
|
||||||
prefix: item.prefix,
|
|
||||||
ignoreToken: ignoreUrls.includes(item.prefix + item.url),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
),
|
|
||||||
"prefix"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 所有实体
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
async entity() {
|
|
||||||
const result = {};
|
|
||||||
const dataSourceNames = this.typeORMDataSourceManager.getDataSourceNames();
|
|
||||||
for (const dataSourceName of dataSourceNames) {
|
|
||||||
const entityMetadatas = await this.typeORMDataSourceManager.getDataSource(
|
|
||||||
dataSourceName
|
|
||||||
).entityMetadatas;
|
|
||||||
for (const entityMetadata of entityMetadatas) {
|
|
||||||
const commColums = [];
|
|
||||||
let columns = entityMetadata.columns;
|
|
||||||
if (entityMetadata.tableType != "regular") continue;
|
|
||||||
columns = _.filter(
|
|
||||||
columns.map((e) => {
|
|
||||||
return {
|
|
||||||
propertyName: e.propertyName,
|
|
||||||
type:
|
|
||||||
typeof e.type == "string" ? e.type : e.type.name.toLowerCase(),
|
|
||||||
length: e.length,
|
|
||||||
comment: e.comment,
|
|
||||||
nullable: e.isNullable,
|
|
||||||
};
|
|
||||||
}),
|
|
||||||
(o) => {
|
|
||||||
if (["createTime", "updateTime"].includes(o.propertyName)) {
|
|
||||||
commColums.push(o);
|
|
||||||
}
|
|
||||||
return o && !["createTime", "updateTime"].includes(o.propertyName);
|
|
||||||
}
|
|
||||||
).concat(commColums);
|
|
||||||
result[entityMetadata.name] = columns;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,287 +0,0 @@
|
|||||||
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 { ERRINFO } from "../constant/global";
|
|
||||||
import { Application, Context } from "@midwayjs/koa";
|
|
||||||
import { TypeORMDataSourceManager } from "@midwayjs/typeorm";
|
|
||||||
import { Repository, SelectQueryBuilder } from "typeorm";
|
|
||||||
import { QueryOp } from "../decorator/controller";
|
|
||||||
import * as _ from "lodash";
|
|
||||||
import { CoolEventManager } from "../event";
|
|
||||||
import { CoolCoreException } from "../exception/core";
|
|
||||||
import { BaseSqliteService } from "./sqlite";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 服务基类
|
|
||||||
*/
|
|
||||||
@Provide()
|
|
||||||
@Scope(ScopeEnum.Request, { allowDowngrade: true })
|
|
||||||
export abstract class BaseService {
|
|
||||||
// mysql的基类
|
|
||||||
@Inject()
|
|
||||||
baseMysqlService: BaseMysqlService;
|
|
||||||
|
|
||||||
// postgres的基类
|
|
||||||
@Inject()
|
|
||||||
basePgService: BasePgService;
|
|
||||||
|
|
||||||
@Inject()
|
|
||||||
baseSqliteService: BaseSqliteService;
|
|
||||||
|
|
||||||
// 数据库类型
|
|
||||||
@Config("typeorm.dataSource.default.type")
|
|
||||||
ormType;
|
|
||||||
|
|
||||||
// 当前服务名称
|
|
||||||
service: BaseMysqlService | BasePgService | BaseSqliteService;
|
|
||||||
|
|
||||||
// 模型
|
|
||||||
protected entity: Repository<any>;
|
|
||||||
|
|
||||||
protected sqlParams;
|
|
||||||
|
|
||||||
@Inject()
|
|
||||||
typeORMDataSourceManager: TypeORMDataSourceManager;
|
|
||||||
|
|
||||||
@Inject()
|
|
||||||
coolEventManager: CoolEventManager;
|
|
||||||
|
|
||||||
@Inject("ctx")
|
|
||||||
baseCtx: Context;
|
|
||||||
|
|
||||||
@App()
|
|
||||||
baseApp: Application;
|
|
||||||
|
|
||||||
@Init()
|
|
||||||
async init() {
|
|
||||||
const services = {
|
|
||||||
mysql: this.baseMysqlService,
|
|
||||||
mariadb: 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) {
|
|
||||||
this.entity = entity;
|
|
||||||
this.service.setEntity(entity);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 设置请求上下文
|
|
||||||
setCtx(ctx: Context) {
|
|
||||||
this.baseCtx = ctx;
|
|
||||||
this.service.setCtx(ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 设置应用对象
|
|
||||||
setApp(app: Application) {
|
|
||||||
this.baseApp = app;
|
|
||||||
this.service.setApp(app);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 设置sql
|
|
||||||
* @param condition 条件是否成立
|
|
||||||
* @param sql sql语句
|
|
||||||
* @param params 参数
|
|
||||||
*/
|
|
||||||
setSql(condition, sql, params) {
|
|
||||||
return this.service.setSql(condition, sql, params);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获得查询个数的SQL
|
|
||||||
* @param sql
|
|
||||||
*/
|
|
||||||
getCountSql(sql) {
|
|
||||||
return this.service.getCountSql(sql);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 参数安全性检查
|
|
||||||
* @param params
|
|
||||||
*/
|
|
||||||
async paramSafetyCheck(params) {
|
|
||||||
return await this.service.paramSafetyCheck(params);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 原生查询
|
|
||||||
* @param sql
|
|
||||||
* @param params
|
|
||||||
* @param connectionName
|
|
||||||
*/
|
|
||||||
async nativeQuery(sql, params?, connectionName?) {
|
|
||||||
return await this.service.nativeQuery(sql, params, connectionName);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获得ORM管理
|
|
||||||
* @param connectionName 连接名称
|
|
||||||
*/
|
|
||||||
getOrmManager(connectionName = "default") {
|
|
||||||
return this.service.getOrmManager(connectionName);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 操作entity获得分页数据,不用写sql
|
|
||||||
* @param find QueryBuilder
|
|
||||||
* @param query
|
|
||||||
* @param autoSort
|
|
||||||
* @param connectionName
|
|
||||||
*/
|
|
||||||
async entityRenderPage(
|
|
||||||
find: SelectQueryBuilder<any>,
|
|
||||||
query,
|
|
||||||
autoSort = true
|
|
||||||
) {
|
|
||||||
return await this.service.entityRenderPage(find, query, autoSort);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 执行SQL并获得分页数据
|
|
||||||
* @param sql 执行的sql语句
|
|
||||||
* @param query 分页查询条件
|
|
||||||
* @param autoSort 是否自动排序
|
|
||||||
* @param connectionName 连接名称
|
|
||||||
*/
|
|
||||||
async sqlRenderPage(sql, query, autoSort = true, connectionName?) {
|
|
||||||
return await this.service.sqlRenderPage(
|
|
||||||
sql,
|
|
||||||
query,
|
|
||||||
autoSort,
|
|
||||||
connectionName
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获得单个ID
|
|
||||||
* @param id ID
|
|
||||||
* @param infoIgnoreProperty 忽略返回属性
|
|
||||||
*/
|
|
||||||
async info(id: any, infoIgnoreProperty?: string[]) {
|
|
||||||
this.service.setEntity(this.entity);
|
|
||||||
return await this.service.info(id, infoIgnoreProperty);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 删除
|
|
||||||
* @param ids 删除的ID集合 如:[1,2,3] 或者 1,2,3
|
|
||||||
*/
|
|
||||||
async delete(ids: any) {
|
|
||||||
this.service.setEntity(this.entity);
|
|
||||||
await this.modifyBefore(ids, "delete");
|
|
||||||
await this.service.delete(ids);
|
|
||||||
await this.modifyAfter(ids, "delete");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 软删除
|
|
||||||
* @param ids 删除的ID数组
|
|
||||||
* @param entity 实体
|
|
||||||
*/
|
|
||||||
async softDelete(ids: number[], entity?: Repository<any>) {
|
|
||||||
this.service.setEntity(this.entity);
|
|
||||||
await this.service.softDelete(ids, entity);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 修改
|
|
||||||
* @param param 数据
|
|
||||||
*/
|
|
||||||
async update(param: any) {
|
|
||||||
this.service.setEntity(this.entity);
|
|
||||||
if (!this.entity) throw new CoolValidateException(ERRINFO.NOENTITY);
|
|
||||||
if (!param.id && !(param instanceof Array))
|
|
||||||
throw new CoolValidateException(ERRINFO.NOID);
|
|
||||||
await this.addOrUpdate(param, "update");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 新增
|
|
||||||
* @param param 数据
|
|
||||||
*/
|
|
||||||
async add(param: any | any[]): Promise<Object> {
|
|
||||||
if (!this.entity) throw new CoolValidateException(ERRINFO.NOENTITY);
|
|
||||||
delete param.id;
|
|
||||||
await this.addOrUpdate(param, "add");
|
|
||||||
return {
|
|
||||||
id:
|
|
||||||
param instanceof Array
|
|
||||||
? param.map((e) => {
|
|
||||||
return e.id ? e.id : e._id;
|
|
||||||
})
|
|
||||||
: param.id
|
|
||||||
? param.id
|
|
||||||
: param._id,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 新增|修改
|
|
||||||
* @param param 数据
|
|
||||||
*/
|
|
||||||
async addOrUpdate(param: any | any[], type: "add" | "update" = "add") {
|
|
||||||
this.service.setEntity(this.entity);
|
|
||||||
await this.modifyBefore(param, type);
|
|
||||||
await this.service.addOrUpdate(param, type);
|
|
||||||
await this.modifyAfter(param, type);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 非分页查询
|
|
||||||
* @param query 查询条件
|
|
||||||
* @param option 查询配置
|
|
||||||
* @param connectionName 连接名
|
|
||||||
*/
|
|
||||||
async list(query, option, connectionName?): Promise<any> {
|
|
||||||
this.service.setEntity(this.entity);
|
|
||||||
return await this.service.list(query, option, connectionName);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 分页查询
|
|
||||||
* @param query 查询条件
|
|
||||||
* @param option 查询配置
|
|
||||||
* @param connectionName 连接名
|
|
||||||
*/
|
|
||||||
async page(query, option, connectionName?) {
|
|
||||||
this.service.setEntity(this.entity);
|
|
||||||
return await this.service.page(query, option, connectionName);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 构建查询配置
|
|
||||||
* @param query 前端查询
|
|
||||||
* @param option
|
|
||||||
*/
|
|
||||||
async getOptionFind(query, option: QueryOp) {
|
|
||||||
this.service.setEntity(this.entity);
|
|
||||||
return await this.service.getOptionFind(query, option);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 新增|修改|删除 之后的操作
|
|
||||||
* @param data 对应数据
|
|
||||||
*/
|
|
||||||
async modifyAfter(
|
|
||||||
data: any,
|
|
||||||
type: "delete" | "update" | "add"
|
|
||||||
): Promise<void> {}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 新增|修改|删除 之前的操作
|
|
||||||
* @param data 对应数据
|
|
||||||
*/
|
|
||||||
async modifyBefore(
|
|
||||||
data: any,
|
|
||||||
type: "delete" | "update" | "add"
|
|
||||||
): Promise<void> {}
|
|
||||||
}
|
|
||||||
@ -1,499 +0,0 @@
|
|||||||
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, Equal, 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) {
|
|
||||||
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: Equal(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))
|
|
||||||
) {
|
|
||||||
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}%`;
|
|
||||||
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) {
|
|
||||||
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) {
|
|
||||||
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(" ");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,639 +0,0 @@
|
|||||||
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) {
|
|
||||||
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))
|
|
||||||
) {
|
|
||||||
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}%`;
|
|
||||||
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) {
|
|
||||||
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) {
|
|
||||||
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]);
|
|
||||||
sqlArr.forEach((item, index) => {
|
|
||||||
if (item.includes("ORDER BY")) {
|
|
||||||
sqlArr[index] = this.replaceOrderByPrefix(item);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return sqlArr.join(" ");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 替换sql中的表别名
|
|
||||||
* @param sql
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
replaceOrderByPrefix(sql) {
|
|
||||||
// 使用正则表达式匹配 ORDER BY 后面的部分
|
|
||||||
// 这里假设 ORDER BY 后面跟着的是由空格分隔的字段名,且字段名由双引号包围
|
|
||||||
const orderByRegex =
|
|
||||||
/ORDER BY\s+("[^"]+_[^"]+")(\s*(ASC|DESC)?\s*(,\s*"[^"]+_[^"]+")*)/gi;
|
|
||||||
|
|
||||||
// 定义替换函数
|
|
||||||
// @ts-ignore
|
|
||||||
function replaceMatch(match, p1, p2) {
|
|
||||||
// 将 p1 中的 "a_" 替换为 "a."
|
|
||||||
const replacedField = p1.replace(/a_([^"]+)/g, "a.$1");
|
|
||||||
// 如果有其他字段,递归调用替换函数
|
|
||||||
const replacedRest = p2.replace(/("[^"]+_)/g, (m, p) =>
|
|
||||||
p.replace("a_", "a.")
|
|
||||||
);
|
|
||||||
// 组合替换后的字段和其他部分
|
|
||||||
return `ORDER BY ${replacedField.replace(/"/g, "")}${replacedRest.replace(
|
|
||||||
/"/g,
|
|
||||||
""
|
|
||||||
)}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 使用替换函数替换匹配到的内容
|
|
||||||
const replacedOrderBySql = sql.replace(orderByRegex, replaceMatch);
|
|
||||||
|
|
||||||
// 移除所有双引号
|
|
||||||
const sqlWithoutQuotes = replacedOrderBySql.replace(/"/g, "");
|
|
||||||
|
|
||||||
return sqlWithoutQuotes;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 筛选的字段匹配
|
|
||||||
* @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";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,532 +0,0 @@
|
|||||||
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) {
|
|
||||||
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))
|
|
||||||
) {
|
|
||||||
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}%`;
|
|
||||||
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) {
|
|
||||||
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) {
|
|
||||||
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(" ");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,89 +0,0 @@
|
|||||||
import { COOL_METHOD_TAG_KEY, CoolUrlTagConfig } from './../decorator/tag';
|
|
||||||
import {
|
|
||||||
CONTROLLER_KEY,
|
|
||||||
getClassMetadata,
|
|
||||||
listPropertyDataFromClass,
|
|
||||||
listModule,
|
|
||||||
Provide,
|
|
||||||
Scope,
|
|
||||||
ScopeEnum,
|
|
||||||
WEB_ROUTER_KEY,
|
|
||||||
} from '@midwayjs/decorator';
|
|
||||||
import { COOL_URL_TAG_KEY } from '../decorator/tag';
|
|
||||||
import * as _ from 'lodash';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* URL标签
|
|
||||||
*/
|
|
||||||
@Provide()
|
|
||||||
@Scope(ScopeEnum.Singleton)
|
|
||||||
export class CoolUrlTagData {
|
|
||||||
data = {};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 初始化
|
|
||||||
*/
|
|
||||||
async init() {
|
|
||||||
// 类标记
|
|
||||||
await this.classTag();
|
|
||||||
// 方法标记
|
|
||||||
await this.methodTag();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 类标记
|
|
||||||
*/
|
|
||||||
async classTag() {
|
|
||||||
const tags = listModule(COOL_URL_TAG_KEY);
|
|
||||||
for (const controller of tags) {
|
|
||||||
// class的标记
|
|
||||||
const controllerOption = getClassMetadata(CONTROLLER_KEY, controller);
|
|
||||||
const tagOption: CoolUrlTagConfig = getClassMetadata(
|
|
||||||
COOL_URL_TAG_KEY,
|
|
||||||
controller
|
|
||||||
);
|
|
||||||
if(tagOption?.key){
|
|
||||||
const data: string[] = this.data[tagOption.key] || [];
|
|
||||||
this.data[tagOption.key] = _.uniq(data.concat(
|
|
||||||
(tagOption?.value || []).map(e => {
|
|
||||||
return controllerOption.prefix + '/' + e;
|
|
||||||
}))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 方法标记
|
|
||||||
*/
|
|
||||||
async methodTag() {
|
|
||||||
const controllers = listModule(CONTROLLER_KEY);
|
|
||||||
for (const controller of controllers) {
|
|
||||||
const controllerOption = getClassMetadata(CONTROLLER_KEY, controller);
|
|
||||||
// 方法标记
|
|
||||||
const listPropertyMetas = listPropertyDataFromClass(COOL_METHOD_TAG_KEY, controller);
|
|
||||||
const requestMetas = getClassMetadata(WEB_ROUTER_KEY, controller);
|
|
||||||
for (const propertyMeta of listPropertyMetas) {
|
|
||||||
const _data = this.data[propertyMeta.tag] || [];
|
|
||||||
const requestMeta = _.find(requestMetas, { method: propertyMeta.key })
|
|
||||||
if(requestMeta){
|
|
||||||
this.data[propertyMeta.tag] = _.uniq(_data.concat(
|
|
||||||
controllerOption.prefix + requestMeta.path
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 根据键获得
|
|
||||||
* @param key
|
|
||||||
* @param type
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
byKey(key: string, type?: 'app' | 'admin'): string[] {
|
|
||||||
return this.data[key].filter(e => {
|
|
||||||
return type? _.startsWith(e, `/${type}/`): true;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,27 +0,0 @@
|
|||||||
import { ILogger } from '@midwayjs/core';
|
|
||||||
import { Init, Logger, Provide, Scope, ScopeEnum } from '@midwayjs/decorator';
|
|
||||||
import * as moment from 'moment';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 常用函数处理
|
|
||||||
*/
|
|
||||||
@Provide()
|
|
||||||
@Scope(ScopeEnum.Singleton)
|
|
||||||
export class FuncUtil {
|
|
||||||
@Logger()
|
|
||||||
coreLogger: ILogger;
|
|
||||||
|
|
||||||
@Init()
|
|
||||||
async init() {
|
|
||||||
Date.prototype.toJSON = function () {
|
|
||||||
return moment(this).format('YYYY-MM-DD HH:mm:ss');
|
|
||||||
};
|
|
||||||
// 新增String支持replaceAll方法
|
|
||||||
String.prototype['replaceAll'] = function (s1, s2) {
|
|
||||||
return this.replace(new RegExp(s1, 'gm'), s2);
|
|
||||||
};
|
|
||||||
this.coreLogger.info(
|
|
||||||
'\x1B[36m [cool:core] midwayjs cool core func handler \x1B[0m'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,52 +0,0 @@
|
|||||||
/**
|
|
||||||
* Location 工具类
|
|
||||||
*/
|
|
||||||
class LocationUtil {
|
|
||||||
private locationCache = new Map<string, any>();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取目标类的定义位置
|
|
||||||
* @param target 目标类
|
|
||||||
* @returns 目标类的定义位置
|
|
||||||
*/
|
|
||||||
async scriptPath(target: any) {
|
|
||||||
const targetName = target.name;
|
|
||||||
|
|
||||||
// 检查缓存
|
|
||||||
if (this.locationCache.has(targetName)) {
|
|
||||||
return this.locationCache.get(targetName);
|
|
||||||
}
|
|
||||||
|
|
||||||
const originalPrepareStackTrace = Error.prepareStackTrace;
|
|
||||||
let targetFile;
|
|
||||||
|
|
||||||
try {
|
|
||||||
Error.prepareStackTrace = (error, stack) => stack;
|
|
||||||
const stack = new Error().stack as any;
|
|
||||||
|
|
||||||
for (const site of stack) {
|
|
||||||
const fileName = site.getFileName();
|
|
||||||
if (!fileName) continue;
|
|
||||||
|
|
||||||
const content = require('fs').readFileSync(fileName, 'utf-8');
|
|
||||||
if (content.includes(`class ${targetName}`)) {
|
|
||||||
targetFile = {
|
|
||||||
path: fileName,
|
|
||||||
line: site.getLineNumber(),
|
|
||||||
column: site.getColumnNumber(),
|
|
||||||
source: `file://${fileName}`
|
|
||||||
};
|
|
||||||
this.locationCache.set(targetName, targetFile);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
Error.prepareStackTrace = originalPrepareStackTrace;
|
|
||||||
}
|
|
||||||
|
|
||||||
return targetFile;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 不再需要单例模式,直接导出实例即可
|
|
||||||
export default new LocationUtil();
|
|
||||||
@ -1,25 +0,0 @@
|
|||||||
{
|
|
||||||
"compileOnSave": true,
|
|
||||||
"compilerOptions": {
|
|
||||||
"target": "es2018",
|
|
||||||
"module": "commonjs",
|
|
||||||
"moduleResolution": "node",
|
|
||||||
"experimentalDecorators": true,
|
|
||||||
"emitDecoratorMetadata": true,
|
|
||||||
"inlineSourceMap":false,
|
|
||||||
"noImplicitThis": true,
|
|
||||||
"noUnusedLocals": true,
|
|
||||||
"stripInternal": true,
|
|
||||||
"skipLibCheck": true,
|
|
||||||
"noImplicitReturns": false,
|
|
||||||
"pretty": true,
|
|
||||||
"declaration": true,
|
|
||||||
"forceConsistentCasingInFileNames": true,
|
|
||||||
"outDir": "dist"
|
|
||||||
},
|
|
||||||
"exclude": [
|
|
||||||
"dist",
|
|
||||||
"node_modules",
|
|
||||||
"test"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@ -1,11 +0,0 @@
|
|||||||
# 🎨 editorconfig.org
|
|
||||||
|
|
||||||
root = true
|
|
||||||
|
|
||||||
[*]
|
|
||||||
charset = utf-8
|
|
||||||
end_of_line = lf
|
|
||||||
indent_style = space
|
|
||||||
indent_size = 2
|
|
||||||
trim_trailing_whitespace = true
|
|
||||||
insert_final_newline = true
|
|
||||||
@ -1,28 +0,0 @@
|
|||||||
{
|
|
||||||
"extends": "./node_modules/mwts/",
|
|
||||||
"ignorePatterns": [
|
|
||||||
"node_modules",
|
|
||||||
"dist",
|
|
||||||
"test",
|
|
||||||
"jest.config.js",
|
|
||||||
"typings",
|
|
||||||
"public/**/**",
|
|
||||||
"view/**/**"
|
|
||||||
],
|
|
||||||
"env": {
|
|
||||||
"jest": true
|
|
||||||
},
|
|
||||||
"rules": {
|
|
||||||
"@typescript-eslint/explicit-module-boundary-types": "off",
|
|
||||||
"@typescript-eslint/no-unused-vars": "off",
|
|
||||||
"@typescript-eslint/ban-ts-comment": "off",
|
|
||||||
"node/no-extraneous-import": "off",
|
|
||||||
"no-empty": "off",
|
|
||||||
"node/no-extraneous-require": "off",
|
|
||||||
"eqeqeq": "off",
|
|
||||||
"node/no-unsupported-features/node-builtins": "off",
|
|
||||||
"@typescript-eslint/ban-types": "off",
|
|
||||||
"no-control-regex": "off",
|
|
||||||
"prefer-const": "off"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
15
es/.gitignore
vendored
15
es/.gitignore
vendored
@ -1,15 +0,0 @@
|
|||||||
logs/
|
|
||||||
npm-debug.log
|
|
||||||
yarn-error.log
|
|
||||||
node_modules/
|
|
||||||
package-lock.json
|
|
||||||
yarn.lock
|
|
||||||
coverage/
|
|
||||||
dist/
|
|
||||||
.idea/
|
|
||||||
run/
|
|
||||||
.DS_Store
|
|
||||||
*.sw*
|
|
||||||
*.un~
|
|
||||||
.tsbuildinfo
|
|
||||||
.tsbuildinfo.*
|
|
||||||
@ -1,3 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
...require('mwts/.prettierrc.json')
|
|
||||||
}
|
|
||||||
@ -1,3 +0,0 @@
|
|||||||
# cool-admin
|
|
||||||
|
|
||||||
https://cool-js.com
|
|
||||||
10
es/index.d.ts
vendored
10
es/index.d.ts
vendored
@ -1,10 +0,0 @@
|
|||||||
export * from './dist/index';
|
|
||||||
|
|
||||||
declare module '@midwayjs/core/dist/interface' {
|
|
||||||
interface MidwayConfig {
|
|
||||||
book?: PowerPartial<{
|
|
||||||
a: number;
|
|
||||||
b: string;
|
|
||||||
}>;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,7 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
preset: 'ts-jest',
|
|
||||||
testEnvironment: 'node',
|
|
||||||
testPathIgnorePatterns: ['<rootDir>/test/fixtures'],
|
|
||||||
coveragePathIgnorePatterns: ['<rootDir>/test/'],
|
|
||||||
setupFilesAfterEnv: ['./jest.setup.js']
|
|
||||||
};
|
|
||||||
@ -1 +0,0 @@
|
|||||||
jest.setTimeout(30000);
|
|
||||||
@ -1,43 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "@cool-midway/es",
|
|
||||||
"version": "7.0.0",
|
|
||||||
"description": "cool-js.com elasticsearch",
|
|
||||||
"main": "dist/index.js",
|
|
||||||
"typings": "index.d.ts",
|
|
||||||
"scripts": {
|
|
||||||
"build": "cross-env midway-bin build -c",
|
|
||||||
"cov": "cross-env midway-bin cov --ts",
|
|
||||||
"lint": "mwts check",
|
|
||||||
"lint:fix": "mwts fix"
|
|
||||||
},
|
|
||||||
"keywords": [
|
|
||||||
"cool",
|
|
||||||
"cool-admin",
|
|
||||||
"cooljs"
|
|
||||||
],
|
|
||||||
"author": "COOL",
|
|
||||||
"readme": "README.md",
|
|
||||||
"files": [
|
|
||||||
"dist/**/*.js",
|
|
||||||
"dist/**/*.d.ts",
|
|
||||||
"index.d.ts"
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
|
||||||
"devDependencies": {
|
|
||||||
"@cool-midway/core": "^7.0.0",
|
|
||||||
"@midwayjs/cli": "^2.0.9",
|
|
||||||
"@midwayjs/core": "^3.9.0",
|
|
||||||
"@midwayjs/decorator": "^3.9.0",
|
|
||||||
"@midwayjs/mock": "^3.9.0",
|
|
||||||
"@types/jest": "^29.2.5",
|
|
||||||
"@types/node": "^18.11.18",
|
|
||||||
"cross-env": "^7.0.3",
|
|
||||||
"jest": "^29.3.1",
|
|
||||||
"mwts": "^1.3.0",
|
|
||||||
"ts-jest": "^29.0.3",
|
|
||||||
"typescript": "^4.9.4"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"@elastic/elasticsearch": "^8.5.0"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,27 +0,0 @@
|
|||||||
### COOL-ADMIN
|
|
||||||
|
|
||||||
cool-admin一个很酷的后台权限管理系统,开源免费,模块化、插件化、极速开发CRUD,方便快速构建迭代后台管理系统
|
|
||||||
|
|
||||||
大数据、微服务、AI编码快速开发!!!
|
|
||||||
|
|
||||||
|
|
||||||
### 技术栈
|
|
||||||
|
|
||||||
- 后端:node.js midway.js koa.js mysql typescript
|
|
||||||
- 前端:vue.js element-plus jsx pinia vue-router
|
|
||||||
|
|
||||||
### 官网
|
|
||||||
|
|
||||||
[https://cool-js.com](https://cool-js.com)
|
|
||||||
|
|
||||||
|
|
||||||
### 演示地址
|
|
||||||
|
|
||||||
[https://show.cool-admin.com](https://show.cool-admin.com)
|
|
||||||
|
|
||||||
- 账户:admin
|
|
||||||
- 密码:123456
|
|
||||||
|
|
||||||
### 项目地址
|
|
||||||
|
|
||||||
[https://github.com/cool-team-official/cool-admin-midway](https://github.com/cool-team-official/cool-admin-midway)
|
|
||||||
615
es/src/base.ts
615
es/src/base.ts
@ -1,615 +0,0 @@
|
|||||||
import { CoolEventManager } from '@cool-midway/core';
|
|
||||||
import { Client } from '@elastic/elasticsearch';
|
|
||||||
import { WaitForActiveShards } from '@elastic/elasticsearch/lib/api/types';
|
|
||||||
import { Inject, Logger } from '@midwayjs/decorator';
|
|
||||||
import { ILogger } from '@midwayjs/logger';
|
|
||||||
import { EsConfig } from '.';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Es索引基类
|
|
||||||
*/
|
|
||||||
export class BaseEsIndex {
|
|
||||||
// 索引
|
|
||||||
public index: string;
|
|
||||||
// es客户端
|
|
||||||
public client: Client;
|
|
||||||
// 日志
|
|
||||||
@Logger()
|
|
||||||
coreLogger: ILogger;
|
|
||||||
// 事件
|
|
||||||
@Inject('cool:coolEventManager')
|
|
||||||
coolEventManager: CoolEventManager;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 设置索引
|
|
||||||
* @param index
|
|
||||||
*/
|
|
||||||
setIndex(index: string) {
|
|
||||||
this.index = index;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 处理es数据变更事件,主要用于同步数据
|
|
||||||
* @param method
|
|
||||||
* @param data
|
|
||||||
*/
|
|
||||||
async handleDataChange(index, method, data) {
|
|
||||||
this.index = index;
|
|
||||||
const {
|
|
||||||
id,
|
|
||||||
ids,
|
|
||||||
bodys,
|
|
||||||
body,
|
|
||||||
type,
|
|
||||||
refresh,
|
|
||||||
waitForActiveShards,
|
|
||||||
properties,
|
|
||||||
config,
|
|
||||||
} = data;
|
|
||||||
switch (method) {
|
|
||||||
case 'upsert':
|
|
||||||
await this.upsert(body, refresh, waitForActiveShards);
|
|
||||||
break;
|
|
||||||
case 'batchIndex':
|
|
||||||
await this.batchIndex(bodys, type, refresh, waitForActiveShards);
|
|
||||||
break;
|
|
||||||
case 'deleteById':
|
|
||||||
await this.deleteById(id, refresh, waitForActiveShards);
|
|
||||||
break;
|
|
||||||
case 'deleteByIds':
|
|
||||||
await this.deleteByIds(ids, refresh, waitForActiveShards);
|
|
||||||
break;
|
|
||||||
case 'deleteByQuery':
|
|
||||||
await this.deleteByQuery(body, refresh, waitForActiveShards);
|
|
||||||
break;
|
|
||||||
case 'updateById':
|
|
||||||
await this.updateById(body, refresh, waitForActiveShards);
|
|
||||||
break;
|
|
||||||
case 'updateByQuery':
|
|
||||||
await this.updateByQuery(body, refresh, waitForActiveShards);
|
|
||||||
break;
|
|
||||||
case 'createIndex':
|
|
||||||
await this.updateByQuery(properties, config);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 数据更新事件
|
|
||||||
* @param method
|
|
||||||
* @param data
|
|
||||||
*/
|
|
||||||
async esDataChange(method, data) {
|
|
||||||
this.coolEventManager?.emit('esDataChange', this.index, method, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 设置客户端
|
|
||||||
* @param client
|
|
||||||
*/
|
|
||||||
setClient(client: Client) {
|
|
||||||
this.client = client;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 对象转body
|
|
||||||
* @param condition
|
|
||||||
*/
|
|
||||||
async objToBody(condition: any){
|
|
||||||
const body = {
|
|
||||||
query: {
|
|
||||||
bool: {
|
|
||||||
must: [],
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for(const key in condition){
|
|
||||||
body.query.bool.must.push({
|
|
||||||
term: {
|
|
||||||
[key]: condition[key]
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return body;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 按字段值查找
|
|
||||||
* @param condition
|
|
||||||
* @param size
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
async findBy(condition: any, size?: number){
|
|
||||||
const body = await this.objToBody(condition)
|
|
||||||
return this.find(body, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 按字段值分页查找
|
|
||||||
* @param condition
|
|
||||||
* @param page
|
|
||||||
* @param size
|
|
||||||
*/
|
|
||||||
async findPageBy(condition: any, page?: number, size?: number){
|
|
||||||
const body = await this.objToBody(condition)
|
|
||||||
return this.findPage(body, page, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 查询
|
|
||||||
* @param body
|
|
||||||
*/
|
|
||||||
async find(body?: any, size?: number) {
|
|
||||||
if (!body) {
|
|
||||||
body = {};
|
|
||||||
}
|
|
||||||
if(!body.size){
|
|
||||||
body.size = size ? size : 10000;
|
|
||||||
}
|
|
||||||
return this.client
|
|
||||||
.search({
|
|
||||||
index: this.index,
|
|
||||||
body,
|
|
||||||
})
|
|
||||||
.then(res => {
|
|
||||||
return (
|
|
||||||
res.hits.hits.map(e => {
|
|
||||||
e._source['id'] = e._id;
|
|
||||||
const _source: any = e._source;
|
|
||||||
['_id', '_index', '_score', '_source'].forEach(key => {
|
|
||||||
delete e[key];
|
|
||||||
});
|
|
||||||
return {
|
|
||||||
..._source,
|
|
||||||
...e,
|
|
||||||
};
|
|
||||||
}) || []
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 分页查询
|
|
||||||
* @param body
|
|
||||||
* @param page
|
|
||||||
* @param size
|
|
||||||
*/
|
|
||||||
async findPage(body?: any, page?: number, size?: number) {
|
|
||||||
if (!page) {
|
|
||||||
page = 1;
|
|
||||||
}
|
|
||||||
if (!body) {
|
|
||||||
body = {};
|
|
||||||
}
|
|
||||||
if (!size && !body.size) {
|
|
||||||
size = 20;
|
|
||||||
body.size = size;
|
|
||||||
}
|
|
||||||
const total = await this.findCount(body);
|
|
||||||
body.from = (page - 1) * size;
|
|
||||||
return this.client.search({ index: this.index, body }).then(res => {
|
|
||||||
const result =
|
|
||||||
res.hits.hits.map(e => {
|
|
||||||
e._source['id'] = e._id;
|
|
||||||
const _source: any = e._source;
|
|
||||||
['_id', '_index', '_score', '_source'].forEach(key => {
|
|
||||||
delete e[key];
|
|
||||||
});
|
|
||||||
return {
|
|
||||||
..._source,
|
|
||||||
...e,
|
|
||||||
};
|
|
||||||
}) || [];
|
|
||||||
return {
|
|
||||||
list: result,
|
|
||||||
pagination: {
|
|
||||||
page,
|
|
||||||
size,
|
|
||||||
total,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 根据ID查询
|
|
||||||
* @param id
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
async findById(id) {
|
|
||||||
return this.client
|
|
||||||
.get({
|
|
||||||
index: this.index,
|
|
||||||
id,
|
|
||||||
})
|
|
||||||
.then(res => {
|
|
||||||
res._source['id'] = res._id;
|
|
||||||
return res._source || undefined;
|
|
||||||
})
|
|
||||||
.catch(e => {
|
|
||||||
return undefined;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 根据多个ID查询
|
|
||||||
* @param ids
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
async findByIds(ids: string[]) {
|
|
||||||
return this.client
|
|
||||||
.mget({ index: this.index, body: { ids } })
|
|
||||||
.then(res => {
|
|
||||||
const result = res.docs.map((e: any) => {
|
|
||||||
e._source.id = e._id;
|
|
||||||
return e._source || 'undefined';
|
|
||||||
});
|
|
||||||
return result.filter(e => {
|
|
||||||
return e !== 'undefined';
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.catch(e => {
|
|
||||||
return undefined;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 插入与更新
|
|
||||||
* @param body
|
|
||||||
* @param refresh
|
|
||||||
* @param waitForActiveShards
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
async upsert(
|
|
||||||
body: any,
|
|
||||||
refresh?: boolean | 'wait_for',
|
|
||||||
waitForActiveShards?: WaitForActiveShards
|
|
||||||
) {
|
|
||||||
if (refresh == undefined) {
|
|
||||||
refresh = true;
|
|
||||||
}
|
|
||||||
if (body.id) {
|
|
||||||
this.esDataChange('upsert', {
|
|
||||||
body,
|
|
||||||
refresh,
|
|
||||||
waitForActiveShards,
|
|
||||||
});
|
|
||||||
const id = body.id;
|
|
||||||
delete body.id;
|
|
||||||
return this.client.index({
|
|
||||||
id,
|
|
||||||
index: this.index,
|
|
||||||
wait_for_active_shards: waitForActiveShards,
|
|
||||||
refresh,
|
|
||||||
body,
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
return this.client
|
|
||||||
.index({
|
|
||||||
index: this.index,
|
|
||||||
wait_for_active_shards: waitForActiveShards,
|
|
||||||
refresh,
|
|
||||||
body,
|
|
||||||
})
|
|
||||||
.then(res => {
|
|
||||||
this.esDataChange('upsert', {
|
|
||||||
body: {
|
|
||||||
...body,
|
|
||||||
id: res._id,
|
|
||||||
},
|
|
||||||
refresh,
|
|
||||||
waitForActiveShards,
|
|
||||||
});
|
|
||||||
return res;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 批量插入更新
|
|
||||||
* @param bodys
|
|
||||||
* @param type
|
|
||||||
* @param refresh
|
|
||||||
* @param waitForActiveShards
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
async batchIndex(
|
|
||||||
bodys: any[],
|
|
||||||
type: 'index' | 'create' | 'delete' | 'update',
|
|
||||||
refresh?: boolean | 'wait_for',
|
|
||||||
waitForActiveShards?: WaitForActiveShards
|
|
||||||
) {
|
|
||||||
this.esDataChange('batchIndex', {
|
|
||||||
bodys,
|
|
||||||
type,
|
|
||||||
refresh,
|
|
||||||
waitForActiveShards,
|
|
||||||
});
|
|
||||||
if (refresh == undefined) {
|
|
||||||
refresh = true;
|
|
||||||
}
|
|
||||||
const list = [];
|
|
||||||
for (const body of bodys) {
|
|
||||||
const typeO = {};
|
|
||||||
typeO[type] = { _index: this.index, _id: body.id };
|
|
||||||
if (body.id) {
|
|
||||||
delete body.id;
|
|
||||||
}
|
|
||||||
list.push(typeO);
|
|
||||||
if (type !== 'delete') {
|
|
||||||
if (type == 'update') {
|
|
||||||
list.push({ doc: body });
|
|
||||||
} else {
|
|
||||||
list.push(body);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return this.client.bulk({
|
|
||||||
wait_for_active_shards: waitForActiveShards,
|
|
||||||
index: this.index,
|
|
||||||
refresh,
|
|
||||||
body: list,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 删除索引
|
|
||||||
* @param id
|
|
||||||
* @param refresh
|
|
||||||
* @param waitForActiveShards
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
async deleteById(
|
|
||||||
id,
|
|
||||||
refresh?: boolean | 'wait_for',
|
|
||||||
waitForActiveShards?: WaitForActiveShards
|
|
||||||
) {
|
|
||||||
this.esDataChange('deleteById', {
|
|
||||||
id,
|
|
||||||
refresh,
|
|
||||||
waitForActiveShards,
|
|
||||||
});
|
|
||||||
if (refresh == undefined) {
|
|
||||||
refresh = true;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
return this.client.delete({
|
|
||||||
index: this.index,
|
|
||||||
refresh,
|
|
||||||
wait_for_active_shards: waitForActiveShards,
|
|
||||||
id,
|
|
||||||
});
|
|
||||||
} catch {}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 删除文档
|
|
||||||
* @param ids
|
|
||||||
* @param refresh
|
|
||||||
* @param waitForActiveShards
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
async deleteByIds(
|
|
||||||
ids: string[],
|
|
||||||
refresh?: boolean,
|
|
||||||
waitForActiveShards?: WaitForActiveShards
|
|
||||||
) {
|
|
||||||
this.esDataChange('deleteByIds', {
|
|
||||||
ids,
|
|
||||||
refresh,
|
|
||||||
waitForActiveShards,
|
|
||||||
});
|
|
||||||
if (refresh == undefined) {
|
|
||||||
refresh = true;
|
|
||||||
}
|
|
||||||
const body = {
|
|
||||||
query: {
|
|
||||||
bool: {
|
|
||||||
must: [
|
|
||||||
{
|
|
||||||
terms: {
|
|
||||||
_id: ids,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
return this.client.deleteByQuery({
|
|
||||||
index: this.index,
|
|
||||||
refresh,
|
|
||||||
wait_for_active_shards: waitForActiveShards,
|
|
||||||
body,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 根据条件批量删除
|
|
||||||
* @param body
|
|
||||||
* @param refresh
|
|
||||||
* @param waitForActiveShards
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
async deleteByQuery(
|
|
||||||
body,
|
|
||||||
refresh?: boolean,
|
|
||||||
waitForActiveShards?: WaitForActiveShards
|
|
||||||
) {
|
|
||||||
this.esDataChange('deleteByQuery', {
|
|
||||||
body,
|
|
||||||
refresh,
|
|
||||||
waitForActiveShards,
|
|
||||||
});
|
|
||||||
if (refresh == undefined) {
|
|
||||||
refresh = true;
|
|
||||||
}
|
|
||||||
return this.client.deleteByQuery({
|
|
||||||
index: this.index,
|
|
||||||
refresh,
|
|
||||||
wait_for_active_shards: waitForActiveShards,
|
|
||||||
body,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 更新索引
|
|
||||||
* @param body
|
|
||||||
* @param refresh
|
|
||||||
* @param waitForActiveShards
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
async updateById(
|
|
||||||
body,
|
|
||||||
refresh?: boolean | 'wait_for',
|
|
||||||
waitForActiveShards?: WaitForActiveShards
|
|
||||||
) {
|
|
||||||
this.esDataChange('updateById', {
|
|
||||||
body,
|
|
||||||
refresh,
|
|
||||||
waitForActiveShards,
|
|
||||||
});
|
|
||||||
if (refresh == undefined) {
|
|
||||||
refresh = true;
|
|
||||||
}
|
|
||||||
const id = body.id;
|
|
||||||
delete body.id;
|
|
||||||
return this.client.update({
|
|
||||||
wait_for_active_shards: waitForActiveShards,
|
|
||||||
index: this.index,
|
|
||||||
id: id,
|
|
||||||
refresh,
|
|
||||||
body: {
|
|
||||||
doc: body,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 根据条件更新
|
|
||||||
* @param body
|
|
||||||
* @param refresh
|
|
||||||
* @param waitForActiveShards
|
|
||||||
*/
|
|
||||||
async updateByQuery(
|
|
||||||
body,
|
|
||||||
refresh?: boolean,
|
|
||||||
waitForActiveShards?: WaitForActiveShards
|
|
||||||
) {
|
|
||||||
this.esDataChange('updateByQuery', {
|
|
||||||
body,
|
|
||||||
refresh,
|
|
||||||
waitForActiveShards,
|
|
||||||
});
|
|
||||||
if (refresh == undefined) {
|
|
||||||
refresh = true;
|
|
||||||
}
|
|
||||||
return this.client.updateByQuery({
|
|
||||||
index: this.index,
|
|
||||||
refresh,
|
|
||||||
wait_for_active_shards: waitForActiveShards,
|
|
||||||
body,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 查询条数
|
|
||||||
* @param body
|
|
||||||
*/
|
|
||||||
async findCount(body?: any) {
|
|
||||||
let _body = Object.assign({}, body || {});
|
|
||||||
delete _body.from;
|
|
||||||
delete _body.size;
|
|
||||||
delete _body.sort;
|
|
||||||
return this.client
|
|
||||||
.count({
|
|
||||||
index: this.index,
|
|
||||||
body: _body,
|
|
||||||
})
|
|
||||||
.then(res => {
|
|
||||||
return res.count;
|
|
||||||
})
|
|
||||||
.catch(e => {
|
|
||||||
return undefined;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 创建更新索引
|
|
||||||
* @param config 配置
|
|
||||||
*/
|
|
||||||
async createIndex(
|
|
||||||
properties: {},
|
|
||||||
config: EsConfig = {
|
|
||||||
name: '',
|
|
||||||
replicas: 1,
|
|
||||||
shards: 8,
|
|
||||||
analyzers: [],
|
|
||||||
}
|
|
||||||
) {
|
|
||||||
this.esDataChange('createIndex', {
|
|
||||||
properties,
|
|
||||||
config,
|
|
||||||
});
|
|
||||||
const body = {
|
|
||||||
settings: {
|
|
||||||
number_of_shards: config.shards,
|
|
||||||
number_of_replicas: config.replicas,
|
|
||||||
analysis: {
|
|
||||||
analyzer: {
|
|
||||||
comma: { type: 'pattern', pattern: ',' },
|
|
||||||
blank: { type: 'pattern', pattern: ' ' },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
mapping: {
|
|
||||||
nested_fields: {
|
|
||||||
limit: 100,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
mappings: {
|
|
||||||
properties: {},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
if (config.analyzers) {
|
|
||||||
for (const analyzer of config.analyzers) {
|
|
||||||
for (const key in analyzer) {
|
|
||||||
body.settings.analysis.analyzer[key] = analyzer[key];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const param = {
|
|
||||||
index: this.index,
|
|
||||||
body,
|
|
||||||
};
|
|
||||||
param.body = body;
|
|
||||||
param.body.mappings.properties = properties;
|
|
||||||
|
|
||||||
this.client.indices.exists({ index: this.index }).then(async res => {
|
|
||||||
if (!res) {
|
|
||||||
await this.client.indices.create(param).then(res => {
|
|
||||||
if (res.acknowledged) {
|
|
||||||
console.info(
|
|
||||||
'\x1B[36m [cool:core] midwayjs cool elasticsearch ES索引创建成功: ' +
|
|
||||||
this.index +
|
|
||||||
' \x1B[0m'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
const updateParam = {
|
|
||||||
index: this.index,
|
|
||||||
body: param.body.mappings,
|
|
||||||
};
|
|
||||||
await this.client.indices.putMapping(updateParam).then(res => {
|
|
||||||
if (res.acknowledged) {
|
|
||||||
console.info(
|
|
||||||
'\x1B[36m [cool:core] midwayjs cool elasticsearch ES索引更新成功: ' +
|
|
||||||
this.index +
|
|
||||||
' \x1B[0m'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,4 +0,0 @@
|
|||||||
export const customKey = {
|
|
||||||
a: 1,
|
|
||||||
b: 'hello',
|
|
||||||
};
|
|
||||||
@ -1,19 +0,0 @@
|
|||||||
import { Configuration } from '@midwayjs/decorator';
|
|
||||||
import * as DefaultConfig from './config/config.default';
|
|
||||||
import { IMidwayContainer } from '@midwayjs/core';
|
|
||||||
import { CoolElasticSearch } from './elasticsearch';
|
|
||||||
|
|
||||||
@Configuration({
|
|
||||||
namespace: 'cool:es',
|
|
||||||
importConfigs: [
|
|
||||||
{
|
|
||||||
default: DefaultConfig,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
})
|
|
||||||
export class CoolEsConfiguration {
|
|
||||||
async onReady(container: IMidwayContainer) {
|
|
||||||
await container.getAsync(CoolElasticSearch);
|
|
||||||
// TODO something
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,41 +0,0 @@
|
|||||||
import {
|
|
||||||
Scope,
|
|
||||||
ScopeEnum,
|
|
||||||
saveClassMetadata,
|
|
||||||
saveModule,
|
|
||||||
} from '@midwayjs/decorator';
|
|
||||||
|
|
||||||
export const COOL_ES_KEY = 'decorator:cool:es';
|
|
||||||
|
|
||||||
export interface EsConfig {
|
|
||||||
shards?: number;
|
|
||||||
name: string;
|
|
||||||
replicas?: number;
|
|
||||||
analyzers?: any[];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 索引
|
|
||||||
* @param config
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
export function CoolEsIndex(
|
|
||||||
config: EsConfig | string = {
|
|
||||||
name: '',
|
|
||||||
replicas: 1,
|
|
||||||
shards: 8,
|
|
||||||
analyzers: [],
|
|
||||||
}
|
|
||||||
): ClassDecorator {
|
|
||||||
if (typeof config == 'string') {
|
|
||||||
config = { name: config, replicas: 1, shards: 8, analyzers: [] };
|
|
||||||
}
|
|
||||||
return (target: any) => {
|
|
||||||
// 将装饰的类,绑定到该装饰器,用于后续能获取到 class
|
|
||||||
saveModule(COOL_ES_KEY, target);
|
|
||||||
// 保存一些元数据信息,任意你希望存的东西
|
|
||||||
saveClassMetadata(COOL_ES_KEY, config, target);
|
|
||||||
// 指定 IoC 容器创建实例的作用域,这里注册为请求作用域,这样能取到 ctx
|
|
||||||
Scope(ScopeEnum.Singleton)(target);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@ -1,154 +0,0 @@
|
|||||||
import {
|
|
||||||
Provide,
|
|
||||||
getClassMetadata,
|
|
||||||
App,
|
|
||||||
Logger,
|
|
||||||
Inject,
|
|
||||||
Init,
|
|
||||||
Scope,
|
|
||||||
ScopeEnum,
|
|
||||||
Config,
|
|
||||||
} from '@midwayjs/decorator';
|
|
||||||
import { COOL_ES_KEY, EsConfig } from './decorator/elasticsearch';
|
|
||||||
import { listModule } from '@midwayjs/decorator';
|
|
||||||
import { IMidwayApplication } from '@midwayjs/core';
|
|
||||||
import { CoolCoreException, CoolEventManager } from '@cool-midway/core';
|
|
||||||
import { ILogger } from '@midwayjs/logger';
|
|
||||||
import { Client } from '@elastic/elasticsearch';
|
|
||||||
import * as _ from 'lodash';
|
|
||||||
import { CoolEsConfig, ICoolEs } from '.';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 搜索引擎
|
|
||||||
*/
|
|
||||||
@Provide()
|
|
||||||
@Scope(ScopeEnum.Singleton)
|
|
||||||
export class CoolElasticSearch {
|
|
||||||
@App()
|
|
||||||
app: IMidwayApplication;
|
|
||||||
|
|
||||||
@Logger()
|
|
||||||
coreLogger: ILogger;
|
|
||||||
|
|
||||||
@Config('cool.es')
|
|
||||||
esConfig: CoolEsConfig;
|
|
||||||
|
|
||||||
client: Client;
|
|
||||||
|
|
||||||
@Inject('cool:coolEventManager')
|
|
||||||
coolEventManager: CoolEventManager;
|
|
||||||
|
|
||||||
@Init()
|
|
||||||
async init() {
|
|
||||||
if (!this.esConfig?.nodes) {
|
|
||||||
throw new CoolCoreException('es.nodes config is require');
|
|
||||||
}
|
|
||||||
if (this.esConfig.nodes.length == 1) {
|
|
||||||
this.client = new Client({ node: this.esConfig.nodes[0], ...this.esConfig.options });
|
|
||||||
} else {
|
|
||||||
this.client = new Client({ nodes: this.esConfig.nodes, ...this.esConfig.options });
|
|
||||||
}
|
|
||||||
this.client.ping({}, { requestTimeout: 30000 }).then(res => {
|
|
||||||
if (res) {
|
|
||||||
this.coolEventManager.emit('esReady', this.client);
|
|
||||||
this.scan();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async scan() {
|
|
||||||
const modules = listModule(COOL_ES_KEY);
|
|
||||||
for (let module of modules) {
|
|
||||||
const cls: ICoolEs = await this.app
|
|
||||||
.getApplicationContext()
|
|
||||||
.getAsync(module);
|
|
||||||
const data = getClassMetadata(COOL_ES_KEY, module);
|
|
||||||
this.createIndex(cls, data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 数据更新事件
|
|
||||||
* @param method
|
|
||||||
* @param data
|
|
||||||
*/
|
|
||||||
async esDataChange(method, data) {
|
|
||||||
//this.coolEventManager.emit('esDataChange', { method, data });
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 创建索引
|
|
||||||
* @param cls
|
|
||||||
* @param config
|
|
||||||
*/
|
|
||||||
async createIndex(cls, config: EsConfig) {
|
|
||||||
cls.index = config.name;
|
|
||||||
cls.client = this.client;
|
|
||||||
const body = {
|
|
||||||
settings: {
|
|
||||||
number_of_shards: config.shards,
|
|
||||||
number_of_replicas: config.replicas,
|
|
||||||
analysis: {
|
|
||||||
analyzer: {
|
|
||||||
comma: { type: 'pattern', pattern: ',' },
|
|
||||||
blank: { type: 'pattern', pattern: ' ' },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
mapping: {
|
|
||||||
nested_fields: {
|
|
||||||
limit: 100,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
mappings: {
|
|
||||||
properties: {},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
if (config.analyzers) {
|
|
||||||
for (const analyzer of config.analyzers) {
|
|
||||||
for (const key in analyzer) {
|
|
||||||
body.settings.analysis.analyzer[key] = analyzer[key];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const param = {
|
|
||||||
index: config.name,
|
|
||||||
body,
|
|
||||||
};
|
|
||||||
param.body = body;
|
|
||||||
param.body.mappings.properties = cls.indexInfo();
|
|
||||||
|
|
||||||
this.esDataChange('createIndex', {
|
|
||||||
properties: param.body.mappings.properties,
|
|
||||||
config,
|
|
||||||
});
|
|
||||||
|
|
||||||
this.client.indices.exists({ index: config.name }).then(async res => {
|
|
||||||
if (!res) {
|
|
||||||
await this.client.indices.create(param).then(res => {
|
|
||||||
if (res.acknowledged) {
|
|
||||||
this.coreLogger.info(
|
|
||||||
'\x1B[36m [cool:core] midwayjs cool elasticsearch ES索引创建成功: ' +
|
|
||||||
config.name +
|
|
||||||
' \x1B[0m'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
const updateParam = {
|
|
||||||
index: config.name,
|
|
||||||
body: param.body.mappings,
|
|
||||||
};
|
|
||||||
await this.client.indices.putMapping(updateParam).then(res => {
|
|
||||||
if (res.acknowledged) {
|
|
||||||
this.coreLogger.info(
|
|
||||||
'\x1B[36m [cool:core] midwayjs cool elasticsearch ES索引更新成功: ' +
|
|
||||||
config.name +
|
|
||||||
' \x1B[0m'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,18 +0,0 @@
|
|||||||
import { ClientOptions } from '@elastic/elasticsearch';
|
|
||||||
|
|
||||||
export { CoolEsConfiguration as Configuration } from './configuration';
|
|
||||||
|
|
||||||
export * from './elasticsearch';
|
|
||||||
|
|
||||||
export * from './decorator/elasticsearch';
|
|
||||||
|
|
||||||
export * from './base';
|
|
||||||
|
|
||||||
export interface ICoolEs {
|
|
||||||
indexInfo(): Object;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface CoolEsConfig {
|
|
||||||
nodes: string[];
|
|
||||||
options?: ClientOptions
|
|
||||||
}
|
|
||||||
@ -1,41 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "@cool-midway/es",
|
|
||||||
"version": "7.0.0",
|
|
||||||
"description": "cool-js.com elasticsearch",
|
|
||||||
"main": "index.js",
|
|
||||||
"typings": "index.d.ts",
|
|
||||||
"scripts": {
|
|
||||||
"build": "cross-env midway-bin build -c",
|
|
||||||
"cov": "cross-env midway-bin cov --ts",
|
|
||||||
"lint": "mwts check",
|
|
||||||
"lint:fix": "mwts fix"
|
|
||||||
},
|
|
||||||
"keywords": [],
|
|
||||||
"author": "",
|
|
||||||
"files": [
|
|
||||||
"**/*.js",
|
|
||||||
"**/*.d.ts"
|
|
||||||
],
|
|
||||||
"repository": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "https://cool-js.com"
|
|
||||||
},
|
|
||||||
"license": "MIT",
|
|
||||||
"devDependencies": {
|
|
||||||
"@cool-midway/core": "^7.0.0",
|
|
||||||
"@midwayjs/cli": "^1.2.38",
|
|
||||||
"@midwayjs/core": "^3.0.0",
|
|
||||||
"@midwayjs/decorator": "^3.0.0",
|
|
||||||
"@midwayjs/mock": "^3.0.0",
|
|
||||||
"@types/jest": "^27.4.0",
|
|
||||||
"@types/node": "^16.11.22",
|
|
||||||
"cross-env": "^6.0.0",
|
|
||||||
"jest": "^27.5.1",
|
|
||||||
"mwts": "^1.0.5",
|
|
||||||
"ts-jest": "^27.1.3",
|
|
||||||
"typescript": "^4.0.0"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"@elastic/elasticsearch": "^8.1.0"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,24 +0,0 @@
|
|||||||
{
|
|
||||||
"compileOnSave": true,
|
|
||||||
"compilerOptions": {
|
|
||||||
"target": "es2018",
|
|
||||||
"module": "commonjs",
|
|
||||||
"moduleResolution": "node",
|
|
||||||
"experimentalDecorators": true,
|
|
||||||
"emitDecoratorMetadata": true,
|
|
||||||
"inlineSourceMap":false,
|
|
||||||
"noImplicitThis": true,
|
|
||||||
"noUnusedLocals": true,
|
|
||||||
"stripInternal": true,
|
|
||||||
"skipLibCheck": true,
|
|
||||||
"noImplicitReturns": false,
|
|
||||||
"pretty": true,
|
|
||||||
"declaration": true,
|
|
||||||
"outDir": "dist"
|
|
||||||
},
|
|
||||||
"exclude": [
|
|
||||||
"dist",
|
|
||||||
"node_modules",
|
|
||||||
"test"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@ -1,11 +0,0 @@
|
|||||||
# 🎨 editorconfig.org
|
|
||||||
|
|
||||||
root = true
|
|
||||||
|
|
||||||
[*]
|
|
||||||
charset = utf-8
|
|
||||||
end_of_line = lf
|
|
||||||
indent_style = space
|
|
||||||
indent_size = 2
|
|
||||||
trim_trailing_whitespace = true
|
|
||||||
insert_final_newline = true
|
|
||||||
@ -1,20 +0,0 @@
|
|||||||
{
|
|
||||||
"extends": "./node_modules/mwts/",
|
|
||||||
"ignorePatterns": ["node_modules", "dist", "test", "jest.config.js", "typings"],
|
|
||||||
"env": {
|
|
||||||
"jest": true
|
|
||||||
},
|
|
||||||
"rules": {
|
|
||||||
"@typescript-eslint/explicit-module-boundary-types": "off",
|
|
||||||
"@typescript-eslint/no-unused-vars": "off",
|
|
||||||
"@typescript-eslint/ban-ts-comment": "off",
|
|
||||||
"node/no-extraneous-import": "off",
|
|
||||||
"no-empty": "off",
|
|
||||||
"node/no-extraneous-require": "off",
|
|
||||||
"eqeqeq": "off",
|
|
||||||
"node/no-unsupported-features/node-builtins": "off",
|
|
||||||
"@typescript-eslint/ban-types": "off",
|
|
||||||
"no-control-regex": "off",
|
|
||||||
"prefer-const": "off"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
15
file/.gitignore
vendored
15
file/.gitignore
vendored
@ -1,15 +0,0 @@
|
|||||||
logs/
|
|
||||||
npm-debug.log
|
|
||||||
yarn-error.log
|
|
||||||
node_modules/
|
|
||||||
package-lock.json
|
|
||||||
yarn.lock
|
|
||||||
coverage/
|
|
||||||
dist/
|
|
||||||
.idea/
|
|
||||||
run/
|
|
||||||
.DS_Store
|
|
||||||
*.sw*
|
|
||||||
*.un~
|
|
||||||
.tsbuildinfo
|
|
||||||
.tsbuildinfo.*
|
|
||||||
@ -1,3 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
...require('mwts/.prettierrc.json')
|
|
||||||
}
|
|
||||||
21
file/LICENSE
21
file/LICENSE
@ -1,21 +0,0 @@
|
|||||||
MIT License
|
|
||||||
|
|
||||||
Copyright (c) 2013 - Now midwayjs
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
||||||
10
file/index.d.ts
vendored
10
file/index.d.ts
vendored
@ -1,10 +0,0 @@
|
|||||||
export * from './dist/index';
|
|
||||||
|
|
||||||
declare module '@midwayjs/core/dist/interface' {
|
|
||||||
interface MidwayConfig {
|
|
||||||
book?: PowerPartial<{
|
|
||||||
a: number;
|
|
||||||
b: string;
|
|
||||||
}>;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,7 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
preset: 'ts-jest',
|
|
||||||
testEnvironment: 'node',
|
|
||||||
testPathIgnorePatterns: ['<rootDir>/test/fixtures'],
|
|
||||||
coveragePathIgnorePatterns: ['<rootDir>/test/'],
|
|
||||||
setupFilesAfterEnv: ['./jest.setup.js']
|
|
||||||
};
|
|
||||||
@ -1 +0,0 @@
|
|||||||
jest.setTimeout(30000);
|
|
||||||
@ -1,54 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "@cool-midway/file",
|
|
||||||
"version": "7.0.5",
|
|
||||||
"description": "",
|
|
||||||
"main": "dist/index.js",
|
|
||||||
"typings": "index.d.ts",
|
|
||||||
"scripts": {
|
|
||||||
"build": "cross-env midway-bin build -c",
|
|
||||||
"cov": "cross-env midway-bin cov --ts",
|
|
||||||
"lint": "mwts check",
|
|
||||||
"lint:fix": "mwts fix"
|
|
||||||
},
|
|
||||||
"keywords": [
|
|
||||||
"cool",
|
|
||||||
"cool-admin",
|
|
||||||
"cooljs"
|
|
||||||
],
|
|
||||||
"author": "COOL",
|
|
||||||
"files": [
|
|
||||||
"dist/**/*.js",
|
|
||||||
"dist/**/*.d.ts",
|
|
||||||
"index.d.ts"
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
|
||||||
"repository": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "https://cool-js.com"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"@cool-midway/core": "^7.0.0",
|
|
||||||
"@midwayjs/cli": "^2.0.9",
|
|
||||||
"@midwayjs/core": "^3.9.0",
|
|
||||||
"@midwayjs/decorator": "^3.9.0",
|
|
||||||
"@midwayjs/mock": "^3.9.0",
|
|
||||||
"@types/jest": "^29.2.5",
|
|
||||||
"@types/node": "^18.11.18",
|
|
||||||
"cross-env": "^7.0.3",
|
|
||||||
"jest": "^29.3.1",
|
|
||||||
"lodash": "^4.17.21",
|
|
||||||
"mwts": "^1.3.0",
|
|
||||||
"ts-jest": "^29.0.3",
|
|
||||||
"typescript": "^4.9.4"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"@aws-sdk/client-s3": "^3.414.0",
|
|
||||||
"@aws-sdk/s3-presigned-post": "^3.414.0",
|
|
||||||
"@midwayjs/upload": "^3.9.9",
|
|
||||||
"ali-oss": "^6.17.1",
|
|
||||||
"cos-nodejs-sdk-v5": "^2.11.19",
|
|
||||||
"download": "^8.0.0",
|
|
||||||
"qcloud-cos-sts": "^3.1.0",
|
|
||||||
"qiniu": "^7.8.0"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user