Files
AIclinicalresearch/backend/src/modules/admin/controllers/tenantController.ts
HaHafeng d595037316 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
2026-01-13 07:34:30 +08:00

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 || '获取用户模块失败',
});
}
}