Merge commit '7fa69c2521eefbfc1b78cca0e8a45264291c967d' into dev_jacy
CI / verify (pull_request) Waiting to run

This commit is contained in:
2026-09-03 21:29:29 +08:00
9 changed files with 308 additions and 13 deletions
@@ -2,6 +2,7 @@ import { describe, expect, it } from 'vitest';
import {
activityPosterPackFileName,
activityPosterQrSlotPx,
activityPosterResizeTarget,
activityPosterTemplateTooLarge,
} from './activity-poster';
@@ -37,6 +38,30 @@ describe('activityPosterTemplateTooLarge', () => {
});
});
describe('activityPosterResizeTarget', () => {
it('returns null for 1080x1920', () => {
expect(activityPosterResizeTarget(1080, 1920)).toBeNull();
});
it('scales edge over 2500', () => {
const target = activityPosterResizeTarget(4000, 3000);
expect(target).not.toBeNull();
expect(Math.max(target!.width, target!.height)).toBeLessThanOrEqual(2500);
expect(activityPosterTemplateTooLarge(target!.width, target!.height)).toBe(false);
});
it('scales pixel count over 4e6', () => {
const target = activityPosterResizeTarget(2500, 2000);
expect(target).not.toBeNull();
expect(target!.width * target!.height).toBeLessThanOrEqual(4_000_000);
expect(activityPosterTemplateTooLarge(target!.width, target!.height)).toBe(false);
});
it('does not upscale small images', () => {
expect(activityPosterResizeTarget(100, 100)).toBeNull();
});
});
describe('activityPosterPackFileName', () => {
it('uses city, company, id', () => {
expect(activityPosterPackFileName({
@@ -72,6 +72,39 @@ export function activityPosterTemplateTooLarge(width: number, height: number): b
return w > ACTIVITY_POSTER_MAX_EDGE || h > ACTIVITY_POSTER_MAX_EDGE || w * h > ACTIVITY_POSTER_MAX_PIXELS;
}
/** HQ 上传活动图时的尺寸说明(前后端共用文案) */
export const ACTIVITY_POSTER_UPLOAD_HINT =
'底图最长边不超过 2500px,总像素不超过 400 万;超出将自动压缩';
export type OssUploadImageMeta = {
width: number;
height: number;
originalWidth?: number;
originalHeight?: number;
compressed: boolean;
fileSize: number;
};
/** 若底图超限,返回等比缩放后的目标宽高;否则 null(不放大) */
export function activityPosterResizeTarget(
width: number,
height: number,
): { width: number; height: number } | null {
const w = Math.max(1, Math.round(width));
const h = Math.max(1, Math.round(height));
if (!activityPosterTemplateTooLarge(w, h)) return null;
const maxEdge = Math.max(w, h);
const scaleEdge = maxEdge > ACTIVITY_POSTER_MAX_EDGE ? ACTIVITY_POSTER_MAX_EDGE / maxEdge : 1;
const scalePixels = w * h > ACTIVITY_POSTER_MAX_PIXELS ? Math.sqrt(ACTIVITY_POSTER_MAX_PIXELS / (w * h)) : 1;
const scale = Math.min(scaleEdge, scalePixels);
return {
width: Math.max(1, Math.round(w * scale)),
height: Math.max(1, Math.round(h * scale)),
};
}
/** zip / 单张下载文件名:`{城市}_{公司或姓名}_{id}.png` */
export function activityPosterPackFileName(input: {
cityName?: string | null;