main
expressgy 1 year ago
commit 03b043e883
  1. 25
      .eslintrc.js
  2. 38
      .gitignore
  3. 5
      .prettierrc
  4. 73
      README.md
  5. 59
      config/development.ts
  6. 12
      config/index.ts
  7. 57
      config/production.ts
  8. 10
      config/test.ts
  9. BIN
      file/11 - 副本.jpg
  10. BIN
      file/121.mp4
  11. BIN
      file/docx测试.docx
  12. BIN
      file/pdf测试.pdf
  13. 8
      nest-cli.json
  14. 84
      package.json
  15. 39
      src/Gdecorator/userinfoDecorator/userinfoDecorator.decorator.ts
  16. 32
      src/Gexceptions/gexceptionsfilter.filter.ts
  17. 35
      src/Ginterceptor/gdevinterceptor.interceptor.ts
  18. 49
      src/Ginterceptor/gresponseinterceptor.interceptor.ts
  19. 8
      src/Gservice/GDATABASE/gdatabase.module.ts
  20. 29
      src/Gservice/GDATABASE/gdatabase.service.ts
  21. 8
      src/Gservice/GEMAIL/gemail.module.ts
  22. 317
      src/Gservice/GEMAIL/gemail.service.ts
  23. 8
      src/Gservice/GLOGGER/glogger.module.ts
  24. 108
      src/Gservice/GLOGGER/glogger.service.ts
  25. 8
      src/Gservice/GREDIS/gredis.module.ts
  26. 360
      src/Gservice/GREDIS/gredis.service.ts
  27. 0
      src/Gservice/GREDIS/testJson.js
  28. 8
      src/Gservice/GTOOLS/gtools.module.ts
  29. 137
      src/Gservice/GTOOLS/gtools.service.ts
  30. 4
      src/Gservice/GTOOLS/testToken.js
  31. 29
      src/app.module.ts
  32. 97
      src/main.ts
  33. 126
      src/rgvsale/dto/account.dto.ts
  34. 156
      src/rgvsale/dto/demoItem.dto.ts
  35. 177
      src/rgvsale/dto/demoScenes.dto.ts
  36. 259
      src/rgvsale/dto/demoScenesProblem.dto.ts
  37. 171
      src/rgvsale/dto/industry.dto.ts
  38. 91
      src/rgvsale/dto/product.dto.ts
  39. 300
      src/rgvsale/dto/productsProgramme.dto.ts
  40. 15
      src/rgvsale/dto/userInfo.dto.ts
  41. 320
      src/rgvsale/rgvsale.controller.ts
  42. 9
      src/rgvsale/rgvsale.module.ts
  43. 2285
      src/rgvsale/rgvsale.service.ts
  44. 24
      test/app.e2e-spec.ts
  45. 9
      test/jest-e2e.json
  46. 14
      test/testToken.js
  47. 4
      tsconfig.build.json
  48. 29
      tsconfig.json

@ -0,0 +1,25 @@
module.exports = {
parser: '@typescript-eslint/parser',
parserOptions: {
project: 'tsconfig.json',
tsconfigRootDir: __dirname,
sourceType: 'module',
},
plugins: ['@typescript-eslint/eslint-plugin'],
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',
},
};

38
.gitignore vendored

@ -0,0 +1,38 @@
# compiled output
/dist
/node_modules
/Log/*
/file/*
# Logs
logs
*.log
npm-debug.log*
pnpm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
pnpm-lock.yaml
# 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

@ -0,0 +1,5 @@
{
"singleQuote": true,
"trailingComma": "all",
"tabWidth": 4
}

@ -0,0 +1,73 @@
<p align="center">
<a href="http://nestjs.com/" target="blank"><img src="https://nestjs.com/img/logo-small.svg" width="200" alt="Nest Logo" /></a>
</p>
[circleci-image]: https://img.shields.io/circleci/build/github/nestjs/nest/master?token=abc123def456
[circleci-url]: https://circleci.com/gh/nestjs/nest
<p align="center">A progressive <a href="http://nodejs.org" target="_blank">Node.js</a> framework for building efficient and scalable server-side applications.</p>
<p align="center">
<a href="https://www.npmjs.com/~nestjscore" target="_blank"><img src="https://img.shields.io/npm/v/@nestjs/core.svg" alt="NPM Version" /></a>
<a href="https://www.npmjs.com/~nestjscore" target="_blank"><img src="https://img.shields.io/npm/l/@nestjs/core.svg" alt="Package License" /></a>
<a href="https://www.npmjs.com/~nestjscore" target="_blank"><img src="https://img.shields.io/npm/dm/@nestjs/common.svg" alt="NPM Downloads" /></a>
<a href="https://circleci.com/gh/nestjs/nest" target="_blank"><img src="https://img.shields.io/circleci/build/github/nestjs/nest/master" alt="CircleCI" /></a>
<a href="https://coveralls.io/github/nestjs/nest?branch=master" target="_blank"><img src="https://coveralls.io/repos/github/nestjs/nest/badge.svg?branch=master#9" alt="Coverage" /></a>
<a href="https://discord.gg/G7Qnnhy" target="_blank"><img src="https://img.shields.io/badge/discord-online-brightgreen.svg" alt="Discord"/></a>
<a href="https://opencollective.com/nest#backer" target="_blank"><img src="https://opencollective.com/nest/backers/badge.svg" alt="Backers on Open Collective" /></a>
<a href="https://opencollective.com/nest#sponsor" target="_blank"><img src="https://opencollective.com/nest/sponsors/badge.svg" alt="Sponsors on Open Collective" /></a>
<a href="https://paypal.me/kamilmysliwiec" target="_blank"><img src="https://img.shields.io/badge/Donate-PayPal-ff3f59.svg"/></a>
<a href="https://opencollective.com/nest#sponsor" target="_blank"><img src="https://img.shields.io/badge/Support%20us-Open%20Collective-41B883.svg" alt="Support us"></a>
<a href="https://twitter.com/nestframework" target="_blank"><img src="https://img.shields.io/twitter/follow/nestframework.svg?style=social&label=Follow"></a>
</p>
<!--[![Backers on Open Collective](https://opencollective.com/nest/backers/badge.svg)](https://opencollective.com/nest#backer)
[![Sponsors on Open Collective](https://opencollective.com/nest/sponsors/badge.svg)](https://opencollective.com/nest#sponsor)-->
## Description
[Nest](https://github.com/nestjs/nest) framework TypeScript starter repository.
## Installation
```bash
$ pnpm install
```
## Running the app
```bash
# development
$ pnpm run start
# watch mode
$ pnpm run start:dev
# production mode
$ pnpm run start:prod
```
## Test
```bash
# unit tests
$ pnpm run test
# e2e tests
$ pnpm run test:e2e
# test coverage
$ pnpm run test:cov
```
## Support
Nest is an MIT-licensed open source project. It can grow thanks to the sponsors and support by the amazing backers. If you'd like to join them, please [read more here](https://docs.nestjs.com/support).
## Stay in touch
- Author - [Kamil Myśliwiec](https://kamilmysliwiec.com)
- Website - [https://nestjs.com](https://nestjs.com/)
- Twitter - [@nestframework](https://twitter.com/nestframework)
## License
Nest is [MIT licensed](LICENSE).

@ -0,0 +1,59 @@
export default {
// 主服务
master: {
systemName: '心曲Tune',
host: '0.0.0.0',
port: '3000',
},
swagger: {
enable: true,
},
dev: {
// 开发模式拦截器,用以提供接口请求时间和响应大小
devInterceptor: true,
},
log: {
level: 'all',
},
databases: {
starLight: {
host: 'localhost',
port: 3306,
username: 'root',
password: 'root',
database: 'rgv-sale-system',
},
},
redis: {
starLight: {
host: 'localhost',
port: 6379,
username: 'default',
password: 'default',
dbNumber: 3,
},
},
email: {
host: 'smtp.qq.com',
user: 'togy.gc@qq.com',
pass: 'miidbsjeuqcxddbf',
},
// 加密
encryption: {
salt: 'время,вперёд!', // 盐
secretKey: 'быть всегда готовым!', // 密钥
},
token: {
timeout: 1000 * 60 * 60 * 24 * 7,
secretKey: 'Missing You!(John waite)',
},
// 登录
signIn: {
// @ 密码验证次数超限后的冷却时长
signInErrorTimeout: 60 * 10,
// @ 允许密码输错几次
signInErrorNumber: 5,
// @ 允许同时在线数
onLineNumber: 6,
},
};

@ -0,0 +1,12 @@
import developmentConfig from './development';
import testConfig from './test';
import productionConfig from './production';
const configs = {
development: developmentConfig,
test: testConfig,
production: productionConfig,
};
const env = process.env.NODE_ENV || 'development';
export default () => configs[env];

@ -0,0 +1,57 @@
export default {
// 主服务
master: {
systemName: '心曲Tune',
host: '127.0.0.1',
port: '3000',
},
swagger: {
enable: false,
},
dev: {
// 开发模式拦截器,用以提供接口请求时间和响应大小
devInterceptor: false,
},
log: {
level: 'info',
},
databases: {
starLight: {
host: 'localhost',
port: 3306,
username: 'root',
password: 'Hxl1314521',
database: 'Starlight',
},
},
redis: {
starLight: {
host: 'localhost',
port: 6379,
username: 'default',
password: 'default',
dbNumber: 3,
},
},
email: {
host: 'smtp.qq.com',
user: 'togy.gc@qq.com',
pass: 'miidbsjeuqcxddbf',
},
// 加密
encryption: {
salt: 'время,вперёд!', // 盐
secretKey: 'быть всегда готовым!', // 密钥
},
token: {
timeout: 1000 * 60 * 60 * 24 * 7,
secretKey: 'Missing You!(John waite)',
},
// 登录
signIn: {
// @ 密码验证次数超限后的冷却时长
signInErrorTimeout: 60 * 10,
// @ 允许密码输错几次
signInErrorNumber: 5,
},
};

@ -0,0 +1,10 @@
export default {
// 主服务
master: {
host: '0.0.0.0',
port: '3000',
},
swagger: {
enable: true,
},
};

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

@ -0,0 +1,8 @@
{
"$schema": "https://json.schemastore.org/nest-cli",
"collection": "@nestjs/schematics",
"sourceRoot": "src",
"compilerOptions": {
"deleteOutDir": true
}
}

@ -0,0 +1,84 @@
{
"name": "tune",
"version": "0.0.1",
"description": "",
"author": "",
"private": true,
"license": "UNLICENSED",
"scripts": {
"build": "nest build",
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
"start": "cross-env NODE_ENV=production nest start",
"start:dev": "cross-env NODE_ENV=development nest start --watch",
"start:debug": "cross-env NODE_ENV=development nest start --debug --watch",
"start: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": {
"@fastify/multipart": "^7.4.2",
"@fastify/static": "^6.9.0",
"@nestjs/common": "^9.0.0",
"@nestjs/config": "^2.3.1",
"@nestjs/core": "^9.0.0",
"@nestjs/mapped-types": "*",
"@nestjs/platform-fastify": "^9.3.9",
"@nestjs/swagger": "^6.2.1",
"class-transformer": "^0.5.1",
"class-validator": "^0.14.0",
"fastify": "^4.19.2",
"fastify-swagger": "^5.2.0",
"jsonwebtoken": "^9.0.0",
"log4js": "^6.9.1",
"mysql2": "^3.2.0",
"nodemailer": "^6.9.1",
"redis": "^4.6.5",
"reflect-metadata": "^0.1.13",
"rxjs": "^7.2.0"
},
"devDependencies": {
"@nestjs/cli": "^9.0.0",
"@nestjs/schematics": "^9.0.0",
"@nestjs/testing": "^9.0.0",
"@types/express": "^4.17.13",
"@types/jest": "29.2.4",
"@types/node": "18.11.18",
"@types/supertest": "^2.0.11",
"@typescript-eslint/eslint-plugin": "^5.0.0",
"@typescript-eslint/parser": "^5.0.0",
"cross-env": "^7.0.3",
"eslint": "^8.0.1",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-prettier": "^4.0.0",
"jest": "29.3.1",
"prettier": "^2.3.2",
"source-map-support": "^0.5.20",
"supertest": "^6.1.3",
"ts-jest": "29.0.3",
"ts-loader": "^9.2.3",
"ts-node": "^10.0.0",
"tsconfig-paths": "4.1.1",
"typescript": "^4.7.4"
},
"jest": {
"moduleFileExtensions": [
"js",
"json",
"ts"
],
"rootDir": "src",
"testRegex": ".*\\.spec\\.ts$",
"transform": {
"^.+\\.(t|j)s$": "ts-jest"
},
"collectCoverageFrom": [
"**/*.(t|j)s"
],
"coverageDirectory": "../coverage",
"testEnvironment": "node"
}
}

