feat(auth): 注册接口 POST /auth/register
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
537ec4fd91
commit
53dbc38bab
@ -1,7 +1,7 @@
|
|||||||
import { Body, Controller, Get, Post, Req, UnauthorizedException } from '@nestjs/common';
|
import { Body, Controller, Get, Post, Req, UnauthorizedException } from '@nestjs/common';
|
||||||
import type { Request } from 'express';
|
import type { Request } from 'express';
|
||||||
import { AuthService } from './auth.service.js';
|
import { AuthService } from './auth.service.js';
|
||||||
import { LoginDto, RefreshDto } from './dto.js';
|
import { LoginDto, RefreshDto, RegisterDto } from './dto.js';
|
||||||
import { Public } from './decorators.js';
|
import { Public } from './decorators.js';
|
||||||
import { getRequestContext } from '../common/context/request-context.js';
|
import { getRequestContext } from '../common/context/request-context.js';
|
||||||
|
|
||||||
@ -15,6 +15,12 @@ export class AuthController {
|
|||||||
return this.auth.login(dto, req.ip, req.headers['user-agent']);
|
return this.auth.login(dto, req.ip, req.headers['user-agent']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Public()
|
||||||
|
@Post('register')
|
||||||
|
register(@Body() dto: RegisterDto) {
|
||||||
|
return this.auth.register(dto);
|
||||||
|
}
|
||||||
|
|
||||||
@Public()
|
@Public()
|
||||||
@Post('refresh')
|
@Post('refresh')
|
||||||
refresh(@Body() dto: RefreshDto) {
|
refresh(@Body() dto: RefreshDto) {
|
||||||
|
|||||||
@ -1,10 +1,10 @@
|
|||||||
import { Injectable, UnauthorizedException } from '@nestjs/common';
|
import { BadRequestException, ConflictException, Injectable, UnauthorizedException } from '@nestjs/common';
|
||||||
import { JwtService } from '@nestjs/jwt';
|
import { JwtService } from '@nestjs/jwt';
|
||||||
import bcrypt from 'bcryptjs';
|
import bcrypt from 'bcryptjs';
|
||||||
import { createHash, randomUUID } from 'node:crypto';
|
import { createHash, randomUUID } from 'node:crypto';
|
||||||
import { PrismaService } from '../prisma/prisma.service.js';
|
import { PrismaService } from '../prisma/prisma.service.js';
|
||||||
import { RedisService } from '../common/redis/redis.service.js';
|
import { RedisService } from '../common/redis/redis.service.js';
|
||||||
import { LoginDto, RefreshDto } from './dto.js';
|
import { LoginDto, RefreshDto, RegisterDto } from './dto.js';
|
||||||
import { resolveDataScope, resolveVisibleOrgs } from '../common/data-scope.js';
|
import { resolveDataScope, resolveVisibleOrgs } from '../common/data-scope.js';
|
||||||
import type { DataScope } from '../common/enums.js';
|
import type { DataScope } from '../common/enums.js';
|
||||||
import type { RequestContext } from '../common/context/request-context.js';
|
import type { RequestContext } from '../common/context/request-context.js';
|
||||||
@ -118,6 +118,28 @@ export class AuthService {
|
|||||||
return this.issue(dto.tenantCode, user.id, user.username, user.tokenVersion);
|
return this.issue(dto.tenantCode, user.id, user.username, user.tokenVersion);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 注册:在指定租户下创建普通用户(无角色,状态启用)。 */
|
||||||
|
async register(dto: RegisterDto): Promise<{ id: string; username: string }> {
|
||||||
|
const tenant = await this.prisma.client.tenant.findUnique({ where: { code: dto.tenantCode } });
|
||||||
|
if (!tenant || tenant.status !== 'enabled') throw new BadRequestException('租户不存在或已停用');
|
||||||
|
const exists = await this.prisma.client.user.findFirst({
|
||||||
|
where: { tenantCode: dto.tenantCode, username: dto.username, deletedAt: null },
|
||||||
|
});
|
||||||
|
if (exists) throw new ConflictException('用户名已存在');
|
||||||
|
const passwordHash = await bcrypt.hash(dto.password, 10);
|
||||||
|
const user = await this.prisma.client.user.create({
|
||||||
|
data: {
|
||||||
|
tenantCode: dto.tenantCode,
|
||||||
|
username: dto.username,
|
||||||
|
passwordHash,
|
||||||
|
email: dto.email ?? null,
|
||||||
|
phone: dto.phone ?? null,
|
||||||
|
status: 'enabled',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return { id: user.id, username: user.username };
|
||||||
|
}
|
||||||
|
|
||||||
async refresh(dto: RefreshDto): Promise<{ accessToken: string; refreshToken: string; expiresIn: number }> {
|
async refresh(dto: RefreshDto): Promise<{ accessToken: string; refreshToken: string; expiresIn: number }> {
|
||||||
const record = await this.prisma.client.refreshToken.findUnique({ where: { jti: dto.refreshToken } });
|
const record = await this.prisma.client.refreshToken.findUnique({ where: { jti: dto.refreshToken } });
|
||||||
if (!record || record.revokedAt || record.expireAt.getTime() < Date.now()) {
|
if (!record || record.revokedAt || record.expireAt.getTime() < Date.now()) {
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { ApiProperty } from '@nestjs/swagger';
|
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
|
||||||
import { IsNotEmpty, IsString } from 'class-validator';
|
import { IsNotEmpty, IsOptional, IsString } from 'class-validator';
|
||||||
|
|
||||||
export class LoginDto {
|
export class LoginDto {
|
||||||
@ApiProperty({ description: '租户编码' })
|
@ApiProperty({ description: '租户编码' })
|
||||||
@ -18,6 +18,33 @@ export class LoginDto {
|
|||||||
password!: string;
|
password!: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class RegisterDto {
|
||||||
|
@ApiProperty({ description: '租户编码' })
|
||||||
|
@IsString()
|
||||||
|
@IsNotEmpty()
|
||||||
|
tenantCode!: string;
|
||||||
|
|
||||||
|
@ApiProperty({ description: '用户名' })
|
||||||
|
@IsString()
|
||||||
|
@IsNotEmpty()
|
||||||
|
username!: string;
|
||||||
|
|
||||||
|
@ApiProperty({ description: '密码' })
|
||||||
|
@IsString()
|
||||||
|
@IsNotEmpty()
|
||||||
|
password!: string;
|
||||||
|
|
||||||
|
@ApiPropertyOptional({ description: '邮箱' })
|
||||||
|
@IsOptional()
|
||||||
|
@IsString()
|
||||||
|
email?: string;
|
||||||
|
|
||||||
|
@ApiPropertyOptional({ description: '手机号' })
|
||||||
|
@IsOptional()
|
||||||
|
@IsString()
|
||||||
|
phone?: string;
|
||||||
|
}
|
||||||
|
|
||||||
export class RefreshDto {
|
export class RefreshDto {
|
||||||
@ApiProperty({ description: '刷新令牌' })
|
@ApiProperty({ description: '刷新令牌' })
|
||||||
@IsString()
|
@IsString()
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user