/** * 租户管理服务 */ import { prisma } from '../../../config/database.js'; import { logger } from '../../../common/logging/index.js'; import { moduleService } from '../../../common/auth/module.service.js'; import type { TenantInfo, TenantDetail, TenantModuleConfig, CreateTenantRequest, UpdateTenantRequest, TenantListQuery, PaginatedResponse, TenantStatus, } from '../types/tenant.types.js'; class TenantService { /** * 获取租户列表(分页) */ async listTenants(query: TenantListQuery): Promise> { const { type, status, search } = query; const page = Number(query.page) || 1; const limit = Number(query.limit) || 20; const skip = (page - 1) * limit; // 构建查询条件 const where: any = {}; if (type) where.type = type; if (status) where.status = status; if (search) { where.OR = [ { name: { contains: search, mode: 'insensitive' } }, { code: { contains: search, mode: 'insensitive' } }, ]; } // 查询总数 const total = await prisma.tenants.count({ where }); // 查询数据 const tenants = await prisma.tenants.findMany({ where, skip, take: limit, orderBy: { created_at: 'desc' }, select: { id: true, code: true, name: true, type: true, status: true, contact_name: true, contact_phone: true, contact_email: true, expires_at: true, created_at: true, updated_at: true, _count: { select: { users: true }, }, }, }); return { data: tenants.map(t => ({ id: t.id, code: t.code, name: t.name, type: t.type as any, status: t.status as any, contactName: t.contact_name, contactPhone: t.contact_phone, contactEmail: t.contact_email, expiresAt: t.expires_at, createdAt: t.created_at, updatedAt: t.updated_at, })), total, page, limit, totalPages: Math.ceil(total / limit), }; } /** * 获取租户详情(含模块配置) */ async getTenantDetail(tenantId: string): Promise { const tenant = await prisma.tenants.findUnique({ where: { id: tenantId }, include: { tenant_modules: true, _count: { select: { users: true }, }, }, }); if (!tenant) return null; // 获取所有可用模块 const allModules = await moduleService.getAllModules(); // 构建模块配置列表 const modules: TenantModuleConfig[] = allModules.map(m => { const tenantModule = tenant.tenant_modules.find(tm => tm.module_code === m.code); return { code: m.code, name: m.name, enabled: tenantModule?.is_enabled ?? false, expiresAt: tenantModule?.expires_at, }; }); return { id: tenant.id, code: tenant.code, name: tenant.name, type: tenant.type as any, status: tenant.status as any, contactName: tenant.contact_name, contactPhone: tenant.contact_phone, contactEmail: tenant.contact_email, expiresAt: tenant.expires_at, createdAt: tenant.created_at, updatedAt: tenant.updated_at, modules, userCount: tenant._count.users, }; } /** * 创建租户 */ async createTenant(data: CreateTenantRequest): Promise { const { randomUUID } = await import('crypto'); const tenantId = randomUUID(); // 检查 code 是否已存在 const existing = await prisma.tenants.findUnique({ where: { code: data.code }, }); if (existing) { throw new Error(`租户代码 "${data.code}" 已存在`); } // 创建租户 const tenant = await prisma.tenants.create({ data: { id: tenantId, code: data.code, name: data.name, type: data.type as any, status: 'ACTIVE', contact_name: data.contactName, contact_phone: data.contactPhone, contact_email: data.contactEmail, expires_at: data.expiresAt ? new Date(data.expiresAt) : null, updated_at: new Date(), }, }); // 如果指定了初始模块,创建模块配置 if (data.modules && data.modules.length > 0) { for (const moduleCode of data.modules) { await prisma.tenant_modules.create({ data: { id: randomUUID(), tenant_id: tenantId, module_code: moduleCode, is_enabled: true, }, }); } } logger.info('[TenantService] 创建租户', { tenantId, code: data.code, name: data.name, modules: data.modules, }); return { id: tenant.id, code: tenant.code, name: tenant.name, type: tenant.type as any, status: tenant.status as any, contactName: tenant.contact_name, contactPhone: tenant.contact_phone, contactEmail: tenant.contact_email, expiresAt: tenant.expires_at, createdAt: tenant.created_at, updatedAt: tenant.updated_at, }; } /** * 更新租户信息 */ async updateTenant(tenantId: string, data: UpdateTenantRequest): Promise { const tenant = await prisma.tenants.update({ where: { id: tenantId }, data: { name: data.name, contact_name: data.contactName, contact_phone: data.contactPhone, contact_email: data.contactEmail, expires_at: data.expiresAt === null ? null : (data.expiresAt ? new Date(data.expiresAt) : undefined), updated_at: new Date(), }, }); logger.info('[TenantService] 更新租户', { tenantId, data }); return { id: tenant.id, code: tenant.code, name: tenant.name, type: tenant.type as any, status: tenant.status as any, contactName: tenant.contact_name, contactPhone: tenant.contact_phone, contactEmail: tenant.contact_email, expiresAt: tenant.expires_at, createdAt: tenant.created_at, updatedAt: tenant.updated_at, }; } /** * 更新租户状态 */ async updateTenantStatus(tenantId: string, status: TenantStatus): Promise { await prisma.tenants.update({ where: { id: tenantId }, data: { status: status as any, updated_at: new Date(), }, }); logger.info('[TenantService] 更新租户状态', { tenantId, status }); } /** * 删除租户(软删除 - 标记为 SUSPENDED) */ async deleteTenant(tenantId: string): Promise { // 检查是否有用户 const userCount = await prisma.user.count({ where: { tenant_id: tenantId }, }); if (userCount > 0) { throw new Error(`无法删除租户:该租户下还有 ${userCount} 个用户`); } // 软删除:标记为 SUSPENDED await prisma.tenants.update({ where: { id: tenantId }, data: { status: 'SUSPENDED', updated_at: new Date(), }, }); logger.info('[TenantService] 删除租户(软删除)', { tenantId }); } /** * 配置租户模块 */ async configureModules( tenantId: string, modules: { code: string; enabled: boolean; expiresAt?: Date | null }[] ): Promise { await moduleService.setTenantModules(tenantId, modules); } /** * 获取租户的模块配置 */ async getTenantModules(tenantId: string): Promise { const allModules = await moduleService.getAllModules(); const tenantModules = await prisma.tenant_modules.findMany({ where: { tenant_id: tenantId }, }); return allModules.map(m => { const tm = tenantModules.find(t => t.module_code === m.code); return { code: m.code, name: m.name, enabled: tm?.is_enabled ?? false, expiresAt: tm?.expires_at, }; }); } } export const tenantService = new TenantService();