@ -0,0 +1,39 @@
import {
createParamDecorator,
ExecutionContext, HttpException,
SetMetadata,
} from '@nestjs/common';
export const UserInfoDecorator = (...args: string[]) =>
SetMetadata('userinfoDecorator', args);
// nest g d [name]
// 自定义装饰器
// ? ?
// ? 函数名称: userinfoDto
// ? 函数描述: 限制userinfo数据格式
// ? ?
export class userinfoDto {
userId: string;
token: string;
}
// ? ?
// ? 函数名称: getUserinfo
// ? 函数描述: 获取token用户信息
// ? ?
export const getUserinfo = createParamDecorator(
(data: string, ctx: ExecutionContext) => {
const req = ctx.switchToHttp().getRequest();
if (!req.headers.userid) {
throw new HttpException('缺少Headers参数userid', 400);
} else if (!req.headers.token) {
throw new HttpException('缺少Headers参数token', 400);
}
return {
userId: req.headers.userid,
token: req.headers.token,
} as userinfoDto;
},
);

@ -0,0 +1,32 @@
import {
ArgumentsHost,
Catch,
ExceptionFilter,
HttpException,
} from '@nestjs/common';
import a from '@nestjs/platform-fastify'
import { Request, Response } from 'express';
@Catch()
export class GexceptionsfilterFilter<T> implements ExceptionFilter {
catch(exception: HttpException, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse<Response>();
const request = ctx.getRequest<Request>();
console.log('拦截外的报错',exception);
const status = exception.getStatus();
const responseBody = exception.getResponse()
if(typeof responseBody === 'object'){
response
.status(status)
.send(responseBody);
}else
response
.status(status)
.send({
statusCode: status,
message: exception.getResponse(),
error:'Bad Request'
});
}
}

@ -0,0 +1,35 @@
import {
CallHandler,
ExecutionContext,
Injectable,
NestInterceptor,
} from '@nestjs/common';
import { Observable } from 'rxjs';
import { GloggerService } from '../Gservice/GLOGGER/glogger.service';
import { tap } from 'rxjs/operators';
@Injectable()
export class GdevinterceptorInterceptor implements NestInterceptor {
logger: GloggerService;
constructor() {
this.logger = new GloggerService();
this.logger.setContext(`[DevlInterceptor]`);
}
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
const request = context.switchToHttp().getRequest();
this.logger.warn(request.method, request.url, '<==');
const now = Date.now();
return next.handle().pipe(
tap((data) => {
this.logger.warn(
request.method,
request.url,
'==>',
`${Date.now() - now}ms`,
`${data ? Buffer.from(data).length : '?'}b`,
);
}),
);
}
}

@ -0,0 +1,49 @@
import {
CallHandler,
ExecutionContext,
Injectable,
NestInterceptor,
} from '@nestjs/common';
import { Observable } from 'rxjs';
import { GloggerService } from '../Gservice/GLOGGER/glogger.service';
import { map } from 'rxjs/operators';
@Injectable()
export class GresponseinterceptorInterceptor implements NestInterceptor {
logger: GloggerService;
constructor() {
this.logger = new GloggerService();
this.logger.setContext(`[Responseinterceptor]`);
}
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
const response = context.switchToHttp().getResponse();
return next.handle().pipe(
map((data) => {
if(!data){
return null
}
// this.logger.debug(response.statusCode);
return JSON.stringify({
data:
typeof data == 'string'
? data
: data.data
? data.data
: data,
statusCode: response.statusCode,
success: data
? data.success === false
? false
: true
: true,
message:
typeof data == 'string'
? 'ok'
: data.message
? data.message
: 'ok',
});
}),
);
}
}

@ -0,0 +1,8 @@
import { Global, Module } from '@nestjs/common';
import { GdatabaseService } from './gdatabase.service';
@Global()
@Module({
providers: [GdatabaseService],
exports: [GdatabaseService],
})
export class GdatabaseModule {}

@ -0,0 +1,29 @@
import { Injectable } from '@nestjs/common';
import * as mysql from 'mysql2/promise';
import config from '@CFG/index';
@Injectable()
export class GdatabaseService {
public DB;
constructor() {
this.start();
}
private async start() {
const DBConfig = config().databases.starLight;
const DB = mysql.createPool({
host: DBConfig.host,
port: DBConfig.port,
user: DBConfig.username,
password: DBConfig.password,
database: DBConfig.database,
connectionLimit: 20, // 用于指定连接池中最大的链接数,默认属性值为10.
multipleStatements: true, //是否允许执行多条sql语句,默认值为false
waitForConnections: true, // 超过最大连接时排队
queueLimit: 0, // 排队最大数量(0 代表不做限制)
maxIdle: 20, // 最大空闲连接数
idleTimeout: 60000, // 空闲连接超时,以毫秒为单位,默认值60000
});
this.DB = DB;
return DB;
}
}

@ -0,0 +1,8 @@
import { Global, Module } from '@nestjs/common';
import { GemailService } from './gemail.service';
@Global()
@Module({
providers: [GemailService],
exports: [GemailService],
})
export class GemailModule {}

@ -0,0 +1,317 @@
import { Injectable } from '@nestjs/common';
import * as nodemailer from 'nodemailer';
import config from '@CFG/index';
@Injectable()
export class GemailService {
// @ 邮件服务
private transporter;
// @ 邮箱配置
private EMAILCONFIG;
// @ 系统名称,作用于邮件主题。
private sysName: string;
constructor() {
this.start();
}
// ? ?
// ? 函数名称: start
// ? 函数描述: 生成邮件服务
// ? ?
private start() {
const EMAILCONFIG = config().email;
const transporter = nodemailer.createTransport({
//node_modules/nodemailer/lib/well-known/services.json 查看相关的配置,如果使用qq邮箱,就查看qq邮箱的相关配置
host: EMAILCONFIG.host,
// secureConnection:true,
service: 'qq', //类型qq邮箱
// port: 465,
secure: true, // true for 465, false for other ports
auth: {
user: EMAILCONFIG.user, // 发送方的邮箱
pass: EMAILCONFIG.pass, // smtp 的授权码
},
//pass 不是邮箱账户的密码而是stmp的授权码(必须是相应邮箱的stmp授权码)
//邮箱---设置--账户--POP3/SMTP服务---开启---获取stmp授权码
});
this.transporter = transporter;
this.EMAILCONFIG = EMAILCONFIG;
this.sysName = config().master.systemName;
}
// ? ?
// ? 函数名称: senMail(邮件内容, 收件人邮箱)
// ? 函数描述: 发送邮件主函数
// ? ?
private senMail(html, mail) {
return new Promise((res, rej) => {
const mailOptions = {
from: '"TOGY | 心曲Tune" <togy.gc@qq.com>', // 发送方
to: mail, //接收者邮箱,多个邮箱用逗号间隔
subject: `${this.sysName}`, // 标题
text: 'Hello world?', // 文本内容
html,
};
this.transporter.sendMail(mailOptions, (error, info) => {
if (error) {
rej([error, info]);
} else {
res(info); //因为是异步 所有需要回调函数通知成功结果
}
});
});
}
// ? ?
// ? 函数名称: sendTestEmail
// ? 函数描述: 给指定邮箱发送测试验证码
// ? ?
public sendTestEmail(email) {
return new Promise(async (res, rej) => {
const test = `
<style>
.emailBody{
position: relative;
min-height: 400px;
min-width: 300px;
height: 100vh;
width:90vw;
display: flex;
align-items: center;
justify-content: center;
margin: 0 auto;
padding: 0;
overflow: overlay;
}
.emailMain{
position:relative;
}
.emailTitle{
line-height: 3em;
font-size: 24px;
font-weight: bold;
text-align: center;
}
.emailTitleLetter{
position:relative;
font-size: 16px;
line-height: 3em;
}
.emailCode{
position:relative;
height: 100px;
width: 100%;
display: flex;
align-items: center;
justify-content: center;
}
.emailCode>div{
position:relative;
box-sizing: border-box;
padding: 1em;
font-size: 30px;
text-align: center;
color: #fefefe;
background-color: #222;
}
.emailDescription{
position:relative;
text-align: center;
padding: 20px 0 ;
line-height: 1.8em;
}
</style>
<div class="emailBody">
<div class="emailMain">
<div class="emailTitle">使${this.sysName}</div>
<div class="emailTitleLetter"></div>
<div class="emailCode">
<div>ABCDEF</div>
</div>
<div class="emailDescription"><br/>5使</div>
</div>
</div>
`;
try {
const resd = await this.senMail(test, email);
res(resd);
} catch (e) {
rej(e);
}
});
}
// ? ?
// ? 函数名称: sendRegisterCodeMail(邮箱, 验证码)
// ? 函数描述: 给制定邮箱发送注册验证码
// ? ?
public sendRegisterCodeMail(email, code) {
return new Promise(async (res, rej) => {
const test = `
<style>
.emailBody{
position: relative;
min-height: 400px;
min-width: 300px;
height: 100%;
width:100%;
display: flex;
align-items: center;
justify-content: center;
margin: 0 auto;
padding: 0;
overflow: overlay;
}
.emailMain{
position:relative;
}
.emailTitle{
line-height: 3em;
font-size: 24px;
font-weight: bold;
text-align: center;
}
.emailTitleLetter{
position:relative;
font-size: 16px;
line-height: 3em;
}
.emailCode{
position:relative;
height: 100px;
width: 100%;
display: flex;
align-items: center;
justify-content: center;
}
.emailCode>div{
position:relative;
box-sizing: border-box;
padding: 1em;
font-size: 30px;
text-align: center;
color: #fefefe;
background-color: #222;
}
.emailDescription{
position:relative;
text-align: center;
padding: 20px 0 ;
line-height: 1.8em;
}
</style>
<div class="emailBody">
<div class="emailMain">
<div class="emailTitle">使${this.sysName}</div>
<div class="emailTitleLetter"></div>
<div class="emailCode">
<div>${code}</div>
</div>
<div class="emailDescription"><br/>5使</div>
</div>
</div>
`;
try {
console.time('EMAIL');
const resd = await this.senMail(test, email);
console.timeEnd('EMAIL');
res(resd);
} catch (e) {
rej({
data: e,
message: '发送邮件失败!',
});
}
});
}
// ? ?
// ? 函数名称: sendSignInCodeMail(邮箱, 验证码)
// ? 函数描述: 给制定邮箱发送注册验证码
// ? ?
public sendSignInCodeMail(email, code) {
return new Promise(async (res, rej) => {
const test = `
<style>
.emailBody{
position: relative;
min-height: 400px;
min-width: 300px;
height: 100%;
width:100%;
display: flex;
align-items: center;
justify-content: center;
margin: 0 auto;
padding: 0;
overflow: overlay;
}
.emailMain{
position:relative;
}
.emailTitle{
line-height: 3em;
font-size: 24px;
font-weight: bold;
text-align: center;
}
.emailTitleLetter{
position:relative;
font-size: 16px;
line-height: 3em;
}
.emailCode{
position:relative;
height: 100px;
width: 100%;
display: flex;
align-items: center;
justify-content: center;
}
.emailCode>div{
position:relative;
box-sizing: border-box;
padding: 1em;
font-size: 30px;
text-align: center;
color: #fefefe;
background-color: #222;
}
.emailDescription{
position:relative;
text-align: center;
padding: 20px 0 ;
line-height: 1.8em;
}
</style>
<div class="emailBody">
<div class="emailMain">
<div class="emailTitle">使${this.sysName}</div>
<div class="emailTitleLetter"></div>
<div class="emailCode">
<div>${code}</div>
</div>
<div class="emailDescription"><br/>5使</div>
</div>
</div>
`;
try {
console.time('EMAIL');
const resd = await this.senMail(test, email);
console.timeEnd('EMAIL');
res(resd);
} catch (e) {
rej({
data: e,
message: '发送邮件失败!',
});
}
});
}
}

