Files
cyh-tk-backend/src/logs/logs.service.ts
zhxiao1124 4eff4d877e refactor: 完成项目整体目录重构与路径别名配置
1.  重构项目文件结构,统一文件存放位置与命名规则
2.  配置tsconfig路径别名,替换原有相对路径导入
3.  迁移drama-status枚举到dramas.types统一管理
4.  新增全局配置文件与工具类,补充完整注释与文档
5.  修复所有导入路径与依赖引用问题
2026-07-02 19:17:56 +08:00

241 lines
6.8 KiB
TypeScript

import { Injectable, NotFoundException, Optional } from '@nestjs/common';
import { InjectDataSource } from '@nestjs/typeorm';
import { DataSource } from 'typeorm';
import { PaginatedResponse } from '@app/common/paginated-response.dto';
import { CreatePlaybackErrorLogDto } from './logs.dto';
import { PlaybackErrorLog } from './playback-error-log.entity';
import { RequestLog } from './request-log.entity';
import {
PlaybackErrorLogRecord,
RequestLogRecord,
} from './logs.types';
interface PaginationInput {
page: number;
pageSize: number;
}
@Injectable()
export class LogsService {
private requestLogSequence = 1;
private playbackErrorSequence = 1;
private readonly requestLogs = new Map<string, RequestLogRecord>();
private readonly playbackErrors: PlaybackErrorLogRecord[] = [];
constructor(
@Optional()
@InjectDataSource()
readonly dataSource?: DataSource,
) {}
async recordRequest(record: Omit<RequestLogRecord, 'id' | 'createdAt'>) {
if (!record.requestId) {
return;
}
if (this.hasDatabase()) {
const repository = this.dataSource.getRepository(RequestLog);
const existing = await repository.findOne({
where: { requestId: record.requestId },
});
await repository.save(
repository.create({
...existing,
...record,
}),
);
return;
}
this.requestLogs.set(record.requestId, {
...record,
id: this.requestLogs.get(record.requestId)?.id ?? this.requestLogSequence++,
createdAt:
this.requestLogs.get(record.requestId)?.createdAt ??
new Date().toISOString(),
});
}
async listRequestLogs(
pagination: PaginationInput,
): Promise<PaginatedResponse<RequestLogRecord>> {
if (this.hasDatabase()) {
const page = Math.max(pagination.page, 1);
const pageSize = Math.min(Math.max(pagination.pageSize, 1), 100);
const repository = this.dataSource.getRepository(RequestLog);
const [records, total] = await repository.findAndCount({
order: { id: 'DESC' },
skip: (page - 1) * pageSize,
take: pageSize,
});
return {
list: records.map((record) => this.toRequestLogRecord(record)),
pagination: { page, pageSize, total },
};
}
const list = Array.from(this.requestLogs.values()).sort(
(left, right) => (right.id ?? 0) - (left.id ?? 0),
);
return this.paginate(list, pagination);
}
async getRequestLogOrThrow(requestId: string) {
if (this.hasDatabase()) {
const log = await this.dataSource
.getRepository(RequestLog)
.findOne({ where: { requestId } });
if (!log) {
throw new NotFoundException('Request log not found');
}
return this.toRequestLogRecord(log);
}
const log = this.requestLogs.get(requestId);
if (!log) {
throw new NotFoundException('Request log not found');
}
return log;
}
async createPlaybackError(dto: CreatePlaybackErrorLogDto) {
if (this.hasDatabase()) {
const repository = this.dataSource.getRepository(PlaybackErrorLog);
const log = await repository.save(
repository.create({
...dto,
occurredAt: new Date(dto.occurredAt),
}),
);
return {
id: Number(log.id),
requestId: log.requestId,
};
}
const log: PlaybackErrorLogRecord = {
id: this.playbackErrorSequence++,
requestId: dto.requestId,
dramaId: dto.dramaId,
episodeId: dto.episodeId,
playUrl: dto.playUrl,
playerErrorCode: dto.playerErrorCode,
message: dto.message,
progressSeconds: dto.progressSeconds,
networkType: dto.networkType,
device: dto.device,
occurredAt: dto.occurredAt,
createdAt: new Date().toISOString(),
};
this.playbackErrors.push(log);
return {
id: log.id,
requestId: log.requestId,
};
}
async listPlaybackErrors(
pagination: PaginationInput,
): Promise<PaginatedResponse<PlaybackErrorLogRecord>> {
if (this.hasDatabase()) {
const page = Math.max(pagination.page, 1);
const pageSize = Math.min(Math.max(pagination.pageSize, 1), 100);
const repository = this.dataSource.getRepository(PlaybackErrorLog);
const [records, total] = await repository.findAndCount({
order: { id: 'DESC' },
skip: (page - 1) * pageSize,
take: pageSize,
});
return {
list: records.map((record) => this.toPlaybackErrorLogRecord(record)),
pagination: { page, pageSize, total },
};
}
return this.paginate([...this.playbackErrors].reverse(), pagination);
}
async getPlaybackErrorOrThrow(id: number) {
if (this.hasDatabase()) {
const log = await this.dataSource
.getRepository(PlaybackErrorLog)
.findOne({ where: { id } });
if (!log) {
throw new NotFoundException('Playback error log not found');
}
return this.toPlaybackErrorLogRecord(log);
}
const log = this.playbackErrors.find((item) => item.id === id);
if (!log) {
throw new NotFoundException('Playback error log not found');
}
return log;
}
private hasDatabase(): this is this & { dataSource: DataSource } {
return Boolean(this.dataSource?.isInitialized);
}
private toRequestLogRecord(log: RequestLog): RequestLogRecord {
return {
id: Number(log.id),
requestId: log.requestId,
method: log.method,
path: log.path,
query: log.query,
body: log.body,
statusCode: log.statusCode,
responseCode: log.responseCode,
message: log.message,
costMs: log.costMs,
userId: log.userId ? Number(log.userId) : undefined,
adminId: log.adminId ? Number(log.adminId) : undefined,
ip: log.ip,
userAgent: log.userAgent,
errorStack: log.errorStack,
createdAt: log.createdAt?.toISOString?.(),
};
}
private toPlaybackErrorLogRecord(log: PlaybackErrorLog): PlaybackErrorLogRecord {
return {
id: Number(log.id),
requestId: log.requestId,
userId: log.userId ? Number(log.userId) : undefined,
dramaId: Number(log.dramaId),
episodeId: Number(log.episodeId),
playUrl: log.playUrl,
playerErrorCode: log.playerErrorCode,
message: log.message,
progressSeconds: log.progressSeconds,
networkType: log.networkType,
device: log.device,
occurredAt: log.occurredAt.toISOString(),
createdAt: log.createdAt?.toISOString?.() ?? new Date().toISOString(),
};
}
private paginate<T>(
records: T[],
input: PaginationInput,
): PaginatedResponse<T> {
const page = Math.max(input.page, 1);
const pageSize = Math.min(Math.max(input.pageSize, 1), 100);
const start = (page - 1) * pageSize;
return {
list: records.slice(start, start + pageSize),
pagination: {
page,
pageSize,
total: records.length,
},
};
}
}