1. 重构项目文件结构,统一文件存放位置与命名规则 2. 配置tsconfig路径别名,替换原有相对路径导入 3. 迁移drama-status枚举到dramas.types统一管理 4. 新增全局配置文件与工具类,补充完整注释与文档 5. 修复所有导入路径与依赖引用问题
241 lines
6.8 KiB
TypeScript
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,
|
|
},
|
|
};
|
|
}
|
|
}
|