@ -0,0 +1,8 @@
import { Global, Module } from '@nestjs/common';
import { GloggerService } from './glogger.service';
@Global()
@Module({
providers: [GloggerService],
exports: [GloggerService],
})
export class GloggerModule {}

@ -0,0 +1,108 @@
import { ConsoleLogger, Injectable, Scope } from '@nestjs/common';
import { configure, getLogger, Logger as log4jsLogger } from 'log4js';
import config from '../../../config';
@Injectable({ scope: Scope.TRANSIENT })
export class GloggerService extends ConsoleLogger {
// 默认日志
defaultLog: log4jsLogger;
// 文件日志
fileLog: log4jsLogger;
context = '';
constructor(context = '') {
super();
const logConfig = config().log;
if (context) {
this.context = context;
}
// this.configService
configure({
appenders: {
default: {
type: 'console',
layout: {
type: 'pattern',
pattern: `%[%d{yyMMdd-hh:mm:ss:SSS} [%p]\t%m%]`,
},
},
file: {
filename: `Log/nestjs.services.log`,
type: 'dateFile',
// 配置 layout,此处使用自定义模式 pattern
layout: {
type: 'pattern',
pattern: '%d{yyMMdd-hh:mm:ss:SSS} [%p]\t%m',
},
// 日志文件按日期(天)切割
pattern: 'yy-MM-dd',
// 回滚旧的日志文件时,保证以 .log 结尾 (只有在 alwaysIncludePattern 为 false 生效)
keepFileExt: true,
// 输出的日志文件名是都始终包含 pattern 日期结尾
alwaysIncludePattern: true,
// 指定日志保留的天数
// daysToKeep: 10,
},
},
categories: {
default: {
appenders: ['default'],
level: logConfig.level || 'all',
enableCallStack: true,
},
fileAll: {
appenders: ['file'],
level: logConfig.level || 'all',
enableCallStack: true,
},
},
});
this.defaultLog = getLogger();
this.fileLog = getLogger('fileAll');
}
log(message: any, ...optionalParams: any[]) {
const context = this.context;
this.defaultLog.info(context, message, ...optionalParams);
this.fileLog.info(context, message, ...optionalParams);
}
info(message: any, ...optionalParams: any[]) {
const context = this.context;
this.defaultLog.info(context, message, ...optionalParams);
this.fileLog.info(context, message, ...optionalParams);
}
error(message: any, ...optionalParams: any[]) {
const context = this.context;
this.defaultLog.error(context, message, ...optionalParams);
this.fileLog.error(context, message, ...optionalParams);
}
warn(message: any, ...optionalParams: any[]) {
// super.warn(message, trace);
const context = this.context;
this.defaultLog.warn(context, message, ...optionalParams);
this.fileLog.warn(context, message, ...optionalParams);
}
debug(message: any, ...optionalParams: any[]) {
// super.debug(message, trace);
const context = this.context;
this.defaultLog.debug(context, message, ...optionalParams);
this.fileLog.debug(context, message, ...optionalParams);
}
verbose(message: any, ...optionalParams: any[]) {
// super.verbose(message, trace);
const context = this.context;
this.defaultLog.info(context, message, ...optionalParams);
this.fileLog.info(context, message, ...optionalParams);
}
setContext(context: string) {
this.context = context;
this.debug('加载日志');
}
}

@ -0,0 +1,8 @@
import { Global, Module } from '@nestjs/common';
import { GredisService } from './gredis.service';
@Global()
@Module({
providers: [GredisService],
exports: [GredisService],
})
export class GredisModule {}

@ -0,0 +1,360 @@
import { Injectable } from '@nestjs/common';
import { createClient } from 'redis';
import config from '../../../config';
import { GtoolsService, HASHT } from '@/Gservice/GTOOLS/gtools.service';
export declare interface ISetRegisterEmailCode {
data: any;
message: string;
registerCode?: string;
}
export declare interface ISetSignInEmailCode {
data: any;
message: string;
signInCode?: string;
}
@Injectable()
export class GredisService {
public DB;
private config;
// @ 专注于注册的数据库
private readonly UserRegisterPoll = 1;
// @ 专注于登录的数据库
private readonly UserSignInPoll = 2;
// @ 登录的相关配置文件
private readonly SignInCFG = config().signIn;
// @ Token相关的配置文件
private readonly TokenCFG = config().token;
constructor(private readonly gtools: GtoolsService) {
this.config = config().redis.starLight;
this.start();
}
//#region 注册
// ? ?
// ? 函数名称: start
// ? 函数描述: 连接redis
// ? ?
private async start() {
const client = createClient({
url: `redis://${this.config.username}:${this.config.password}@${this.config.host}:${this.config.port}/${this.config.dbNumber}`,
});
await client.connect();
this.DB = client;
}
// ? ?
// ? 函数名称: setRegisterEmailCode(邮箱)
// ? 函数描述: 设置注册邮箱验证
// ? ?
public setRegisterEmailCode(email): Promise<ISetRegisterEmailCode> {
return new Promise(async (res, rej) => {
this.DB.select(this.UserRegisterPoll);
try {
const key = 'Register-' + email;
const result = await this.DB.get(key);
if (result === null) {
const registerCode = this.gtools.getRandomString(6);
try {
const data = await this.DB.setEx(
key,
300,
registerCode,
);
res({
data,
registerCode,
message: '生成验证码成功。',
});
} catch (e) {
rej({
data: e,
message: '生成验证码失败。',
});
}
} else {
rej({
data: null,
message: '已发送过邮件,请稍后再重试。',
});
}
} catch (e) {
rej({
data: e,
message: '查找注册码失败。',
});
}
});
}
// ? ?
// ? 函数名称: getRegisterEmailCode(邮箱)
// ? 函数描述: 读取注册验证码
// ? ?
public getRegisterEmailCode(email): Promise<ISetRegisterEmailCode> {
return new Promise(async (res, rej) => {
this.DB.select(this.UserRegisterPoll);
const key = 'Register-' + email;
try {
const result = await this.DB.get(key);
if (result === null) {
rej({
data: null,
message: '未找到相匹配的验证码!',
});
} else {
res({
data: null,
message: '获取成功',
registerCode: result as string,
});
}
} catch (e) {
rej({
data: e,
message: '从Redis获取注册码出现错误!',
});
}
});
}
// ? ?
// ? 函数名称: removeRegisterEmailCode
// ? 函数描述:
// ? ?
public removeRegisterEmailCode(email): Promise<ISetRegisterEmailCode> {
return new Promise(async (res, rej) => {
this.DB.select(this.UserRegisterPoll);
const key = 'Register-' + email;
try {
const result = await this.DB.del(key);
res({
message: '清除注册验证码成功!',
data: {},
});
} catch (e) {
res({
message: '清除注册验证码失败!',
data: e,
});
}
});
}
//#endregion
//#region 密码登录
// ? ?
// ? 函数名称: getSignInErrorNumber
// ? 函数描述: 获取异常登录信息
// ? ?
public async getSignInErrorNumber(uuid) {
const key = 'SIN' + uuid;
this.DB.select(this.UserSignInPoll);
try {
const result = await this.DB.get(key);
if (
result === null ||
Number(result) < this.SignInCFG.signInErrorNumber
) {
return {
state: true,
message: '获取成功',
error: null,
};
} else {
const ttl = await this.DB.ttl(key);
return {
state: false,
message: '登陆异常已达上限,请稍后重试!',
ttl,
error: null,
};
}
} catch (e) {
return {
state: false,
message: '查找登录异常数据出错!',
error: e,
};
}
}
// ? ?
// ? 函数名称: setSignInErrorNumber
// ? 函数描述: 设置用户登陆异常数字
// ? ?
public async setSignInErrorNumber(uuid) {
const key = 'SIN' + uuid;
this.DB.select(this.UserSignInPoll);
try {
const exist = await this.DB.exists(key);
if (exist == 0) {
console.log(key);
await this.DB.set(key, 1);
await this.DB.expire(key, this.SignInCFG.signInErrorTimeout);
return {
error: null,
number: 1,
state: true,
};
} else {
const number = await this.DB.get(key);
await this.DB.set(key, Number(number) + 1);
await this.DB.expire(key, this.SignInCFG.signInErrorTimeout);
return {
error: null,
number: Number(number) + 1,
state: true,
};
}
} catch (e) {
return {
error: e,
message: '设置登陆异常数量出现错误!',
};
}
}
// ? ?
// ? 函数名称: setToken
// ? 函数描述: 设置Token到Redis
// ? ?
public async setToken(uuid, token) {
this.DB.select(this.UserSignInPoll);
const tokenKey =
'TK' + this.gtools.makeHASH(token, HASHT.MD5).slice(0, 32);
const uuidKey = 'UK' + uuid;
await this.DB.setEx(tokenKey, this.TokenCFG.timeout / 1000, uuid);
// 获取此用户的登陆数量
let signInNumber: number;
try {
signInNumber = await this.DB.zCard(uuidKey);
} catch (e) {
return {
state: false,
message: '获取登陆数量出错!',
error: e,
};
}
// 写入Token有序集合
try {
const score = new Date().getTime();
await this.DB.zAdd(uuidKey, [{ score, value: tokenKey }]);
} catch (e) {
return {
state: false,
message: '写入Token有序集合出错!',
error: e,
};
}
// 清除最早的TokenKey和uuidKey
try {
if (signInNumber >= this.SignInCFG.onLineNumber) {
// 超出范围内
// 删除最先的
const uuidKeyZList = await this.DB.zRange(
uuidKey,
0,
signInNumber,
);
// 删除第一个
const popTokenKey = await this.DB.zRemRangeByRank(
uuidKey,
0,
0,
);
// 删除token
const delUuidKey = await this.DB.del(uuidKeyZList[0]);
// 加入新的
}
} catch (e) {
return {
state: false,
message: '清除最早的TokenKey和uuidKey出错!',
error: e,
};
}
return {
data: {
tokenKey,
uuidKey,
},
state: true,
message: '设置成功!',
};
}
// ? ?
// ? 函数名称: setSignInEmailCode(邮箱)
// ? 函数描述: 设置邮箱登录验证
// ? ?
public setSignInEmailCode(email): Promise<ISetSignInEmailCode> {
return new Promise(async (res, rej) => {
this.DB.select(this.UserSignInPoll);
try {
const key = 'SignIn-' + email;
const result = await this.DB.get(key);
if (result === null) {
const signInCode = this.gtools.getRandomString(6);
try {
const data = await this.DB.setEx(key, 300, signInCode);
res({
data,
signInCode,
message: '生成验证码成功。',
});
} catch (e) {
rej({
data: e,
message: '生成验证码失败。',
});
}
} else {
rej({
data: null,
message: '已发送过邮件,请稍后再重试。',
});
}
} catch (e) {
rej({
data: e,
message: '查找登录码失败。',
});
}
});
}
// ? ?
// ? 函数名称: getSignInEmailEntryCode(邮箱)
// ? 函数描述: 读取登录验证码
// ? ?
public getSignInEmailEntryCode(email): Promise<ISetSignInEmailCode> {
return new Promise(async (res, rej) => {
this.DB.select(this.UserSignInPoll);
const key = 'SignIn-' + email;
try {
const result = await this.DB.get(key);
if (result === null) {
rej({
data: null,
message: '未找到相匹配的验证码!',
});
} else {
res({
data: null,
message: '获取成功',
signInCode: result as string,
});
}
} catch (e) {
rej({
data: e,
message: '从Redis获取登录码出现错误!',
});
}
});
}
//#endregion
}

