feat(worker): watermark versioning with atomic history regeneration
Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
@@ -317,6 +317,83 @@ export class AlbumTransferRequestDto {
|
||||
keepAsCollaborator!: boolean;
|
||||
}
|
||||
|
||||
// ── 水印(P4a Task 3)──
|
||||
|
||||
/** 契约 WatermarkPosition 枚举(与 contracts WATERMARK_POSITIONS 同值)。 */
|
||||
export const WATERMARK_POSITION_VALUES = [
|
||||
'topLeft',
|
||||
'topRight',
|
||||
'bottomLeft',
|
||||
'bottomRight',
|
||||
'center',
|
||||
] as const;
|
||||
export type WatermarkPositionValue = (typeof WATERMARK_POSITION_VALUES)[number];
|
||||
|
||||
/** 契约 WatermarkTextConfig:content 1..64;fontSize 8..72。 */
|
||||
class WatermarkTextConfigDto {
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
@MaxLength(64)
|
||||
content?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsInt()
|
||||
@Min(8)
|
||||
@Max(72)
|
||||
fontSize?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* enabled=true 时 text.content 必填(技术方案 7.2:P4a 文字水印——无内容的
|
||||
* 开启配置在 API 层即 400,worker 不再解释「开启但无字」的形态)。约束挂在
|
||||
* enabled 上(text 缺席时其字段级装饰器不运行,挂 text 无法拒绝缺省)。
|
||||
*/
|
||||
@ValidatorConstraint({ name: 'watermarkTextRequired', async: false })
|
||||
class WatermarkTextRequiredConstraint implements ValidatorConstraintInterface {
|
||||
validate(value: unknown, args: ValidationArguments): boolean {
|
||||
if (value !== true) {
|
||||
return true; // 关闭配置无文字要求。
|
||||
}
|
||||
const { text } = args.object as { text?: { content?: unknown } };
|
||||
return (
|
||||
typeof text?.content === 'string' && (text.content as string).length > 0
|
||||
);
|
||||
}
|
||||
|
||||
defaultMessage(): string {
|
||||
return 'enabled=true 时必须提供 text.content(文字水印内容)';
|
||||
}
|
||||
}
|
||||
|
||||
/** 契约 WatermarkConfig(创建请求;数值边界对齐契约 min/max)。 */
|
||||
export class WatermarkConfigDto {
|
||||
@IsBoolean()
|
||||
@Validate(WatermarkTextRequiredConstraint)
|
||||
enabled!: boolean;
|
||||
|
||||
@IsOptional()
|
||||
@IsIn(WATERMARK_POSITION_VALUES)
|
||||
position?: WatermarkPositionValue;
|
||||
|
||||
@IsOptional()
|
||||
@IsInt()
|
||||
@Min(0)
|
||||
@Max(100)
|
||||
opacity?: number;
|
||||
|
||||
@IsOptional()
|
||||
@IsInt()
|
||||
@Min(5)
|
||||
@Max(50)
|
||||
scalePercent?: number;
|
||||
|
||||
@IsOptional()
|
||||
@ValidateNested()
|
||||
@Type(() => WatermarkTextConfigDto)
|
||||
text?: WatermarkTextConfigDto;
|
||||
}
|
||||
|
||||
// ── 资产(P1b Task 3)──
|
||||
|
||||
/** 契约 AssetKind:banner(观众页宣传图,PublicAlbum.banner 消费方);cover/logo 建链就绪,展示消费随后续阶段。 */
|
||||
|
||||
Reference in New Issue
Block a user