Compare commits

...

11 Commits

Author SHA1 Message Date
developer_liu c26ebdde1b v4.0.15上传图片自动压缩 2026-09-03 16:04:10 +08:00
jacy b60b16a84f Merge pull request 'Dev' (#69) from dev into main
CI / verify (push) Waiting to run
Reviewed-on: https://git.yqidian.com/jacy/dukang/pulls/69
2026-09-03 13:26:35 +08:00
jacy b74dfd391a Merge pull request '推广码模块,在后端增加文案表达' (#68) from dev_jacy into dev
CI / verify (pull_request) Waiting to run
Reviewed-on: https://git.yqidian.com/jacy/dukang/pulls/68
2026-09-03 13:26:23 +08:00
jacy d04ca615ff merge(dev): 修复企微报告表零日期
CI / verify (push) Waiting to run
2026-09-02 22:19:50 +08:00
jacy ebb34ad112 merge(dev_jacy): 修复企微报告表零日期 2026-09-02 22:19:44 +08:00
jacy c29eb2c383 merge(dev): 企微报告保存改走 Prisma Client
CI / verify (push) Waiting to run
2026-09-02 22:15:33 +08:00
jacy 29e95bd5a3 merge(dev_jacy): 企微报告保存改走 Prisma Client 2026-09-02 22:15:27 +08:00
jacy 62d75a6869 merge(dev): 企微报告截账至发送日前一天24点
CI / verify (push) Waiting to run
2026-09-02 22:05:55 +08:00
jacy 8d70f0da72 merge(dev_jacy): 企微报告截账至发送日前一天24点 2026-09-02 22:05:48 +08:00
jacy 42c1b65632 Merge pull request 'Dev' (#67) from dev into main
CI / verify (push) Has been cancelled
Reviewed-on: https://git.yqidian.com/jacy/dukang/pulls/67
2026-09-02 21:13:16 +08:00
jacy de7bd34aa8 Merge pull request 'v4.0.14概览和日报' (#66) from dev_jacy into dev
CI / verify (pull_request) Has been cancelled
Reviewed-on: https://git.yqidian.com/jacy/dukang/pulls/66
2026-09-02 21:13:02 +08:00
9 changed files with 308 additions and 13 deletions
+44 -3
View File
@@ -1,5 +1,5 @@
import { useState } from 'react'; import { useState, type ReactNode } from 'react';
import { Button, Image, Input, Modal, Space, Upload, message } from 'antd'; import { Button, Image, Input, Modal, Space, Typography, Upload, message } from 'antd';
import { EyeOutlined, FilePdfOutlined, UploadOutlined } from '@ant-design/icons'; import { EyeOutlined, FilePdfOutlined, UploadOutlined } from '@ant-design/icons';
import type { UploadProps } from 'antd'; import type { UploadProps } from 'antd';
import { uploadFileToOss, type OssMediaType, type UploadFileResult } from '../lib/upload'; import { uploadFileToOss, type OssMediaType, type UploadFileResult } from '../lib/upload';
@@ -13,6 +13,7 @@ type OssUploadProps = {
accept?: string; accept?: string;
maxSizeMb?: number; maxSizeMb?: number;
placeholder?: string; placeholder?: string;
uploadHint?: ReactNode;
}; };
function isImageUrl(url: string) { function isImageUrl(url: string) {
@@ -23,6 +24,32 @@ function isPdfUrl(url: string) {
return /\.pdf(\?|#|$)/i.test(url); return /\.pdf(\?|#|$)/i.test(url);
} }
function formatUploadImageMessage(result: UploadFileResult): string {
const img = result.image;
if (!img) return '上传成功';
const sizeLabel = `${img.width}×${img.height}`;
if (img.compressed && img.originalWidth && img.originalHeight) {
const original = `${img.originalWidth}×${img.originalHeight}`;
if (original !== sizeLabel) {
return `上传成功(${sizeLabel},已由 ${original} 自动压缩)`;
}
}
return `上传成功(${sizeLabel}`;
}
function formatUploadImageHint(result: UploadFileResult): string | null {
const img = result.image;
if (!img) return null;
const sizeLabel = `${img.width}×${img.height}`;
if (img.compressed && img.originalWidth && img.originalHeight) {
const original = `${img.originalWidth}×${img.originalHeight}`;
if (original !== sizeLabel) {
return `${sizeLabel}(已由 ${original} 自动压缩)`;
}
}
return sizeLabel;
}
export default function OssUpload({ export default function OssUpload({
value, value,
onChange, onChange,
@@ -31,9 +58,11 @@ export default function OssUpload({
mediaType = 'IMAGE', mediaType = 'IMAGE',
accept, accept,
placeholder = '上传后自动填入,或手动粘贴 URL', placeholder = '上传后自动填入,或手动粘贴 URL',
uploadHint,
}: OssUploadProps) { }: OssUploadProps) {
const [uploading, setUploading] = useState(false); const [uploading, setUploading] = useState(false);
const [pdfPreviewOpen, setPdfPreviewOpen] = useState(false); const [pdfPreviewOpen, setPdfPreviewOpen] = useState(false);
const [imageMetaHint, setImageMetaHint] = useState<string | null>(null);
const resolvedAccept = const resolvedAccept =
accept ?? (mediaType === 'VIDEO' ? 'video/*' : mediaType === 'FILE' ? undefined : 'image/*'); accept ?? (mediaType === 'VIDEO' ? 'video/*' : mediaType === 'FILE' ? undefined : 'image/*');
@@ -45,7 +74,9 @@ export default function OssUpload({
const result = await uploadFileToOss(raw, { bizType, mediaType }); const result = await uploadFileToOss(raw, { bizType, mediaType });
onChange?.(result.url); onChange?.(result.url);
onUploaded?.(result); onUploaded?.(result);
message.success('上传成功'); const successMessage = formatUploadImageMessage(result);
setImageMetaHint(formatUploadImageHint(result));
message.success(successMessage);
onSuccess?.(result); onSuccess?.(result);
} catch (e) { } catch (e) {
const err = e instanceof Error ? e : new Error('上传失败'); const err = e instanceof Error ? e : new Error('上传失败');
@@ -116,6 +147,16 @@ export default function OssUpload({
<video src={value} controls style={{ maxWidth: '100%', maxHeight: 160, borderRadius: 4 }} /> <video src={value} controls style={{ maxWidth: '100%', maxHeight: 160, borderRadius: 4 }} />
)} )}
{filePreview} {filePreview}
{uploadHint ? (
<Typography.Text type="secondary" style={{ fontSize: 12 }}>
{uploadHint}
</Typography.Text>
) : null}
{imageMetaHint ? (
<Typography.Text type="secondary" style={{ fontSize: 12 }}>
{imageMetaHint}
</Typography.Text>
) : null}
<Space wrap> <Space wrap>
<Upload <Upload
accept={resolvedAccept} accept={resolvedAccept}
+2
View File
@@ -4,6 +4,7 @@ import {
DEFAULT_OSS_MAX_UPLOAD_BYTES, DEFAULT_OSS_MAX_UPLOAD_BYTES,
formatOssMaxSizeMb, formatOssMaxSizeMb,
} from '@dukang/shared-ui/compressImage'; } from '@dukang/shared-ui/compressImage';
import type { OssUploadImageMeta } from '@dukang/shared-types';
export type OssMediaType = 'IMAGE' | 'VIDEO' | 'FILE'; export type OssMediaType = 'IMAGE' | 'VIDEO' | 'FILE';
@@ -12,6 +13,7 @@ export type UploadFileResult = {
ossKey: string; ossKey: string;
bucket: string; bucket: string;
mock: boolean; mock: boolean;
image?: OssUploadImageMeta;
}; };
async function prepareUploadFile(file: File, mediaType: OssMediaType): Promise<File> { async function prepareUploadFile(file: File, mediaType: OssMediaType): Promise<File> {
@@ -6,6 +6,7 @@ import {
import type { ColumnsType } from 'antd/es/table'; import type { ColumnsType } from 'antd/es/table';
import { import {
ACTIVITY_POSTER_STATUS_LABELS, ACTIVITY_POSTER_STATUS_LABELS,
ACTIVITY_POSTER_UPLOAD_HINT,
DEFAULT_ACTIVITY_POSTER_QR_SLOT, DEFAULT_ACTIVITY_POSTER_QR_SLOT,
type ActivityPosterItem, type ActivityPosterItem,
type ActivityPosterQrSlot, type ActivityPosterQrSlot,
@@ -280,7 +281,7 @@ export default function ActivityPostersPage() {
<Input maxLength={128} /> <Input maxLength={128} />
</Form.Item> </Form.Item>
<Form.Item name="imageUrl" label="活动图" rules={[{ required: true, message: '请上传活动图' }]}> <Form.Item name="imageUrl" label="活动图" rules={[{ required: true, message: '请上传活动图' }]}>
<OssUpload bizType="ACTIVITY_POSTER" /> <OssUpload bizType="ACTIVITY_POSTER" uploadHint={ACTIVITY_POSTER_UPLOAD_HINT} />
</Form.Item> </Form.Item>
<Form.Item name="qrXPct" hidden><InputNumber /></Form.Item> <Form.Item name="qrXPct" hidden><InputNumber /></Form.Item>
<Form.Item name="qrYPct" hidden><InputNumber /></Form.Item> <Form.Item name="qrYPct" hidden><InputNumber /></Form.Item>
+2 -2
View File
@@ -10,7 +10,7 @@
| 版本线 | **v4.0.15** HQ 概览折线图(粒度分桶、总量/增量、维度线条) | | 版本线 | **v4.0.15** HQ 概览折线图(粒度分桶、总量/增量、维度线条) |
| 订单佣金 | 区县归属已删除;只认关联 / 代下单选择 | | 订单佣金 | 区县归属已删除;只认关联 / 代下单选择 |
| 账单 | 酒订单 / 核销订单分列;合伙人改为周账(周一 08:00);零元不同步合伙人;酒厂含现场提货,零应付仍出账(无需打款) | | 账单 | 酒订单 / 核销订单分列;合伙人改为周账(周一 08:00);零元不同步合伙人;酒厂含现场提货,零应付仍出账(无需打款) |
| 活动图 | HQ 上传底图/码栏/文案;合伙人选择写入库;HQ 可指定一张图为勾选主合伙人合成下载;子账号不可看活动图 | | 活动图 | HQ 上传底图/码栏/文案;**v4.0.15 上传超限自动压缩并提示尺寸**;合伙人选择写入库;HQ 可指定一张图为勾选主合伙人合成下载;子账号不可看活动图 |
## 1. 版本交付 ## 1. 版本交付
@@ -36,4 +36,4 @@
| 2026-09-02 | v4.0.9:子账号默认启用;关联码已扫码;零元账单不同步;银行账号自填;周账周一 08:00;预付款预估;子账号用户管理无活动图 | | 2026-09-02 | v4.0.9:子账号默认启用;关联码已扫码;零元账单不同步;银行账号自填;周账周一 08:00;预付款预估;子账号用户管理无活动图 |
| 2026-09-02 | v4.0.13:收货禁「全市」;脏地址下单拦截;小飞侠超区/推单失败挂 `fulfillmentHold`(不做仓/收件坐标) | | 2026-09-02 | v4.0.13:收货禁「全市」;脏地址下单拦截;小飞侠超区/推单失败挂 `fulfillmentHold`(不做仓/收件坐标) |
| 2026-09-02 | v4.0.14HQ 概览日/周/月/季/年、环比;全局城市/时间 + 五板块筛;订单/核销笔数与金额 | | 2026-09-02 | v4.0.14HQ 概览日/周/月/季/年、环比;全局城市/时间 + 五板块筛;订单/核销笔数与金额 |
| 2026-09-02 | v4.0.15HQ 概览改为全宽折线图;去掉板块筛;查看快链只带全局城市与日期 | | 2026-09-02 | v4.0.15HQ 概览改为全宽折线图;去掉板块筛;查看快链只带全局城市与日期;活动图上传超限自动压缩并提示尺寸 |
+35
View File
@@ -77,3 +77,38 @@
- [ ] 客服无合伙人/门店/核销图 - [ ] 客服无合伙人/门店/核销图
- [ ] 待办卡与超管发版区不变 - [ ] 待办卡与超管发版区不变
- [ ] domain 折线单测通过 - [ ] domain 折线单测通过
---
## 6. 活动图上传压缩(DPT-20260902-849
### 6.1 目标
HQ 上传活动底图时,后端用 Sharp 自动等比缩放至 v4.0.7 底图限制(最长边 ≤ 2500px、宽×高 ≤ 400 万),并在响应与表单中提示尺寸。
### 6.2 规则
- 入口:`POST /common/resources/upload``bizType=ACTIVITY_POSTER``mediaType=IMAGE`
- 尺寸合规:原样存 OSS,响应 `image.compressed=false`
- 尺寸超限:Sharp 等比缩放后存 OSS,响应含 `width/height/originalWidth/originalHeight/compressed`
- 有 alpha 通道保留 PNG;否则按原 mime 或 JPEG quality 85 输出
- 压缩后仍超过 `OSS_MAX_UPLOAD_BYTES` → 4xx,文案含当前尺寸
- **手动粘贴 URL** 仍不经上传压缩;合成时沿用 v4.0.7 校验
### 6.3 变更面
| 层 | 路径 |
|----|------|
| shared-types | `activity-poster.ts``activityPosterResizeTarget``OssUploadImageMeta``ACTIVITY_POSTER_UPLOAD_HINT` |
| API | `common/image/activity-poster-upload.util.ts``resource.service.ts` |
| HQ | `OssUpload.tsx``ActivityPostersPage.tsx``lib/upload.ts` |
无 Prisma 迁移。
### 6.4 验收
- [ ] 上传 1080×1920:成功,提示尺寸,未标记压缩
- [ ] 上传 4000×3000:成功,提示压缩后尺寸与原尺寸
- [ ] 保存后「为该合伙人下载」/ zip 导出不再报底图过大
- [ ] 非图片 / 损坏文件:明确 4xx
- [ ] AVATAR 等其他 bizType 行为不变
@@ -2,6 +2,7 @@ import { describe, expect, it } from 'vitest';
import { import {
activityPosterPackFileName, activityPosterPackFileName,
activityPosterQrSlotPx, activityPosterQrSlotPx,
activityPosterResizeTarget,
activityPosterTemplateTooLarge, activityPosterTemplateTooLarge,
} from './activity-poster'; } 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', () => { describe('activityPosterPackFileName', () => {
it('uses city, company, id', () => { it('uses city, company, id', () => {
expect(activityPosterPackFileName({ 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; 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` */ /** zip / 单张下载文件名:`{城市}_{公司或姓名}_{id}.png` */
export function activityPosterPackFileName(input: { export function activityPosterPackFileName(input: {
cityName?: string | null; cityName?: string | null;
@@ -0,0 +1,101 @@
import { BadRequestException } from '@nestjs/common';
import {
activityPosterResizeTarget,
activityPosterTemplateTooLarge,
} from '@dukang/shared-types';
import sharp from 'sharp';
const JPEG_QUALITY = 85;
export type ActivityPosterUploadPrepared = {
buffer: Buffer;
mimeType: string;
width: number;
height: number;
originalWidth: number;
originalHeight: number;
compressed: boolean;
};
function resolveOutputMime(hasAlpha: boolean, inputMime?: string): string {
if (hasAlpha) return 'image/png';
if (inputMime === 'image/webp') return 'image/jpeg';
if (inputMime?.startsWith('image/')) return inputMime;
return 'image/jpeg';
}
async function encodeImage(
pipeline: sharp.Sharp,
mimeType: string,
): Promise<Buffer> {
if (mimeType === 'image/png') {
return pipeline.png().toBuffer();
}
if (mimeType === 'image/jpeg' || mimeType === 'image/jpg') {
return pipeline.jpeg({ quality: JPEG_QUALITY }).toBuffer();
}
if (mimeType === 'image/webp') {
return pipeline.webp({ quality: JPEG_QUALITY }).toBuffer();
}
return pipeline.jpeg({ quality: JPEG_QUALITY }).toBuffer();
}
export async function prepareActivityPosterUpload(input: {
buffer: Buffer;
mimeType?: string;
}): Promise<ActivityPosterUploadPrepared> {
if (!input.buffer?.length) {
throw new BadRequestException('活动图文件为空');
}
if (input.mimeType && !input.mimeType.startsWith('image/')) {
throw new BadRequestException('活动图仅支持图片文件');
}
const meta = await sharp(input.buffer, { failOn: 'none' }).metadata();
if (!meta.width || !meta.height) {
throw new BadRequestException('活动图无法读取尺寸,请换一张图片');
}
const originalWidth = meta.width;
const originalHeight = meta.height;
const resizeTarget = activityPosterResizeTarget(originalWidth, originalHeight);
if (!resizeTarget) {
return {
buffer: input.buffer,
mimeType: input.mimeType || 'image/jpeg',
width: originalWidth,
height: originalHeight,
originalWidth,
originalHeight,
compressed: false,
};
}
const hasAlpha = meta.hasAlpha === true;
const outputMime = resolveOutputMime(hasAlpha, input.mimeType);
const buffer = await encodeImage(
sharp(input.buffer, { failOn: 'none' }).resize(resizeTarget.width, resizeTarget.height, {
fit: 'inside',
withoutEnlargement: true,
}),
outputMime,
);
const metaOut = await sharp(buffer).metadata();
const width = metaOut.width ?? resizeTarget.width;
const height = metaOut.height ?? resizeTarget.height;
if (activityPosterTemplateTooLarge(width, height)) {
throw new BadRequestException('活动图压缩后仍超出尺寸限制,请换一张较小的图片');
}
return {
buffer,
mimeType: outputMime,
width,
height,
originalWidth,
originalHeight,
compressed: true,
};
}
@@ -3,6 +3,7 @@ import type { ResourceBizType, ResourceMediaType, ResourceOwnerType } from '@pri
import { Prisma } from '@prisma/client'; import { Prisma } from '@prisma/client';
import { PrismaService } from '../../common/prisma/prisma.module'; import { PrismaService } from '../../common/prisma/prisma.module';
import { serializeBigInt } from '../../common/decorators/current-user.decorator'; import { serializeBigInt } from '../../common/decorators/current-user.decorator';
import { prepareActivityPosterUpload } from '../../common/image/activity-poster-upload.util';
import { OSS_PROVIDER } from '../../integrations/integrations.constants'; import { OSS_PROVIDER } from '../../integrations/integrations.constants';
import type { IOssProvider } from '../../integrations/oss/oss.interface'; import type { IOssProvider } from '../../integrations/oss/oss.interface';
import { logOssUpload, type OssActorRef } from '../../integrations/oss/oss-log.util'; import { logOssUpload, type OssActorRef } from '../../integrations/oss/oss-log.util';
@@ -105,19 +106,75 @@ export class ResourceService {
if (dto.bizType === 'AVATAR' && !file.mimetype?.startsWith('image/')) { if (dto.bizType === 'AVATAR' && !file.mimetype?.startsWith('image/')) {
throw new BadRequestException('头像仅支持图片文件'); throw new BadRequestException('头像仅支持图片文件');
} }
if (dto.bizType === 'ACTIVITY_POSTER' && dto.mediaType === 'IMAGE' && !file.mimetype?.startsWith('image/')) {
throw new BadRequestException('活动图仅支持图片文件');
}
let uploadBuffer = file.buffer;
let uploadMimeType = file.mimetype;
let imageMeta: {
width: number;
height: number;
originalWidth: number;
originalHeight: number;
compressed: boolean;
fileSize: number;
} | undefined;
if (dto.bizType === 'ACTIVITY_POSTER' && dto.mediaType === 'IMAGE') {
const prepared = await prepareActivityPosterUpload({
buffer: file.buffer,
mimeType: file.mimetype,
});
if (prepared.buffer.length > maxUploadBytes) {
const message = `活动图压缩后仍超过 ${Math.floor(maxUploadBytes / 1024 / 1024)}MB(当前 ${prepared.width}×${prepared.height}`;
await logOssUpload(this.prisma, {
scene: 'UPLOAD_PUT_OBJECT',
actorRef: actor,
requestBody: {
...requestBody,
imageWidth: prepared.width,
imageHeight: prepared.height,
compressed: prepared.compressed,
},
status: 'FAILED',
errorMessage: message,
});
throw new BadRequestException(message);
}
uploadBuffer = prepared.buffer;
uploadMimeType = prepared.mimeType;
imageMeta = {
width: prepared.width,
height: prepared.height,
originalWidth: prepared.originalWidth,
originalHeight: prepared.originalHeight,
compressed: prepared.compressed,
fileSize: prepared.buffer.length,
};
}
try { try {
const result = await this.oss.putObject({ const result = await this.oss.putObject({
bizType: dto.bizType, bizType: dto.bizType,
mediaType: dto.mediaType, mediaType: dto.mediaType,
fileName: file.originalname || 'upload.bin', fileName: file.originalname || 'upload.bin',
buffer: file.buffer, buffer: uploadBuffer,
mimeType: file.mimetype, mimeType: uploadMimeType,
}); });
await logOssUpload(this.prisma, { await logOssUpload(this.prisma, {
scene: 'UPLOAD_PUT_OBJECT', scene: 'UPLOAD_PUT_OBJECT',
actorRef: actor, actorRef: actor,
requestBody, requestBody: {
...requestBody,
...(imageMeta
? {
imageWidth: imageMeta.width,
imageHeight: imageMeta.height,
compressed: imageMeta.compressed,
}
: {}),
},
responseBody: { responseBody: {
bucket: result.bucket, bucket: result.bucket,
region: result.region, region: result.region,
@@ -139,14 +196,14 @@ export class ResourceService {
ossKey: result.ossKey, ossKey: result.ossKey,
url: result.url, url: result.url,
fileName: file.originalname || 'avatar', fileName: file.originalname || 'avatar',
fileSize: BigInt(file.size), fileSize: BigInt(uploadBuffer.length),
mimeType: file.mimetype, mimeType: uploadMimeType,
status: 'ACTIVE', status: 'ACTIVE',
}, },
}); });
return serializeBigInt({ ...result, resourceId: resource.id }); return serializeBigInt({ ...result, resourceId: resource.id, ...(imageMeta ? { image: imageMeta } : {}) });
} }
return result; return { ...result, ...(imageMeta ? { image: imageMeta } : {}) };
} catch (err) { } catch (err) {
await logOssUpload(this.prisma, { await logOssUpload(this.prisma, {
scene: 'UPLOAD_PUT_OBJECT', scene: 'UPLOAD_PUT_OBJECT',