@ -0,0 +1,8 @@
import { Global, Module } from '@nestjs/common';
import { GtoolsService } from './gtools.service';
@Global()
@Module({
providers: [GtoolsService],
exports: [GtoolsService],
})
export class GtoolsModule {}

@ -0,0 +1,137 @@
import { Injectable } from '@nestjs/common';
import * as crypto from 'crypto';
import config from '@CFG/index';
import * as jwt from 'jsonwebtoken';
// @ 不可逆加密
export enum HASHT {
MD5 = 'md5', // 32位
SHA256 = 'sha256', // 64位
SHA512 = 'sha512', // 128位
}
// @ 可逆加密
export enum AEST {
AES128 = 'aes-128-cbc',
AES256 = 'aes-256-gcm',
}
@Injectable()
export class GtoolsService {
// @ 加解密配置文件
private readonly encryptionCFG = config().encryption;
private readonly tokenCFG = config().token;
// @ AES-128-cec加解密 iv和key
private AES128CEC_key;
private AES128CEC_iv;
constructor() {
this.AES128CEC_key = Buffer.from(
this.makeHASH(this.encryptionCFG.secretKey, HASHT.MD5).slice(-16),
'utf8',
);
this.AES128CEC_iv = Buffer.from(
this.makeHASH(this.encryptionCFG.salt, HASHT.MD5).slice(-16),
'utf8',
);
}
// ? ?
// ? 函数名称: getRandomString(长度,大小写默认big)
// ? 函数描述: 获取制定长度的随机字符串
// ? ?
public getRandomString(len: number, size = 'big'): string {
if (size == 'big') {
return Math.random()
.toString(36)
.slice(-len)
.toString()
.toUpperCase();
} else {
return Math.random()
.toString(36)
.slice(-len)
.toString()
.toLowerCase();
}
}
// ? ?
// ? 函数名称: makeUUID
// ? 函数描述: 生成UUID
// ? ?
public makeUUID() {
return crypto.randomUUID().split('-').join('').toUpperCase();
}
// ? ?
// ? 函数名称: makeHASH(加密内容, 加密编码默认为SHA512)
// ? 函数描述: =加密字符串
// ? ?
public makeHASH(plaintext, algorithm = HASHT.SHA512) {
const sha512 = crypto.createHash(algorithm);
const sha512Sum = sha512.update(plaintext + this.encryptionCFG.salt);
const ciphertext = sha512Sum.digest('hex');
return ciphertext;
}
//#region 可逆加密
// ? ?
// ? 函数名称: encrypt
// ? 函数描述: 加密字符串
// ? ?
encrypt(plaintext, algorithm = AEST.AES128) {
const cipher = crypto.createCipheriv(
algorithm,
this.AES128CEC_key,
this.AES128CEC_iv,
); // 初始化加密算法
let ciphertext = cipher.update(plaintext, 'utf8', 'hex');
ciphertext += cipher.final('hex');
return ciphertext;
}
// ? ?
// ? 函数名称: decrypt
// ? 函数描述: 解密字符串
// ? ?
decrypt(ciphertext, algorithm = AEST.AES128) {
let plaintext = '';
const cipher = crypto.createDecipheriv(
algorithm,
this.AES128CEC_key,
this.AES128CEC_iv,
);
plaintext += cipher.update(ciphertext, 'hex', 'utf8');
plaintext += cipher.final('utf8');
return plaintext;
}
//#endredion
//#region jwt
createToken(data) {
const plaintextToken = jwt.sign(data, this.tokenCFG.secretKey, {
expiresIn: this.tokenCFG.timeout / 1000,
});
const ciphertextToken = this.encrypt(plaintextToken);
return ciphertextToken;
}
resolveToken(ciphertextToken) {
try {
// 解析密文
const plaintextToken = this.decrypt(ciphertextToken);
const data = jwt.verify(plaintextToken, this.tokenCFG.secretKey);
return {
token: true,
data,
};
} catch (e) {
return {
token: false,
data: e,
};
}
}
//#endredion
}

@ -0,0 +1,4 @@
import GtoolsService from './gtools.service'
const g = new GtoolsService()

@ -0,0 +1,29 @@
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { GredisModule } from '@/Gservice/GREDIS/gredis.module';
import { GloggerModule } from '@/Gservice/GLOGGER/glogger.module';
import { GdatabaseModule } from '@/Gservice/GDATABASE/gdatabase.module';
import { GemailModule } from '@/Gservice/GEMAIL/gemail.module';
import { GtoolsModule } from '@/Gservice/GTOOLS/gtools.module';
import config from '@CFG/index';
// import { StarlightModule } from '@/starlight/starlight.module';
import { RgvsaleModule } from './rgvsale/rgvsale.module';
@Module({
imports: [
ConfigModule.forRoot({
isGlobal: true, // 作用于全局
load: [config], // 加载自定义配置项
}),
GloggerModule,
// StarlightModule,
GdatabaseModule,
// GredisModule,
GemailModule,
GtoolsModule,
RgvsaleModule,
],
controllers: [],
providers: [],
})
export class AppModule {}

@ -0,0 +1,97 @@
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import {
FastifyAdapter,
NestFastifyApplication,
} from '@nestjs/platform-fastify';
// 参数验证
import { ValidationPipe } from '@nestjs/common';
// Fastify文件上传插件
import fastifyMultipart from '@fastify/multipart';
// 日志服务
import { GloggerService } from '@/Gservice/GLOGGER/glogger.service';
// API文档
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';
// 配置文件
import { ConfigService } from '@nestjs/config';
// 异常捕获
import { GexceptionsfilterFilter } from '@/Gexceptions/gexceptionsfilter.filter';
// 拦截器
// --全局开发拦截
import { GdevinterceptorInterceptor } from '@/Ginterceptor/gdevinterceptor.interceptor';
// --全局响应拦截
import { GresponseinterceptorInterceptor } from '@/Ginterceptor/gresponseinterceptor.interceptor';
async function bootstrap() {
const app = await NestFactory.create<NestFastifyApplication>(
// 加载主程序模块
AppModule,
// 使用Fastify作为底层框架
new FastifyAdapter({
logger: false,
}),
);
// 配置服务
const appConfig = app.get(ConfigService);
// 日志 Log为文件路径
const logger = new GloggerService('[BOOTSTRAP]');
app.useLogger(logger);
// 全局路由前缀
app.setGlobalPrefix('api');
// 全局开启参数验证--需要安装class-validator 又依赖 class-transformer
// app.useGlobalPipes(new ValidationPipe());
app.useGlobalPipes(new ValidationPipe({
transform: true,
}));// 开启参数转换,就不用一个一个写了
// fastify文件上传中间件
await app.register(fastifyMultipart, {
addToBody: true,
throwFileSizeLimit: true,
limits:{
fileSize: 1024 * 1024 * 500
}
});
// 开发者工具
const swaggerState = appConfig.get('swagger').enable;
Swagger(app, swaggerState, logger);
const devInterceptorState = appConfig.get('dev').devInterceptor;
DevInterceptor(app, devInterceptorState, logger);
//全局响应拦截器
app.useGlobalInterceptors(new GresponseinterceptorInterceptor());
// 全局异常捕获
app.useGlobalFilters(new GexceptionsfilterFilter());
// 从配置文件的信息中启动服务
const listenConfig = appConfig.get('master');
await app.listen(listenConfig.port, listenConfig.host);
logger.log(
'🚀 服务应用已经成功启动!',
`http://${listenConfig.host}:${listenConfig.port}`,
);
}
// Swagger接口测试和说明文档
function Swagger(app, state, logger) {
if (!state) return;
const config = new DocumentBuilder()
.setTitle('系统接口文档')
.setDescription('这是一份接口文档')
.setVersion('1.0.0')
.addBearerAuth()
.build();
const document = SwaggerModule.createDocument(app, config);
SwaggerModule.setup('docs', app, document);
logger.warn('Swagger外接程序已加载!');
logger.warn('应用程序文档地址: http://localhost:3000/docs');
}
// DevInterceptorService 输出请求处理时间和响应数据大小的拦截器
function DevInterceptor(app, state, logger) {
// 全局拦截器
if (!state) return;
app.useGlobalInterceptors(new GdevinterceptorInterceptor());
logger.warn('DevInterceptorService请求响应拦截器已开启!');
}
bootstrap();

