All checks were successful
Deploy API Server / build-and-deploy (push) Successful in 48s
- ChatCitation.content → excerptText(对齐 Prisma schema) - ChatCitation.score 不存在→ 移除 - UploadedFile.sizeBytes BigInt → Number() 转换 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
95 lines
3.3 KiB
TypeScript
95 lines
3.3 KiB
TypeScript
import { Injectable, BadRequestException, NotFoundException } from '@nestjs/common';
|
|
import { UsersRepository } from './users.repository';
|
|
import { PrismaService } from '../../infrastructure/database/prisma.service';
|
|
|
|
const DELETION_COOLING_DAYS = 7;
|
|
|
|
@Injectable()
|
|
export class UsersService {
|
|
constructor(
|
|
private readonly usersRepository: UsersRepository,
|
|
private readonly prisma: PrismaService,
|
|
) {}
|
|
|
|
async getProfile(userId: string) { return this.usersRepository.findProfileByUserId(userId); }
|
|
async updateProfile(userId: string, dto: any) { return this.usersRepository.updateProfile(userId, dto); }
|
|
async getProfileDetail(userId: string) { return this.usersRepository.findUserProfile(userId); }
|
|
async updateProfileDetail(userId: string, dto: any) { return this.usersRepository.upsertUserProfile(userId, dto); }
|
|
async updatePreferences(userId: string, dto: any) { return this.usersRepository.updatePreferences(userId, dto); }
|
|
|
|
// ── Membership ──
|
|
|
|
async getMembership(userId: string) {
|
|
return this.prisma.userMembership.findFirst({
|
|
where: { userId, active: true },
|
|
include: { plan: true },
|
|
orderBy: { createdAt: 'desc' },
|
|
});
|
|
}
|
|
|
|
// ── Account Deletion ──
|
|
|
|
async requestDeletion(userId: string) {
|
|
const existing = await this.prisma.accountDeletionRequest.findFirst({
|
|
where: { userId, status: 'pending' },
|
|
});
|
|
if (existing) throw new BadRequestException('已有进行中的注销申请');
|
|
|
|
const coolingEndsAt = new Date(Date.now() + DELETION_COOLING_DAYS * 86400000);
|
|
return this.prisma.accountDeletionRequest.create({
|
|
data: { userId, coolingEndsAt },
|
|
});
|
|
}
|
|
|
|
async cancelDeletion(userId: string) {
|
|
const req = await this.prisma.accountDeletionRequest.findFirst({
|
|
where: { userId, status: 'pending' },
|
|
});
|
|
if (!req) throw new NotFoundException('未找到进行中的注销申请');
|
|
|
|
return this.prisma.accountDeletionRequest.update({
|
|
where: { id: req.id },
|
|
data: { status: 'cancelled', cancelledAt: new Date() },
|
|
});
|
|
}
|
|
|
|
// ── Device Management ──
|
|
|
|
async getDevices(userId: string) {
|
|
return this.prisma.userDevice.findMany({
|
|
where: { userId },
|
|
orderBy: { lastSeenAt: 'desc' },
|
|
});
|
|
}
|
|
|
|
async removeDevice(userId: string, deviceId: string) {
|
|
await this.prisma.userDevice.deleteMany({
|
|
where: { userId, deviceId },
|
|
});
|
|
return { success: true };
|
|
}
|
|
|
|
// ── Storage ──
|
|
|
|
async getStorage(userId: string) {
|
|
const files = await this.prisma.uploadedFile.findMany({
|
|
where: { userId },
|
|
select: { sizeBytes: true, mimeType: true },
|
|
});
|
|
const usedBytes = files.reduce((sum, f) => sum + Number(f.sizeBytes), 0);
|
|
const totalBytes = 1024 * 1024 * 1024; // 1GB hardcoded for now
|
|
return { totalBytes, usedBytes, fileCount: files.length };
|
|
}
|
|
|
|
// ── Assets Summary ──
|
|
|
|
async getAssetsSummary(userId: string) {
|
|
const [kbCount, itemCount, cardCount] = await Promise.all([
|
|
this.prisma.knowledgeBase.count({ where: { userId, deletedAt: null } }),
|
|
this.prisma.knowledgeItem.count({ where: { userId, deletedAt: null } }),
|
|
this.prisma.reviewCard.count({ where: { userId } }),
|
|
]);
|
|
return { knowledgeBaseCount: kbCount, knowledgeItemCount: itemCount, reviewCardCount: cardCount };
|
|
}
|
|
}
|