mirror of
https://github.com/KwiTsukasa/kt-template-online-api.git
synced 2026-05-27 15:44:54 +08:00
first commit
This commit is contained in:
commit
73c4429a52
11
.env
Normal file
11
.env
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
DB_HOST=localhost
|
||||||
|
DB_PORT=3306
|
||||||
|
DB_USERNAME=root
|
||||||
|
DB_PASSWORD=qwqvqaqeq2333KT
|
||||||
|
DB_DATABASE=shy_template
|
||||||
|
DB_SYNC=true
|
||||||
|
|
||||||
|
MINIO_ENDPOINT=192.168.1.206
|
||||||
|
MINIO_PORT=9000
|
||||||
|
MINIO_ACCESS_KEY=minioadmin
|
||||||
|
MINIO_SECRET_KEY=minioadmin
|
||||||
11
.env.prod
Normal file
11
.env.prod
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
DB_HOST=192.168.1.206
|
||||||
|
DB_PORT=3306
|
||||||
|
DB_USERNAME=root
|
||||||
|
DB_PASSWORD=3h1@admin
|
||||||
|
DB_DATABASE=shy_template
|
||||||
|
DB_SYNC=false
|
||||||
|
|
||||||
|
MINIO_ENDPOINT=192.168.1.206
|
||||||
|
MINIO_PORT=9000
|
||||||
|
MINIO_ACCESS_KEY=minioadmin
|
||||||
|
MINIO_SECRET_KEY=minioadmin
|
||||||
26
.eslintrc.js
Normal file
26
.eslintrc.js
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
module.exports = {
|
||||||
|
parser: '@typescript-eslint/parser',
|
||||||
|
parserOptions: {
|
||||||
|
project: 'tsconfig.json',
|
||||||
|
tsconfigRootDir: __dirname,
|
||||||
|
sourceType: 'module',
|
||||||
|
},
|
||||||
|
plugins: ['@typescript-eslint/eslint-plugin',"typeorm"],
|
||||||
|
extends: [
|
||||||
|
'plugin:@typescript-eslint/recommended',
|
||||||
|
'plugin:prettier/recommended',
|
||||||
|
],
|
||||||
|
root: true,
|
||||||
|
env: {
|
||||||
|
node: true,
|
||||||
|
jest: true,
|
||||||
|
},
|
||||||
|
ignorePatterns: ['.eslintrc.js'],
|
||||||
|
rules: {
|
||||||
|
'@typescript-eslint/interface-name-prefix': 'off',
|
||||||
|
'@typescript-eslint/explicit-function-return-type': 'off',
|
||||||
|
'@typescript-eslint/explicit-module-boundary-types': 'off',
|
||||||
|
'@typescript-eslint/no-explicit-any': 'off',
|
||||||
|
'prettier/prettier': 'off',
|
||||||
|
},
|
||||||
|
};
|
||||||
38
.gitignore
vendored
Normal file
38
.gitignore
vendored
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
# compiled output
|
||||||
|
/dist
|
||||||
|
/node_modules
|
||||||
|
|
||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
pnpm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
lerna-debug.log*
|
||||||
|
|
||||||
|
# OS
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
|
# Tests
|
||||||
|
/coverage
|
||||||
|
/.nyc_output
|
||||||
|
|
||||||
|
# IDEs and editors
|
||||||
|
/.idea
|
||||||
|
.project
|
||||||
|
.classpath
|
||||||
|
.c9/
|
||||||
|
*.launch
|
||||||
|
.settings/
|
||||||
|
*.sublime-workspace
|
||||||
|
|
||||||
|
# IDE - VSCode
|
||||||
|
.vscode/*
|
||||||
|
!.vscode/settings.json
|
||||||
|
!.vscode/tasks.json
|
||||||
|
!.vscode/launch.json
|
||||||
|
!.vscode/extensions.json
|
||||||
|
|
||||||
|
# env
|
||||||
|
/.env.dev
|
||||||
5
.prettierrc
Normal file
5
.prettierrc
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"singleQuote": true,
|
||||||
|
"trailingComma": "all",
|
||||||
|
"endOfLine": "auto"
|
||||||
|
}
|
||||||
35
README.md
Normal file
35
README.md
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
## 技术总览
|
||||||
|
|
||||||
|
`Node`、`Ts`、`Nest.js`、`TypeORM`、`MySQL`、`Express`
|
||||||
|
|
||||||
|
## 项目简介
|
||||||
|
|
||||||
|
此项目为`kt-template-online`服务端,基于`Node`、`Ts`开发
|
||||||
|
|
||||||
|
使用服务端框架`Nest.js`构建项目以及`ORM`框架`TypeORM`快速生成`SQL`语句以及映射`SQL`库表字段关系
|
||||||
|
|
||||||
|
## 运行项目
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 运行
|
||||||
|
$ pnpm start
|
||||||
|
|
||||||
|
# 开发环境
|
||||||
|
$ pnpm start:dev
|
||||||
|
|
||||||
|
# 生产环境
|
||||||
|
$ pnpm start:prod
|
||||||
|
```
|
||||||
|
|
||||||
|
## 测试
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# unit tests
|
||||||
|
$ pnpm test
|
||||||
|
|
||||||
|
# e2e tests
|
||||||
|
$ pnpm test:e2e
|
||||||
|
|
||||||
|
# test coverage
|
||||||
|
$ pnpm test:cov
|
||||||
|
```
|
||||||
19
dockerfile
Normal file
19
dockerfile
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
# 引用基础镜像
|
||||||
|
FROM node:22.14.0-release
|
||||||
|
|
||||||
|
# 指定工作目录
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# 拷贝文件
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
# 安装依赖
|
||||||
|
RUN npm install
|
||||||
|
|
||||||
|
RUN npm install pm2 -g
|
||||||
|
|
||||||
|
# # 声明暴露端口号
|
||||||
|
EXPOSE 48085
|
||||||
|
|
||||||
|
CMD npm run start:prod
|
||||||
|
|
||||||
14
ecosystem.config.js
Normal file
14
ecosystem.config.js
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
module.exports = {
|
||||||
|
apps: [
|
||||||
|
{
|
||||||
|
name: 'shy-template-server',
|
||||||
|
script: './dist/main.js',
|
||||||
|
env_prod: {
|
||||||
|
NODE_ENV: 'prod',
|
||||||
|
},
|
||||||
|
env_dev: {
|
||||||
|
NODE_ENV: 'dev',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
8
nest-cli.json
Normal file
8
nest-cli.json
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://json.schemastore.org/nest-cli",
|
||||||
|
"collection": "@nestjs/schematics",
|
||||||
|
"sourceRoot": "src",
|
||||||
|
"compilerOptions": {
|
||||||
|
"deleteOutDir": true
|
||||||
|
}
|
||||||
|
}
|
||||||
98
package.json
Normal file
98
package.json
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
{
|
||||||
|
"name": "kt-template-online-api",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"description": "",
|
||||||
|
"author": "",
|
||||||
|
"private": true,
|
||||||
|
"license": "UNLICENSED",
|
||||||
|
"scripts": {
|
||||||
|
"build": "nest build",
|
||||||
|
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
|
||||||
|
"start": "nest start",
|
||||||
|
"start:dev": "nest start --watch",
|
||||||
|
"start:debug": "nest start --debug --watch",
|
||||||
|
"start:prod": "nest build && NODE_ENV=prod node dist/main",
|
||||||
|
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
|
||||||
|
"test": "jest",
|
||||||
|
"test:watch": "jest --watch",
|
||||||
|
"test:cov": "jest --coverage",
|
||||||
|
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
|
||||||
|
"test:e2e": "jest --config ./test/jest-e2e.json"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@nestjs/common": "^9.4.3",
|
||||||
|
"@nestjs/config": "^2.3.4",
|
||||||
|
"@nestjs/core": "^9.4.3",
|
||||||
|
"@nestjs/jwt": "^10.2.0",
|
||||||
|
"@nestjs/mapped-types": "^2.1.1",
|
||||||
|
"@nestjs/passport": "^9.0.3",
|
||||||
|
"@nestjs/platform-express": "^9.4.3",
|
||||||
|
"@nestjs/swagger": "^7.4.2",
|
||||||
|
"@nestjs/typeorm": "^9.0.1",
|
||||||
|
"@types/dotenv": "^8.2.3",
|
||||||
|
"@types/lodash": "^4.17.24",
|
||||||
|
"@types/lodash-es": "^4.17.12",
|
||||||
|
"@types/multer": "^1.4.13",
|
||||||
|
"cross-env": "^7.0.3",
|
||||||
|
"dotenv": "^16.6.1",
|
||||||
|
"express-session": "^1.19.0",
|
||||||
|
"lodash": "^4.18.1",
|
||||||
|
"minio": "^8.0.7",
|
||||||
|
"mssql": "^9.3.2",
|
||||||
|
"multer": "1.4.5-lts.1",
|
||||||
|
"mysql2": "^3.22.3",
|
||||||
|
"nestjs-knife4j-plus": "^1.0.7",
|
||||||
|
"nestjs-minio-client": "^2.2.0",
|
||||||
|
"passport": "^0.6.0",
|
||||||
|
"passport-jwt": "^4.0.1",
|
||||||
|
"passport-local": "^1.0.0",
|
||||||
|
"reflect-metadata": "^0.1.14",
|
||||||
|
"rxjs": "^7.8.2",
|
||||||
|
"svg-captcha": "^1.4.0",
|
||||||
|
"typeorm": "^0.3.28"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@nestjs/cli": "^9.5.0",
|
||||||
|
"@nestjs/schematics": "^9.2.0",
|
||||||
|
"@nestjs/testing": "^9.4.3",
|
||||||
|
"@types/express": "^4.17.25",
|
||||||
|
"@types/express-session": "^1.19.0",
|
||||||
|
"@types/jest": "29.2.4",
|
||||||
|
"@types/node": "18.11.18",
|
||||||
|
"@types/passport-jwt": "^3.0.13",
|
||||||
|
"@types/passport-local": "^1.0.38",
|
||||||
|
"@types/supertest": "^2.0.16",
|
||||||
|
"@typescript-eslint/eslint-plugin": "^5.62.0",
|
||||||
|
"@typescript-eslint/parser": "^5.62.0",
|
||||||
|
"eslint": "^8.57.1",
|
||||||
|
"eslint-config-prettier": "^8.10.2",
|
||||||
|
"eslint-plugin-prettier": "^4.2.5",
|
||||||
|
"eslint-plugin-typeorm": "0.0.19",
|
||||||
|
"jest": "29.3.1",
|
||||||
|
"prettier": "^2.8.8",
|
||||||
|
"source-map-support": "^0.5.21",
|
||||||
|
"supertest": "^6.3.4",
|
||||||
|
"ts-jest": "29.0.3",
|
||||||
|
"ts-loader": "^9.5.7",
|
||||||
|
"ts-node": "^10.9.2",
|
||||||
|
"tsconfig-paths": "4.1.1",
|
||||||
|
"typescript": "^4.9.5"
|
||||||
|
},
|
||||||
|
"jest": {
|
||||||
|
"moduleFileExtensions": [
|
||||||
|
"js",
|
||||||
|
"json",
|
||||||
|
"ts"
|
||||||
|
],
|
||||||
|
"rootDir": "src",
|
||||||
|
"testRegex": ".*\\.spec\\.ts$",
|
||||||
|
"transform": {
|
||||||
|
"^.+\\.(t|j)s$": "ts-jest"
|
||||||
|
},
|
||||||
|
"collectCoverageFrom": [
|
||||||
|
"**/*.(t|j)s"
|
||||||
|
],
|
||||||
|
"coverageDirectory": "../coverage",
|
||||||
|
"testEnvironment": "node"
|
||||||
|
}
|
||||||
|
}
|
||||||
8241
pnpm-lock.yaml
Normal file
8241
pnpm-lock.yaml
Normal file
File diff suppressed because it is too large
Load Diff
13
src/app.controller.ts
Normal file
13
src/app.controller.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import { Controller, Get, Redirect } from '@nestjs/common';
|
||||||
|
import { AppService } from './app.service';
|
||||||
|
|
||||||
|
@Controller()
|
||||||
|
export class AppController {
|
||||||
|
constructor(private readonly appService: AppService) {}
|
||||||
|
|
||||||
|
@Get()
|
||||||
|
@Redirect('/api#/', 301)
|
||||||
|
getHome() {
|
||||||
|
return { url: '/api#/' };
|
||||||
|
}
|
||||||
|
}
|
||||||
57
src/app.module.ts
Normal file
57
src/app.module.ts
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
import { MiddlewareConsumer, Module, NestModule } from '@nestjs/common';
|
||||||
|
import { ConfigModule } from '@nestjs/config';
|
||||||
|
import { ConfigService } from '@nestjs/config';
|
||||||
|
import { AppService } from './app.service';
|
||||||
|
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||||
|
import { MinioModule } from 'nestjs-minio-client';
|
||||||
|
import { ComponentModule } from './component/component.module';
|
||||||
|
import { DictModule } from './dict/dict.module';
|
||||||
|
import { SaveMiddleware } from './middleware/save.middleware';
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
imports: [
|
||||||
|
ConfigModule.forRoot({
|
||||||
|
envFilePath: `.env${
|
||||||
|
process.env.NODE_ENV ? `.${process.env.NODE_ENV}` : ''
|
||||||
|
}`,
|
||||||
|
}),
|
||||||
|
TypeOrmModule.forRootAsync({
|
||||||
|
imports: [ConfigModule],
|
||||||
|
useFactory: async (configService: ConfigService) => {
|
||||||
|
return {
|
||||||
|
type: 'mysql',
|
||||||
|
host: configService.get('DB_HOST'),
|
||||||
|
port: configService.get('DB_PORT'),
|
||||||
|
username: configService.get('DB_USERNAME'),
|
||||||
|
password: configService.get('DB_PASSWORD'),
|
||||||
|
database: configService.get('DB_DATABASE'),
|
||||||
|
synchronize: configService.get('DB_SYNC'),
|
||||||
|
entities: [__dirname + '/**/*.entity.js'],
|
||||||
|
};
|
||||||
|
},
|
||||||
|
inject: [ConfigService],
|
||||||
|
}),
|
||||||
|
MinioModule.registerAsync({
|
||||||
|
isGlobal: true,
|
||||||
|
imports: [ConfigModule],
|
||||||
|
useFactory: (configService: ConfigService) => {
|
||||||
|
return {
|
||||||
|
endPoint: configService.get('MINIO_ENDPOINT'),
|
||||||
|
port: parseInt(configService.get('MINIO_PORT')),
|
||||||
|
useSSL: false,
|
||||||
|
accessKey: configService.get('MINIO_ACCESS_KEY'),
|
||||||
|
secretKey: configService.get('MINIO_SECRET_KEY'),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
inject: [ConfigService],
|
||||||
|
}),
|
||||||
|
ComponentModule,
|
||||||
|
DictModule,
|
||||||
|
],
|
||||||
|
providers: [AppService, ConfigService],
|
||||||
|
})
|
||||||
|
export class AppModule implements NestModule {
|
||||||
|
configure(consumer: MiddlewareConsumer) {
|
||||||
|
consumer.apply(SaveMiddleware).forRoutes('*/save');
|
||||||
|
}
|
||||||
|
}
|
||||||
8
src/app.service.ts
Normal file
8
src/app.service.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class AppService {
|
||||||
|
getHello(): string {
|
||||||
|
return 'Hello World!';
|
||||||
|
}
|
||||||
|
}
|
||||||
9
src/common/index.ts
Normal file
9
src/common/index.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
export function DecodeDictKey(dict: Dict[]): PropertyDecorator {
|
||||||
|
return (target, key: string | symbol) => {
|
||||||
|
Reflect.defineProperty(target, `_${key.toString()}`, {
|
||||||
|
set(newVal) {
|
||||||
|
this[key] = dict.find((i) => i.value == newVal).label;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
150
src/component/component.controller.ts
Normal file
150
src/component/component.controller.ts
Normal file
@ -0,0 +1,150 @@
|
|||||||
|
import {
|
||||||
|
Controller,
|
||||||
|
Get,
|
||||||
|
Post,
|
||||||
|
Res,
|
||||||
|
Query,
|
||||||
|
Body,
|
||||||
|
HttpStatus,
|
||||||
|
HttpCode,
|
||||||
|
} from '@nestjs/common';
|
||||||
|
import {
|
||||||
|
ApiExtraModels,
|
||||||
|
ApiOkResponse,
|
||||||
|
ApiOperation,
|
||||||
|
ApiProperty,
|
||||||
|
ApiQuery,
|
||||||
|
ApiTags,
|
||||||
|
PartialType,
|
||||||
|
} from '@nestjs/swagger';
|
||||||
|
import { ToolsService } from 'src/utils/tool.service';
|
||||||
|
import { ComponentService } from './component.service';
|
||||||
|
import { Component } from './component.entity';
|
||||||
|
import { PaginatedDto } from '@/utils/constant';
|
||||||
|
import { ComponentDto } from './component.dto';
|
||||||
|
|
||||||
|
class CompPageDto
|
||||||
|
extends PartialType(Component)
|
||||||
|
implements PageParams<Component>
|
||||||
|
{
|
||||||
|
@ApiProperty({
|
||||||
|
type: Number,
|
||||||
|
default: 1,
|
||||||
|
})
|
||||||
|
pageNo: number;
|
||||||
|
@ApiProperty({
|
||||||
|
type: Number,
|
||||||
|
default: 10,
|
||||||
|
})
|
||||||
|
pageSize: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
class CompPageResDto extends PaginatedDto {
|
||||||
|
@ApiProperty({
|
||||||
|
type: [ComponentDto],
|
||||||
|
})
|
||||||
|
list: ComponentDto[];
|
||||||
|
}
|
||||||
|
|
||||||
|
@Controller('component')
|
||||||
|
@ApiTags('component')
|
||||||
|
@ApiExtraModels(PaginatedDto, ComponentDto)
|
||||||
|
export class ComponentController {
|
||||||
|
constructor(
|
||||||
|
private readonly toolsService: ToolsService,
|
||||||
|
private readonly componentService: ComponentService,
|
||||||
|
) {} //注入服务
|
||||||
|
|
||||||
|
@Get('allList')
|
||||||
|
@ApiOperation({ summary: '获取组件列表' })
|
||||||
|
@ApiOkResponse({ type: [ComponentDto] })
|
||||||
|
async getAllList(@Res() res) {
|
||||||
|
const list = await this.componentService.all();
|
||||||
|
res.send(this.toolsService.res(HttpStatus.OK, '操作成功', list));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get('list')
|
||||||
|
@ApiOperation({ summary: '获取组件列表分页' })
|
||||||
|
@ApiQuery({ type: [CompPageDto] })
|
||||||
|
@ApiOkResponse({
|
||||||
|
type: CompPageResDto,
|
||||||
|
})
|
||||||
|
async getList(
|
||||||
|
@Res() res,
|
||||||
|
@Query() { pageNo, pageSize, ...args }: PageParams<ComponentDto>,
|
||||||
|
): Promise<CompPageResDto> {
|
||||||
|
const list = await this.componentService.page({
|
||||||
|
pageNo,
|
||||||
|
pageSize,
|
||||||
|
...args,
|
||||||
|
});
|
||||||
|
res.send(this.toolsService.res(HttpStatus.OK, '操作成功', list));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Post('save')
|
||||||
|
@HttpCode(HttpStatus.OK)
|
||||||
|
@ApiOperation({ summary: '保存组件' })
|
||||||
|
async save(@Res() res, @Body() component: Component) {
|
||||||
|
const save = await this.componentService.save(component);
|
||||||
|
|
||||||
|
if (!save) {
|
||||||
|
res.send(this.toolsService.res(HttpStatus.BAD_REQUEST, '操作失败', null));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
res.send(this.toolsService.res(HttpStatus.OK, '操作成功', save.id));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Post('remove')
|
||||||
|
@HttpCode(HttpStatus.OK)
|
||||||
|
@ApiOperation({ summary: '删除组件' })
|
||||||
|
@ApiQuery({ name: 'id', type: String })
|
||||||
|
async remove(@Res() res, @Query('id') id) {
|
||||||
|
const remove = await this.componentService.remove(id);
|
||||||
|
|
||||||
|
if (!remove) {
|
||||||
|
res.send(
|
||||||
|
this.toolsService.res(HttpStatus.BAD_REQUEST, '操作失败', remove),
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
res.send(this.toolsService.res(HttpStatus.OK, '操作成功', remove));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Post('update')
|
||||||
|
@HttpCode(HttpStatus.OK)
|
||||||
|
@ApiOperation({ summary: '编辑组件' })
|
||||||
|
async update(@Res() res, @Body() component: Component) {
|
||||||
|
const update = await this.componentService.update(component);
|
||||||
|
|
||||||
|
if (!update) {
|
||||||
|
res.send(
|
||||||
|
this.toolsService.res(HttpStatus.BAD_REQUEST, '操作失败', update),
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
res.send(this.toolsService.res(HttpStatus.OK, '操作成功', update));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get('detail')
|
||||||
|
@ApiOperation({ summary: '组件详情' })
|
||||||
|
@ApiQuery({ name: 'id', type: String })
|
||||||
|
@ApiOkResponse({ type: ComponentDto })
|
||||||
|
async detail(@Res() res, @Query('id') id) {
|
||||||
|
const detail = await this.componentService.find(id);
|
||||||
|
|
||||||
|
if (!detail) {
|
||||||
|
res.send(this.toolsService.res(HttpStatus.BAD_REQUEST, '操作失败', null));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
res.send(this.toolsService.res(HttpStatus.OK, '操作成功', detail));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
23
src/component/component.dto.ts
Normal file
23
src/component/component.dto.ts
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import { DecodeDictKey } from '@/common';
|
||||||
|
import { Component } from './component.entity';
|
||||||
|
import { DictKeyEnum, DictKeyMap } from '@/utils/constant';
|
||||||
|
import { ApiProperty } from '@nestjs/swagger';
|
||||||
|
|
||||||
|
export class ComponentDto extends Component {
|
||||||
|
[x: string]: any;
|
||||||
|
@ApiProperty()
|
||||||
|
@DecodeDictKey(DictKeyMap.get(DictKeyEnum.COMPONENT_TYPE))
|
||||||
|
typeMsg: string;
|
||||||
|
|
||||||
|
@ApiProperty()
|
||||||
|
@DecodeDictKey([
|
||||||
|
...DictKeyMap.get(DictKeyEnum.CHART),
|
||||||
|
...DictKeyMap.get(DictKeyEnum.COMPONENT),
|
||||||
|
])
|
||||||
|
componentTypeMsg: string;
|
||||||
|
constructor(component: Component) {
|
||||||
|
super(component);
|
||||||
|
this._typeMsg = component.type;
|
||||||
|
this._componentTypeMsg = component.componentType;
|
||||||
|
}
|
||||||
|
}
|
||||||
77
src/component/component.entity.ts
Normal file
77
src/component/component.entity.ts
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
import {
|
||||||
|
Entity,
|
||||||
|
PrimaryGeneratedColumn,
|
||||||
|
Column,
|
||||||
|
CreateDateColumn,
|
||||||
|
UpdateDateColumn,
|
||||||
|
} from 'typeorm';
|
||||||
|
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
|
||||||
|
import { ComponentTypeEnum, ComponentEnum } from '@/utils/constant';
|
||||||
|
|
||||||
|
|
||||||
|
@Entity()
|
||||||
|
export class Component {
|
||||||
|
constructor(component?: Component) {
|
||||||
|
Object.assign(this, component);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ApiPropertyOptional()
|
||||||
|
@PrimaryGeneratedColumn('uuid')
|
||||||
|
id: string;
|
||||||
|
|
||||||
|
@ApiProperty()
|
||||||
|
@Column({
|
||||||
|
default: '',
|
||||||
|
})
|
||||||
|
name: string;
|
||||||
|
|
||||||
|
@ApiProperty({
|
||||||
|
type: 'enum',
|
||||||
|
enum: ComponentTypeEnum,
|
||||||
|
})
|
||||||
|
@Column({
|
||||||
|
type: 'enum',
|
||||||
|
enum: ComponentTypeEnum,
|
||||||
|
})
|
||||||
|
type: number;
|
||||||
|
|
||||||
|
@ApiProperty({
|
||||||
|
type: 'enum',
|
||||||
|
enum: ComponentEnum,
|
||||||
|
})
|
||||||
|
@Column({
|
||||||
|
name: 'component_type',
|
||||||
|
type: 'enum',
|
||||||
|
enum: ComponentEnum,
|
||||||
|
})
|
||||||
|
componentType: number;
|
||||||
|
|
||||||
|
@ApiProperty()
|
||||||
|
@Column({
|
||||||
|
type: 'mediumtext',
|
||||||
|
nullable: false,
|
||||||
|
})
|
||||||
|
image: string;
|
||||||
|
|
||||||
|
@ApiProperty()
|
||||||
|
@Column({
|
||||||
|
type: 'mediumtext',
|
||||||
|
nullable: false,
|
||||||
|
})
|
||||||
|
template: string;
|
||||||
|
|
||||||
|
@CreateDateColumn({
|
||||||
|
name: 'create_time',
|
||||||
|
})
|
||||||
|
createTime: Date;
|
||||||
|
|
||||||
|
@UpdateDateColumn({
|
||||||
|
name: 'update_time',
|
||||||
|
})
|
||||||
|
updateTime: Date;
|
||||||
|
|
||||||
|
@Column({
|
||||||
|
default: 0,
|
||||||
|
})
|
||||||
|
is_deleted: boolean;
|
||||||
|
}
|
||||||
14
src/component/component.module.ts
Normal file
14
src/component/component.module.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import { Module } from '@nestjs/common';
|
||||||
|
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||||
|
import { ComponentController } from './component.controller';
|
||||||
|
import { ComponentService } from './component.service';
|
||||||
|
import { Component } from './component.entity';
|
||||||
|
import { ToolsService } from 'src/utils/tool.service';
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
imports: [TypeOrmModule.forFeature([Component])],
|
||||||
|
controllers: [ComponentController],
|
||||||
|
providers: [ComponentService, ToolsService],
|
||||||
|
exports: [ComponentService],
|
||||||
|
})
|
||||||
|
export class ComponentModule {}
|
||||||
124
src/component/component.service.ts
Normal file
124
src/component/component.service.ts
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
import { InjectRepository } from '@nestjs/typeorm';
|
||||||
|
import { Repository } from 'typeorm';
|
||||||
|
import { Component } from './component.entity';
|
||||||
|
import { ToolsService } from 'src/utils/tool.service';
|
||||||
|
import { isNumber, omit, pick } from 'lodash';
|
||||||
|
import { ComponentDto } from './component.dto';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class ComponentService {
|
||||||
|
constructor(
|
||||||
|
@InjectRepository(Component)
|
||||||
|
private readonly userRepository: Repository<Component>,
|
||||||
|
private readonly toolsService: ToolsService,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
async all(): Promise<Component[]> {
|
||||||
|
const components = await this.userRepository
|
||||||
|
.createQueryBuilder('component')
|
||||||
|
.getMany();
|
||||||
|
return components;
|
||||||
|
}
|
||||||
|
|
||||||
|
async page({
|
||||||
|
pageNo,
|
||||||
|
pageSize,
|
||||||
|
...args
|
||||||
|
}: PageParams<Component>): Promise<Page<Component>> {
|
||||||
|
const hasOwnEntity = new Component();
|
||||||
|
|
||||||
|
const [wheres, likes] = [['is_deleted'], ['name']] as Array<
|
||||||
|
Array<keyof Component>
|
||||||
|
>;
|
||||||
|
|
||||||
|
const [likeWhereSql, likeWhereValue] =
|
||||||
|
this.toolsService.getLikeWhere<Component>(
|
||||||
|
'component',
|
||||||
|
wheres,
|
||||||
|
likes,
|
||||||
|
pick({ ...args, is_deleted: false }, ...wheres, ...likes),
|
||||||
|
);
|
||||||
|
|
||||||
|
const [list, total] = await this.userRepository
|
||||||
|
.createQueryBuilder('component')
|
||||||
|
.select([
|
||||||
|
'component.id',
|
||||||
|
'component.name',
|
||||||
|
'component.type',
|
||||||
|
'component.componentType',
|
||||||
|
'component.image',
|
||||||
|
'component.createTime',
|
||||||
|
])
|
||||||
|
.where(likeWhereSql, likeWhereValue)
|
||||||
|
.andWhere(
|
||||||
|
omit(
|
||||||
|
pick(
|
||||||
|
args,
|
||||||
|
Object.keys(args).filter(
|
||||||
|
(key) =>
|
||||||
|
Object.hasOwn(hasOwnEntity, key) &&
|
||||||
|
(isNumber(args[key]) ? true : !!args[key]),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
...wheres,
|
||||||
|
...likes,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.skip((pageNo - 1) * pageSize)
|
||||||
|
.take(pageSize)
|
||||||
|
.getManyAndCount();
|
||||||
|
|
||||||
|
return this.toolsService.page<Component>(
|
||||||
|
list.map((component) => new ComponentDto(component)),
|
||||||
|
total,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
async save(component: Component): Promise<Component> {
|
||||||
|
const link = this.userRepository.create(component);
|
||||||
|
const save = await this.userRepository.save(link);
|
||||||
|
return save;
|
||||||
|
}
|
||||||
|
|
||||||
|
async remove(id: string): Promise<boolean> {
|
||||||
|
const link = await this.userRepository
|
||||||
|
.createQueryBuilder('component')
|
||||||
|
.update()
|
||||||
|
.set({ is_deleted: true } as any)
|
||||||
|
.where('id = :id', { id })
|
||||||
|
.execute();
|
||||||
|
|
||||||
|
return link.affected > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
async update(component: Component): Promise<boolean> {
|
||||||
|
const link = await this.userRepository
|
||||||
|
.createQueryBuilder('component')
|
||||||
|
.update()
|
||||||
|
.set(component)
|
||||||
|
.where('id = :id', { id: component.id })
|
||||||
|
.execute();
|
||||||
|
|
||||||
|
return link.affected > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
async find(id: number): Promise<Component> {
|
||||||
|
const component = await this.userRepository
|
||||||
|
.createQueryBuilder('component')
|
||||||
|
.select([
|
||||||
|
'component.id',
|
||||||
|
'component.name',
|
||||||
|
'component.type',
|
||||||
|
'component.componentType',
|
||||||
|
'component.image',
|
||||||
|
'component.template',
|
||||||
|
'component.createTime',
|
||||||
|
])
|
||||||
|
.where('component.id = :id', {
|
||||||
|
id,
|
||||||
|
})
|
||||||
|
.getOne();
|
||||||
|
return component;
|
||||||
|
}
|
||||||
|
}
|
||||||
44
src/dict/dict.controller.ts
Normal file
44
src/dict/dict.controller.ts
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
import { Controller, Get, HttpStatus, ParseIntPipe, Query, Res } from '@nestjs/common';
|
||||||
|
import { ToolsService } from 'src/utils/tool.service';
|
||||||
|
import { DictService } from './dict.service';
|
||||||
|
import { ApiOperation, ApiQuery, ApiTags } from '@nestjs/swagger';
|
||||||
|
import { ComponentTypeEnum, DictKeyEnum, DictKeyType } from '@/utils/constant';
|
||||||
|
|
||||||
|
@ApiTags('dict')
|
||||||
|
@Controller('dict')
|
||||||
|
export class DictController {
|
||||||
|
constructor(
|
||||||
|
private readonly toolsService: ToolsService,
|
||||||
|
private readonly dictService: DictService,
|
||||||
|
) {} //注入服务
|
||||||
|
|
||||||
|
@ApiOperation({ summary: '根据key获取字典' })
|
||||||
|
@ApiQuery({ name: 'dictKey', enum: DictKeyEnum })
|
||||||
|
@Get('getDictByKey')
|
||||||
|
async getDictByKey(@Res() res, @Query('dictKey') dictKey: DictKeyType) {
|
||||||
|
const dict = this.toolsService.getDictByKey(dictKey)
|
||||||
|
|
||||||
|
return res.send(
|
||||||
|
this.toolsService.res(
|
||||||
|
HttpStatus.OK,
|
||||||
|
'操作成功',
|
||||||
|
dict,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ApiOperation({ summary: '根据组件类型获取组件字典' })
|
||||||
|
@ApiQuery({ name: 'type', enum: ComponentTypeEnum })
|
||||||
|
@Get('getComponentDictByType')
|
||||||
|
async getComponentDictByType(@Res() res, @Query('type', ParseIntPipe) type) {
|
||||||
|
const dict = await this.dictService.getComponentDictByType(type)
|
||||||
|
|
||||||
|
return res.send(
|
||||||
|
this.toolsService.res(
|
||||||
|
HttpStatus.OK,
|
||||||
|
'操作成功',
|
||||||
|
dict,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
11
src/dict/dict.module.ts
Normal file
11
src/dict/dict.module.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import { Module } from '@nestjs/common';
|
||||||
|
import { DictController } from './dict.controller';
|
||||||
|
import { DictService } from './dict.service';
|
||||||
|
import { ToolsService } from 'src/utils/tool.service';
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
controllers: [DictController],
|
||||||
|
providers: [DictService, ToolsService],
|
||||||
|
exports: [DictService],
|
||||||
|
})
|
||||||
|
export class DictModule {}
|
||||||
21
src/dict/dict.service.ts
Normal file
21
src/dict/dict.service.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
import { ToolsService } from '@/utils/tool.service';
|
||||||
|
import { ComponentTypeEnum, DictKeyEnum } from '@/utils/constant';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class DictService {
|
||||||
|
constructor(private readonly toolsService: ToolsService) {}
|
||||||
|
|
||||||
|
async getComponentDictByType(type: ComponentTypeEnum): Promise<Dict[]> {
|
||||||
|
switch (type) {
|
||||||
|
case ComponentTypeEnum.CHART:
|
||||||
|
return this.toolsService.getDictByKey(DictKeyEnum.CHART);
|
||||||
|
|
||||||
|
case ComponentTypeEnum.COMPONENT:
|
||||||
|
return this.toolsService.getDictByKey(DictKeyEnum.COMPONENT);
|
||||||
|
|
||||||
|
default:
|
||||||
|
return this.toolsService.getDictByKey(DictKeyEnum.CHART);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
29
src/main.ts
Normal file
29
src/main.ts
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
import { NestFactory } from '@nestjs/core';
|
||||||
|
import { AppModule } from './app.module';
|
||||||
|
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
|
||||||
|
import { urlencoded, json } from 'express';
|
||||||
|
import { knife4jSetup } from 'nestjs-knife4j-plus';
|
||||||
|
|
||||||
|
async function bootstrap() {
|
||||||
|
const app = await NestFactory.create(AppModule);
|
||||||
|
app.use(json({ limit: '50mb' }));
|
||||||
|
app.use(urlencoded({ extended: true, limit: '50mb' }));
|
||||||
|
|
||||||
|
const options = new DocumentBuilder()
|
||||||
|
.setTitle('KT-Template API')
|
||||||
|
.setVersion('1.0')
|
||||||
|
.build();
|
||||||
|
const document = SwaggerModule.createDocument(app, options);
|
||||||
|
SwaggerModule.setup('api', app, document);
|
||||||
|
|
||||||
|
// 启用knife4j增强(关键代码)
|
||||||
|
knife4jSetup(app, [
|
||||||
|
{
|
||||||
|
name: '1.0', // 文档版本名称
|
||||||
|
url: `/api-json`, // Swagger openapi JSON地址
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
await app.listen(48085);
|
||||||
|
}
|
||||||
|
bootstrap();
|
||||||
10
src/middleware/save.middleware.ts
Normal file
10
src/middleware/save.middleware.ts
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import { Injectable, NestMiddleware } from '@nestjs/common';
|
||||||
|
import { Request, Response } from 'express';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class SaveMiddleware implements NestMiddleware {
|
||||||
|
use(req: Request, res: Response, next: VoidFunction) {
|
||||||
|
Reflect.deleteProperty(req.body, 'id');
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
}
|
||||||
16
src/minio/minio.controller.ts
Normal file
16
src/minio/minio.controller.ts
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import {
|
||||||
|
Controller,
|
||||||
|
|
||||||
|
} from '@nestjs/common';
|
||||||
|
import { ToolsService } from 'src/utils/tool.service';
|
||||||
|
import { MinioClientService } from './minio.service';
|
||||||
|
|
||||||
|
@Controller('minio')
|
||||||
|
export class MinioClientController {
|
||||||
|
constructor(
|
||||||
|
private readonly toolsService: ToolsService,
|
||||||
|
private readonly minioClientService: MinioClientService,
|
||||||
|
) {} //注入服务
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
13
src/minio/minio.module.ts
Normal file
13
src/minio/minio.module.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import { Module } from '@nestjs/common';
|
||||||
|
import { MinioClientController } from './minio.controller';
|
||||||
|
import { MinioClientService } from './minio.service';
|
||||||
|
import { ToolsService } from 'src/utils/tool.service';
|
||||||
|
import { MinioModule } from 'nestjs-minio-client';
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
imports: [MinioModule],
|
||||||
|
controllers: [MinioClientController],
|
||||||
|
providers: [MinioClientService, ToolsService],
|
||||||
|
exports: [MinioClientService],
|
||||||
|
})
|
||||||
|
export class MinioClientModule {}
|
||||||
11
src/minio/minio.service.ts
Normal file
11
src/minio/minio.service.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
|
||||||
|
import { ToolsService } from 'src/utils/tool.service';
|
||||||
|
@Injectable()
|
||||||
|
export class MinioClientService {
|
||||||
|
constructor(
|
||||||
|
private readonly toolsService: ToolsService,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
22
src/types/res.d.ts
vendored
Normal file
22
src/types/res.d.ts
vendored
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
type Res = {
|
||||||
|
code: number;
|
||||||
|
msg: string;
|
||||||
|
data: any;
|
||||||
|
};
|
||||||
|
|
||||||
|
type Page<T = any> = {
|
||||||
|
list: T[];
|
||||||
|
total: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
type Dict<T = object> = {
|
||||||
|
label: string;
|
||||||
|
value: any;
|
||||||
|
} & Partial<T>;
|
||||||
|
|
||||||
|
type PageParams<T> = {
|
||||||
|
pageSize: number;
|
||||||
|
pageNo: number;
|
||||||
|
} & Partial<T>;
|
||||||
|
|
||||||
|
type Many<T> = T | readonly T[];
|
||||||
179
src/utils/constant.ts
Normal file
179
src/utils/constant.ts
Normal file
@ -0,0 +1,179 @@
|
|||||||
|
import { ApiProperty } from '@nestjs/swagger';
|
||||||
|
|
||||||
|
export class PaginatedDto {
|
||||||
|
@ApiProperty()
|
||||||
|
total: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum ComponentTypeEnum {
|
||||||
|
CHART = 1,
|
||||||
|
COMPONENT = 2,
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum ComponentEnum {
|
||||||
|
NOT_CATEGORY = -1,
|
||||||
|
LINE = 1,
|
||||||
|
BAR = 2,
|
||||||
|
PIE = 3,
|
||||||
|
SCATTER = 4,
|
||||||
|
MAP = 5,
|
||||||
|
CANDLESTICK = 6,
|
||||||
|
RADAR = 7,
|
||||||
|
BOX_PLOT = 8,
|
||||||
|
HEATMAP = 9,
|
||||||
|
GRAPH = 10,
|
||||||
|
LINES = 11,
|
||||||
|
TREE = 12,
|
||||||
|
TREE_MAP = 13,
|
||||||
|
SUNBURST = 14,
|
||||||
|
PARALLEL = 15,
|
||||||
|
SAN_KEY = 16,
|
||||||
|
FUNNEL = 17,
|
||||||
|
GAUGE = 18,
|
||||||
|
PICTORIAL_BAR = 19,
|
||||||
|
THEME_RIVER = 20,
|
||||||
|
LIQUID_FILL = 21,
|
||||||
|
WORD_CLOUD = 22,
|
||||||
|
TABLE = 23,
|
||||||
|
FORM = 24,
|
||||||
|
CONTAINER = 25,
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum DictKeyEnum {
|
||||||
|
COMPONENT_TYPE = 'COMPONENT_TYPE',
|
||||||
|
CHART = 'CHART',
|
||||||
|
COMPONENT = 'COMPONENT',
|
||||||
|
}
|
||||||
|
|
||||||
|
export type DictKeyType = keyof typeof DictKeyEnum;
|
||||||
|
|
||||||
|
export const DictKeyMap: Map<DictKeyType, Dict[]> = new Map();
|
||||||
|
|
||||||
|
const ComponentTypeDict = [
|
||||||
|
{
|
||||||
|
label: '图表',
|
||||||
|
value: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '组件',
|
||||||
|
value: 2,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const ChartDict = [
|
||||||
|
{
|
||||||
|
label: '未分类',
|
||||||
|
value: -1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '折线图',
|
||||||
|
value: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '柱状图',
|
||||||
|
value: 2,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '饼图',
|
||||||
|
value: 3,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '散点图',
|
||||||
|
value: 4,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '地图',
|
||||||
|
value: 5,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'K线图',
|
||||||
|
value: 6,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '雷达图',
|
||||||
|
value: 7,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '盒须图',
|
||||||
|
value: 8,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '热力图',
|
||||||
|
value: 9,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '关系图',
|
||||||
|
value: 10,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '路径图',
|
||||||
|
value: 11,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '树图',
|
||||||
|
value: 12,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '矩树图',
|
||||||
|
value: 13,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '旭日图',
|
||||||
|
value: 14,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '平行坐标系',
|
||||||
|
value: 15,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '桑基图',
|
||||||
|
value: 16,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '漏斗图',
|
||||||
|
value: 17,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '仪表盘',
|
||||||
|
value: 18,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '象形图',
|
||||||
|
value: 19,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '河流图',
|
||||||
|
value: 20,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '水球',
|
||||||
|
value: 21,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '词云',
|
||||||
|
value: 22,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const ComponentDict = [
|
||||||
|
{
|
||||||
|
label: '未分类',
|
||||||
|
value: -1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '表格',
|
||||||
|
value: 23,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '表单',
|
||||||
|
value: 24,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '容器',
|
||||||
|
value: 25,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
DictKeyMap.set(DictKeyEnum.COMPONENT_TYPE, ComponentTypeDict);
|
||||||
|
DictKeyMap.set(DictKeyEnum.CHART, ChartDict);
|
||||||
|
DictKeyMap.set(DictKeyEnum.COMPONENT, ComponentDict);
|
||||||
105
src/utils/tool.service.ts
Normal file
105
src/utils/tool.service.ts
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
import * as svgCaptcha from 'svg-captcha';
|
||||||
|
import { DictKeyMap, DictKeyEnum } from './constant';
|
||||||
|
import type { DictKeyType } from './constant';
|
||||||
|
import { isBoolean } from 'lodash';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class ToolsService {
|
||||||
|
async captche(size = 4) {
|
||||||
|
const captcha = svgCaptcha.create({
|
||||||
|
//可配置返回的图片信息
|
||||||
|
size, //生成几个验证码
|
||||||
|
fontSize: 50, //文字大小
|
||||||
|
width: 100, //宽度
|
||||||
|
height: 34, //高度
|
||||||
|
background: '#ffffff', //背景颜色
|
||||||
|
});
|
||||||
|
return captcha;
|
||||||
|
}
|
||||||
|
|
||||||
|
res(code: number, msg: string, data: any): Res {
|
||||||
|
const retn: Res = {
|
||||||
|
code,
|
||||||
|
msg,
|
||||||
|
data,
|
||||||
|
};
|
||||||
|
return retn;
|
||||||
|
}
|
||||||
|
|
||||||
|
page<T = any>(list: T[], total: number): Page<T> {
|
||||||
|
const retn = {
|
||||||
|
list,
|
||||||
|
total,
|
||||||
|
};
|
||||||
|
return retn;
|
||||||
|
}
|
||||||
|
|
||||||
|
getWhereStr(alias: string, key) {
|
||||||
|
return `${alias}.${key.toString()} = :${key.toString()}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
getLikeStr(alias: string, key) {
|
||||||
|
return `${alias}.${key.toString()} like :${key.toString()}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
getLikeWhere<T = object>(
|
||||||
|
alias: string,
|
||||||
|
wheres: Array<keyof T>,
|
||||||
|
likes: Array<keyof T>,
|
||||||
|
values: Partial<T>,
|
||||||
|
operator = 'AND',
|
||||||
|
): [string, Record<keyof T, string>] {
|
||||||
|
const linkOperator = ` ${operator} `;
|
||||||
|
const wheresEndIndex = wheres.length;
|
||||||
|
|
||||||
|
return [
|
||||||
|
[...wheres, ...likes].reduce((pre, cur, index, source) => {
|
||||||
|
const isLink = !!source
|
||||||
|
.slice(0, index)
|
||||||
|
.some((key) => isBoolean(values[key]) || !!values[key]);
|
||||||
|
|
||||||
|
const { getLikeStr, getWhereStr } = this;
|
||||||
|
|
||||||
|
if (!isBoolean(values[cur]) && !values[cur]) return pre;
|
||||||
|
|
||||||
|
if (!index) getWhereStr(alias, cur);
|
||||||
|
|
||||||
|
const matchSqlFn = index >= wheresEndIndex ? getLikeStr : getWhereStr;
|
||||||
|
const beforeSql = `${pre}${isLink ? linkOperator : ' '}`;
|
||||||
|
|
||||||
|
return `${beforeSql}${matchSqlFn(alias, cur)}`;
|
||||||
|
}, ''),
|
||||||
|
Object.entries(values).reduce((pre, [key, value]) => {
|
||||||
|
if (!isBoolean(value) && !value) return pre;
|
||||||
|
|
||||||
|
if (likes.includes(key as keyof T))
|
||||||
|
return { ...pre, ...{ [key]: `%${value}%` } };
|
||||||
|
|
||||||
|
return { ...pre, ...{ [key]: value } };
|
||||||
|
}, {} as Record<keyof T, string>),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
getDictByKey(key: DictKeyType): Dict[] {
|
||||||
|
if (!DictKeyEnum[key]) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return DictKeyMap.get(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
dictFormat<T = object>(
|
||||||
|
label: string,
|
||||||
|
value: any,
|
||||||
|
other: Partial<T>,
|
||||||
|
): Dict<T> {
|
||||||
|
const options = {
|
||||||
|
label,
|
||||||
|
value,
|
||||||
|
...other,
|
||||||
|
};
|
||||||
|
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
}
|
||||||
24
test/app.e2e-spec.ts
Normal file
24
test/app.e2e-spec.ts
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import { Test, TestingModule } from '@nestjs/testing';
|
||||||
|
import { INestApplication } from '@nestjs/common';
|
||||||
|
import * as request from 'supertest';
|
||||||
|
import { AppModule } from './../src/app.module';
|
||||||
|
|
||||||
|
describe('AppController (e2e)', () => {
|
||||||
|
let app: INestApplication;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
const moduleFixture: TestingModule = await Test.createTestingModule({
|
||||||
|
imports: [AppModule],
|
||||||
|
}).compile();
|
||||||
|
|
||||||
|
app = moduleFixture.createNestApplication();
|
||||||
|
await app.init();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('/ (GET)', () => {
|
||||||
|
return request(app.getHttpServer())
|
||||||
|
.get('/')
|
||||||
|
.expect(200)
|
||||||
|
.expect('Hello World!');
|
||||||
|
});
|
||||||
|
});
|
||||||
9
test/jest-e2e.json
Normal file
9
test/jest-e2e.json
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"moduleFileExtensions": ["js", "json", "ts"],
|
||||||
|
"rootDir": ".",
|
||||||
|
"testEnvironment": "node",
|
||||||
|
"testRegex": ".e2e-spec.ts$",
|
||||||
|
"transform": {
|
||||||
|
"^.+\\.(t|j)s$": "ts-jest"
|
||||||
|
}
|
||||||
|
}
|
||||||
4
tsconfig.build.json
Normal file
4
tsconfig.build.json
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"extends": "./tsconfig.json",
|
||||||
|
"exclude": ["node_modules", "test", "dist", "**/*spec.ts"]
|
||||||
|
}
|
||||||
25
tsconfig.json
Normal file
25
tsconfig.json
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"module": "commonjs",
|
||||||
|
"declaration": true,
|
||||||
|
"removeComments": true,
|
||||||
|
"emitDecoratorMetadata": true,
|
||||||
|
"experimentalDecorators": true,
|
||||||
|
"allowSyntheticDefaultImports": true,
|
||||||
|
"target": "ESNext",
|
||||||
|
"sourceMap": true,
|
||||||
|
"outDir": "./dist",
|
||||||
|
"incremental": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"strictNullChecks": false,
|
||||||
|
"noImplicitAny": false,
|
||||||
|
"strictBindCallApply": false,
|
||||||
|
"forceConsistentCasingInFileNames": false,
|
||||||
|
"noFallthroughCasesInSwitch": false,
|
||||||
|
"paths": {
|
||||||
|
"@/*": [
|
||||||
|
"./src/*"
|
||||||
|
],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user