@ -0,0 +1,126 @@
import { IsNotEmpty, IsNumber, IsOptional, IsString } from 'class-validator';
import { Param, ValidationPipe, Query, HttpException, HttpStatus } from '@nestjs/common';
import { Transform } from 'class-transformer';
// 获取产品账户分页
export class GetAccountPageDto {
// 产品ID
@IsOptional()
@Transform((val) => {
if (Number(val.value)) {
return Number(val.value);
} else if(!val.value){
return undefined
}else {
throw new HttpException('产品ID必须为数字', 400);
}
})
productId: number;
// 页码
@IsOptional()
@Transform((val) => {
if (Number(val.value)) {
return Number(val.value);
} else if(!val.value){
return undefined
} else {
throw new HttpException('pageNum必须为数字', 400);
}
})
pageNum: number;
// 页大小
@IsOptional()
@Transform((val) => {
if (Number(val.value)) {
return Number(val.value);
} else if(!val.value){
return undefined
} else {
throw new HttpException('pageSize必须为数字', 400);
}
})
pageSize: number;
}
// 添加产品账户
export class CreateAccountDto{
// 产品ID
@Transform((val) => {
if (Number(val.value)) {
return Number(val.value);
} else {
throw new HttpException('产品ID必须为数字', 400);
}
})
productId: number;
@IsOptional()
@IsString({
message: '产品账户角色必须为字符串',
})
accountRoleName: string
@IsString({
message: '产品账户用户名必须为字符串',
})
accountUsername: string
@IsString({
message: '产品账户密码必须为字符串',
})
accountPassword: string
}
// 编辑产品账户
export class EditAccountDto{
// 账户ID
@Transform((val) => {
if (Number(val.value)) {
return Number(val.value);
} else {
throw new HttpException('产品账户ID必须为数字', 400);
}
})
accountId: number;
@Transform((val) => {
if (Number(val.value)) {
return Number(val.value);
} else if(!val.value){
return undefined
}else {
throw new HttpException('产品ID必须为数字', 400);
}
})
productId: number;
@IsOptional()
@IsString({
message: '产品账户角色必须为字符串',
})
accountRoleName: string
@IsString({
message: '产品账户用户名必须为字符串',
})
accountUsername: string
@IsString({
message: '产品账户密码必须为字符串',
})
accountPassword: string
}
// 删除产品账户
export class DeleteAccountDto{
@Transform((val) => {
if (Number(val.value)) {
return Number(val.value);
} else {
throw new HttpException('产品账户ID必须为数字', 400);
}
})
accountId: Number;
}

@ -0,0 +1,156 @@
import { IsNotEmpty, IsNumber, IsOptional, IsString } from 'class-validator';
import { Param, ValidationPipe, Query, HttpException, HttpStatus } from '@nestjs/common';
import { Transform } from 'class-transformer';
// 获取演示项分页
export class GetDemoItemPageDto {
// 产品ID
@IsOptional()
@Transform((val) => {
if (Number(val.value)) {
return Number(val.value);
} else if(!val.value){
return undefined
}else {
throw new HttpException('产品ID必须为数字', 400);
}
})
productId: number;
// 页码
@IsOptional()
@Transform((val) => {
if (Number(val.value)) {
return Number(val.value);
} else if(!val.value){
return undefined
} else {
throw new HttpException('pageNum必须为数字', 400);
}
})
pageNum: number;
// 页大小
@IsOptional()
@Transform((val) => {
if (Number(val.value)) {
return Number(val.value);
} else if(!val.value){
return undefined
} else {
throw new HttpException('pageSize必须为数字', 400);
}
})
pageSize: number;
}
export class GetDemoItemListDto {
// 产品ID
@IsOptional()
@Transform((val) => {
if (Number(val.value)) {
return Number(val.value);
} else if(!val.value){
return undefined
}else {
throw new HttpException('产品ID必须为数字', 400);
}
})
productId: number;
}
// 添加演示项
export class CreateDemoItemDto{
// 产品ID
@Transform((val) => {
if (Number(val.value)) {
return Number(val.value);
} else {
throw new HttpException('产品ID必须为数字', 400);
}
})
productId: number;
@IsOptional()
@IsString({
message: '演示项名称必须为字符串',
})
itemName: string
@Transform((val) => {
const filepathList = val.value.split('/rgvsale/staticFile/')
if(filepathList.length == 2){
let fileId = filepathList[1]
if (Number(fileId)) {
return Number(fileId);
} else {
throw new HttpException('演示项图解地址格式不正确', 400);
}
}else{
throw new HttpException('演示项图解地址格式不正确', 400);
}
})
itemUrl: Number
}
// 编辑演示项
export class EditDemoItemDto{
// 产品ID
@IsNotEmpty({
message: '演示项Id不能为空',
})
@Transform((val) => {
if (Number(val.value)) {
return Number(val.value);
} else {
throw new HttpException('产品ID必须为数字', 400);
}
})
productId: number;
// 产品ID
@Transform((val) => {
if (Number(val.value)) {
return Number(val.value);
} else {
throw new HttpException('演示项ID必须为数字', 400);
}
})
itemId: number;
@IsString({
message: '演示项名称必须为字符串',
})
itemName: string
@Transform((val) => {
const filepathList = val.value.split('/rgvsale/staticFile/')
if(filepathList.length == 2){
let fileId = filepathList[1]
if (Number(fileId)) {
return Number(fileId);
} else {
throw new HttpException('演示项图解地址格式不正确', 400);
}
}else{
throw new HttpException('演示项图解地址格式不正确', 400);
}
})
itemUrl: Number
}
// 删除演示项
export class DeleteDemoItemDto{
@IsNotEmpty({
message: '演示项Id不能为空',
})
@Transform((val) => {
if (Number(val.value)) {
return Number(val.value);
} else {
throw new HttpException('演示项ID必须为数字', 400);
}
})
itemId: Number;
}

@ -0,0 +1,177 @@
import {
ArrayMinSize,
IsArray, IsDate,
IsEnum,
IsMilitaryTime,
IsNotEmpty,
IsNumber,
IsOptional,
IsString,
} from 'class-validator';
import { Param, ValidationPipe, Query, HttpException, HttpStatus } from '@nestjs/common';
import { Transform, Type } from 'class-transformer';
export enum sex {
'上' = 0,
'下' = 1,
}
// 添加演示方案
export class CreateProgrammeDto{
@IsString({
message: '方案名称必须为字符串',
})
programmeName: string
@IsNotEmpty({
message: '产品ID不能为空',
})
@Transform((val) => {
if (Number(val.value)) {
return Number(val.value);
} else {
throw new HttpException('产品ID必须为数字', 400);
}
})
productId: Number;
@IsNotEmpty({
message: '行业ID不能为空',
})
@Transform((val) => {
if (Number(val.value)) {
return Number(val.value);
} else {
throw new HttpException('行业ID必须为数字', 400);
}
})
industryId: Number;
@ArrayMinSize(1, { message: '演示项至少有一个' })
// 文档建议如果是字符串的数组,使用字符串约束更好,因为js其实不存在数字数组,字符串数组等
@Type(() => Number)
// 上面虽然解决了不是字符串的数组的问题,但是如果传进来的是一个字符串呢?这就太tm难了,所以再在编译时检查一下算了吧,运行时不管了
@IsArray({ message: '演示项列表必须是一个数组' })
@Transform((val) => {
const newList = []
val.value.map(i => {
const newNum = Number(i)
if (newNum) {
if(isNaN(newNum)) throw new HttpException('演示项列表必须为数字', 400)
newList.push(newNum)
} else {
throw new HttpException('演示项列表必须为数字', 400);
}
})
console.log(newList);
return newList
})
demoItemsList: Number[]
}
// 创建
export class CreateScenesDto{
// 方案ID
@IsNotEmpty({
message: '方案ID不能为空',
})
@Transform((val) => {
if (Number(val.value)) {
return Number(val.value);
} else {
throw new HttpException('方案ID必须为数字', 400);
}
})
programmeId: Number;
// 预计开始时间
@Type(() => Date)
@IsDate({
message: '预计开始时间格式不正确'
})
@IsNotEmpty({
message: '预计开始时间不能为空',
})
expectedStarttime: string;
// 目标客户
@IsString()
@IsNotEmpty({
message: '目标客户不能为空',
})
targetCustomers: string;
}
// 删除现场
export class DeleteSceneDto{
@IsNotEmpty({
message: '演示现场Id不能为空',
})
@Transform((val) => {
if (Number(val.value)) {
return Number(val.value);
} else {
throw new HttpException('演示现场ID必须为数字', 400);
}
})
sceneId: Number;
}
// 编辑场景信息
export class EditSceneDto{
@IsNotEmpty({
message: '演示现场Id不能为空',
})
@Transform((val) => {
if (Number(val.value)) {
return Number(val.value);
} else {
throw new HttpException('演示现场ID必须为数字', 400);
}
})
sceneId: Number;
// 预计开始时间
@IsMilitaryTime()
@IsNotEmpty({
message: '预计开始时间不能为空',
})
expectedStarttime: string;
// 目标客户
@IsString()
@IsNotEmpty({
message: '目标客户不能为空',
})
targetCustomers: string;
}
// 增加演示项点击次数
export class AddDemoItemAccumulateForSceneDto{
@IsNotEmpty({
message: '演示项Id不能为空',
})
@Transform((val) => {
if (Number(val.value)) {
return Number(val.value);
} else {
throw new HttpException('演示项ID必须为数字', 400);
}
})
demoItemId: Number;
@IsNotEmpty({
message: '演示现场Id不能为空',
})
@Transform((val) => {
if (Number(val.value)) {
return Number(val.value);
} else {
throw new HttpException('演示现场ID必须为数字', 400);
}
})
sceneId: Number;
}

