feat: 添加管理员以及部署内容
This commit is contained in:
parent
bc77a579c8
commit
227a3c5b7b
|
|
@ -0,0 +1,10 @@
|
||||||
|
node_modules
|
||||||
|
dist
|
||||||
|
.env
|
||||||
|
.env.local
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
pnpm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
.DS_Store
|
||||||
|
|
@ -0,0 +1,107 @@
|
||||||
|
name: Backend CI/CD
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
- master
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
- master
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
# 构建和测试
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: 检出代码
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: 设置 Node.js
|
||||||
|
uses: actions/setup-node@v3
|
||||||
|
with:
|
||||||
|
node-version: '18'
|
||||||
|
|
||||||
|
- name: 安装 pnpm
|
||||||
|
run: npm install -g pnpm
|
||||||
|
|
||||||
|
- name: 安装依赖
|
||||||
|
run: pnpm install --frozen-lockfile
|
||||||
|
|
||||||
|
- name: 代码检查
|
||||||
|
run: |
|
||||||
|
echo "运行代码检查..."
|
||||||
|
# pnpm run lint
|
||||||
|
|
||||||
|
- name: 运行测试
|
||||||
|
run: |
|
||||||
|
echo "运行单元测试..."
|
||||||
|
# pnpm run test
|
||||||
|
|
||||||
|
- name: 构建项目
|
||||||
|
run: pnpm run build
|
||||||
|
|
||||||
|
- name: 上传构建产物
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: dist
|
||||||
|
path: dist/
|
||||||
|
retention-days: 1
|
||||||
|
|
||||||
|
# 部署到生产环境
|
||||||
|
deploy:
|
||||||
|
needs: build
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master'
|
||||||
|
steps:
|
||||||
|
- name: 检出代码
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: 下载构建产物
|
||||||
|
uses: actions/download-artifact@v3
|
||||||
|
with:
|
||||||
|
name: dist
|
||||||
|
path: dist/
|
||||||
|
|
||||||
|
- name: 部署到服务器
|
||||||
|
uses: appleboy/ssh-action@master
|
||||||
|
with:
|
||||||
|
host: ${{ secrets.SERVER_HOST }}
|
||||||
|
username: ${{ secrets.SERVER_USER }}
|
||||||
|
key: ${{ secrets.SSH_PRIVATE_KEY }}
|
||||||
|
port: ${{ secrets.SERVER_PORT || 22 }}
|
||||||
|
script: |
|
||||||
|
cd /path/to/self_proj
|
||||||
|
|
||||||
|
# 备份数据库
|
||||||
|
bash deploy.sh backup
|
||||||
|
|
||||||
|
# 拉取最新代码
|
||||||
|
cd oauth_nest_demo
|
||||||
|
git pull
|
||||||
|
cd ..
|
||||||
|
|
||||||
|
# 重新构建并启动后端
|
||||||
|
docker-compose up -d --build backend
|
||||||
|
|
||||||
|
# 等待服务启动
|
||||||
|
sleep 15
|
||||||
|
|
||||||
|
# 检查服务状态
|
||||||
|
docker-compose ps
|
||||||
|
|
||||||
|
- name: 健康检查
|
||||||
|
run: |
|
||||||
|
echo "等待服务启动..."
|
||||||
|
sleep 10
|
||||||
|
curl -f http://${{ secrets.SERVER_HOST }}:3000/api || exit 1
|
||||||
|
|
||||||
|
- name: 通知部署结果
|
||||||
|
if: always()
|
||||||
|
run: |
|
||||||
|
if [ ${{ job.status }} == 'success' ]; then
|
||||||
|
echo "✅ 后端部署成功!"
|
||||||
|
else
|
||||||
|
echo "❌ 后端部署失败!"
|
||||||
|
fi
|
||||||
|
|
@ -0,0 +1,42 @@
|
||||||
|
# 构建阶段
|
||||||
|
FROM node:18-alpine AS builder
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# 安装 pnpm
|
||||||
|
RUN npm install -g pnpm
|
||||||
|
|
||||||
|
# 复制依赖文件
|
||||||
|
COPY package.json pnpm-lock.yaml ./
|
||||||
|
|
||||||
|
# 安装依赖
|
||||||
|
RUN pnpm install --frozen-lockfile
|
||||||
|
|
||||||
|
# 复制源码
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
# 构建
|
||||||
|
RUN pnpm run build
|
||||||
|
|
||||||
|
# 生产阶段
|
||||||
|
FROM node:18-alpine
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# 安装 pnpm
|
||||||
|
RUN npm install -g pnpm
|
||||||
|
|
||||||
|
# 复制依赖文件
|
||||||
|
COPY package.json pnpm-lock.yaml ./
|
||||||
|
|
||||||
|
# 只安装生产依赖
|
||||||
|
RUN pnpm install --prod --frozen-lockfile
|
||||||
|
|
||||||
|
# 从构建阶段复制构建产物
|
||||||
|
COPY --from=builder /app/dist ./dist
|
||||||
|
|
||||||
|
# 暴露端口
|
||||||
|
EXPOSE 3000
|
||||||
|
|
||||||
|
# 启动应用
|
||||||
|
CMD ["node", "dist/main"]
|
||||||
|
|
@ -3,11 +3,14 @@ import { TypeOrmModule } from '@nestjs/typeorm';
|
||||||
import { User } from '../user/entities/user.entity';
|
import { User } from '../user/entities/user.entity';
|
||||||
import { AdminService } from './services/admin.service';
|
import { AdminService } from './services/admin.service';
|
||||||
import { AdminGuard } from './guards/admin.guard';
|
import { AdminGuard } from './guards/admin.guard';
|
||||||
|
import { SuperAdminGuard } from './guards/super-admin.guard';
|
||||||
|
import { AdminController } from './controllers/admin.controller';
|
||||||
|
|
||||||
@Global()
|
@Global()
|
||||||
@Module({
|
@Module({
|
||||||
imports: [TypeOrmModule.forFeature([User])],
|
imports: [TypeOrmModule.forFeature([User])],
|
||||||
providers: [AdminService, AdminGuard],
|
controllers: [AdminController],
|
||||||
exports: [AdminService, AdminGuard],
|
providers: [AdminService, AdminGuard, SuperAdminGuard],
|
||||||
|
exports: [AdminService, AdminGuard, SuperAdminGuard],
|
||||||
})
|
})
|
||||||
export class CommonModule {}
|
export class CommonModule {}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,180 @@
|
||||||
|
import {
|
||||||
|
Controller,
|
||||||
|
Get,
|
||||||
|
Post,
|
||||||
|
Body,
|
||||||
|
Param,
|
||||||
|
UseGuards,
|
||||||
|
Req,
|
||||||
|
} from '@nestjs/common';
|
||||||
|
import {
|
||||||
|
ApiTags,
|
||||||
|
ApiOperation,
|
||||||
|
ApiResponse,
|
||||||
|
ApiBearerAuth,
|
||||||
|
ApiParam,
|
||||||
|
ApiBody,
|
||||||
|
} from '@nestjs/swagger';
|
||||||
|
import { AdminService } from '../services/admin.service';
|
||||||
|
import { SuperAdminGuard } from '../guards/super-admin.guard';
|
||||||
|
import { AdminGuard } from '../guards/admin.guard';
|
||||||
|
import { SetUserRoleDto } from '../dto/set-user-role.dto';
|
||||||
|
import { User } from '../../user/entities/user.entity';
|
||||||
|
import { UserRole } from '../enums/user-role.enum';
|
||||||
|
import {
|
||||||
|
UnauthorizedResponseDto,
|
||||||
|
NotFoundResponseDto,
|
||||||
|
} from '../dto/error-response.dto';
|
||||||
|
|
||||||
|
@ApiTags('管理员管理模块')
|
||||||
|
@ApiBearerAuth()
|
||||||
|
@Controller('admin')
|
||||||
|
export class AdminController {
|
||||||
|
constructor(private readonly adminService: AdminService) {}
|
||||||
|
|
||||||
|
@Get('users')
|
||||||
|
@UseGuards(AdminGuard)
|
||||||
|
@ApiOperation({
|
||||||
|
summary: '获取所有管理员用户',
|
||||||
|
description: '查询所有管理员和超级管理员用户列表。需要管理员权限',
|
||||||
|
})
|
||||||
|
@ApiResponse({
|
||||||
|
status: 200,
|
||||||
|
description: '查询成功',
|
||||||
|
type: [User],
|
||||||
|
})
|
||||||
|
@ApiResponse({
|
||||||
|
status: 401,
|
||||||
|
description: '未授权,需要管理员权限',
|
||||||
|
type: UnauthorizedResponseDto,
|
||||||
|
})
|
||||||
|
async getAdminUsers(): Promise<User[]> {
|
||||||
|
return this.adminService.getAdminUsers();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get('check')
|
||||||
|
@UseGuards(AdminGuard)
|
||||||
|
@ApiOperation({
|
||||||
|
summary: '检查当前用户权限',
|
||||||
|
description: '返回当前用户的角色和权限信息',
|
||||||
|
})
|
||||||
|
@ApiResponse({
|
||||||
|
status: 200,
|
||||||
|
description: '查询成功',
|
||||||
|
schema: {
|
||||||
|
example: {
|
||||||
|
userId: 1,
|
||||||
|
role: 'admin',
|
||||||
|
isAdmin: true,
|
||||||
|
isSuperAdmin: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
@ApiResponse({
|
||||||
|
status: 401,
|
||||||
|
description: '未授权',
|
||||||
|
type: UnauthorizedResponseDto,
|
||||||
|
})
|
||||||
|
async checkPermission(@Req() req: any): Promise<{
|
||||||
|
userId: number;
|
||||||
|
role: UserRole | null;
|
||||||
|
isAdmin: boolean;
|
||||||
|
isSuperAdmin: boolean;
|
||||||
|
}> {
|
||||||
|
const userId = req.user?.userId || req.user?.id;
|
||||||
|
const role = await this.adminService.getUserRole(userId);
|
||||||
|
const isAdmin = await this.adminService.isAdmin(userId);
|
||||||
|
const isSuperAdmin = await this.adminService.isSuperAdmin(userId);
|
||||||
|
|
||||||
|
return {
|
||||||
|
userId,
|
||||||
|
role,
|
||||||
|
isAdmin,
|
||||||
|
isSuperAdmin,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Post('users/:id/role')
|
||||||
|
@UseGuards(SuperAdminGuard)
|
||||||
|
@ApiOperation({
|
||||||
|
summary: '设置用户角色(超级管理员)',
|
||||||
|
description: '修改指定用户的角色。仅超级管理员可访问',
|
||||||
|
})
|
||||||
|
@ApiParam({
|
||||||
|
name: 'id',
|
||||||
|
description: '用户ID',
|
||||||
|
type: Number,
|
||||||
|
})
|
||||||
|
@ApiBody({ type: SetUserRoleDto })
|
||||||
|
@ApiResponse({
|
||||||
|
status: 200,
|
||||||
|
description: '角色设置成功',
|
||||||
|
schema: {
|
||||||
|
example: {
|
||||||
|
message: '用户角色已更新',
|
||||||
|
user: {
|
||||||
|
id: 1,
|
||||||
|
username: 'testuser',
|
||||||
|
email: 'test@example.com',
|
||||||
|
role: 'admin',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
@ApiResponse({
|
||||||
|
status: 401,
|
||||||
|
description: '未授权,需要超级管理员权限',
|
||||||
|
type: UnauthorizedResponseDto,
|
||||||
|
})
|
||||||
|
@ApiResponse({
|
||||||
|
status: 404,
|
||||||
|
description: '用户不存在',
|
||||||
|
type: NotFoundResponseDto,
|
||||||
|
})
|
||||||
|
async setUserRole(
|
||||||
|
@Param('id') id: string,
|
||||||
|
@Body() dto: SetUserRoleDto,
|
||||||
|
): Promise<{ message: string; user: User }> {
|
||||||
|
const user = await this.adminService.setUserRole(+id, dto.role);
|
||||||
|
return {
|
||||||
|
message: '用户角色已更新',
|
||||||
|
user,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get('users/:id/role')
|
||||||
|
@UseGuards(AdminGuard)
|
||||||
|
@ApiOperation({
|
||||||
|
summary: '查询用户角色',
|
||||||
|
description: '获取指定用户的角色信息。需要管理员权限',
|
||||||
|
})
|
||||||
|
@ApiParam({
|
||||||
|
name: 'id',
|
||||||
|
description: '用户ID',
|
||||||
|
type: Number,
|
||||||
|
})
|
||||||
|
@ApiResponse({
|
||||||
|
status: 200,
|
||||||
|
description: '查询成功',
|
||||||
|
schema: {
|
||||||
|
example: {
|
||||||
|
userId: 1,
|
||||||
|
role: 'admin',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
@ApiResponse({
|
||||||
|
status: 401,
|
||||||
|
description: '未授权,需要管理员权限',
|
||||||
|
type: UnauthorizedResponseDto,
|
||||||
|
})
|
||||||
|
async getUserRole(
|
||||||
|
@Param('id') id: string,
|
||||||
|
): Promise<{ userId: number; role: UserRole | null }> {
|
||||||
|
const role = await this.adminService.getUserRole(+id);
|
||||||
|
return {
|
||||||
|
userId: +id,
|
||||||
|
role,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
import { ApiProperty } from '@nestjs/swagger';
|
||||||
|
import { IsEnum, IsNotEmpty } from 'class-validator';
|
||||||
|
import { UserRole } from '../enums/user-role.enum';
|
||||||
|
|
||||||
|
export class SetUserRoleDto {
|
||||||
|
@ApiProperty({
|
||||||
|
description: '用户角色',
|
||||||
|
enum: UserRole,
|
||||||
|
example: UserRole.ADMIN,
|
||||||
|
})
|
||||||
|
@IsEnum(UserRole, { message: '角色必须是有效的枚举值' })
|
||||||
|
@IsNotEmpty({ message: '角色不能为空' })
|
||||||
|
role: UserRole;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
/**
|
||||||
|
* 用户角色枚举
|
||||||
|
*/
|
||||||
|
export enum UserRole {
|
||||||
|
/** 普通用户 */
|
||||||
|
USER = 'user',
|
||||||
|
/** 管理员 */
|
||||||
|
ADMIN = 'admin',
|
||||||
|
/** 超级管理员 */
|
||||||
|
SUPER_ADMIN = 'super_admin',
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
import {
|
||||||
|
Injectable,
|
||||||
|
CanActivate,
|
||||||
|
ExecutionContext,
|
||||||
|
ForbiddenException,
|
||||||
|
} from '@nestjs/common';
|
||||||
|
import { AdminService } from '../services/admin.service';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 超级管理员守卫
|
||||||
|
* 仅允许超级管理员访问
|
||||||
|
*/
|
||||||
|
@Injectable()
|
||||||
|
export class SuperAdminGuard implements CanActivate {
|
||||||
|
constructor(private adminService: AdminService) {}
|
||||||
|
|
||||||
|
async canActivate(context: ExecutionContext): Promise<boolean> {
|
||||||
|
const request = context.switchToHttp().getRequest();
|
||||||
|
const user = request.user;
|
||||||
|
|
||||||
|
if (!user || !user.id) {
|
||||||
|
throw new ForbiddenException('请先登录');
|
||||||
|
}
|
||||||
|
|
||||||
|
const isSuperAdmin = await this.adminService.isSuperAdmin(user.id);
|
||||||
|
|
||||||
|
if (!isSuperAdmin) {
|
||||||
|
throw new ForbiddenException('需要超级管理员权限');
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
import { Injectable } from '@nestjs/common';
|
import { Injectable } from '@nestjs/common';
|
||||||
import { InjectRepository } from '@nestjs/typeorm';
|
import { InjectRepository } from '@nestjs/typeorm';
|
||||||
import { Repository } from 'typeorm';
|
import { Repository, In } from 'typeorm';
|
||||||
import { User } from '../../user/entities/user.entity';
|
import { User } from '../../user/entities/user.entity';
|
||||||
|
import { UserRole } from '../enums/user-role.enum';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class AdminService {
|
export class AdminService {
|
||||||
|
|
@ -12,43 +13,77 @@ export class AdminService {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 检查用户是否是管理员
|
* 检查用户是否是管理员
|
||||||
* 简单实现:检查用户名是否为 admin 或邮箱包含 admin
|
* 基于角色的权限判断:ADMIN 或 SUPER_ADMIN
|
||||||
* 生产环境应该使用更复杂的角色系统
|
|
||||||
*/
|
*/
|
||||||
async isAdmin(userId: number): Promise<boolean> {
|
async isAdmin(userId: number): Promise<boolean> {
|
||||||
const user = await this.userRepository.findOne({
|
const user = await this.userRepository.findOne({
|
||||||
where: { id: userId },
|
where: { id: userId },
|
||||||
|
select: ['id', 'role', 'isActive'],
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!user) {
|
if (!user || !user.isActive) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 简单的管理员判断逻辑(可根据实际需求调整)
|
return user.role === UserRole.ADMIN || user.role === UserRole.SUPER_ADMIN;
|
||||||
return (
|
}
|
||||||
user.username === 'admin' ||
|
|
||||||
user.email.includes('admin') ||
|
/**
|
||||||
user.username.startsWith('admin_')
|
* 检查用户是否是超级管理员
|
||||||
);
|
*/
|
||||||
|
async isSuperAdmin(userId: number): Promise<boolean> {
|
||||||
|
const user = await this.userRepository.findOne({
|
||||||
|
where: { id: userId },
|
||||||
|
select: ['id', 'role', 'isActive'],
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!user || !user.isActive) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return user.role === UserRole.SUPER_ADMIN;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取所有管理员用户
|
* 获取所有管理员用户
|
||||||
*/
|
*/
|
||||||
async getAdminUsers(): Promise<User[]> {
|
async getAdminUsers(): Promise<User[]> {
|
||||||
const users = await this.userRepository.find();
|
return this.userRepository.find({
|
||||||
const admins: User[] = [];
|
where: {
|
||||||
|
role: In([UserRole.ADMIN, UserRole.SUPER_ADMIN]),
|
||||||
|
isActive: true,
|
||||||
|
},
|
||||||
|
order: {
|
||||||
|
role: 'DESC', // SUPER_ADMIN 排在前面
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
for (const user of users) {
|
/**
|
||||||
if (
|
* 设置用户角色(仅超级管理员可调用)
|
||||||
user.username === 'admin' ||
|
*/
|
||||||
user.email.includes('admin') ||
|
async setUserRole(userId: number, role: UserRole): Promise<User> {
|
||||||
user.username.startsWith('admin_')
|
const user = await this.userRepository.findOne({
|
||||||
) {
|
where: { id: userId },
|
||||||
admins.push(user);
|
});
|
||||||
}
|
|
||||||
|
if (!user) {
|
||||||
|
throw new Error('用户不存在');
|
||||||
}
|
}
|
||||||
|
|
||||||
return admins;
|
user.role = role;
|
||||||
|
return this.userRepository.save(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取用户角色
|
||||||
|
*/
|
||||||
|
async getUserRole(userId: number): Promise<UserRole | null> {
|
||||||
|
const user = await this.userRepository.findOne({
|
||||||
|
where: { id: userId },
|
||||||
|
select: ['id', 'role'],
|
||||||
|
});
|
||||||
|
|
||||||
|
return user?.role || null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -111,7 +111,7 @@ export class DeepseekService {
|
||||||
{
|
{
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
'Authorization': `Bearer ${apiKey}`,
|
Authorization: `Bearer ${apiKey}`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -221,7 +221,8 @@ export class LicenseController {
|
||||||
@UseGuards(AdminGuard)
|
@UseGuards(AdminGuard)
|
||||||
@ApiOperation({
|
@ApiOperation({
|
||||||
summary: '获取卡密统计信息(管理员)',
|
summary: '获取卡密统计信息(管理员)',
|
||||||
description: '获取卡密的统计数据,包括总数、已激活、未使用等。仅管理员可访问',
|
description:
|
||||||
|
'获取卡密的统计数据,包括总数、已激活、未使用等。仅管理员可访问',
|
||||||
})
|
})
|
||||||
@ApiResponse({
|
@ApiResponse({
|
||||||
status: 200,
|
status: 200,
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import { ApiProperty } from '@nestjs/swagger';
|
import { ApiProperty } from '@nestjs/swagger';
|
||||||
import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm';
|
import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm';
|
||||||
|
import { UserRole } from '../../common/enums/user-role.enum';
|
||||||
|
|
||||||
@Entity()
|
@Entity()
|
||||||
export class User {
|
export class User {
|
||||||
|
|
@ -19,6 +20,14 @@ export class User {
|
||||||
@ApiProperty({ description: '邮箱' })
|
@ApiProperty({ description: '邮箱' })
|
||||||
email: string;
|
email: string;
|
||||||
|
|
||||||
|
@Column({ type: 'varchar', default: UserRole.USER })
|
||||||
|
@ApiProperty({
|
||||||
|
description: '用户角色',
|
||||||
|
enum: UserRole,
|
||||||
|
default: UserRole.USER,
|
||||||
|
})
|
||||||
|
role: UserRole;
|
||||||
|
|
||||||
@Column({ default: true })
|
@Column({ default: true })
|
||||||
@ApiProperty({ description: '是否激活', default: true })
|
@ApiProperty({ description: '是否激活', default: true })
|
||||||
isActive: boolean;
|
isActive: boolean;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue