feat(admin): Complete tenant management and module access control system
Major Features: - Tenant management CRUD (list, create, edit, delete, module configuration) - Dynamic module management system (modules table with 8 modules) - Multi-tenant module permission merging (ModuleService) - Module access control middleware (requireModule) - User module permission API (GET /api/v1/auth/me/modules) - Frontend module permission filtering (HomePage + TopNavigation) Module Integration: - RVW module integrated with PromptService (editorial + methodology) - All modules (RVW/PKB/ASL/DC) added authenticate + requireModule middleware - Fixed ReviewTask foreign key constraint (cross-schema issue) - Removed all MOCK_USER_ID, unified to request.user?.userId Prompt Management Enhancements: - Module names displayed in Chinese (RVW -> 智能审稿) - Enhanced version history with view content and rollback features - List page shows both activeVersion and draftVersion columns Database Changes: - Added platform_schema.modules table - Modified tenant_modules table (added index and UUID) - Removed ReviewTask foreign key to public.users (cross-schema fix) - Seeded 8 modules: RVW, PKB, ASL, DC, IIT, AIA, SSA, ST Documentation Updates: - Updated ADMIN module development status - Updated TODO checklist (89% progress) - Updated Prompt management plan (Phase 3.5.5 completed) - Added module authentication specification Files Changed: 80+ Status: All features tested and verified locally Next: User management module development
This commit is contained in:
275
backend/src/modules/admin/controllers/tenantController.ts
Normal file
275
backend/src/modules/admin/controllers/tenantController.ts
Normal file
@@ -0,0 +1,275 @@
|
||||
/**
|
||||
* 租户管理控制器
|
||||
*/
|
||||
|
||||
import type { FastifyRequest, FastifyReply } from 'fastify';
|
||||
import { tenantService } from '../services/tenantService.js';
|
||||
import { logger } from '../../../common/logging/index.js';
|
||||
import type {
|
||||
CreateTenantRequest,
|
||||
UpdateTenantRequest,
|
||||
UpdateTenantStatusRequest,
|
||||
ConfigureModulesRequest,
|
||||
TenantListQuery,
|
||||
} from '../types/tenant.types.js';
|
||||
|
||||
/**
|
||||
* 获取租户列表
|
||||
* GET /api/admin/tenants
|
||||
*/
|
||||
export async function listTenants(
|
||||
request: FastifyRequest<{ Querystring: TenantListQuery }>,
|
||||
reply: FastifyReply
|
||||
) {
|
||||
try {
|
||||
const result = await tenantService.listTenants(request.query);
|
||||
return reply.send({
|
||||
success: true,
|
||||
...result,
|
||||
});
|
||||
} catch (error: any) {
|
||||
logger.error('[TenantController] 获取租户列表失败', { error: error.message });
|
||||
return reply.status(500).send({
|
||||
success: false,
|
||||
message: error.message || '获取租户列表失败',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取租户详情
|
||||
* GET /api/admin/tenants/:id
|
||||
*/
|
||||
export async function getTenant(
|
||||
request: FastifyRequest<{ Params: { id: string } }>,
|
||||
reply: FastifyReply
|
||||
) {
|
||||
try {
|
||||
const { id } = request.params;
|
||||
const tenant = await tenantService.getTenantDetail(id);
|
||||
|
||||
if (!tenant) {
|
||||
return reply.status(404).send({
|
||||
success: false,
|
||||
message: '租户不存在',
|
||||
});
|
||||
}
|
||||
|
||||
return reply.send({
|
||||
success: true,
|
||||
data: tenant,
|
||||
});
|
||||
} catch (error: any) {
|
||||
logger.error('[TenantController] 获取租户详情失败', { error: error.message });
|
||||
return reply.status(500).send({
|
||||
success: false,
|
||||
message: error.message || '获取租户详情失败',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建租户
|
||||
* POST /api/admin/tenants
|
||||
*/
|
||||
export async function createTenant(
|
||||
request: FastifyRequest<{ Body: CreateTenantRequest }>,
|
||||
reply: FastifyReply
|
||||
) {
|
||||
try {
|
||||
const tenant = await tenantService.createTenant(request.body);
|
||||
return reply.status(201).send({
|
||||
success: true,
|
||||
data: tenant,
|
||||
});
|
||||
} catch (error: any) {
|
||||
logger.error('[TenantController] 创建租户失败', { error: error.message });
|
||||
|
||||
if (error.message.includes('已存在')) {
|
||||
return reply.status(400).send({
|
||||
success: false,
|
||||
message: error.message,
|
||||
});
|
||||
}
|
||||
|
||||
return reply.status(500).send({
|
||||
success: false,
|
||||
message: error.message || '创建租户失败',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新租户信息
|
||||
* PUT /api/admin/tenants/:id
|
||||
*/
|
||||
export async function updateTenant(
|
||||
request: FastifyRequest<{ Params: { id: string }; Body: UpdateTenantRequest }>,
|
||||
reply: FastifyReply
|
||||
) {
|
||||
try {
|
||||
const { id } = request.params;
|
||||
const tenant = await tenantService.updateTenant(id, request.body);
|
||||
return reply.send({
|
||||
success: true,
|
||||
data: tenant,
|
||||
});
|
||||
} catch (error: any) {
|
||||
logger.error('[TenantController] 更新租户失败', { error: error.message });
|
||||
return reply.status(500).send({
|
||||
success: false,
|
||||
message: error.message || '更新租户失败',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新租户状态
|
||||
* PUT /api/admin/tenants/:id/status
|
||||
*/
|
||||
export async function updateTenantStatus(
|
||||
request: FastifyRequest<{ Params: { id: string }; Body: UpdateTenantStatusRequest }>,
|
||||
reply: FastifyReply
|
||||
) {
|
||||
try {
|
||||
const { id } = request.params;
|
||||
const { status } = request.body;
|
||||
await tenantService.updateTenantStatus(id, status);
|
||||
return reply.send({
|
||||
success: true,
|
||||
message: '状态更新成功',
|
||||
});
|
||||
} catch (error: any) {
|
||||
logger.error('[TenantController] 更新租户状态失败', { error: error.message });
|
||||
return reply.status(500).send({
|
||||
success: false,
|
||||
message: error.message || '更新租户状态失败',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除租户
|
||||
* DELETE /api/admin/tenants/:id
|
||||
*/
|
||||
export async function deleteTenant(
|
||||
request: FastifyRequest<{ Params: { id: string } }>,
|
||||
reply: FastifyReply
|
||||
) {
|
||||
try {
|
||||
const { id } = request.params;
|
||||
await tenantService.deleteTenant(id);
|
||||
return reply.send({
|
||||
success: true,
|
||||
message: '租户已删除',
|
||||
});
|
||||
} catch (error: any) {
|
||||
logger.error('[TenantController] 删除租户失败', { error: error.message });
|
||||
|
||||
if (error.message.includes('无法删除')) {
|
||||
return reply.status(400).send({
|
||||
success: false,
|
||||
message: error.message,
|
||||
});
|
||||
}
|
||||
|
||||
return reply.status(500).send({
|
||||
success: false,
|
||||
message: error.message || '删除租户失败',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 配置租户模块
|
||||
* PUT /api/admin/tenants/:id/modules
|
||||
*/
|
||||
export async function configureModules(
|
||||
request: FastifyRequest<{ Params: { id: string }; Body: ConfigureModulesRequest }>,
|
||||
reply: FastifyReply
|
||||
) {
|
||||
try {
|
||||
const { id } = request.params;
|
||||
const { modules } = request.body;
|
||||
|
||||
// 转换日期格式
|
||||
const moduleConfigs = modules.map(m => ({
|
||||
code: m.code,
|
||||
enabled: m.enabled,
|
||||
expiresAt: m.expiresAt ? new Date(m.expiresAt) : null,
|
||||
}));
|
||||
|
||||
await tenantService.configureModules(id, moduleConfigs);
|
||||
|
||||
// 返回更新后的模块配置
|
||||
const updatedModules = await tenantService.getTenantModules(id);
|
||||
|
||||
return reply.send({
|
||||
success: true,
|
||||
data: updatedModules,
|
||||
});
|
||||
} catch (error: any) {
|
||||
logger.error('[TenantController] 配置租户模块失败', { error: error.message });
|
||||
return reply.status(500).send({
|
||||
success: false,
|
||||
message: error.message || '配置租户模块失败',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有可用模块列表
|
||||
* GET /api/admin/modules
|
||||
*/
|
||||
export async function listModules(
|
||||
request: FastifyRequest,
|
||||
reply: FastifyReply
|
||||
) {
|
||||
try {
|
||||
const { moduleService } = await import('../../../common/auth/module.service.js');
|
||||
const modules = await moduleService.getAllModules();
|
||||
return reply.send({
|
||||
success: true,
|
||||
data: modules,
|
||||
});
|
||||
} catch (error: any) {
|
||||
logger.error('[TenantController] 获取模块列表失败', { error: error.message });
|
||||
return reply.status(500).send({
|
||||
success: false,
|
||||
message: error.message || '获取模块列表失败',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前用户可访问的模块
|
||||
* GET /api/auth/me/modules
|
||||
*/
|
||||
export async function getUserModules(
|
||||
request: FastifyRequest,
|
||||
reply: FastifyReply
|
||||
) {
|
||||
try {
|
||||
if (!request.user) {
|
||||
return reply.status(401).send({
|
||||
success: false,
|
||||
message: '未认证',
|
||||
});
|
||||
}
|
||||
|
||||
const { moduleService } = await import('../../../common/auth/module.service.js');
|
||||
const result = await moduleService.getUserModules(request.user.userId);
|
||||
|
||||
return reply.send({
|
||||
success: true,
|
||||
data: result.moduleDetails.map(m => m.code),
|
||||
});
|
||||
} catch (error: any) {
|
||||
logger.error('[TenantController] 获取用户模块失败', { error: error.message });
|
||||
return reply.status(500).send({
|
||||
success: false,
|
||||
message: error.message || '获取用户模块失败',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user