staff_data/apps/server/src/upload/share-code.service.ts

175 lines
4.8 KiB
TypeScript
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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, fileName?: string): Promise<GenerateShareCodeResponse> {
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小时后过期
// 查找是否已有分享码记录
const existingShareCode = await db.shareCode.findUnique({
where: { fileId },
});
if (existingShareCode) {
// 更新现有记录,但保留原有文件名
await db.shareCode.update({
where: { fileId },
data: {
code,
expiresAt,
isUsed: false,
// 只在没有现有文件名且提供了新文件名时才更新文件名
...(fileName && !existingShareCode.fileName
? { fileName }
: {})
},
});
} else {
// 创建新记录
await db.shareCode.create({
data: {
code,
fileId,
expiresAt,
isUsed: false,
fileName: fileName || null,
},
});
}
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<ShareCode | null> {
try {
console.log(`尝试验证分享码: ${code}`);
// 查找有效的分享码
const shareCode = await db.shareCode.findFirst({
where: {
code,
isUsed: false,
expiresAt: { gt: new Date() },
},
});
console.log('查询结果:', shareCode);
if (!shareCode) {
console.log('分享码无效或已过期');
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;
} 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<ShareCode | null> {
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<boolean> {
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 [];
}
}
}