This commit is contained in:
linfeng 2025-04-14 11:25:31 +08:00
commit 348c6780ff
4 changed files with 123 additions and 83 deletions

View File

@ -62,5 +62,9 @@ export class ShareCodeRouter {
.query(async ({ input }) => { .query(async ({ input }) => {
return this.shareCodeService.findShareCodes(input); return this.shareCodeService.findShareCodes(input);
}), }),
getAllreadlyDeletedShareCodes:this.trpc.procedure
.query(async () => {
return this.shareCodeService.getAllreadlyDeletedShareCodes();
})
}); });
} }

View File

@ -106,24 +106,45 @@ export class ShareCodeService extends BaseService<Prisma.ShareCodeDelegate> {
throw new NotFoundException('文件不存在'); throw new NotFoundException('文件不存在');
} }
const { filename, filetype, size } = resource.meta as any as ResourceMeta const { filename, filetype, size } = resource.meta as any as ResourceMeta
// 生成分享码 // 生成分享码(修改的逻辑保证分享码的唯一性)
const code = this.generateCode(); let code = this.generateCode();
// 查找是否已有分享码记录 let existingShareCode;
const existingShareCode = await super.findUnique({ do {
where: { fileId }, // 查找是否已有相同 shareCode 或者相同 FileID 的分享码记录
}); existingShareCode = await super.findFirst({
where: {
OR: [
{ code },
{ fileId }
]
},
});
// 如果找到的是已经被删除的码,则可以使用并更新其他信息,否则重新生成
if(!existingShareCode){
break
}
if (existingShareCode.deleteAt !== null) {
break
}
if (existingShareCode && existingShareCode.code === code) {
code = this.generateCode();
}
} while (existingShareCode && existingShareCode.code === code);
if (existingShareCode) { if (existingShareCode) {
// 更新现有记录,但保留原有文件名 // 更新现有记录,但保留原有文件名
await super.update({ await super.update({
where: { fileId }, where: { id: existingShareCode.id },
data: { data: {
code, code,
expiresAt, expiresAt,
canUseTimes, canUseTimes,
isUsed: false, isUsed: false,
fileId,
fileName: filename || "downloaded_file", fileName: filename || "downloaded_file",
uploadIp, uploadIp,
createdAt: new Date(),
deletedAt: null
}, },
}); });
} else { } else {
@ -218,24 +239,25 @@ export class ShareCodeService extends BaseService<Prisma.ShareCodeDelegate> {
} }
}) })
this.logger.log('需要清理的分享码:', shareCodes); this.logger.log('需要清理的分享码:', shareCodes);
//文件资源硬删除
shareCodes.forEach(code => { shareCodes.forEach(code => {
this.cleanupUploadFolder(code.fileId); this.cleanupUploadFolder(code.fileId);
}) })
const result = await super.deleteMany({ //数据库资源软删除
const result = await super.softDeleteByIds(
[...shareCodes.map(code => code.id)]
);
const deleteResource = await this.resourceService.updateMany({
where: { where: {
fileId: { fileId: {
in: shareCodes.map(code => code.fileId) in: shareCodes.map(code => code.fileId)
} }
}, },
}); data: {
const deleteResource = await this.resourceService.deleteMany({ deletedAt: new Date()
where: {
fileId: {
in: shareCodes.map(code => code.fileId)
}
} }
}) })
this.logger.log(`Cleaned up ${result.count} ${deleteResource.count} expired share codes`); this.logger.log(`Cleaned up ${result} ${deleteResource.count} expired share codes`);
} catch (error) { } catch (error) {
this.logger.error('Failed to cleanup expired share codes', error); this.logger.error('Failed to cleanup expired share codes', error);
} }
@ -384,38 +406,7 @@ export class ShareCodeService extends BaseService<Prisma.ShareCodeDelegate> {
}, },
select: { ...ShareCodeSelect } select: { ...ShareCodeSelect }
}); });
const {totalSize, resourceCount} = this.calculateTotalSize(shareCodes as any as GenerateShareCodeResponse[]);
// 计算总大小和资源数量
let totalSize = 0;
let resourceCount = 0;
shareCodes.forEach(shareCode => {
if ((shareCode as any as GenerateShareCodeResponse).resource && (shareCode as any as GenerateShareCodeResponse).resource.meta) {
const meta = (shareCode as any as GenerateShareCodeResponse).resource.meta as any;
if (meta.size) {
// 如果size是字符串格式(如 "1024"或"1 MB"),需要转换
let sizeValue: number;
if (typeof meta.size === 'string') {
// 尝试直接解析数字
sizeValue = parseInt(meta.size, 10);
// 如果解析失败,可能需要更复杂的处理
if (isNaN(sizeValue)) {
// 简单处理,实际应用中可能需要更复杂的单位转换
this.logger.warn(`无法解析资源大小: ${meta.size}`);
sizeValue = 0;
}
} else if (typeof meta.size === 'number') {
sizeValue = meta.size;
} else {
sizeValue = 0;
}
totalSize += sizeValue;
resourceCount++;
}
}
});
this.logger.log(`资源总大小: ${totalSize}, 资源数量: ${resourceCount}`); this.logger.log(`资源总大小: ${totalSize}, 资源数量: ${resourceCount}`);
return { totalSize, resourceCount }; return { totalSize, resourceCount };
@ -458,38 +449,7 @@ export class ShareCodeService extends BaseService<Prisma.ShareCodeDelegate> {
...ShareCodeSelect ...ShareCodeSelect
} }
}); });
const {totalSize, resourceCount} = this.calculateTotalSize(shareCodes as any as GenerateShareCodeResponse[]);
// 计算总大小和资源数量
let totalSize = 0;
let resourceCount = 0;
shareCodes.forEach(shareCode => {
if ((shareCode as any as GenerateShareCodeResponse).resource && (shareCode as any as GenerateShareCodeResponse).resource.meta) {
const meta = (shareCode as any as GenerateShareCodeResponse).resource.meta as any;
if (meta.size) {
// 如果size是字符串格式(如 "1024"或"1 MB"),需要转换
let sizeValue: number;
if (typeof meta.size === 'string') {
// 尝试直接解析数字
sizeValue = parseInt(meta.size, 10);
// 如果解析失败,可能需要更复杂的处理
if (isNaN(sizeValue)) {
// 简单处理,实际应用中可能需要更复杂的单位转换
this.logger.warn(`无法解析资源大小: ${meta.size}`);
sizeValue = 0;
}
} else if (typeof meta.size === 'number') {
sizeValue = meta.size;
} else {
sizeValue = 0;
}
totalSize += sizeValue;
resourceCount++;
}
}
});
this.logger.log(`${dateType}资源总大小: ${totalSize}, 资源数量: ${resourceCount}`); this.logger.log(`${dateType}资源总大小: ${totalSize}, 资源数量: ${resourceCount}`);
return { totalSize, resourceCount }; return { totalSize, resourceCount };
} catch (error) { } catch (error) {
@ -576,7 +536,7 @@ export class ShareCodeService extends BaseService<Prisma.ShareCodeDelegate> {
throw error; throw error;
} }
} }
/** /**
* 使ShareCodeSelect并按创建时间倒序排序 * 使ShareCodeSelect并按创建时间倒序排序
* @param args * @param args
@ -595,4 +555,56 @@ export class ShareCodeService extends BaseService<Prisma.ShareCodeDelegate> {
throw error; throw error;
} }
} }
async getAllreadlyDeletedShareCodes(args?: Omit<Prisma.ShareCodeFindManyArgs, 'select' | 'orderBy'>):Promise<{ totalSize: number; resourceCount: number; }> {
try {
const result = await super.findMany({
...args,
where: {
deletedAt: {
not: null
}
},
select: ShareCodeSelect,
});
// 计算总大小和资源数量
const { totalSize, resourceCount } = this.calculateTotalSize(result as unknown as GenerateShareCodeResponse[]);
this.logger.log(`获取已删除分享码列表成功, 数量: ${resourceCount}, 总大小: ${totalSize}`);
return {totalSize,resourceCount}
} catch (err) {
this.logger.error('获取已删除分享码列表失败', err)
throw err
}
}
calculateTotalSize(shareCodes: GenerateShareCodeResponse[]): { totalSize: number; resourceCount: number } {
let totalSize = 0;
let resourceCount = 0;
shareCodes.forEach(shareCode => {
if ((shareCode as any as GenerateShareCodeResponse).resource && (shareCode as any as GenerateShareCodeResponse).resource.meta) {
const meta = (shareCode as any as GenerateShareCodeResponse).resource.meta as any;
if (meta.size) {
// 如果size是字符串格式(如 "1024"或"1 MB"),需要转换
let sizeValue: number;
if (typeof meta.size === 'string') {
// 尝试直接解析数字
sizeValue = parseInt(meta.size, 10);
// 如果解析失败,可能需要更复杂的处理
if (isNaN(sizeValue)) {
// 简单处理,实际应用中可能需要更复杂的单位转换
this.logger.warn(`无法解析资源大小: ${meta.size}`);
sizeValue = 0;
}
} else if (typeof meta.size === 'number') {
sizeValue = meta.size;
} else {
sizeValue = 0;
}
totalSize += sizeValue;
resourceCount++;
}
}
})
return { totalSize, resourceCount }
}
} }

View File

@ -12,6 +12,8 @@ interface DashboardContextType {
isDistinctUploadIPsLoading: boolean; isDistinctUploadIPsLoading: boolean;
shareCodeList: ShareCodeResponse[]; shareCodeList: ShareCodeResponse[];
isShareCodeListLoading: boolean; isShareCodeListLoading: boolean;
deletedData: { totalSize?: number; resourceCount?: number; };
isDeletedLoading: boolean;
} }
interface ShareCodeResourcesSizeByDateRange { interface ShareCodeResourcesSizeByDateRange {
totalSize: number; totalSize: number;
@ -45,6 +47,7 @@ export const DashboardProvider = ({ children }: { children: React.ReactNode }) =
createdAt: 'desc', createdAt: 'desc',
}, },
}) })
const {data:deletedData , isLoading:isDeletedLoading} = api.shareCode.getAllreadlyDeletedShareCodes.useQuery()
return <> return <>
<DashboardContext.Provider value={{ <DashboardContext.Provider value={{
shareCodeAll, shareCodeAll,
@ -56,7 +59,9 @@ export const DashboardProvider = ({ children }: { children: React.ReactNode }) =
distinctUploadIPs, distinctUploadIPs,
isDistinctUploadIPsLoading, isDistinctUploadIPsLoading,
shareCodeList, shareCodeList,
isShareCodeListLoading isShareCodeListLoading,
deletedData,
isDeletedLoading
}}> }}>
{children} {children}
</DashboardContext.Provider> </DashboardContext.Provider>

View File

@ -7,13 +7,13 @@ const { Title, Text } = Typography;
export default function Board() { export default function Board() {
const { shareCodeAll, shareCodeToday, shareCodeYesterday, const { shareCodeAll, shareCodeToday, shareCodeYesterday,
isShareCodeAllLoading, isShareCodeTodayLoading, isShareCodeYesterdayLoading, isShareCodeAllLoading, isShareCodeTodayLoading, isShareCodeYesterdayLoading,
distinctUploadIPs, isDistinctUploadIPsLoading } = useDashboardContext(); distinctUploadIPs, isDistinctUploadIPsLoading,deletedData,isDeletedLoading } = useDashboardContext();
const [serverUptime, setServerUptime] = useState(''); const [serverUptime, setServerUptime] = useState('');
useEffect(() => { useEffect(() => {
const calculateTimeDifference = () => { const calculateTimeDifference = () => {
const now = new Date(); const now = new Date();
const targetDate = new Date('2025-04-09T15:00:00'); const targetDate = new Date('2025-04-09T15:00:00');
const diffMs = now.getTime()- targetDate.getTime(); const diffMs = now.getTime() - targetDate.getTime();
// 如果是负数,表示目标日期已过 // 如果是负数,表示目标日期已过
if (diffMs < 0) { if (diffMs < 0) {
@ -133,6 +133,25 @@ export default function Board() {
</div> </div>
</DashboardCard> </DashboardCard>
</Col> </Col>
{/* 删除状态卡片 */}
{/* <Col xs={24} sm={12} md={6}>
<DashboardCard
title={
<div className="flex items-center">
<CheckCircleOutlined style={{ marginRight: 8, fontSize: 16 }} />
</div>
}
className="h-full"
>
<div className="flex flex-col h-full justify-between py-2">
<Statistic value={isDeletedLoading ? 0 : `${deletedData.resourceCount}`} />
<div className="text-gray-500 text-sm mt-2">
: {isDeletedLoading? 0 : `${(deletedData.totalSize / 1024 / 1024 / 1024).toFixed(2)}GB`}
</div>
</div>
</DashboardCard>
</Col> */}
</Row> </Row>
</> </>
} }