From ccceb683675e7f7abaddac5e642ae163f0e4fade Mon Sep 17 00:00:00 2001 From: rustdreamer Date: Wed, 8 May 2024 01:16:30 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E7=94=A8=E6=88=B7=E7=AE=A1?= =?UTF-8?q?=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app.controller.ts | 208 +++++--------------- src/auth/auth.controller.ts | 12 ++ src/auth/auth.module.ts | 4 +- src/auth/auth.params.ts | 10 +- src/auth/auth.service.ts | 40 +++- src/boot/index.ts | 89 +++++++++ src/common/guards/roles.guard.ts | 19 +- src/main.ts | 4 +- src/role/dto/create-role.dto.ts | 15 +- src/role/dto/query-role.dto.ts | 17 ++ src/role/entities/role-permission.entity.ts | 16 ++ src/role/entities/role.entity.ts | 12 +- src/role/role.controller.ts | 106 +++++++++- src/role/role.db.ts | 38 ++++ src/role/role.module.ts | 9 +- src/user/update-user.dto.ts | 87 +++++++- src/user/user.controller.ts | 159 +++++++++++++-- src/user/user.entity.ts | 21 +- src/user/user.module.ts | 2 + 19 files changed, 677 insertions(+), 191 deletions(-) create mode 100644 src/boot/index.ts create mode 100644 src/role/dto/query-role.dto.ts create mode 100644 src/role/entities/role-permission.entity.ts create mode 100644 src/role/role.db.ts diff --git a/src/app.controller.ts b/src/app.controller.ts index 3a57fc2..7e92d53 100644 --- a/src/app.controller.ts +++ b/src/app.controller.ts @@ -1,14 +1,14 @@ -import { BadRequestException, Body, Controller, Delete, Get, NotFoundException, Param, Post, Put, Query, Req, Res } from '@nestjs/common'; +import { BadRequestException, Body, Controller, Delete, Get, NotFoundException, Param, Post, Put, Query, Req, Res, UseGuards, UsePipes } from '@nestjs/common'; import { AppService } from './app.service'; import * as fs from 'fs'; -import { ApiBearerAuth, ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { ApiBearerAuth, ApiOperation, ApiProperty, ApiPropertyOptional, ApiResponse } from '@nestjs/swagger'; import { IsArray, IsBoolean, IsDateString, IsNotEmpty, IsOptional, IsString } from 'class-validator'; import { NodeVisitorEntity } from './node-visitor.entity'; import { Between, DataSource, IsNull, LessThan, Not, Repository } from 'typeorm'; import * as bluebird from 'bluebird'; import { NodeOtherVisitorEntity } from './node-other-visitor.entity'; import * as moment from 'moment'; -import { CommonPageArgs } from './common'; +import { CommonPageArgs, Roles, RolesGuard, ValidationPipe } from './common'; import { InjectRepository } from '@nestjs/typeorm'; import axios from 'axios'; import { v4 as uuid } from 'uuid'; @@ -16,6 +16,7 @@ import { verificationService } from './lib/face'; import { join } from 'path'; import { AreaEntity } from './area.entity'; import { DeviceEntity } from './device.entity'; +import { AuthGuard } from '@nestjs/passport'; export class Electronics { @ApiProperty({ description: "电子产品名称" }) @@ -433,24 +434,17 @@ export class AppController { } @Get("/visitors") + @ApiOperation({ summary: '查看预约记录' }) + @UseGuards(AuthGuard('jwt'), RolesGuard) + @UsePipes(new ValidationPipe()) + @Roles('super_admin', '查看预约记录') @ApiBearerAuth() + // @ApiResponse({ + // status: 200, + // description: '返回参数说明', + // type: UserEntity, + // }) async get(@Query() query_data: QueryVisitorDto, @Req() req: any) { - // console.log("req", req.headers) - if (!req.headers.authorization) { - throw new BadRequestException("无权限") - } - try { - // console.log(`${process.env.RUST_URI}/api/viewer`) - const result = await axios.get(`${process.env.RUST_URI}/api/viewer`, { - headers: { - authorization: req.headers.authorization - } - }) - // console.log("result", result) - } catch (e) { - // console.log(e) - throw new BadRequestException("无权限, 请联系管理员") - } const where: any = {}; if (query_data.applicant) { where.applicant = query_data.applicant @@ -513,24 +507,12 @@ export class AppController { } @Get("/visitor-records") + @ApiOperation({ summary: '查看到访记录' }) + @UseGuards(AuthGuard('jwt'), RolesGuard) + @UsePipes(new ValidationPipe()) + @Roles('super_admin', '查看到访记录') @ApiBearerAuth() async getRecord(@Query() query_data: QueryVisitorDto, @Req() req: any) { - // console.log("req", req.headers) - if (!req.headers.authorization) { - throw new BadRequestException("无权限") - } - try { - // console.log(`${process.env.RUST_URI}/api/viewer`) - const result = await axios.get(`${process.env.RUST_URI}/api/viewer`, { - headers: { - authorization: req.headers.authorization - } - }) - // console.log("result", result) - } catch (e) { - // console.log(e) - throw new BadRequestException("无权限, 请联系管理员") - } const where: any = { start_time: Not(IsNull()) }; @@ -653,22 +635,12 @@ export class AppController { @Post("/area") + @ApiOperation({ summary: '创建厂区' }) + @UseGuards(AuthGuard('jwt'), RolesGuard) + @UsePipes(new ValidationPipe()) + @Roles('super_admin', '创建厂区') + @ApiBearerAuth() async createArea(@Body() data: CreateArea, @Req() req: any) { - if (!req.headers.authorization) { - throw new BadRequestException("无权限") - } - try { - // console.log(`${process.env.RUST_URI}/api/viewer`) - const result = await axios.get(`${process.env.RUST_URI}/api/viewer`, { - headers: { - authorization: req.headers.authorization - } - }) - // console.log("result", result) - } catch (e) { - // console.log(e) - throw new BadRequestException("无权限, 请联系管理员") - } return await this.dataSource.transaction(async transactionalEntityManager => { const area = new AreaEntity(); @@ -682,22 +654,12 @@ export class AppController { } @Put("/area/:id") + @ApiOperation({ summary: '更新厂区' }) + @UseGuards(AuthGuard('jwt'), RolesGuard) + @UsePipes(new ValidationPipe()) + @Roles('super_admin', '更新厂区') + @ApiBearerAuth() async updateArea(@Param() { id }: any, @Body() data: UpdateArea, @Req() req: any) { - if (!req.headers.authorization) { - throw new BadRequestException("无权限") - } - try { - // console.log(`${process.env.RUST_URI}/api/viewer`) - const result = await axios.get(`${process.env.RUST_URI}/api/viewer`, { - headers: { - authorization: req.headers.authorization - } - }) - // console.log("result", result) - } catch (e) { - // console.log(e) - throw new BadRequestException("无权限, 请联系管理员") - } const area = await this.areaRepository.findOne({ where: { id } }); if (!area) { throw new NotFoundException("area not found"); @@ -709,22 +671,12 @@ export class AppController { } @Delete("/area/:id") + @ApiOperation({ summary: '删除厂区' }) + @UseGuards(AuthGuard('jwt'), RolesGuard) + @UsePipes(new ValidationPipe()) + @Roles('super_admin', '删除厂区') + @ApiBearerAuth() async deleteArea(@Param() { id }: any, @Req() req: any) { - if (!req.headers.authorization) { - throw new BadRequestException("无权限") - } - try { - // console.log(`${process.env.RUST_URI}/api/viewer`) - const result = await axios.get(`${process.env.RUST_URI}/api/viewer`, { - headers: { - authorization: req.headers.authorization - } - }) - // console.log("result", result) - } catch (e) { - // console.log(e) - throw new BadRequestException("无权限, 请联系管理员") - } const result = await this.areaRepository.findOne({ where: { id } }); if (!result) { throw new NotFoundException("area not found"); @@ -736,22 +688,6 @@ export class AppController { @Get("/areas") @ApiBearerAuth() async areas(@Query() query_data: AreaDto, @Req() req: any) { - // console.log("req", req.headers) - // if (!req.headers.authorization) { - // throw new BadRequestException("无权限") - // } - // try { - // // console.log(`${process.env.RUST_URI}/api/viewer`) - // const result = await axios.get(`${process.env.RUST_URI}/api/viewer`, { - // headers: { - // authorization: req.headers.authorization - // } - // }) - // // console.log("result", result) - // } catch (e) { - // // console.log(e) - // throw new BadRequestException("无权限, 请联系管理员") - // } const where: any = {}; const query = this.areaRepository.createQueryBuilder('area'); query.where(where); @@ -774,22 +710,12 @@ export class AppController { } @Post("/device") + @ApiOperation({ summary: '创建设备' }) + @UseGuards(AuthGuard('jwt'), RolesGuard) + @UsePipes(new ValidationPipe()) + @Roles('super_admin', '创建设备') + @ApiBearerAuth() async createDevice(@Body() data: CreateDevice, @Req() req: any) { - if (!req.headers.authorization) { - throw new BadRequestException("无权限") - } - try { - // console.log(`${process.env.RUST_URI}/api/viewer`) - const result = await axios.get(`${process.env.RUST_URI}/api/viewer`, { - headers: { - authorization: req.headers.authorization - } - }) - // console.log("result", result) - } catch (e) { - // console.log(e) - throw new BadRequestException("无权限, 请联系管理员") - } return await this.dataSource.transaction(async transactionalEntityManager => { const device = new DeviceEntity(); @@ -805,22 +731,12 @@ export class AppController { } @Put("/device/:id") + @ApiOperation({ summary: '更新设备' }) + @UseGuards(AuthGuard('jwt'), RolesGuard) + @UsePipes(new ValidationPipe()) + @Roles('super_admin', '更新设备') + @ApiBearerAuth() async updateDevice(@Param() { id }: any, @Body() data: UpdateDevice, @Req() req: any) { - if (!req.headers.authorization) { - throw new BadRequestException("无权限") - } - try { - // console.log(`${process.env.RUST_URI}/api/viewer`) - const result = await axios.get(`${process.env.RUST_URI}/api/viewer`, { - headers: { - authorization: req.headers.authorization - } - }) - // console.log("result", result) - } catch (e) { - // console.log(e) - throw new BadRequestException("无权限, 请联系管理员") - } const device = await this.deviceRepository.findOne({ where: { id } }); if (!device) { throw new NotFoundException("device not found"); @@ -835,22 +751,12 @@ export class AppController { } @Delete("/device/:id") + @ApiOperation({ summary: '删除设备' }) + @UseGuards(AuthGuard('jwt'), RolesGuard) + @UsePipes(new ValidationPipe()) + @Roles('super_admin', '删除设备') + @ApiBearerAuth() async deleteDevice(@Param() { id }: any, @Req() req: any) { - if (!req.headers.authorization) { - throw new BadRequestException("无权限") - } - try { - // console.log(`${process.env.RUST_URI}/api/viewer`) - const result = await axios.get(`${process.env.RUST_URI}/api/viewer`, { - headers: { - authorization: req.headers.authorization - } - }) - // console.log("result", result) - } catch (e) { - // console.log(e) - throw new BadRequestException("无权限, 请联系管理员") - } const result = await this.deviceRepository.findOne({ where: { id } }); if (!result) { throw new NotFoundException("device not found"); @@ -860,24 +766,12 @@ export class AppController { } @Get("/devices") + @ApiOperation({ summary: '查看设备' }) + @UseGuards(AuthGuard('jwt'), RolesGuard) + @UsePipes(new ValidationPipe()) + @Roles('super_admin', '查看设备') @ApiBearerAuth() async devices(@Query() query_data: AreaDto, @Req() req: any) { - // console.log("req", req.headers) - if (!req.headers.authorization) { - throw new BadRequestException("无权限") - } - try { - // console.log(`${process.env.RUST_URI}/api/viewer`) - const result = await axios.get(`${process.env.RUST_URI}/api/viewer`, { - headers: { - authorization: req.headers.authorization - } - }) - // console.log("result", result) - } catch (e) { - // console.log(e) - throw new BadRequestException("无权限, 请联系管理员") - } const where: any = {}; const query = this.deviceRepository.createQueryBuilder('device'); query.where(where); diff --git a/src/auth/auth.controller.ts b/src/auth/auth.controller.ts index 96ee3b8..1fb15cf 100644 --- a/src/auth/auth.controller.ts +++ b/src/auth/auth.controller.ts @@ -53,4 +53,16 @@ export class AuthController { // status: false // } // } + + @Post('login/admin') + @ApiOperation({ summary: '后台管理员登录' }) + @UsePipes(new ValidationPipe()) + @ApiResponse({ + status: 200, + description: '返回参数说明', + type: ResponseType, + }) + async loginByAdmin(@Body() loginData: Login): Promise { + return this.authService.loginByAdmin(loginData.mobile, loginData.password); + } } diff --git a/src/auth/auth.module.ts b/src/auth/auth.module.ts index 53056c3..00c4dbc 100644 --- a/src/auth/auth.module.ts +++ b/src/auth/auth.module.ts @@ -6,15 +6,15 @@ import { AuthService, expires_in } from './auth.service'; import { JwtStrategy } from './jwt.strategy'; import { TypeOrmModule } from '@nestjs/typeorm'; import { UserEntity } from '../user/user.entity'; +import { RoleEntity } from 'src/role/entities/role.entity'; // import AuthResolver from './auth.resolver'; -// import { RoleEntity } from '../role/role.entity'; // import { RoleItemEntity } from '../role-item/role-item.entity'; @Module({ imports: [ TypeOrmModule.forFeature([ UserEntity, - // RoleEntity, + RoleEntity, // RoleItemEntity, ]), PassportModule.register({ defaultStrategy: 'jwt' }), diff --git a/src/auth/auth.params.ts b/src/auth/auth.params.ts index f619767..160c8a1 100644 --- a/src/auth/auth.params.ts +++ b/src/auth/auth.params.ts @@ -11,15 +11,15 @@ import { } from 'class-validator'; export class Login { - @ApiProperty({ description: '从小程序端获取的code' }) + @ApiProperty({ description: '' }) @IsString() @IsNotEmpty() - public code: string; + public mobile: string; - @ApiProperty({ description: '小程序码' }) + @ApiProperty({ description: '' }) @IsString() - @IsOptional() - public qunsense_code: string; + @IsNotEmpty() + public password: string; } export class SureLogin { diff --git a/src/auth/auth.service.ts b/src/auth/auth.service.ts index a3cbf08..52aa41d 100644 --- a/src/auth/auth.service.ts +++ b/src/auth/auth.service.ts @@ -13,8 +13,10 @@ import { JwtPayload, UserJwtPayload } from './interfaces/jwt-payload.interface'; import { UserEntity } from '../user/user.entity'; import { Login, PollingDto, LoginMobile, UpdateUserDto, IsLogin } from './auth.params'; import redis from 'src/redis'; +import { RoleEntity } from 'src/role/entities/role.entity'; // import { RoleItemEntity } from '../role-item/role-item.entity'; // import { RoleEntity } from '../role/role.entity'; +export const cache_in = 60 * 3; export const expires_in = 3600 * 24 * 30; @Injectable() @@ -25,8 +27,8 @@ export class AuthService { private readonly userRepository: Repository, // @InjectRepository(RoleItemEntity) // private readonly roleItemRepository: Repository, - // @InjectRepository(RoleEntity) - // private readonly roleRepository: Repository, + @InjectRepository(RoleEntity) + private readonly roleRepository: Repository, ) { } async verify(token: string) { @@ -76,6 +78,40 @@ export class AuthService { throw new BadRequestException() } } + + async loginByAdmin(mobile: string, password: string) { + const user = await this.userRepository.findOne({ + where: { mobile }, + select: ['id', 'password', 'role'], + }); + if (!user) { + throw new BadRequestException('用户不存在'); + } + const result = await bcrypt.compare(password, user.password); + if (!result) { + throw new BadRequestException('用户名或密码错误'); + } + const access_token = await this.createUserToken(user); + Logger.log(`${user.id}: 管理员登录`); + // const user_role_permission = await this.getRoleAndPermission(user.id); + const user_permissions_key = `user_permissions:${user.id}`; + let user_permissions = [user.role] + const role = await this.roleRepository.findOne({ where: { name: user.role } }) + if (role) { + user_permissions = [...user_permissions, ...role.permissions.split('、')] + } + redis.set( + user_permissions_key, + JSON.stringify(user_permissions), + // 'EX', + // cache_in, + ); + return { + // ...user_role_permission, + ...access_token, + user_id: user.id, + }; + } } diff --git a/src/boot/index.ts b/src/boot/index.ts new file mode 100644 index 0000000..cbe25e6 --- /dev/null +++ b/src/boot/index.ts @@ -0,0 +1,89 @@ +import * as bcrypt from 'bcryptjs'; +import * as bluebird from 'bluebird'; +import { DataSource, getRepository } from 'typeorm'; +import { Logger } from '@nestjs/common'; + +import { UserEntity } from '../user/user.entity'; +import { BCRYPT_HASH_ROUNDS } from '../common'; +import { RoleEntity } from 'src/role/entities/role.entity'; +import { RolePermissionEntity } from 'src/role/entities/role-permission.entity'; +import { mysql_migration_config } from 'src/config/config.mysql'; + +const admins = [ + { + mobile: 'admin', + password: 'admin2022uu', + true_name: '超级管理员', + headimgurl: '', + }, + +]; + +// (async () => { +// const new_password = await bcrypt.hash('13813977385', BCRYPT_HASH_ROUNDS); +// console.log('---- new_password', new_password); +// })(); + +// export const a = () => { +// setTimeout(async () => { +// const new_password = await bcrypt.hash('13914730324', BCRYPT_HASH_ROUNDS); +// console.log('---- new_password', new_password); +// }, 1000) +// } + +export async function createAdmin() { + const myDataSource = new DataSource(mysql_migration_config); + await myDataSource.initialize(); + const roleRepository = myDataSource.getRepository(RoleEntity); + const userRepository = myDataSource.getRepository(UserEntity); + const rolePermissionRepository = myDataSource.getRepository(RolePermissionEntity); + await bluebird.each( + admins, + async ({ mobile, password, true_name, headimgurl }) => { + let role = await roleRepository.findOne({ + where: { name: 'super_admin' }, + }); + if (!role) { + const new_role = new RoleEntity(); + new_role.name = 'super_admin'; + role = await roleRepository.save(new_role); + const permission = new RolePermissionEntity(); + permission.role_id = role.id; + permission.name = 'super_admin'; + await rolePermissionRepository.save(permission); + } + + let user = await userRepository.findOne({ where: { mobile } }); + if (!user) { + const new_password = await bcrypt.hash(password, BCRYPT_HASH_ROUNDS); + const new_user = Object.assign(new UserEntity(), { + mobile, + password: new_password, + true_name, + headimgurl, + role: 'super_admin', + role_id: role.id, + }); + user = await userRepository.save(new_user); + } + }, + ); +} + +export async function createRole() { + const myDataSource = new DataSource(mysql_migration_config); + await myDataSource.initialize(); + const roleRepository = myDataSource.getRepository(RoleEntity); + const rolePermissionRepository = myDataSource.getRepository(RolePermissionEntity); + + let role = await roleRepository.findOne({ + where: { name: '厂区管理员' }, + }); + if (!role) { + const new_role = new RoleEntity(); + new_role.name = '厂区管理员'; + new_role.permissions = + "查看预约记录、查看到访记录"; + role = await roleRepository.save(new_role); + } +} diff --git a/src/common/guards/roles.guard.ts b/src/common/guards/roles.guard.ts index b2fad15..183c943 100644 --- a/src/common/guards/roles.guard.ts +++ b/src/common/guards/roles.guard.ts @@ -1,9 +1,10 @@ import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common'; import { Reflector } from '@nestjs/core'; +import redis from 'src/redis'; @Injectable() export class RolesGuard implements CanActivate { - constructor(private readonly reflector: Reflector) {} + constructor(private readonly reflector: Reflector) { } async canActivate(context: ExecutionContext): Promise { const roles = this.reflector.get('roles', context.getHandler()); @@ -18,9 +19,23 @@ export class RolesGuard implements CanActivate { // fix graphql 需要特殊处理 user = context.getArgs()[2].req.user; } + + const user_permissions_key = `user_permissions:${user.id}`; + const result = await redis.get( + user_permissions_key, + ); + + console.log("result", result) + + if (!result) { + return false; + } + + user.roles = JSON.parse(result); + const hasRole = () => user.roles.some(role => !!roles.find(item => item === role)); - console.log(roles,user.roles) + console.log(roles, user.roles) return user && user.roles && hasRole(); } } diff --git a/src/main.ts b/src/main.ts index 0ff2a61..a93c284 100644 --- a/src/main.ts +++ b/src/main.ts @@ -7,6 +7,7 @@ import { SwaggerModule } from '@nestjs/swagger'; import { LoggingInterceptor } from './common/interceptor/logging.interceptor'; import * as fs from 'fs'; import { urlencoded, json } from 'express'; +import { createAdmin, createRole } from './boot'; /** * @@ -57,7 +58,8 @@ async function bootstrap() { // RequestId: 'ce06f1e7-163a-40ab-90c5-fc227b3ecbd2', // Url: 'https://open.weixin.qq.com/connect/oauth2/authorize?appid=wxa50a8ac379585f53&redirect_uri=https%3A%2F%2Feid.faceid.qq.com%2Fapi%2Fv1%2FGetOpenId%3Ftoken%3D095FA750-2234-4B1C-B395-22D2197657AB%26v%3Dv2&response_type=code&scope=snsapi_base&state=&component_appid=wx9802ee81e68d6dee#wechat_redirect' // } - + createAdmin(); + createRole(); const port: number = 9998 await app.listen(port); Logger.log(`the server listing on ${port}`) diff --git a/src/role/dto/create-role.dto.ts b/src/role/dto/create-role.dto.ts index 3044c2b..7996c93 100644 --- a/src/role/dto/create-role.dto.ts +++ b/src/role/dto/create-role.dto.ts @@ -1 +1,14 @@ -export class CreateRoleDto {} +import { ApiProperty } from "@nestjs/swagger"; +import { IsNotEmpty, IsString } from "class-validator"; + +export class CreateRoleDto { + @ApiProperty({ description: '' }) + @IsString() + @IsNotEmpty() + public name: string; + + @ApiProperty({ description: '' }) + @IsString() + @IsNotEmpty() + public permissions: string; +} diff --git a/src/role/dto/query-role.dto.ts b/src/role/dto/query-role.dto.ts new file mode 100644 index 0000000..f6b1e0c --- /dev/null +++ b/src/role/dto/query-role.dto.ts @@ -0,0 +1,17 @@ +import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger" +import { IsEnum, IsOptional, IsString } from "class-validator" +import { CommonPageArgs } from "src/common" + +export class QueryRoleDto extends CommonPageArgs { + @ApiPropertyOptional({ description: '' }) + @IsString() + @IsOptional() + name: string + + @ApiPropertyOptional({ description: "根据名称模糊搜索", required: false }) + @IsString() + @IsOptional() + search?: string +} + + diff --git a/src/role/entities/role-permission.entity.ts b/src/role/entities/role-permission.entity.ts new file mode 100644 index 0000000..9e8bfba --- /dev/null +++ b/src/role/entities/role-permission.entity.ts @@ -0,0 +1,16 @@ +import { Column, Entity, OneToOne, JoinColumn, ManyToOne } from 'typeorm'; +import { RoleEntity } from './role.entity'; +import { Base } from 'src/common'; + +@Entity({ name: 'role-permission' }) +export class RolePermissionEntity extends Base { + @Column({ nullable: false }) + public name: string; + + @Column({ type: "bigint", width: 18, nullable: true }) + public role_id: string; + + // @ManyToOne(type => RoleEntity) + // @JoinColumn({ name: 'role_id' }) + // role: RoleEntity; +} diff --git a/src/role/entities/role.entity.ts b/src/role/entities/role.entity.ts index ec816d5..85b4364 100644 --- a/src/role/entities/role.entity.ts +++ b/src/role/entities/role.entity.ts @@ -1 +1,11 @@ -export class Role {} +import { Base } from 'src/common'; +import { Column, Entity } from 'typeorm'; + +@Entity({ name: 'node-roles' }) +export class RoleEntity extends Base { + @Column({ length: 50, nullable: false }) + public name: string; + + @Column({ type: 'text', nullable: true }) + public permissions: string; +} diff --git a/src/role/role.controller.ts b/src/role/role.controller.ts index aed2c71..5f1ab4b 100644 --- a/src/role/role.controller.ts +++ b/src/role/role.controller.ts @@ -1,13 +1,26 @@ -import { Controller, Get, Post, Body, Patch, Param, Delete } from '@nestjs/common'; +import { Controller, Get, Post, Body, Patch, Param, Delete, Put, UseGuards, UsePipes, NotFoundException, Query } from '@nestjs/common'; import { RoleService } from './role.service'; import { CreateRoleDto } from './dto/create-role.dto'; import { UpdateRoleDto } from './dto/update-role.dto'; -import { ApiTags } from '@nestjs/swagger'; +import { ApiBearerAuth, ApiOperation, ApiTags } from '@nestjs/swagger'; +import { AuthGuard } from '@nestjs/passport'; +import { Roles, RolesGuard, ValidationPipe } from 'src/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { DataSource, Repository } from 'typeorm'; +import { RoleEntity } from './entities/role.entity'; +import { QueryRoleDto } from './dto/query-role.dto'; +import { all_role } from './role.db'; @ApiTags('用户角色') @Controller('roles') export class RoleController { - constructor(private readonly roleService: RoleService) {} + constructor( + private readonly roleService: RoleService, + @InjectRepository(RoleEntity) + private readonly roleRepository: Repository, + private readonly dataSource: DataSource, + + ) { } // @Post() // create(@Body() createRoleDto: CreateRoleDto) { @@ -33,4 +46,91 @@ export class RoleController { // remove(@Param('id') id: string) { // return this.roleService.remove(+id); // } + + @Get('/all') + all() { + return all_role; + } + + @Get("/list") + @ApiOperation({ summary: '查看角色' }) + @UseGuards(AuthGuard('jwt'), RolesGuard) + @UsePipes(new ValidationPipe()) + @Roles('super_admin', '查看角色', '创建管理员', '更新管理员') + @ApiBearerAuth() + async areas(@Query() query_data: QueryRoleDto) { + const where: any = {}; + if (query_data.name) { + where['name'] = query_data.name; + } + const query = this.roleRepository.createQueryBuilder('role'); + query.where(where); + if (query_data.search) { + const string = `%${query_data.search}%`; + const fields = ['name']; + const searchString = fields.join(' like :search OR role.'); + query.where(`role.${searchString} like :search`, { + search: string, + }); + } + const order_key = 'role.created_date'; + let order_value: any = 'DESC'; + const [list, count] = await query + .skip(query_data.skip) + .take(query_data.take) + .orderBy(order_key, order_value) + .getManyAndCount(); + return { list, count } + } + + @Post("/") + @ApiOperation({ summary: '创建角色' }) + @UseGuards(AuthGuard('jwt'), RolesGuard) + @UsePipes(new ValidationPipe()) + @Roles('super_admin', '创建角色') + @ApiBearerAuth() + async createRole(@Body() data: CreateRoleDto) { + return await this.dataSource.transaction(async transactionalEntityManager => { + + const role = new RoleEntity(); + role.name = data.name; + role.permissions = data.permissions; + + const result = await transactionalEntityManager.save(role); + + return { statusCode: 201, data: result }; + }); + } + + @Put("/:id") + @ApiOperation({ summary: '更新角色' }) + @UseGuards(AuthGuard('jwt'), RolesGuard) + @UsePipes(new ValidationPipe()) + @Roles('super_admin', '更新角色') + @ApiBearerAuth() + async updateRole(@Param() { id }: any, @Body() data: UpdateRoleDto) { + const role = await this.roleRepository.findOne({ where: { id } }); + if (!role) { + throw new NotFoundException("role not found"); + } + role.name = data.name; + role.permissions = data.permissions; + const result = await this.roleRepository.save(role); + return result; + } + + @Delete("/:id") + @ApiOperation({ summary: '删除角色' }) + @UseGuards(AuthGuard('jwt'), RolesGuard) + @UsePipes(new ValidationPipe()) + @Roles('super_admin', '删除角色') + @ApiBearerAuth() + async deleteRole(@Param() { id }: any) { + const result = await this.roleRepository.findOne({ where: { id } }); + if (!result) { + throw new NotFoundException("role not found"); + } + await this.roleRepository.delete(id); + return result; + } } diff --git a/src/role/role.db.ts b/src/role/role.db.ts new file mode 100644 index 0000000..bf7018a --- /dev/null +++ b/src/role/role.db.ts @@ -0,0 +1,38 @@ +export const all_role = { + "管理员管理": [ + "查看管理员", + "创建管理员", + "修改管理员", + "删除管理员" + ], + "角色管理": [ + "查看角色", + "创建角色", + "修改角色", + "删除角色" + ], + "设备管理": [ + "查看设备", + "创建设备", + "修改设备", + "删除设备" + ], + // "权限管理": [ + // "查看权限", + // "创建权限", + // "修改权限", + // "删除权限" + // ], + "厂区管理": [ + "查看厂区", + "创建厂区", + "修改厂区", + "删除厂区" + ], + "访客管理": [ + "查看预约记录", + "查看到访记录", + "导出访客预约", + "导出到访记录" + ], +} \ No newline at end of file diff --git a/src/role/role.module.ts b/src/role/role.module.ts index 1127f13..d74325e 100644 --- a/src/role/role.module.ts +++ b/src/role/role.module.ts @@ -1,9 +1,16 @@ import { Module } from '@nestjs/common'; import { RoleService } from './role.service'; import { RoleController } from './role.controller'; +import { RoleEntity } from './entities/role.entity'; +import { TypeOrmModule } from '@nestjs/typeorm'; @Module({ + imports: [ + TypeOrmModule.forFeature([ + RoleEntity, + ]), + ], controllers: [RoleController], providers: [RoleService] }) -export class RoleModule {} +export class RoleModule { } diff --git a/src/user/update-user.dto.ts b/src/user/update-user.dto.ts index 2974620..bc59c63 100644 --- a/src/user/update-user.dto.ts +++ b/src/user/update-user.dto.ts @@ -1,4 +1,89 @@ import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger" -import { IsEnum, IsOptional, IsString } from "class-validator" +import { IsEnum, IsNotEmpty, IsOptional, IsString } from "class-validator" +export class CreateUser { + @ApiProperty({ description: '真实姓名' }) + @IsString() + @IsNotEmpty() + public true_name: string; + + @ApiProperty({ description: '角色' }) + @IsString() + @IsNotEmpty() + public role: string; + + @ApiProperty({ description: '角色 id' }) + @IsString() + @IsNotEmpty() + public role_id: string; + + @ApiProperty({ description: '手机号' }) + @IsString() + @IsOptional() + public mobile?: string; + + @ApiProperty({ description: '用户头像' }) + @IsString() + @IsOptional() + public avatar?: string; + + @ApiProperty({ description: 'area' }) + @IsString() + @IsNotEmpty() + public area: string; + + @ApiProperty({ description: 'area id' }) + @IsString() + @IsNotEmpty() + public area_id: string; + + @ApiProperty({ description: '密码' }) + @IsString() + @IsOptional() + public password?: string; +} + +export class UpdateUser { + @ApiProperty({ description: '真实姓名' }) + @IsString() + @IsNotEmpty() + public true_name: string; + + @ApiProperty({ description: '角色' }) + @IsString() + @IsNotEmpty() + public role: string; + + @ApiProperty({ description: '角色 id' }) + @IsString() + @IsNotEmpty() + public role_id: string; + + @ApiProperty({ description: 'area' }) + @IsString() + @IsNotEmpty() + public area: string; + + @ApiProperty({ description: 'area id' }) + @IsString() + @IsNotEmpty() + public area_id: string; + + @ApiProperty({ description: '手机号' }) + @IsString() + @IsOptional() + public mobile?: string; + + @ApiProperty({ description: '用户头像' }) + @IsString() + @IsOptional() + public avatar?: string; +} + +export class UpdatePassword { + @ApiProperty({ description: '' }) + @IsString() + @IsNotEmpty() + public password: string; +} \ No newline at end of file diff --git a/src/user/user.controller.ts b/src/user/user.controller.ts index 2a7a118..ad57d00 100644 --- a/src/user/user.controller.ts +++ b/src/user/user.controller.ts @@ -3,22 +3,35 @@ import { Get, UsePipes, UseGuards, + Query, + Post, + Body, + Delete, + Param, + Put, + NotFoundException, } from '@nestjs/common'; import { ApiBearerAuth, ApiTags, ApiOperation, ApiResponse } from '@nestjs/swagger'; import { InjectRepository } from '@nestjs/typeorm'; import { + DataSource, Repository, } from 'typeorm'; import { AuthGuard } from '@nestjs/passport'; +import * as bcrypt from 'bcryptjs'; import { ValidationPipe, User, Roles, RolesGuard, + BCRYPT_HASH_ROUNDS, } from '../common'; import { UserEntity } from './user.entity'; import { UserService } from './user.service'; +import { QueryUserDto } from './query-user.dto'; +import { CreateUser, UpdatePassword, UpdateUser } from './update-user.dto'; +import { RoleEntity } from 'src/role/entities/role.entity'; @ApiTags('系统用户') @ApiBearerAuth() @@ -27,23 +40,141 @@ export class UserController { constructor( @InjectRepository(UserEntity) private readonly userRepository: Repository, - private readonly userService: UserService + @InjectRepository(RoleEntity) + private readonly roleRepository: Repository, + private readonly userService: UserService, + private readonly dataSource: DataSource, // @Inject(forwardRef(() => AuthService)) // private readonly authService: AuthService, ) { } - // @Get('/viewer') - // @ApiOperation({ summary: '获取个人信息' }) - // @UseGuards(AuthGuard('jwt'), RolesGuard) - // @UsePipes(new ValidationPipe()) + @Get('/viewer') + @ApiOperation({ summary: '获取个人信息' }) + @UseGuards(AuthGuard('jwt'), RolesGuard) + @UsePipes(new ValidationPipe()) // @Roles('company', 'tester', 'admin') - // @ApiBearerAuth() - // @ApiResponse({ - // status: 200, - // description: '返回参数说明', - // }) - // async viewer(@User() viewer: UserEntity) { - // const result = await this.userRepository.findOne({ where: { id: viewer.id } }) - // return result; - // } + @ApiBearerAuth() + @ApiResponse({ + status: 200, + description: '返回参数说明', + }) + async viewer(@User() viewer: UserEntity) { + const result = await this.userRepository.findOne({ where: { id: viewer.id } }) + const role = await this.roleRepository.findOne({ where: { id: result.role_id } }) + if (role) { + (result as any).permissions = role.permissions.split('、'); + } + return result; + } + + @Get("/list") + @ApiOperation({ summary: '查看管理员' }) + @UseGuards(AuthGuard('jwt'), RolesGuard) + @UsePipes(new ValidationPipe()) + @Roles('super_admin', '查看管理员') + @ApiBearerAuth() + async areas(@Query() query_data: QueryUserDto) { + const where: any = {}; + if (query_data.true_name) { + where['true_name'] = query_data.true_name; + } + const query = this.userRepository.createQueryBuilder('user'); + query.where(where); + if (query_data.search) { + const string = `%${query_data.search}%`; + const fields = ['true_name']; + const searchString = fields.join(' like :search OR user.'); + query.where(`user.${searchString} like :search`, { + search: string, + }); + } + const order_key = 'user.created_date'; + let order_value: any = 'DESC'; + const [list, count] = await query + .skip(query_data.skip) + .take(query_data.take) + .orderBy(order_key, order_value) + .getManyAndCount(); + return { list, count } + } + + @Post("/") + @ApiOperation({ summary: '创建管理员' }) + @UseGuards(AuthGuard('jwt'), RolesGuard) + @UsePipes(new ValidationPipe()) + @Roles('super_admin', '创建管理员') + @ApiBearerAuth() + async createUser(@Body() data: CreateUser) { + const new_password = await bcrypt.hash(data.password, BCRYPT_HASH_ROUNDS); + return await this.dataSource.transaction(async transactionalEntityManager => { + + const user = new UserEntity(); + user.true_name = data.true_name; + user.role = data.role; + user.role_id = data.role_id; + user.mobile = data.mobile; + user.avatar = data.avatar; + user.area_id = data.area_id; + user.area = data.area; + user.password = new_password; + + const result = await transactionalEntityManager.save(user); + + return { statusCode: 201, data: result }; + }); + } + + @Put("/:id") + @ApiOperation({ summary: '更新管理员' }) + @UseGuards(AuthGuard('jwt'), RolesGuard) + @UsePipes(new ValidationPipe()) + @Roles('super_admin', '更新管理员') + @ApiBearerAuth() + async updateUser(@Param() { id }: any, @Body() data: UpdateUser) { + const user = await this.userRepository.findOne({ where: { id } }); + if (!user) { + throw new NotFoundException("user not found"); + } + user.true_name = data.true_name; + user.role = data.role; + user.role_id = data.role_id; + user.mobile = data.mobile; + user.avatar = data.avatar; + user.area_id = data.area_id; + user.area = data.area; + const result = await this.userRepository.save(user); + return result; + } + + @Put("/:id/password") + @ApiOperation({ summary: '更新管理员' }) + @UseGuards(AuthGuard('jwt'), RolesGuard) + @UsePipes(new ValidationPipe()) + @Roles('super_admin', '更新管理员') + @ApiBearerAuth() + async passwordUser(@Param() { id }: any, @Body() data: UpdatePassword) { + const user = await this.userRepository.findOne({ where: { id } }); + if (!user) { + throw new NotFoundException("user not found"); + } + const new_password = await bcrypt.hash(data.password, BCRYPT_HASH_ROUNDS); + user.password = new_password; + const result = await this.userRepository.save(user); + return result; + } + + @Delete("/:id") + @ApiOperation({ summary: '删除管理员' }) + @UseGuards(AuthGuard('jwt'), RolesGuard) + @UsePipes(new ValidationPipe()) + @Roles('super_admin', '删除管理员') + @ApiBearerAuth() + async deleteUser(@Param() { id }: any) { + const result = await this.userRepository.findOne({ where: { id } }); + if (!result) { + throw new NotFoundException("user not found"); + } + await this.userRepository.delete(id); + return result; + } } diff --git a/src/user/user.entity.ts b/src/user/user.entity.ts index 9446cc3..cf34bf3 100644 --- a/src/user/user.entity.ts +++ b/src/user/user.entity.ts @@ -14,11 +14,30 @@ export class UserEntity extends Base { @Column({ nullable: true, type: 'int' }) public age: string; + @ApiProperty({ description: '角色' }) + @Column({ nullable: true }) + public role: string; + + @ApiProperty({ description: '角色 id' }) + @Column({ length: 36, nullable: true }) + public role_id: string; + + @ApiProperty({ description: 'area' }) + @Column({ nullable: true }) + public area: string; + + @ApiProperty({ description: 'area id' }) + @Column({ length: 36, nullable: true }) + public area_id: string; + @ApiProperty({ description: '手机号' }) - @Column({ length: 50, nullable: true, type: 'char', select: false }) + @Column({ length: 50, nullable: true, type: 'char' }) public mobile?: string; @ApiProperty({ description: '用户头像' }) @Column({ nullable: true, length: 255, type: 'char' }) public avatar?: string; + + @Column({ select: false, nullable: true }) + public password: string; } diff --git a/src/user/user.module.ts b/src/user/user.module.ts index bd276e3..47df8c0 100644 --- a/src/user/user.module.ts +++ b/src/user/user.module.ts @@ -4,12 +4,14 @@ import { TypeOrmModule } from '@nestjs/typeorm'; import { UserService } from './user.service'; import { UserEntity } from './user.entity'; import { UserController } from './user.controller'; +import { RoleEntity } from 'src/role/entities/role.entity'; // import { AuthService } from '../auth/auth.service'; @Module({ imports: [ TypeOrmModule.forFeature([ UserEntity, + RoleEntity ], ), // TesterModule