feat(api): usage summary with threshold alerts
P4a Task 5(A23 后台侧):用量统计与阈值告警后端全链。 - cn 迁移(校对手决 utf8mb4_0900_bin):usage_thresholds 单行配置表 (迁移内种子行,storageBytes/perAlbumBytes BigInt?,null=维度停用)+ usage_alerts 告警账本(kind storage|perAlbum、albumId?、resolvedAt? 索引)。「同 kind+albumId 活跃告警唯一」为应用级约束(MySQL 无部分 唯一索引):评估事务以单行配置 FOR UPDATE 作串行化锚点闭合竞态。 - 共享核心 packages/db-cn/src/usage.ts:computeUsageAggregates(非 deleted 口径实时 SUM:photos groupBy + 变体 JOIN raw)+ evaluateUsage (storage/perAlbum 两维超阈 ensure、回落/停用 resolve、去重)+ evaluateUsageSafe(自开事务、失败仅记日志不拖垮业务主流程)。api 与 worker 共用(放置原因见文件头)。 - api:GET /admin/usage/summary(相册聚合 + totals + 阈值 + 告警:活跃 在前、近期解除补位,合计 100)与 PUT /admin/usage/thresholds(缺省 保持/显式 null 清除 + 契约「即刻重新评估」);仅 admin。BigInt 运输 决策:库内 BIGINT、JSON 出入口 Number() 收敛(2^53≈9PB 上限,DTO 以 MAX_SAFE_INTEGER 拒绝超界输入杜绝静默失真)。 - 检查点(提交后评估):worker 发布事务后(仅真实 published)、worker 水印任务完成后、api photos 可见性迁移(PATCH/软删除)与批量隐藏后、 阈值 PUT 后。 - tests:usage e2e +6(汇总数学/降序/标题、403/401、400 校验矩阵、 perAlbum 告警产生-去重-上调解除、storage 告警-null 清除解除、hidden 计入+deleted 回落且穿过 api 检查点);两侧 reset 增补 usage 表截断 + 单行重种。全量回归:api e2e 214/214、api 单测 51/51、worker 单测 14/14 + e2e 13/13。 Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
@@ -10,7 +10,10 @@
|
||||
// bump entityVersion、不再补 outbox——事务即幂等边界。
|
||||
// 变体对象先于事务写入 MinIO(键含 version 段,内容确定,重复写无害);流式拉取
|
||||
// 原图整缓冲驻内存(50MB 上限内可接受,P5 再考虑流式)。
|
||||
// P4a Task 5 检查点:发布事务提交后评估用量阈值告警(新增照片存储可能超阈;
|
||||
// 评估自开事务且失败只记日志——发布是既成事实,见 evaluateUsageSafe)。
|
||||
import { Readable } from 'node:stream';
|
||||
import { evaluateUsageSafe } from '@photo/db-cn';
|
||||
import type { Prisma, PrismaClient } from '@photo/db-cn';
|
||||
import type { StoragePort } from '@photo/storage';
|
||||
import {
|
||||
@@ -208,6 +211,15 @@ export class PublishService {
|
||||
return 'published' as const;
|
||||
});
|
||||
this.logger?.info({ photoId, outcome }, '照片处理完成');
|
||||
if (outcome === 'published') {
|
||||
// P4a Task 5 检查点(仅真实发布路径;already/skipped 无存储变化)。
|
||||
await evaluateUsageSafe(this.prisma, (err) =>
|
||||
this.logger?.warn(
|
||||
{ photoId, err: err instanceof Error ? err.message : String(err) },
|
||||
'用量告警评估失败(发布后)',
|
||||
),
|
||||
);
|
||||
}
|
||||
return outcome;
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +19,10 @@
|
||||
// 完成事务:job 置 done + 推进 albums「有效版本位」(watermarkEffectiveVersion
|
||||
// /watermarkEnabled,仅更高版本推进——乱序完成的旧任务自然让位)+ entityVersion+1
|
||||
// + outbox album.updated(载荷经 contracts 共享构建器,ov 副本据此切换模式键)。
|
||||
// P4a Task 5 检查点:任务完成后评估用量阈值告警(水印变体行切换改变存储;
|
||||
// 评估自开事务且失败只记日志——任务 done 是既成事实,见 evaluateUsageSafe)。
|
||||
import { Readable } from 'node:stream';
|
||||
import { evaluateUsageSafe } from '@photo/db-cn';
|
||||
import type { Prisma, PrismaClient } from '@photo/db-cn';
|
||||
import {
|
||||
buildAlbumUpdatedPayloadCore,
|
||||
@@ -145,6 +148,16 @@ export class WatermarkRegenService {
|
||||
}
|
||||
|
||||
await this.completeJob(jobRowId, job.albumId, job.configVersion, config);
|
||||
// P4a Task 5 检查点:完成事务提交后评估(变体行已切换,存储口径随之变化)。
|
||||
await evaluateUsageSafe(this.prisma, (err) =>
|
||||
this.logger?.warn(
|
||||
{
|
||||
jobId: jobRowId,
|
||||
err: err instanceof Error ? err.message : String(err),
|
||||
},
|
||||
'用量告警评估失败(水印任务完成后)',
|
||||
),
|
||||
);
|
||||
this.logger?.info(
|
||||
{ jobId: jobRowId, albumId: job.albumId, configVersion: job.configVersion, total: processed, failed },
|
||||
'水印重生成任务完成',
|
||||
|
||||
Reference in New Issue
Block a user