All checks were successful
Deploy API Server / build-and-deploy (push) Successful in 50s
- New shared helper: src/common/helpers/name-resolver.ts - Applied to: reviews, users/members, files, knowledge-bases, imports - Learning service refactored to use shared helper Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
99 lines
3.8 KiB
TypeScript
99 lines
3.8 KiB
TypeScript
import { Controller, Get, Post, Delete, Body, Param, Query, UseGuards } from '@nestjs/common';
|
|
import { ApiTags, ApiBearerAuth, ApiOperation } from '@nestjs/swagger';
|
|
import { PrismaService } from '../../infrastructure/database/prisma.service';
|
|
import { enrichWithNames } from '../../common/helpers/name-resolver';
|
|
import { AdminAuthGuard } from '../../common/guards/admin-auth.guard';
|
|
import { AdminRolesGuard } from '../../common/guards/admin-roles.guard';
|
|
import { AdminRoles } from '../../common/decorators/admin-roles.decorator';
|
|
import type { AdminRole } from '../../common/types/admin-role.enum';
|
|
|
|
@ApiTags('admin-users')
|
|
@Controller('admin-api/users')
|
|
@UseGuards(AdminAuthGuard, AdminRolesGuard)
|
|
@ApiBearerAuth()
|
|
export class AdminUsersMgmtController {
|
|
constructor(private readonly prisma: PrismaService) {}
|
|
|
|
// ── User List ──
|
|
|
|
@Get()
|
|
@AdminRoles('ADMIN' as AdminRole)
|
|
@ApiOperation({ summary: 'C 端用户列表' })
|
|
async listUsers(@Query('search') search?: string, @Query('page') page?: string, @Query('limit') limit?: string) {
|
|
const take = Math.min(Number(limit) || 20, 100);
|
|
const skip = (Math.max(Number(page) || 1, 1) - 1) * take;
|
|
const where: any = { deletedAt: null };
|
|
if (search) where.OR = [{ email: { contains: search } }, { nickname: { contains: search } }];
|
|
const [items, total] = await Promise.all([
|
|
this.prisma.user.findMany({ where, orderBy: { createdAt: 'desc' }, take, skip, select: { id: true, email: true, nickname: true, role: true, status: true, lastLoginAt: true, createdAt: true } }),
|
|
this.prisma.user.count({ where }),
|
|
]);
|
|
const enriched = await enrichWithNames(this.prisma, items); return { items: enriched, total };
|
|
}
|
|
|
|
// ── Membership ──
|
|
|
|
@Get('memberships')
|
|
@AdminRoles('ADMIN' as AdminRole)
|
|
@ApiOperation({ summary: '用户会员列表' })
|
|
async memberships(@Query('userId') userId?: string) {
|
|
return this.prisma.userMembership.findMany({
|
|
where: userId ? { userId } : undefined,
|
|
include: { plan: true },
|
|
orderBy: { createdAt: 'desc' },
|
|
take: 100,
|
|
});
|
|
}
|
|
|
|
@Post('memberships')
|
|
@AdminRoles('SUPER_ADMIN' as AdminRole)
|
|
@ApiOperation({ summary: '手动分配会员' })
|
|
async addMembership(@Body() d: { userId: string; planId: string; expiresAt?: string }) {
|
|
return this.prisma.userMembership.create({
|
|
data: { userId: d.userId, planId: d.planId, expiresAt: d.expiresAt ? new Date(d.expiresAt) : null },
|
|
});
|
|
}
|
|
|
|
// ── Deletion Requests ──
|
|
|
|
@Get('deletion-requests')
|
|
@AdminRoles('ADMIN' as AdminRole)
|
|
@ApiOperation({ summary: '注销申请列表' })
|
|
async deletionRequests(@Query('status') status?: string) {
|
|
return this.prisma.accountDeletionRequest.findMany({
|
|
where: status ? { status } : undefined,
|
|
orderBy: { createdAt: 'desc' },
|
|
take: 100,
|
|
});
|
|
}
|
|
|
|
@Post('deletion-requests/:id/approve')
|
|
@AdminRoles('SUPER_ADMIN' as AdminRole)
|
|
@ApiOperation({ summary: '批准注销(立即执行)' })
|
|
async approveDeletion(@Param('id') id: string) {
|
|
return this.prisma.accountDeletionRequest.update({
|
|
where: { id },
|
|
data: { status: 'completed', reviewedAt: new Date(), completedAt: new Date() },
|
|
});
|
|
}
|
|
|
|
@Post('deletion-requests/:id/reject')
|
|
@AdminRoles('SUPER_ADMIN' as AdminRole)
|
|
@ApiOperation({ summary: '驳回注销' })
|
|
async rejectDeletion(@Param('id') id: string) {
|
|
return this.prisma.accountDeletionRequest.update({
|
|
where: { id },
|
|
data: { status: 'cancelled', reviewedAt: new Date(), cancelledAt: new Date() },
|
|
});
|
|
}
|
|
|
|
// ── Devices ──
|
|
|
|
@Get(':userId/devices')
|
|
@AdminRoles('ADMIN' as AdminRole)
|
|
@ApiOperation({ summary: '查看用户设备' })
|
|
async userDevices(@Param('userId') userId: string) {
|
|
return this.prisma.userDevice.findMany({ where: { userId }, orderBy: { lastSeenAt: 'desc' } });
|
|
}
|
|
}
|