import { Injectable, Logger, NotFoundException } from '@nestjs/common'; import { customAlphabet } from 'nanoid-cjs'; import { db } from '@nice/common'; import { ShareCode, GenerateShareCodeResponse } from './types'; import { Cron, CronExpression } from '@nestjs/schedule'; import { ResourceService } from '@server/models/resource/resource.service'; @Injectable() export class ShareCodeService { private readonly logger = new Logger(ShareCodeService.name); // 生成8位分享码,使用易读的字符 private readonly generateCode = customAlphabet( '23456789ABCDEFGHJKLMNPQRSTUVWXYZ', 8 ); constructor(private readonly resourceService: ResourceService) {} async generateShareCode(fileId: string): Promise { try { // 检查文件是否存在 const resource = await this.resourceService.findUnique({ where: { fileId }, }); console.log('完整 fileId:', fileId); // 确保与前端一致 if (!resource) { throw new NotFoundException('文件不存在'); } // 生成分享码 const code = this.generateCode(); const expiresAt = new Date(Date.now() + 24 * 60 * 60 * 1000); // 24小时后过期 // 创建分享码记录 await db.shareCode.create({ data: { code, fileId, expiresAt, isUsed: false, }, }); this.logger.log(`Generated share code ${code} for file ${fileId}`); return { code, expiresAt, }; } catch (error) { this.logger.error('Failed to generate share code', error); throw error; } } async validateAndUseCode(code: string): Promise { try { // 查找有效的分享码 const shareCode = await db.shareCode.findFirst({ where: { code, isUsed: false, expiresAt: { gt: new Date() }, }, }); if (!shareCode) { return null; } // 标记分享码为已使用 await db.shareCode.update({ where: { id: shareCode.id }, data: { isUsed: true }, }); // 记录使用日志 this.logger.log(`Share code ${code} used for file ${shareCode.fileId}`); return shareCode.fileId; } catch (error) { this.logger.error('Failed to validate share code', error); return null; } } // 每天清理过期的分享码 @Cron(CronExpression.EVERY_DAY_AT_MIDNIGHT) async cleanupExpiredShareCodes() { try { const result = await db.shareCode.deleteMany({ where: { OR: [ { expiresAt: { lt: new Date() } }, { isUsed: true }, ], }, }); this.logger.log(`Cleaned up ${result.count} expired share codes`); } catch (error) { this.logger.error('Failed to cleanup expired share codes', error); } } // 获取分享码信息 async getShareCodeInfo(code: string): Promise { try { return await db.shareCode.findFirst({ where: { code }, }); } catch (error) { this.logger.error('Failed to get share code info', error); return null; } } // 检查文件是否已经生成过分享码 async hasActiveShareCode(fileId: string): Promise { try { const activeCode = await db.shareCode.findFirst({ where: { fileId, isUsed: false, expiresAt: { gt: new Date() }, }, }); return !!activeCode; } catch (error) { this.logger.error('Failed to check active share code', error); return false; } } // 获取文件的所有分享记录 async getFileShareHistory(fileId: string) { try { return await db.shareCode.findMany({ where: { fileId }, orderBy: { createdAt: 'desc' }, }); } catch (error) { this.logger.error('Failed to get file share history', error); return []; } } }