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
276 lines
6.8 KiB
TypeScript
276 lines
6.8 KiB
TypeScript
/**
|
|
* 租户管理控制器
|
|
*/
|
|
|
|
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 || '获取用户模块失败',
|
|
});
|
|
}
|
|
}
|
|
|