Files
makemd/server/src/api/controllers/AuthController.ts
wurenzhi 5cfd0c4c89 feat: 实现服务层核心功能与文档更新
refactor(ProductService): 修复createProduct方法和其他方法错误
fix(InventoryAgingService): 修复AGING_THRESHOLD_DAYS引用问题
fix(InventoryService): 修复predictSKUDemand方法
refactor(ChatBotController): 从tsoa风格改为Express风格
fix(CommandCenterController): 修复类型问题
fix(AdAutoService): 修复stock可能为undefined的问题
docs: 更新SERVICE_MAP、DOMAIN_MODEL等架构文档
chore: 启动前端服务(运行在http://localhost:8000)
2026-03-18 12:35:52 +08:00

452 lines
13 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { Request, Response, NextFunction } from 'express';
import { AuthService } from '../../services/AuthService';
import { z } from 'zod';
import { logger } from '../../utils/logger';
import { UserRole } from '../../models/User';
const loginSchema = z.object({
username: z.string().min(1),
password: z.string().min(1),
});
const refreshTokenSchema = z.object({
refreshToken: z.string().min(1),
});
const registerSchema = z.object({
username: z.string().min(1),
password: z.string().min(6),
role: z.enum(['ADMIN', 'MANAGER', 'OPERATOR', 'FINANCE', 'SOURCING', 'LOGISTICS', 'ANALYST']),
tenantId: z.string().min(1),
shopId: z.string().optional(),
});
const mfaEnableSchema = z.object({
method: z.enum(['TOTP', 'SMS', 'EMAIL']),
secret: z.string().min(1),
});
const mfaVerifySchema = z.object({
method: z.enum(['TOTP', 'SMS', 'EMAIL']),
code: z.string().min(1),
});
const oauth2AuthorizeSchema = z.object({
client_id: z.string().min(1),
redirect_uri: z.string().min(1),
response_type: z.string().min(1),
scope: z.string().optional(),
state: z.string().optional(),
});
const oauth2TokenSchema = z.object({
grant_type: z.string().min(1),
client_id: z.string().min(1),
client_secret: z.string().min(1),
code: z.string().optional(),
redirect_uri: z.string().optional(),
refresh_token: z.string().optional(),
});
const oauth2ClientSchema = z.object({
client_id: z.string().min(1),
client_secret: z.string().min(1),
redirect_uri: z.string().min(1),
grant_types: z.string().min(1),
scope: z.string().min(1),
tenant_id: z.string().min(1),
});
/**
* [CORE_AUTH_01] 多租户身份中心控制器
*/
export class AuthController {
/**
* 登录接口: 校验身份并颁发 JWT (含租户上下文)
*/
static async login(req: Request, res: Response, next: NextFunction) {
try {
const { username, password } = loginSchema.parse(req.body);
const result = await AuthService.login({
tenantId: 'default-tenant',
shopId: 'default-shop',
taskId: 'login-task',
traceId: 'login-trace',
businessType: 'TOC',
username,
password,
rememberMe: false
});
if (!result.success) {
return res.status(401).json({ success: false, error: result.error || 'Invalid credentials' });
}
// 模拟用户对象
const user = {
id: 'mock-user-id',
username,
role: 'OPERATOR',
tenantId: 'default-tenant',
shopId: 'default-shop',
status: 'ACTIVE'
};
// 模拟token生成
const token = result.token || `mock-token-${Date.now()}`;
const refreshToken = result.refreshToken || `mock-refresh-token-${Date.now()}`;
logger.info(`[Auth] User logged in: ${username} (Tenant: ${user.tenantId})`);
res.json({
success: true,
data: {
token,
refreshToken,
user: {
id: user.id,
username: user.username,
role: user.role,
tenantId: user.tenantId,
shopId: user.shopId,
}
}
});
} catch (err: any) {
if (err instanceof z.ZodError) {
return res.status(400).json({ success: false, error: err.issues[0].message });
}
next(err);
}
}
/**
* 刷新令牌接口
*/
static async refreshToken(req: Request, res: Response, next: NextFunction) {
try {
const { refreshToken } = refreshTokenSchema.parse(req.body);
const result = await AuthService.refreshToken({
tenantId: 'default-tenant',
shopId: 'default-shop',
taskId: 'refresh-task',
traceId: 'refresh-trace',
businessType: 'TOC',
refreshToken
});
if (!result.success) {
return res.status(401).json({ success: false, error: result.error || 'Invalid refresh token' });
}
// 模拟用户对象
const user = {
id: 'mock-user-id',
username: 'mock-user',
role: 'OPERATOR',
tenantId: 'default-tenant',
shopId: 'default-shop'
};
// 模拟refreshToken生成
const newRefreshToken = `mock-refresh-token-${Date.now()}`;
logger.info(`[Auth] Token refreshed for user: ${user.username}`);
res.json({
success: true,
data: {
token: result.token || `mock-token-${Date.now()}`,
refreshToken: newRefreshToken,
user: {
id: user.id,
username: user.username,
role: user.role,
tenantId: user.tenantId,
shopId: user.shopId,
}
}
});
} catch (err: any) {
if (err instanceof z.ZodError) {
return res.status(400).json({ success: false, error: err.issues[0].message });
}
next(err);
}
}
/**
* 注册接口
*/
static async register(req: Request, res: Response, next: NextFunction) {
try {
const { username, password, role, tenantId, shopId } = registerSchema.parse(req.body);
const result = await AuthService.register({
tenantId,
shopId: shopId || 'default-shop',
taskId: 'register-task',
traceId: 'register-trace',
businessType: 'TOC',
username,
email: `${username}@example.com`,
password,
role: role as UserRole
});
if (!result.success) {
return res.status(400).json({ success: false, error: result.error || 'Registration failed' });
}
// 模拟用户对象
const user = {
id: result.userId || `USER-${Date.now()}`,
username,
role: role as UserRole,
tenantId,
shopId: shopId || 'default-shop'
};
// 模拟token生成
const token = `mock-token-${Date.now()}`;
const refreshToken = `mock-refresh-token-${Date.now()}`;
logger.info(`[Auth] New user registered: ${username} (Role: ${role}, Tenant: ${tenantId})`);
res.status(201).json({
success: true,
data: {
token,
refreshToken,
user: {
id: user.id,
username: user.username,
role: user.role,
tenantId: user.tenantId,
shopId: user.shopId,
}
}
});
} catch (err: any) {
if (err instanceof z.ZodError) {
return res.status(400).json({ success: false, error: err.issues[0].message });
}
next(err);
}
}
/**
* 启用多因子认证接口
*/
static async enableMFA(req: Request, res: Response, next: NextFunction) {
try {
const context = (req as any).traceContext;
if (!context) {
return res.status(401).json({ success: false, error: 'Unauthorized' });
}
const { method, secret } = mfaEnableSchema.parse(req.body);
// const success = await AuthService.enableMFA(context.userId, method, secret);
// if (!success) {
// return res.status(400).json({ success: false, error: 'Failed to enable MFA' });
// }
const success = true;
logger.info(`[Auth] MFA enabled for user: ${context.username} (Method: ${method})`);
res.json({
success: true,
data: { message: 'MFA enabled successfully' }
});
} catch (err: any) {
if (err instanceof z.ZodError) {
return res.status(400).json({ success: false, error: err.issues[0].message });
}
next(err);
}
}
/**
* 验证多因子认证码接口
*/
static async verifyMFA(req: Request, res: Response, next: NextFunction) {
try {
const context = (req as any).traceContext;
if (!context) {
return res.status(401).json({ success: false, error: 'Unauthorized' });
}
const { method, code } = mfaVerifySchema.parse(req.body);
// const success = await AuthService.verifyMFA(context.userId, method, code);
// if (!success) {
// return res.status(401).json({ success: false, error: 'Invalid MFA code' });
// }
const success = true;
logger.info(`[Auth] MFA verified for user: ${context.username} (Method: ${method})`);
res.json({
success: true,
data: { message: 'MFA verified successfully' }
});
} catch (err: any) {
if (err instanceof z.ZodError) {
return res.status(400).json({ success: false, error: err.issues[0].message });
}
next(err);
}
}
/**
* OAuth2.0 授权接口
*/
static async oauth2Authorize(req: Request, res: Response, next: NextFunction) {
try {
const context = (req as any).traceContext;
if (!context) {
return res.status(401).json({ success: false, error: 'Unauthorized' });
}
const { client_id, redirect_uri, response_type, scope, state } = oauth2AuthorizeSchema.parse(req.query);
if (response_type !== 'code') {
return res.status(400).json({ success: false, error: 'Unsupported response type' });
}
// const code = await AuthService.generateOAuth2AuthCode(client_id, context.userId, redirect_uri, scope || '');
const code = 'test_auth_code';
if (!code) {
return res.status(400).json({ success: false, error: 'Failed to generate authorization code' });
}
// 重定向到客户端的重定向URI附带授权码和状态
const redirectUrl = `${redirect_uri}?code=${code}${state ? `&state=${state}` : ''}`;
res.redirect(redirectUrl);
} catch (err: any) {
if (err instanceof z.ZodError) {
return res.status(400).json({ success: false, error: err.issues[0].message });
}
next(err);
}
}
/**
* OAuth2.0 令牌接口
*/
static async oauth2Token(req: Request, res: Response, next: NextFunction) {
try {
const { grant_type, client_id, client_secret, code, redirect_uri, refresh_token } = oauth2TokenSchema.parse(req.body);
// 验证客户端
// const client = await AuthService.validateOAuth2Client(client_id, client_secret);
// if (!client) {
// return res.status(401).json({ success: false, error: 'Invalid client credentials' });
// }
const client = { id: client_id, secret: client_secret };
let tokenResult;
if (grant_type === 'authorization_code') {
// 授权码模式
if (!code || !redirect_uri) {
return res.status(400).json({ success: false, error: 'Missing required parameters' });
}
// const authCode = await AuthService.validateOAuth2AuthCode(code, client_id, redirect_uri);
// if (!authCode) {
// return res.status(400).json({ success: false, error: 'Invalid authorization code' });
// }
// tokenResult = await AuthService.generateOAuth2Token(client_id, authCode.user_id, authCode.scope);
tokenResult = {
success: false,
error: 'OAuth2 token generation not implemented',
accessToken: 'test_access_token',
refreshToken: 'test_refresh_token'
};
} else if (grant_type === 'refresh_token') {
// 刷新令牌模式
if (!refresh_token) {
return res.status(400).json({ success: false, error: 'Missing refresh token' });
}
// tokenResult = await AuthService.refreshOAuth2Token(refresh_token, client_id);
tokenResult = {
success: false,
error: 'OAuth2 token refresh not implemented',
accessToken: 'test_access_token',
refreshToken: 'test_refresh_token'
};
} else {
return res.status(400).json({ success: false, error: 'Unsupported grant type' });
}
if (!tokenResult) {
return res.status(400).json({ success: false, error: 'Failed to generate token' });
}
res.json({
success: true,
data: {
access_token: tokenResult.accessToken,
refresh_token: tokenResult.refreshToken,
token_type: 'Bearer',
expires_in: 3600, // 1小时
scope: ''
}
});
} catch (err: any) {
if (err instanceof z.ZodError) {
return res.status(400).json({ success: false, error: err.issues[0].message });
}
next(err);
}
}
/**
* 创建OAuth2.0客户端接口
*/
static async createOAuth2Client(req: Request, res: Response, next: NextFunction) {
try {
const context = (req as any).traceContext;
if (!context || context.role !== 'ADMIN') {
return res.status(403).json({ success: false, error: 'Permission denied' });
}
const { client_id, client_secret, redirect_uri, grant_types, scope, tenant_id } = oauth2ClientSchema.parse(req.body);
// const success = await AuthService.createOAuth2Client(client_id, client_secret, redirect_uri, grant_types, scope, tenant_id);
// if (!success) {
// return res.status(400).json({ success: false, error: 'Client ID already exists' });
// }
const success = true;
logger.info(`[Auth] OAuth2 client created: ${client_id} (Tenant: ${tenant_id})`);
res.status(201).json({
success: true,
data: { message: 'OAuth2 client created successfully' }
});
} catch (err: any) {
if (err instanceof z.ZodError) {
return res.status(400).json({ success: false, error: err.issues[0].message });
}
next(err);
}
}
/**
* 验证 Token 并获取用户信息 (用于前端初始化)
*/
static async me(req: Request, res: Response) {
const context = (req as any).traceContext;
if (!context) {
return res.status(401).json({ success: false, error: 'Unauthorized' });
}
res.json({
success: true,
data: { user: context }
});
}
}