@ -0,0 +1,259 @@
import { IsNotEmpty, IsNumber, IsOptional, IsString } from 'class-validator';
import { Param, ValidationPipe, Query, HttpException, HttpStatus } from '@nestjs/common';
import { Transform } from 'class-transformer';
// 获取演示问题分页
export class GetScenesProblemPageDto{
// 产品ID
@Transform((val) => {
if (Number(val.value)) {
return Number(val.value);
} else if(!val.value){
return undefined
}else {
throw new HttpException('产品ID必须为数字', 400);
}
})
productId: number;
// 行业ID
@Transform((val) => {
if (Number(val.value)) {
return Number(val.value);
} else if(!val.value){
return undefined
}else {
throw new HttpException('行业ID必须为数字', 400);
}
})
industryId: number;
// 方案ID
@Transform((val) => {
if (Number(val.value)) {
return Number(val.value);
} else if(!val.value){
return undefined
}else {
throw new HttpException('方案ID必须为数字', 400);
}
})
programmeId: number;
// 现场ID
@Transform((val) => {
if (Number(val.value)) {
return Number(val.value);
} else if(!val.value){
return undefined
}else {
throw new HttpException('现场ID必须为数字', 400);
}
})
sceneId: number;
// 模糊查询
@IsOptional()
@IsString({
message: '客户角色必须为字符串',
})
search: string;
// 利用状态
@IsOptional()
@Transform((val) => {
if (Number(val.value) || Number(val.value) === 0) {
return Number(val.value);
} else if(!val.value){
return undefined
}else if(Number(val.value) !== 0){
throw new HttpException('利用状态必须为数字', 400);
}
})
isUse: number;
// 页码
@IsOptional()
@Transform((val) => {
if (Number(val.value)) {
return Number(val.value);
} else if(!val.value){
return undefined
} else {
throw new HttpException('pageNum必须为数字', 400);
}
})
pageNum: number;
// 页大小
@IsOptional()
@Transform((val) => {
if (Number(val.value)) {
return Number(val.value);
} else if(!val.value){
return undefined
} else {
throw new HttpException('pageSize必须为数字', 400);
}
})
pageSize: number;
}
// 新增演示问题
export class CreateScenesProblemDto{
// 问题内容
@IsString({
message: '问题内容必须为字符串',
})
problemContent: string;
// 演示现场ID
@IsNotEmpty({
message: '演示现场ID不能为空',
})
@Transform((val) => {
if (Number(val.value)) {
return Number(val.value);
} else if(!val.value){
return undefined
}else {
throw new HttpException('演示现场ID必须为数字', 400);
}
})
sceneId: number;
// 演示项ID
@IsNotEmpty({
message: '演示现项ID不能为空',
})
@Transform((val) => {
if (Number(val.value)) {
return Number(val.value);
} else if(!val.value){
return undefined
}else {
throw new HttpException('演示项ID必须为数字', 400);
}
})
demoItemId: number;
// 客户角色
@IsOptional()
@IsString({
message: '客户角色必须为字符串',
})
targetCustomersRole: string;
// 客户名称
@IsString({
message: '客户名称必须为字符串',
})
targetCustomers: string;
}
// 编辑演示问题
export class EditScenesProblemDto{
// 问题内容
@IsString({
message: '问题内容必须为字符串',
})
problemContent: string;
// 问题ID
@IsNotEmpty({
message: '演示现场ID不能为空',
})
@Transform((val) => {
if (Number(val.value)) {
return Number(val.value);
} else if(!val.value){
return undefined
}else {
throw new HttpException('演示现场ID必须为数字', 400);
}
})
problemId: number;
// 客户角色
@IsOptional()
@IsString({
message: '客户角色必须为字符串',
})
targetCustomersRole: string;
// 客户名称
@IsString({
message: '客户名称必须为字符串',
})
targetCustomers: string;
// 演示现场ID
@IsNotEmpty({
message: '演示现场ID不能为空',
})
@Transform((val) => {
if (Number(val.value)) {
return Number(val.value);
} else if(!val.value){
return undefined
}else {
throw new HttpException('演示现场ID必须为数字', 400);
}
})
sceneId: number;
// 演示项ID
@IsNotEmpty({
message: '演示现项ID不能为空',
})
@Transform((val) => {
if (Number(val.value)) {
return Number(val.value);
} else if(!val.value){
return undefined
}else {
throw new HttpException('演示项ID必须为数字', 400);
}
})
demoItemId: number;
}
// 删除演示问题
export class DeleteScenesProblemDto{
// 问题ID
@IsNotEmpty({
message: '问题ID不能为空',
})
@Transform((val) => {
if (Number(val.value)) {
return Number(val.value);
} else if(!val.value){
return undefined
}else {
throw new HttpException('问题ID必须为数字', 400);
}
})
problemId: number;
}
// 修改问题利用状态
export class ChangeUseStateDto{
// 问题ID
@IsNotEmpty({
message: '问题ID不能为空',
})
@Transform((val) => {
if (Number(val.value)) {
return Number(val.value);
} else if(!val.value){
return undefined
}else {
throw new HttpException('问题ID必须为数字', 400);
}
})
problemId: number;
}

@ -0,0 +1,171 @@
import { IsNotEmpty, IsNumber, IsOptional, IsString } from 'class-validator';
import { Param, ValidationPipe, Query, HttpException, HttpStatus } from '@nestjs/common';
import { Transform } from 'class-transformer';
// 获取行业分页
export class GetIndustryPageDto {
// 产品ID
@IsOptional()
@Transform((val) => {
if (Number(val.value)) {
return Number(val.value);
} else if(!val.value){
return undefined
}else {
throw new HttpException('行业ID必须为数字', 400);
}
})
productId: number;
// 页码
@IsOptional()
@Transform((val) => {
if (Number(val.value)) {
return Number(val.value);
} else if(!val.value){
return undefined
} else {
throw new HttpException('pageNum必须为数字', 400);
}
})
pageNum: number;
// 页大小
@IsOptional()
@Transform((val) => {
if (Number(val.value)) {
return Number(val.value);
} else if(!val.value){
return undefined
} else {
throw new HttpException('pageSize必须为数字', 400);
}
})
pageSize: number;
}
// 获取行业下拉列表
export class GetIndustryListDto{
// 产品ID
@IsOptional()
@Transform((val) => {
if (Number(val.value)) {
return Number(val.value);
} else if(!val.value){
return undefined
}else {
throw new HttpException('行业ID必须为数字', 400);
}
})
productId: number;
}
// 添加行业
export class CreateIndustryDto{
// 行业ID
@Transform((val) => {
if (Number(val.value)) {
return Number(val.value);
} else if(!val.value){
return undefined
}else {
throw new HttpException('行业ID必须为数字', 400);
}
})
productId: number;
// 行业名称
@IsString({
message: '行业名称必须为字符串',
})
industryName: string
// 行业描述
@IsOptional()
@IsString({
message: '行业描述必须为字符串',
})
industryDescription: string
// 数据库文件
@Transform((val) => {
const filepathList = val.value.split('/rgvsale/staticFile/')
if(filepathList.length == 2){
let fileId = filepathList[1]
if (Number(fileId)) {
return Number(fileId);
} else {
throw new HttpException('数据库文件地址格式不正确', 400);
}
}else{
throw new HttpException('数据库文件地址格式不正确', 400);
}
})
sqlId: Number
}
// 编辑行业
export class EditIndustryDto{
@Transform((val) => {
if (Number(val.value)) {
return Number(val.value);
} else {
throw new HttpException('行业ID必须为数字', 400);
}
})
industryId: Number;
// 行业ID
@Transform((val) => {
if (Number(val.value)) {
return Number(val.value);
} else if(!val.value){
return undefined
}else {
throw new HttpException('行业ID必须为数字', 400);
}
})
productId: number;
// 行业名称
@IsString({
message: '行业名称必须为字符串',
})
industryName: string
// 行业描述
@IsOptional()
@IsString({
message: '行业描述必须为字符串',
})
industryDescription: string
// 数据库文件
@Transform((val) => {
const filepathList = val.value.split('/rgvsale/staticFile/')
if(filepathList.length == 2){
let fileId = filepathList[1]
if (Number(fileId)) {
return Number(fileId);
} else {
throw new HttpException('数据库文件地址格式不正确', 400);
}
}else{
throw new HttpException('数据库文件地址格式不正确', 400);
}
})
sqlId: Number
}
// 删除行业
export class DeleteIndustryDto{
@Transform((val) => {
if (Number(val.value)) {
return Number(val.value);
} else {
throw new HttpException('行业ID必须为数字', 400);
}
})
industryId: Number;
}

@ -0,0 +1,91 @@
import { IsNotEmpty, IsNumber, IsOptional, IsString } from 'class-validator';
import { Param, ValidationPipe, Query, HttpException, HttpStatus } from '@nestjs/common';
import { Transform } from 'class-transformer';
// 获取产品分页
export class GetProductPageDto {
// 页码
@IsOptional()
@Transform((val) => {
if (Number(val.value)) {
return Number(val.value);
} else if(!val.value){
return undefined
} else {
throw new HttpException('pageNum必须为数字', 400);
}
})
pageNum: number;
// 页大小
@IsOptional()
@Transform((val) => {
if (Number(val.value)) {
return Number(val.value);
} else if(!val.value){
return undefined
} else {
throw new HttpException('pageSize必须为数字', 400);
}
})
pageSize: number;
}
// 添加产品
export class CreateProductDto{
@IsString({
message: '产品名称必须为字符串',
})
productName: string
@IsOptional()
@IsString({
message: '产品描述必须为字符串',
})
productDescription: string
@IsString({
message: '产品链接必须为字符串',
})
productsUrl: string
}
// 编辑产品
export class EditProductDto{
@Transform((val) => {
if (Number(val.value)) {
return Number(val.value);
} else {
throw new HttpException('产品ID必须为数字', 400);
}
})
productId: Number;
@IsString({
message: '产品名称必须为字符串',
})
productName: string
@IsOptional()
@IsString({
message: '产品描述必须为字符串',
})
productDescription: string
@IsString({
message: '产品链接必须为字符串',
})
productUrl: string
}
// 删除产品
export class DeleteProductDto{
@Transform((val) => {
if (Number(val.value)) {
return Number(val.value);
} else {
throw new HttpException('产品ID必须为数字', 400);
}
})
productId: Number;
}

