feat: 初始化 WWJ Cloud 企业级框架项目
- 后端:基于 NestJS 的分层架构设计 - 前端:基于 VbenAdmin + Element Plus 的管理系统 - 支持 SaaS + 独立版双架构模式 - 完整的用户权限管理系统 - 系统设置、文件上传、通知等核心功能 - 多租户支持和插件化扩展架构
This commit is contained in:
156
wwjcloud/src/common/admin/admin.controller.ts
Normal file
156
wwjcloud/src/common/admin/admin.controller.ts
Normal file
@@ -0,0 +1,156 @@
|
||||
import {
|
||||
Controller,
|
||||
Get,
|
||||
Post,
|
||||
Body,
|
||||
Patch,
|
||||
Param,
|
||||
Delete,
|
||||
Query,
|
||||
ParseIntPipe,
|
||||
HttpStatus,
|
||||
Req,
|
||||
} from '@nestjs/common';
|
||||
import { ApiTags, ApiOperation, ApiResponse } from '@nestjs/swagger';
|
||||
import { AdminService } from './admin.service';
|
||||
import { CreateAdminDto, UpdateAdminDto, QueryAdminDto } from './dto';
|
||||
import { SysUser } from './entities/sys-user.entity';
|
||||
import { Request } from 'express';
|
||||
|
||||
@ApiTags('管理员管理')
|
||||
@Controller('admin')
|
||||
export class AdminController {
|
||||
constructor(private readonly adminService: AdminService) {}
|
||||
|
||||
@Post()
|
||||
@ApiOperation({ summary: '创建管理员' })
|
||||
@ApiResponse({ status: HttpStatus.CREATED, description: '创建成功', type: SysUser })
|
||||
@ApiResponse({ status: HttpStatus.CONFLICT, description: '用户名或手机号已存在' })
|
||||
async create(@Body() createAdminDto: CreateAdminDto) {
|
||||
const admin = await this.adminService.create(createAdminDto);
|
||||
return {
|
||||
code: 200,
|
||||
message: '创建成功',
|
||||
data: admin,
|
||||
};
|
||||
}
|
||||
|
||||
@Get()
|
||||
@ApiOperation({ summary: '获取管理员列表' })
|
||||
@ApiResponse({ status: HttpStatus.OK, description: '获取成功' })
|
||||
async findAll(@Query() queryDto: QueryAdminDto) {
|
||||
const result = await this.adminService.findAll(queryDto);
|
||||
return {
|
||||
code: 200,
|
||||
message: '获取成功',
|
||||
data: result,
|
||||
};
|
||||
}
|
||||
|
||||
@Get(':id')
|
||||
@ApiOperation({ summary: '获取管理员详情' })
|
||||
@ApiResponse({ status: HttpStatus.OK, description: '获取成功', type: SysUser })
|
||||
@ApiResponse({ status: HttpStatus.NOT_FOUND, description: '管理员不存在' })
|
||||
async findOne(@Param('id', ParseIntPipe) id: number) {
|
||||
const admin = await this.adminService.findOne(id);
|
||||
return {
|
||||
code: 200,
|
||||
message: '获取成功',
|
||||
data: admin,
|
||||
};
|
||||
}
|
||||
|
||||
@Patch(':id')
|
||||
@ApiOperation({ summary: '更新管理员信息' })
|
||||
@ApiResponse({ status: HttpStatus.OK, description: '更新成功', type: SysUser })
|
||||
@ApiResponse({ status: HttpStatus.NOT_FOUND, description: '管理员不存在' })
|
||||
@ApiResponse({ status: HttpStatus.CONFLICT, description: '用户名或手机号已存在' })
|
||||
async update(
|
||||
@Param('id', ParseIntPipe) id: number,
|
||||
@Body() updateAdminDto: UpdateAdminDto,
|
||||
) {
|
||||
const admin = await this.adminService.update(id, updateAdminDto);
|
||||
return {
|
||||
code: 200,
|
||||
message: '更新成功',
|
||||
data: admin,
|
||||
};
|
||||
}
|
||||
|
||||
@Delete(':id')
|
||||
@ApiOperation({ summary: '删除管理员' })
|
||||
@ApiResponse({ status: HttpStatus.OK, description: '删除成功' })
|
||||
@ApiResponse({ status: HttpStatus.NOT_FOUND, description: '管理员不存在' })
|
||||
async remove(@Param('id', ParseIntPipe) id: number) {
|
||||
await this.adminService.remove(id);
|
||||
return {
|
||||
code: 200,
|
||||
message: '删除成功',
|
||||
};
|
||||
}
|
||||
|
||||
@Post('batch-delete')
|
||||
@ApiOperation({ summary: '批量删除管理员' })
|
||||
@ApiResponse({ status: HttpStatus.OK, description: '批量删除成功' })
|
||||
async batchRemove(@Body('ids') ids: number[]) {
|
||||
await this.adminService.batchRemove(ids);
|
||||
return {
|
||||
code: 200,
|
||||
message: '批量删除成功',
|
||||
};
|
||||
}
|
||||
|
||||
@Post(':id/update-last-login')
|
||||
@ApiOperation({ summary: '更新最后登录信息' })
|
||||
@ApiResponse({ status: HttpStatus.OK, description: '更新成功' })
|
||||
async updateLastLogin(
|
||||
@Param('id', ParseIntPipe) id: number,
|
||||
@Req() request: Request,
|
||||
) {
|
||||
const ip = request.ip || request.connection.remoteAddress || '';
|
||||
await this.adminService.updateLastLogin(id, ip);
|
||||
return {
|
||||
code: 200,
|
||||
message: '更新成功',
|
||||
};
|
||||
}
|
||||
|
||||
@Get('search/by-username/:username')
|
||||
@ApiOperation({ summary: '根据用户名查询管理员' })
|
||||
@ApiResponse({ status: HttpStatus.OK, description: '查询成功' })
|
||||
async findByUsername(@Param('username') username: string) {
|
||||
const admin = await this.adminService.findByUsername(username);
|
||||
return {
|
||||
code: 200,
|
||||
message: '查询成功',
|
||||
data: admin,
|
||||
};
|
||||
}
|
||||
|
||||
@Post(':id/assign-roles')
|
||||
@ApiOperation({ summary: '分配角色' })
|
||||
@ApiResponse({ status: HttpStatus.OK, description: '分配成功' })
|
||||
async assignRoles(
|
||||
@Param('id', ParseIntPipe) id: number,
|
||||
@Body('roleIds') roleIds: number[],
|
||||
@Body('siteId') siteId: number,
|
||||
) {
|
||||
await this.adminService.assignRoles(id, roleIds, siteId);
|
||||
return {
|
||||
code: 200,
|
||||
message: '分配成功',
|
||||
};
|
||||
}
|
||||
|
||||
@Get(':id/roles')
|
||||
@ApiOperation({ summary: '获取用户角色' })
|
||||
@ApiResponse({ status: HttpStatus.OK, description: '获取成功' })
|
||||
async getUserRoles(@Param('id', ParseIntPipe) id: number) {
|
||||
const roleIds = await this.adminService.getUserRoles(id);
|
||||
return {
|
||||
code: 200,
|
||||
message: '获取成功',
|
||||
data: roleIds,
|
||||
};
|
||||
}
|
||||
}
|
||||
14
wwjcloud/src/common/admin/admin.module.ts
Normal file
14
wwjcloud/src/common/admin/admin.module.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
import { AdminService } from './admin.service';
|
||||
import { AdminController } from './admin.controller';
|
||||
import { SysUser } from './entities/sys-user.entity';
|
||||
import { SysUserRole } from './entities/sys-user-role.entity';
|
||||
|
||||
@Module({
|
||||
imports: [TypeOrmModule.forFeature([SysUser, SysUserRole])],
|
||||
controllers: [AdminController],
|
||||
providers: [AdminService],
|
||||
exports: [AdminService, TypeOrmModule],
|
||||
})
|
||||
export class AdminModule {}
|
||||
311
wwjcloud/src/common/admin/admin.service.ts
Normal file
311
wwjcloud/src/common/admin/admin.service.ts
Normal file
@@ -0,0 +1,311 @@
|
||||
import { Injectable, NotFoundException, ConflictException } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { Repository, In } from 'typeorm';
|
||||
import { SysUser } from './entities/sys-user.entity';
|
||||
import { SysUserRole } from './entities/sys-user-role.entity';
|
||||
import { CreateAdminDto, UpdateAdminDto, QueryAdminDto } from './dto';
|
||||
import * as bcrypt from 'bcrypt';
|
||||
|
||||
@Injectable()
|
||||
export class AdminService {
|
||||
constructor(
|
||||
@InjectRepository(SysUser)
|
||||
private readonly sysUserRepository: Repository<SysUser>,
|
||||
@InjectRepository(SysUserRole)
|
||||
private readonly sysUserRoleRepository: Repository<SysUserRole>,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* 创建管理员
|
||||
*/
|
||||
async create(createAdminDto: CreateAdminDto): Promise<SysUser> {
|
||||
// 检查用户名是否已存在
|
||||
const existingByUsername = await this.sysUserRepository.findOne({
|
||||
where: { username: createAdminDto.username, deleteTime: 0 },
|
||||
});
|
||||
if (existingByUsername) {
|
||||
throw new ConflictException('用户名已存在');
|
||||
}
|
||||
|
||||
// 检查手机号是否已存在
|
||||
if (createAdminDto.mobile) {
|
||||
const existingByMobile = await this.sysUserRepository.findOne({
|
||||
where: { mobile: createAdminDto.mobile, deleteTime: 0 },
|
||||
});
|
||||
if (existingByMobile) {
|
||||
throw new ConflictException('手机号已存在');
|
||||
}
|
||||
}
|
||||
|
||||
// 密码加密
|
||||
const hashedPassword = await bcrypt.hash(createAdminDto.password, 10);
|
||||
|
||||
const { roleIds, ...adminData } = createAdminDto;
|
||||
|
||||
const admin = this.sysUserRepository.create({
|
||||
...adminData,
|
||||
password: hashedPassword,
|
||||
createTime: Math.floor(Date.now() / 1000),
|
||||
updateTime: Math.floor(Date.now() / 1000),
|
||||
});
|
||||
|
||||
const savedAdmin = await this.sysUserRepository.save(admin);
|
||||
|
||||
// 分配角色
|
||||
if (roleIds && roleIds.length > 0) {
|
||||
await this.assignRoles(savedAdmin.uid, roleIds, createAdminDto.siteId);
|
||||
}
|
||||
|
||||
return await this.findOne(savedAdmin.uid);
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页查询管理员列表
|
||||
*/
|
||||
async findAll(queryDto: QueryAdminDto) {
|
||||
const { page = 1, limit = 10, keyword, siteId, sex, status, isAdmin, roleId, startTime, endTime } = queryDto;
|
||||
const skip = (page - 1) * limit;
|
||||
|
||||
const queryBuilder = this.sysUserRepository.createQueryBuilder('admin')
|
||||
.leftJoinAndSelect('admin.userRoles', 'userRole')
|
||||
.where('admin.deleteTime = :deleteTime', { deleteTime: 0 });
|
||||
|
||||
// 关键词搜索
|
||||
if (keyword) {
|
||||
queryBuilder.andWhere(
|
||||
'(admin.username LIKE :keyword OR admin.realName LIKE :keyword OR admin.mobile LIKE :keyword)',
|
||||
{ keyword: `%${keyword}%` }
|
||||
);
|
||||
}
|
||||
|
||||
// 站点ID筛选
|
||||
if (siteId !== undefined) {
|
||||
queryBuilder.andWhere('admin.siteId = :siteId', { siteId });
|
||||
}
|
||||
|
||||
// 性别筛选
|
||||
if (sex !== undefined) {
|
||||
queryBuilder.andWhere('admin.sex = :sex', { sex });
|
||||
}
|
||||
|
||||
// 状态筛选
|
||||
if (status !== undefined) {
|
||||
queryBuilder.andWhere('admin.status = :status', { status });
|
||||
}
|
||||
|
||||
// 超级管理员筛选
|
||||
if (isAdmin !== undefined) {
|
||||
queryBuilder.andWhere('admin.isAdmin = :isAdmin', { isAdmin });
|
||||
}
|
||||
|
||||
// 角色筛选
|
||||
if (roleId !== undefined) {
|
||||
queryBuilder.andWhere('userRole.roleId = :roleId', { roleId });
|
||||
}
|
||||
|
||||
// 时间范围筛选
|
||||
if (startTime && endTime) {
|
||||
queryBuilder.andWhere('admin.createTime BETWEEN :startTime AND :endTime', {
|
||||
startTime,
|
||||
endTime,
|
||||
});
|
||||
} else if (startTime) {
|
||||
queryBuilder.andWhere('admin.createTime >= :startTime', { startTime });
|
||||
} else if (endTime) {
|
||||
queryBuilder.andWhere('admin.createTime <= :endTime', { endTime });
|
||||
}
|
||||
|
||||
// 排序
|
||||
queryBuilder.orderBy('admin.createTime', 'DESC');
|
||||
|
||||
// 分页
|
||||
const [list, total] = await queryBuilder
|
||||
.skip(skip)
|
||||
.take(limit)
|
||||
.getManyAndCount();
|
||||
|
||||
// 移除密码字段
|
||||
const safeList = list.map(admin => {
|
||||
const { password, ...safeAdmin } = admin;
|
||||
return {
|
||||
...safeAdmin,
|
||||
roleIds: admin.userRoles?.map(ur => ur.roleId) || [],
|
||||
};
|
||||
});
|
||||
|
||||
return {
|
||||
list: safeList,
|
||||
total,
|
||||
page,
|
||||
limit,
|
||||
totalPages: Math.ceil(total / limit),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据ID查询管理员详情
|
||||
*/
|
||||
async findOne(id: number): Promise<SysUser> {
|
||||
const admin = await this.sysUserRepository.findOne({
|
||||
where: { uid: id, deleteTime: 0 },
|
||||
relations: ['userRoles'],
|
||||
});
|
||||
|
||||
if (!admin) {
|
||||
throw new NotFoundException('管理员不存在');
|
||||
}
|
||||
|
||||
// 移除密码字段
|
||||
const { password, ...safeAdmin } = admin;
|
||||
return {
|
||||
...safeAdmin,
|
||||
roleIds: admin.userRoles?.map(ur => ur.roleId) || [],
|
||||
} as any;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据用户名查询管理员
|
||||
*/
|
||||
async findByUsername(username: string): Promise<SysUser | null> {
|
||||
return await this.sysUserRepository.findOne({
|
||||
where: { username, deleteTime: 0 },
|
||||
relations: ['userRoles'],
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新管理员信息
|
||||
*/
|
||||
async update(id: number, updateAdminDto: UpdateAdminDto): Promise<SysUser> {
|
||||
const admin = await this.findOne(id);
|
||||
|
||||
// 检查用户名是否已被其他用户使用
|
||||
if (updateAdminDto.username && updateAdminDto.username !== admin.username) {
|
||||
const existingByUsername = await this.sysUserRepository.findOne({
|
||||
where: { username: updateAdminDto.username, deleteTime: 0 },
|
||||
});
|
||||
if (existingByUsername && existingByUsername.uid !== id) {
|
||||
throw new ConflictException('用户名已存在');
|
||||
}
|
||||
}
|
||||
|
||||
// 检查手机号是否已被其他用户使用
|
||||
if (updateAdminDto.mobile && updateAdminDto.mobile !== admin.mobile) {
|
||||
const existingByMobile = await this.sysUserRepository.findOne({
|
||||
where: { mobile: updateAdminDto.mobile, deleteTime: 0 },
|
||||
});
|
||||
if (existingByMobile && existingByMobile.uid !== id) {
|
||||
throw new ConflictException('手机号已存在');
|
||||
}
|
||||
}
|
||||
|
||||
const { roleIds, ...adminData } = updateAdminDto;
|
||||
|
||||
// 如果更新密码,需要加密
|
||||
if (adminData.password) {
|
||||
adminData.password = await bcrypt.hash(adminData.password, 10);
|
||||
}
|
||||
|
||||
await this.sysUserRepository.update(id, {
|
||||
...adminData,
|
||||
updateTime: Math.floor(Date.now() / 1000),
|
||||
});
|
||||
|
||||
// 更新角色分配
|
||||
if (roleIds !== undefined) {
|
||||
await this.updateRoles(id, roleIds, admin.siteId);
|
||||
}
|
||||
|
||||
return await this.findOne(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* 软删除管理员
|
||||
*/
|
||||
async remove(id: number): Promise<void> {
|
||||
const admin = await this.findOne(id);
|
||||
|
||||
await this.sysUserRepository.update(id, {
|
||||
deleteTime: Math.floor(Date.now() / 1000),
|
||||
updateTime: Math.floor(Date.now() / 1000),
|
||||
});
|
||||
|
||||
// 删除用户角色关联
|
||||
await this.sysUserRoleRepository.delete({ uid: id });
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量软删除管理员
|
||||
*/
|
||||
async batchRemove(ids: number[]): Promise<void> {
|
||||
const deleteTime = Math.floor(Date.now() / 1000);
|
||||
|
||||
await this.sysUserRepository.update(
|
||||
{ uid: In(ids) },
|
||||
{
|
||||
deleteTime,
|
||||
updateTime: deleteTime,
|
||||
}
|
||||
);
|
||||
|
||||
// 删除用户角色关联
|
||||
await this.sysUserRoleRepository.delete({ uid: In(ids) });
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新最后登录信息
|
||||
*/
|
||||
async updateLastLogin(id: number, ip: string): Promise<void> {
|
||||
const now = Math.floor(Date.now() / 1000);
|
||||
await this.sysUserRepository.update(id, {
|
||||
lastTime: now,
|
||||
lastIp: ip,
|
||||
updateTime: now,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证密码
|
||||
*/
|
||||
async validatePassword(admin: SysUser, password: string): Promise<boolean> {
|
||||
return await bcrypt.compare(password, admin.password);
|
||||
}
|
||||
|
||||
/**
|
||||
* 分配角色
|
||||
*/
|
||||
async assignRoles(uid: number, roleIds: number[], siteId: number): Promise<void> {
|
||||
const userRoles = roleIds.map(roleId =>
|
||||
this.sysUserRoleRepository.create({
|
||||
uid,
|
||||
roleId,
|
||||
siteId,
|
||||
})
|
||||
);
|
||||
|
||||
await this.sysUserRoleRepository.save(userRoles);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新用户角色
|
||||
*/
|
||||
async updateRoles(uid: number, roleIds: number[], siteId: number): Promise<void> {
|
||||
// 删除现有角色
|
||||
await this.sysUserRoleRepository.delete({ uid });
|
||||
|
||||
// 分配新角色
|
||||
if (roleIds.length > 0) {
|
||||
await this.assignRoles(uid, roleIds, siteId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户角色
|
||||
*/
|
||||
async getUserRoles(uid: number): Promise<number[]> {
|
||||
const userRoles = await this.sysUserRoleRepository.find({
|
||||
where: { uid },
|
||||
});
|
||||
return userRoles.map(ur => ur.roleId);
|
||||
}
|
||||
}
|
||||
94
wwjcloud/src/common/admin/dto/create-admin.dto.ts
Normal file
94
wwjcloud/src/common/admin/dto/create-admin.dto.ts
Normal file
@@ -0,0 +1,94 @@
|
||||
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
|
||||
import { IsString, IsOptional, IsInt, IsEmail, IsIn, Length, IsArray } from 'class-validator';
|
||||
import { Transform } from 'class-transformer';
|
||||
|
||||
export class CreateAdminDto {
|
||||
@ApiProperty({ description: '站点ID' })
|
||||
@IsInt()
|
||||
@Transform(({ value }) => parseInt(value))
|
||||
siteId: number;
|
||||
|
||||
@ApiProperty({ description: '用户名' })
|
||||
@IsString()
|
||||
@Length(1, 255)
|
||||
username: string;
|
||||
|
||||
@ApiProperty({ description: '密码' })
|
||||
@IsString()
|
||||
@Length(6, 255)
|
||||
password: string;
|
||||
|
||||
@ApiProperty({ description: '真实姓名' })
|
||||
@IsString()
|
||||
@Length(1, 255)
|
||||
realName: string;
|
||||
|
||||
@ApiPropertyOptional({ description: '头像' })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
headImg?: string;
|
||||
|
||||
@ApiPropertyOptional({ description: '手机号' })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
@Length(11, 11)
|
||||
mobile?: string;
|
||||
|
||||
@ApiPropertyOptional({ description: '邮箱' })
|
||||
@IsOptional()
|
||||
@IsEmail()
|
||||
email?: string;
|
||||
|
||||
@ApiPropertyOptional({ description: '性别:1男 2女 0保密' })
|
||||
@IsOptional()
|
||||
@IsIn([0, 1, 2])
|
||||
@Transform(({ value }) => parseInt(value))
|
||||
sex?: number;
|
||||
|
||||
@ApiPropertyOptional({ description: '生日' })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
birthday?: string;
|
||||
|
||||
@ApiPropertyOptional({ description: '省份ID' })
|
||||
@IsOptional()
|
||||
@IsInt()
|
||||
@Transform(({ value }) => parseInt(value))
|
||||
pid?: number;
|
||||
|
||||
@ApiPropertyOptional({ description: '城市ID' })
|
||||
@IsOptional()
|
||||
@IsInt()
|
||||
@Transform(({ value }) => parseInt(value))
|
||||
cid?: number;
|
||||
|
||||
@ApiPropertyOptional({ description: '区县ID' })
|
||||
@IsOptional()
|
||||
@IsInt()
|
||||
@Transform(({ value }) => parseInt(value))
|
||||
did?: number;
|
||||
|
||||
@ApiPropertyOptional({ description: '详细地址' })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
address?: string;
|
||||
|
||||
@ApiPropertyOptional({ description: '状态:1正常 0禁用', default: 1 })
|
||||
@IsOptional()
|
||||
@IsIn([0, 1])
|
||||
@Transform(({ value }) => parseInt(value))
|
||||
status?: number;
|
||||
|
||||
@ApiPropertyOptional({ description: '是否超级管理员:1是 0否', default: 0 })
|
||||
@IsOptional()
|
||||
@IsIn([0, 1])
|
||||
@Transform(({ value }) => parseInt(value))
|
||||
isAdmin?: number;
|
||||
|
||||
@ApiPropertyOptional({ description: '角色ID数组' })
|
||||
@IsOptional()
|
||||
@IsArray()
|
||||
@IsInt({ each: true })
|
||||
@Transform(({ value }) => Array.isArray(value) ? value.map(v => parseInt(v)) : [])
|
||||
roleIds?: number[];
|
||||
}
|
||||
3
wwjcloud/src/common/admin/dto/index.ts
Normal file
3
wwjcloud/src/common/admin/dto/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export { CreateAdminDto } from './create-admin.dto';
|
||||
export { UpdateAdminDto } from './update-admin.dto';
|
||||
export { QueryAdminDto } from './query-admin.dto';
|
||||
64
wwjcloud/src/common/admin/dto/query-admin.dto.ts
Normal file
64
wwjcloud/src/common/admin/dto/query-admin.dto.ts
Normal file
@@ -0,0 +1,64 @@
|
||||
import { ApiPropertyOptional } from '@nestjs/swagger';
|
||||
import { IsOptional, IsString, IsInt, IsIn } from 'class-validator';
|
||||
import { Transform } from 'class-transformer';
|
||||
|
||||
export class QueryAdminDto {
|
||||
@ApiPropertyOptional({ description: '页码', default: 1 })
|
||||
@IsOptional()
|
||||
@IsInt()
|
||||
@Transform(({ value }) => parseInt(value) || 1)
|
||||
page?: number = 1;
|
||||
|
||||
@ApiPropertyOptional({ description: '每页数量', default: 10 })
|
||||
@IsOptional()
|
||||
@IsInt()
|
||||
@Transform(({ value }) => parseInt(value) || 10)
|
||||
limit?: number = 10;
|
||||
|
||||
@ApiPropertyOptional({ description: '关键词搜索(用户名/真实姓名/手机号)' })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
keyword?: string;
|
||||
|
||||
@ApiPropertyOptional({ description: '站点ID' })
|
||||
@IsOptional()
|
||||
@IsInt()
|
||||
@Transform(({ value }) => parseInt(value))
|
||||
siteId?: number;
|
||||
|
||||
@ApiPropertyOptional({ description: '性别:1男 2女 0保密' })
|
||||
@IsOptional()
|
||||
@IsIn([0, 1, 2])
|
||||
@Transform(({ value }) => parseInt(value))
|
||||
sex?: number;
|
||||
|
||||
@ApiPropertyOptional({ description: '状态:1正常 0禁用' })
|
||||
@IsOptional()
|
||||
@IsIn([0, 1])
|
||||
@Transform(({ value }) => parseInt(value))
|
||||
status?: number;
|
||||
|
||||
@ApiPropertyOptional({ description: '是否超级管理员:1是 0否' })
|
||||
@IsOptional()
|
||||
@IsIn([0, 1])
|
||||
@Transform(({ value }) => parseInt(value))
|
||||
isAdmin?: number;
|
||||
|
||||
@ApiPropertyOptional({ description: '角色ID' })
|
||||
@IsOptional()
|
||||
@IsInt()
|
||||
@Transform(({ value }) => parseInt(value))
|
||||
roleId?: number;
|
||||
|
||||
@ApiPropertyOptional({ description: '开始时间(时间戳)' })
|
||||
@IsOptional()
|
||||
@IsInt()
|
||||
@Transform(({ value }) => parseInt(value))
|
||||
startTime?: number;
|
||||
|
||||
@ApiPropertyOptional({ description: '结束时间(时间戳)' })
|
||||
@IsOptional()
|
||||
@IsInt()
|
||||
@Transform(({ value }) => parseInt(value))
|
||||
endTime?: number;
|
||||
}
|
||||
4
wwjcloud/src/common/admin/dto/update-admin.dto.ts
Normal file
4
wwjcloud/src/common/admin/dto/update-admin.dto.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
import { PartialType } from '@nestjs/swagger';
|
||||
import { CreateAdminDto } from './create-admin.dto';
|
||||
|
||||
export class UpdateAdminDto extends PartialType(CreateAdminDto) {}
|
||||
27
wwjcloud/src/common/admin/entities/sys-user-role.entity.ts
Normal file
27
wwjcloud/src/common/admin/entities/sys-user-role.entity.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import { Entity, PrimaryGeneratedColumn, Column, ManyToOne, JoinColumn } from 'typeorm';
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { SysUser } from './sys-user.entity';
|
||||
|
||||
@Entity('sys_user_role')
|
||||
export class SysUserRole {
|
||||
@ApiProperty({ description: '主键ID' })
|
||||
@PrimaryGeneratedColumn({ name: 'id', type: 'int', unsigned: true })
|
||||
id: number;
|
||||
|
||||
@ApiProperty({ description: '用户ID' })
|
||||
@Column({ name: 'uid', type: 'int', default: 0 })
|
||||
uid: number;
|
||||
|
||||
@ApiProperty({ description: '角色ID' })
|
||||
@Column({ name: 'role_id', type: 'int', default: 0 })
|
||||
roleId: number;
|
||||
|
||||
@ApiProperty({ description: '站点ID' })
|
||||
@Column({ name: 'site_id', type: 'int', default: 0 })
|
||||
siteId: number;
|
||||
|
||||
// 关联用户
|
||||
@ManyToOne(() => SysUser, user => user.userRoles)
|
||||
@JoinColumn({ name: 'uid' })
|
||||
user: SysUser;
|
||||
}
|
||||
94
wwjcloud/src/common/admin/entities/sys-user.entity.ts
Normal file
94
wwjcloud/src/common/admin/entities/sys-user.entity.ts
Normal file
@@ -0,0 +1,94 @@
|
||||
import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn, OneToMany } from 'typeorm';
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { SysUserRole } from './sys-user-role.entity';
|
||||
|
||||
@Entity('sys_user')
|
||||
export class SysUser {
|
||||
@ApiProperty({ description: '用户ID' })
|
||||
@PrimaryGeneratedColumn({ name: 'uid', type: 'int', unsigned: true })
|
||||
uid: number;
|
||||
|
||||
@ApiProperty({ description: '站点ID' })
|
||||
@Column({ name: 'site_id', type: 'int', default: 0 })
|
||||
siteId: number;
|
||||
|
||||
@ApiProperty({ description: '用户名' })
|
||||
@Column({ name: 'username', type: 'varchar', length: 255, default: '' })
|
||||
username: string;
|
||||
|
||||
@ApiProperty({ description: '密码' })
|
||||
@Column({ name: 'password', type: 'varchar', length: 255, default: '' })
|
||||
password: string;
|
||||
|
||||
@ApiProperty({ description: '真实姓名' })
|
||||
@Column({ name: 'real_name', type: 'varchar', length: 255, default: '' })
|
||||
realName: string;
|
||||
|
||||
@ApiProperty({ description: '头像' })
|
||||
@Column({ name: 'head_img', type: 'varchar', length: 255, default: '' })
|
||||
headImg: string;
|
||||
|
||||
@ApiProperty({ description: '手机号' })
|
||||
@Column({ name: 'mobile', type: 'varchar', length: 20, default: '' })
|
||||
mobile: string;
|
||||
|
||||
@ApiProperty({ description: '邮箱' })
|
||||
@Column({ name: 'email', type: 'varchar', length: 255, default: '' })
|
||||
email: string;
|
||||
|
||||
@ApiProperty({ description: '性别:1男 2女 0保密' })
|
||||
@Column({ name: 'sex', type: 'tinyint', default: 0 })
|
||||
sex: number;
|
||||
|
||||
@ApiProperty({ description: '生日' })
|
||||
@Column({ name: 'birthday', type: 'varchar', length: 255, default: '' })
|
||||
birthday: string;
|
||||
|
||||
@ApiProperty({ description: '省份ID' })
|
||||
@Column({ name: 'pid', type: 'int', default: 0 })
|
||||
pid: number;
|
||||
|
||||
@ApiProperty({ description: '城市ID' })
|
||||
@Column({ name: 'cid', type: 'int', default: 0 })
|
||||
cid: number;
|
||||
|
||||
@ApiProperty({ description: '区县ID' })
|
||||
@Column({ name: 'did', type: 'int', default: 0 })
|
||||
did: number;
|
||||
|
||||
@ApiProperty({ description: '详细地址' })
|
||||
@Column({ name: 'address', type: 'varchar', length: 255, default: '' })
|
||||
address: string;
|
||||
|
||||
@ApiProperty({ description: '状态:1正常 0禁用' })
|
||||
@Column({ name: 'status', type: 'tinyint', default: 1 })
|
||||
status: number;
|
||||
|
||||
@ApiProperty({ description: '是否超级管理员:1是 0否' })
|
||||
@Column({ name: 'is_admin', type: 'tinyint', default: 0 })
|
||||
isAdmin: number;
|
||||
|
||||
@ApiProperty({ description: '最后登录时间' })
|
||||
@Column({ name: 'last_time', type: 'int', default: 0 })
|
||||
lastTime: number;
|
||||
|
||||
@ApiProperty({ description: '最后登录IP' })
|
||||
@Column({ name: 'last_ip', type: 'varchar', length: 255, default: '' })
|
||||
lastIp: string;
|
||||
|
||||
@ApiProperty({ description: '创建时间' })
|
||||
@CreateDateColumn({ name: 'create_time', type: 'int' })
|
||||
createTime: number;
|
||||
|
||||
@ApiProperty({ description: '更新时间' })
|
||||
@UpdateDateColumn({ name: 'update_time', type: 'int' })
|
||||
updateTime: number;
|
||||
|
||||
@ApiProperty({ description: '删除时间' })
|
||||
@Column({ name: 'delete_time', type: 'int', default: 0 })
|
||||
deleteTime: number;
|
||||
|
||||
// 关联用户角色
|
||||
@OneToMany(() => SysUserRole, userRole => userRole.user)
|
||||
userRoles: SysUserRole[];
|
||||
}
|
||||
6
wwjcloud/src/common/admin/index.ts
Normal file
6
wwjcloud/src/common/admin/index.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
export { AdminModule } from './admin.module';
|
||||
export { AdminService } from './admin.service';
|
||||
export { AdminController } from './admin.controller';
|
||||
export { SysUser } from './entities/sys-user.entity';
|
||||
export { SysUserRole } from './entities/sys-user-role.entity';
|
||||
export * from './dto';
|
||||
Reference in New Issue
Block a user