@ -0,0 +1,300 @@
import { ArrayMinSize, IsArray, IsEnum, IsNotEmpty, IsNumber, IsOptional, IsString } from 'class-validator';
import { Param, ValidationPipe, Query, HttpException, HttpStatus } from '@nestjs/common';
import { Transform, Type } from 'class-transformer';
export enum sex {
'上' = 0,
'下' = 1,
}
// 添加演示方案
export class CreateProgrammeDto{
@IsString({
message: '方案名称必须为字符串',
})
programmeName: string
@IsNotEmpty({
message: '产品ID不能为空',
})
@Transform((val) => {
if (Number(val.value)) {
return Number(val.value);
} else {
throw new HttpException('产品ID必须为数字', 400);
}
})
productId: Number;
@IsNotEmpty({
message: '行业ID不能为空',
})
@Transform((val) => {
if (Number(val.value)) {
return Number(val.value);
} else {
throw new HttpException('行业ID必须为数字', 400);
}
})
industryId: Number;
@ArrayMinSize(1, { message: '演示项至少有一个' })
// 文档建议如果是字符串的数组,使用字符串约束更好,因为js其实不存在数字数组,字符串数组等
@Type(() => Number)
// 上面虽然解决了不是字符串的数组的问题,但是如果传进来的是一个字符串呢?这就太tm难了,所以再在编译时检查一下算了吧,运行时不管了
@IsArray({ message: '演示项列表必须是一个数组' })
@Transform((val) => {
const newList = []
val.value.map(i => {
const newNum = Number(i)
if (newNum) {
if(isNaN(newNum)) throw new HttpException('演示项列表必须为数字', 400)
newList.push(newNum)
} else {
throw new HttpException('演示项列表必须为数字', 400);
}
})
console.log(newList);
return newList
})
demoItemsList: Number[]
}
// 编辑演示方案
export class EditProgrammeDto{
@IsNotEmpty({
message: '产品ID不能为空',
})
@Transform((val) => {
if (Number(val.value)) {
return Number(val.value);
} else {
throw new HttpException('产品ID必须为数字', 400);
}
})
productId: Number;
@IsNotEmpty({
message: '方案ID不能为空',
})
@Transform((val) => {
if (Number(val.value)) {
return Number(val.value);
} else {
throw new HttpException('方案ID必须为数字', 400);
}
})
programmeId: Number;
@IsNotEmpty({
message: '行业ID不能为空',
})
@Transform((val) => {
if (Number(val.value)) {
return Number(val.value);
} else {
throw new HttpException('行业ID必须为数字', 400);
}
})
industryId: Number;
@IsNotEmpty({
message: '产品名称不能为空',
})
@IsString({
message: '产品名称必须为字符串',
})
programmeName: string
@ArrayMinSize(1, { message: '演示项至少有一个' })
// 文档建议如果是字符串的数组,使用字符串约束更好,因为js其实不存在数字数组,字符串数组等
@Type(() => Number)
// 上面虽然解决了不是字符串的数组的问题,但是如果传进来的是一个字符串呢?这就太tm难了,所以再在编译时检查一下算了吧,运行时不管了
@IsArray({ message: '演示项列表必须是一个数组' })
@Transform((val) => {
const newList = []
val.value.map(i => {
const newNum = Number(i)
if (newNum) {
if(isNaN(newNum)) throw new HttpException('演示项列表必须为数字', 400)
newList.push(newNum)
} else {
throw new HttpException('演示项列表必须为数字', 400);
}
})
return newList
})
demoItemsList: Number[]
}
// 删除产品
export class DeleteProgrammeDto{
@Transform((val) => {
if (Number(val.value)) {
return Number(val.value);
} else {
throw new HttpException('方案ID必须为数字', 400);
}
})
programmeId: Number;
}
// 排序演示项
export class EditDemoItemsListRankDto{
@Transform((val) => {
if (Number(val.value)) {
return Number(val.value);
} else {
throw new HttpException('方案ID必须为数字', 400);
}
})
programmeId: Number;
@Transform((val) => {
if (Number(val.value)) {
return Number(val.value);
} else {
throw new HttpException('演示项ID必须为数字', 400);
}
})
demoItemId: Number;
@IsEnum(sex, { message: '顺序参数必须为:' + Object.keys(sex) })
offset: Number;
}
// 获取我的演示方案分页
export class GetProgrammePageDto{
// 产品ID
@IsOptional()
@Transform((val) => {
if (Number(val.value)) {
return Number(val.value);
} else if(!val.value){
return undefined
}else {
throw new HttpException('产品ID必须为数字', 400);
}
})
productId: number;
// 行业Id
@IsOptional()
@Transform((val) => {
if (Number(val.value)) {
return Number(val.value);
} else if(!val.value){
return undefined
}else {
throw new HttpException('行业ID必须为数字', 400);
}
})
industryId: number;
// 页码
@IsOptional()
@Transform((val) => {
if (Number(val.value)) {
return Number(val.value);
} else if(!val.value){
return undefined
} else {
throw new HttpException('pageNum必须为数字', 400);
}
})
pageNum: number;
// 页大小
@IsOptional()
@Transform((val) => {
if (Number(val.value)) {
return Number(val.value);
} else if(!val.value){
return undefined
} else {
throw new HttpException('pageSize必须为数字', 400);
}
})
pageSize: number;
}
// 获取方案的演示项分页
export class GetDemoItemsPageForProgrammeDto{
// 页码
@IsOptional()
@Transform((val) => {
if (Number(val.value)) {
return Number(val.value);
} else if(!val.value){
return undefined
} else {
throw new HttpException('pageNum必须为数字', 400);
}
})
pageNum: number;
// 页大小
@IsOptional()
@Transform((val) => {
if (Number(val.value)) {
return Number(val.value);
} else if(!val.value){
return undefined
} else {
throw new HttpException('pageSize必须为数字', 400);
}
})
pageSize: number;
// 方案Id
@IsNotEmpty({
message: '方案Id不能为空',
})
@Transform((val) => {
if (Number(val.value)) {
return Number(val.value);
} else if(!val.value){
return undefined
}else {
throw new HttpException('方案ID必须为数字', 400);
}
})
programmeId: number;
}
// 删除方案演示项
export class DeleteDemoItemForProgrammeDto{
// 方案Id
@IsNotEmpty({
message: '方案Id不能为空',
})
@Transform((val) => {
if (Number(val.value)) {
return Number(val.value);
} else if(!val.value){
return undefined
}else {
throw new HttpException('方案ID必须为数字', 400);
}
})
programmeId: number;
// 演示项ID
@IsNotEmpty({
message: '演示项Id不能为空',
})
@Transform((val) => {
if (Number(val.value)) {
return Number(val.value);
} else if(!val.value){
return undefined
}else {
throw new HttpException('演示项ID必须为数字', 400);
}
})
demoItemId: number;
}

@ -0,0 +1,15 @@
import { IsNotEmpty, IsNumber, IsOptional, IsString } from 'class-validator';
export class UserInfoDto {
// 用户ID
@IsString({
message: 'headers:userId必须为字符串',
})
userId: string;
// 用户ID
@IsString({
message: 'headers:token必须为字符串',
})
token: string;
}

@ -0,0 +1,320 @@
import {
Controller,
Get,
Post,
Body,
Patch,
Param,
Delete,
Query,
ValidationPipe,
Req,
Res,
Response,
HttpStatus, HttpException,
} from '@nestjs/common';
import { FastifyRequest } from 'fastify';
import { RgvsaleService } from './rgvsale.service';
import { GetProductPageDto, CreateProductDto, EditProductDto, DeleteProductDto } from './dto/product.dto';
import { getUserinfo, userinfoDto } from '@/Gdecorator/userinfoDecorator/userinfoDecorator.decorator';
import { UserInfoDto } from './dto/userInfo.dto';
import { CreateAccountDto, EditAccountDto, DeleteAccountDto, GetAccountPageDto } from '@/rgvsale/dto/account.dto';
import {
CreateDemoItemDto,
DeleteDemoItemDto,
EditDemoItemDto,
GetDemoItemListDto,
GetDemoItemPageDto,
} from '@/rgvsale/dto/demoItem.dto';
import {
CreateIndustryDto,
DeleteIndustryDto,
EditIndustryDto,
GetIndustryListDto,
GetIndustryPageDto,
} from '@/rgvsale/dto/industry.dto';
import {
CreateProgrammeDto, DeleteDemoItemForProgrammeDto,
DeleteProgrammeDto,
EditDemoItemsListRankDto,
EditProgrammeDto, GetDemoItemsPageForProgrammeDto, GetProgrammePageDto,
} from '@/rgvsale/dto/productsProgramme.dto';
import { AddDemoItemAccumulateForSceneDto, CreateScenesDto, DeleteSceneDto } from '@/rgvsale/dto/demoScenes.dto';
import {
ChangeUseStateDto,
CreateScenesProblemDto,
DeleteScenesProblemDto, EditScenesProblemDto,
GetScenesProblemPageDto,
} from '@/rgvsale/dto/demoScenesProblem.dto';
@Controller('rgvsale')
export class RgvsaleController {
constructor(private readonly rgvsaleService: RgvsaleService) {
}
//#region 产品
// 分页获取产品列表
// , @getUserinfo() userInfo: UserInfoDto
@Get('/products/getPage')
getProductPage(@Query(new ValidationPipe({ transform: true })) query: GetProductPageDto, @getUserinfo() userInfo: UserInfoDto): Promise<object> {
return this.rgvsaleService.getProductPage(query);
}
// 获取产品下拉菜单
@Get('/products/getList')
getProductList(): Promise<object> {
return this.rgvsaleService.getProductList();
}
// 新增产品
@Post('/products/createProduct')
createProduct(@Body() body: CreateProductDto, @getUserinfo() userInfo: UserInfoDto): Promise<object> {
return this.rgvsaleService.createProduct(body, userInfo);
}
// 删除产品
@Delete('/products/deleteProduct')
deleteProduct(@Body() body: DeleteProductDto): Promise<object> {
return this.rgvsaleService.deleteProduct(body);
}
// 编辑产品
@Patch('/products/editProduct')
editProduct(@Body() body: EditProductDto): Promise<object> {
return this.rgvsaleService.editProduct(body);
}
//#endregion
//#region 账户
//分页获取账户列表
@Get('/productsAccount/getPage')
getAccountPage(@Query(new ValidationPipe({ transform: true })) query: GetAccountPageDto): Promise<object> {
return this.rgvsaleService.getAccountPage(query);
}
// 创建账户
@Post('/productsAccount/createAccount')
createAccount(@Body() body: CreateAccountDto, @getUserinfo() userInfo: UserInfoDto): Promise<object> {
return this.rgvsaleService.createAccount(body, userInfo);
}
// 删除账户
@Delete('/productsAccount/deleteAccount')
deleteAccount(@Body() body: DeleteAccountDto): Promise<object> {
return this.rgvsaleService.deleteAccount(body);
}
// 编辑账户
@Patch('/productsAccount/editAccount')
editAccount(@Body() body: EditAccountDto): Promise<object> {
return this.rgvsaleService.editAccount(body);
}
//#endregion
//#region 演示项
// 分页查询演示项列表
@Get('/demoItems/getPage')
getDemoPage(@Query(new ValidationPipe({ transform: true })) query: GetDemoItemPageDto, @getUserinfo() userInfo: UserInfoDto): Promise<object> {
return this.rgvsaleService.getDemoPage(query);
}
// 获取产品演示项列表
@Get('/demoItems/getList')
getDemoList(@Query() params: GetDemoItemListDto){
return this.rgvsaleService.getDemoList(params)
}
// 添加演示项
@Post('/demoItems/createItem')
createDemo(@Body() body: CreateDemoItemDto, @getUserinfo() userInfo: UserInfoDto) {
return this.rgvsaleService.createDemo(body, userInfo);
}
// 删除演示项
@Delete('/demoItems/deleteItem')
deleteDemo(@Body() body: DeleteDemoItemDto) {
return this.rgvsaleService.deleteDemo(body);
}
// 编辑演示项
@Patch('/demoItems/editItem')
editDemo(@Body() body: EditDemoItemDto) {
return this.rgvsaleService.editDemo(body);
}
//#endregion
//#region 文件
// 文件上传
@Post('/upload')
upload(@Body() body, @getUserinfo() userInfo: UserInfoDto): Promise<object> {
return this.rgvsaleService.upload(body, userInfo);
}
// 文件下载
@Get('download/:id')
async download(@Param('id') id: string, @Res() res) {
return this.rgvsaleService.download(id, res)
}
// 静态文件
@Get('staticFile/:id')
async staticFile(@Param('id') id: string, @Res() res) {
return this.rgvsaleService.download(id, res)
}
//#endregion
//#region 行业
//分页获取行业列表
@Get('/productsIndustry/getPage')
getIndustryPage(@Query() params: GetIndustryPageDto, @getUserinfo() userInfo: UserInfoDto) {
return this.rgvsaleService.getIndustryPage(params, userInfo)
}
// 获取行业下拉列表
@Get('/productsIndustry/getList')
getIndustryList(@Query() params: GetIndustryListDto){
return this.rgvsaleService.getIndustryList(params)
}
// 创建行业
@Post('/productsIndustry/createIndustry')
createIndustry(@Body() body: CreateIndustryDto, @getUserinfo() userInfo: UserInfoDto) {
return this.rgvsaleService.createIndustry(body, userInfo)
}
// 删除行业
@Delete('/productsIndustry/deleteIndustry')
deleteIndustry(@Body() body: DeleteIndustryDto) {
return this.rgvsaleService.deleteIndustry(body)
}
// 编辑行业
@Patch('/productsIndustry/editIndustry')
editIndustry(@Body() body: EditIndustryDto) {
return this.rgvsaleService.editIndustry(body)
}
//#endregion
//#region 演示项
// 获取演示方案树
@Get('/productsProgramme/getTree')
getProductsProgrammeTree(@getUserinfo() userInfo: UserInfoDto){
return this.rgvsaleService.getProductsProgrammeTree(userInfo)
}
// 新增演示方案
@Post('/productsProgramme/createProgramme')
createProductsProgramme(@Body() body: CreateProgrammeDto, @getUserinfo() userInfo: UserInfoDto){
return this.rgvsaleService.createProductsProgramme(body, userInfo)
}
// 删除演示方案
@Delete('/productsProgramme/deleteProgramme')
deleteProductsProgramme(@Body() body: DeleteProgrammeDto){
return this.rgvsaleService.deleteProductsProgramme(body)
}
// 编辑演示方案
@Patch('/productsProgramme/editProgramme')
editProductsProgramme(@Body() body: EditProgrammeDto, @getUserinfo() userInfo: UserInfoDto){
return this.rgvsaleService.editProductsProgramme(body, userInfo)
}
// 排序方案演示项
@Patch('/productsProgramme/editDemoItemsListRank')
editProductsProgrammeForDemoItemsListRank(@Body() body: EditDemoItemsListRankDto){
return this.rgvsaleService.editProductsProgrammeForDemoItemsListRank(body)
}
// 获取个人方案分页
@Get('/productsProgramme/getPage')
getProductsProgrammePage(@Query() params: GetProgrammePageDto, @getUserinfo() userInfo: UserInfoDto){
return this.rgvsaleService.getProductsProgrammePage(params, userInfo)
}
// 获取方案演示项分页
@Get('/productsProgramme/getDemoItemPageForProgramme')
getDemoItemPageForProgramme(@Query() params: GetDemoItemsPageForProgrammeDto){
return this.rgvsaleService.getDemoItemPageForProgramme(params)
}
// 获取方案演示项列表
@Get('/productsProgramme/getDemoItemListForProgramme')
getDemoItemListForProgramme(@Query() params: GetDemoItemsPageForProgrammeDto){
return this.rgvsaleService.getDemoItemListForProgramme(params)
}
// 获取我的方案列表
@Get('/productsProgramme/getList')
getProductsProgrammeList(@getUserinfo() userInfo: UserInfoDto){
return this.rgvsaleService.getProductsProgrammeList(userInfo)
}
// 删除方案演示项
@Delete('/productsProgramme/deleteDemoItemForProgramme')
deleteDemoItemForProgramme(@Body() body: DeleteDemoItemForProgrammeDto){
return this.rgvsaleService.deleteDemoItemForProgramme(body)
}
//#endregion
//#region 演示现场 场景
// 获取我的演示现场
@Get('/demoScenes/getList')
getSceneListAndTree(@getUserinfo() userInfo: UserInfoDto){
return this.rgvsaleService.getSceneListAndTree(userInfo)
}
// 根据ID获取演示现场
@Get('/demoScenes/getListById')
getListById(@Query() params: DeleteSceneDto){
return this.rgvsaleService.getListById(params)
}
// 创建演示现场
@Post('/demoScenes/createScene')
createScene(@Body() body: CreateScenesDto, @getUserinfo() userInfo: UserInfoDto){
return this.rgvsaleService.createScene(body, userInfo)
}
// 删除演示现场
@Delete('/demoScenes/deleteScene')
deleteScene(@Body() body:DeleteSceneDto){
return this.rgvsaleService.deleteScene(body)
}
//
@Patch('/demoScenes/addSceneRecordDemoItem')
addDemoItemAccumulateForScene(@Body() body: AddDemoItemAccumulateForSceneDto){
return this.rgvsaleService.addDemoItemAccumulateForScene(body)
}
//#endregion
//#region 问题
@Get('/demoScenesProblem/getScenesProblemList')
getScenesProblemList(@Query() params: GetScenesProblemPageDto){
return this.rgvsaleService.getScenesProblemList(params)
}
@Post('/demoScenesProblem/createScenesProblem')
createScenesProblem(@Body() body: CreateScenesProblemDto, @getUserinfo() userInfo: UserInfoDto){
return this.rgvsaleService.createScenesProblem(body, userInfo)
}
@Delete('/demoScenesProblem/deleteScenesProblem')
deleteScenesProblem(@Body() body: DeleteScenesProblemDto){
return this.rgvsaleService.deleteScenesProblem(body)
}
@Patch('/demoScenesProblem/editScenesProblem')
editScenesProblem(@Body() body: EditScenesProblemDto){
return this.rgvsaleService.editScenesProblem(body)
}
@Patch('/demoScenesProblem/changeUseState')
changeScenesProblemUseState(@Body() body: ChangeUseStateDto){
return this.rgvsaleService.changeScenesProblemUseState(body)
}
//#endregion
}

@ -0,0 +1,9 @@
import { Module } from '@nestjs/common';
import { RgvsaleService } from './rgvsale.service';
import { RgvsaleController } from './rgvsale.controller';
@Module({
controllers: [RgvsaleController],
providers: [RgvsaleService]
})
export class RgvsaleModule {}

File diff suppressed because it is too large Load Diff

@ -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!');
});
});

@ -0,0 +1,9 @@
{
"moduleFileExtensions": ["js", "json", "ts"],
"rootDir": ".",
"testEnvironment": "node",
"testRegex": ".e2e-spec.ts$",
"transform": {
"^.+\\.(t|j)s$": "ts-jest"
}
}

@ -0,0 +1,14 @@
import jwt from 'jsonwebtoken'
// const token = 'eyJhbGciOiJIUzUxMiJ9.eyJsb2dpbl91c2VyX2tleSI6IjVjMDdlNzRjLTFiYzctNDAzYy05NjUxLTI5YjgzZGM5ZGE4MSJ9.bC2MO_7dzBeuMH33z7l3Wlovl489aja1B1D3SB4ukJmGye_RDZCy5rmDw0tPGsNpbeYryxBG9OgWZSxEAKX5Kg'
//
const secretkey = 'abcdefghijklmnopqrstuvwxyz'
console.log(jwt);
const a = jwt.sign({ secretkey },secretkey, { expiresIn: '30m' })
// console.log(a);
const token = 'Bearer eyJhbGciOiJIUzUxMiJ9.eyJsb2dpbl91c2VyX2tleSI6IjVlOWQ1ZTMzLTI0MjEtNDJkYy1iZmU5LTI5Y2M1YjIxOWU4ZCJ9.VmZzLm2si5d51oe_ZPq9sB7ZF7GLuttjSjxQHdxQg49W4QU6PnLFzhAIeKYx0l4mfKj4uL4_IKyqPsm4t6znHg'
jwt.verify(token, secretkey, (error, data)=>{
console.log(error);
console.log(data);
})

@ -0,0 +1,4 @@
{
"extends": "./tsconfig.json",
"exclude": ["node_modules", "test", "dist", "**/*spec.ts"]
}

@ -0,0 +1,29 @@
{
"compilerOptions": {
"module": "commonjs",
"declaration": true,
"removeComments": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"allowSyntheticDefaultImports": true,
"target": "es2017",
"sourceMap": true,
"outDir": "./dist",
"baseUrl": "./",
"incremental": true,
"skipLibCheck": true,
"strictNullChecks": false,
"noImplicitAny": false,
"strictBindCallApply": false,
"forceConsistentCasingInFileNames": false,
"noFallthroughCasesInSwitch": false,
"paths": {
"@/*": [
"src/*"
],
"@CFG/*": [
"config/*"
]
}
},
}
Loading…
Cancel
Save