修复(08-28-reimbursement-detail-material-preview-fixes): 收紧撤回展示并校正材料图片尺寸

这个提交包含在:
2026-08-28 21:53:26 +08:00
父节点 f178aa0de3
当前提交 46bfa7e00f
共修改 14 个文件,包含 342 行新增和 13 行删除
@@ -689,10 +689,18 @@ reply.header(
- PDF originals contribute every original page; JPG/PNG/WebP materials are
normalized with `sharp` and embedded one per page. The merged PDF is a
derived approval view, never an original or digital-signature fact source.
- Image page geometry must use the width and height returned by Sharp's final
normalized output (`toBuffer({ resolveWithObject: true }).info`), not input
`metadata()`. `rotate()` applies EXIF orientation during output, so input
metadata may describe the opposite portrait/landscape shape.
- A stable collection hash covers the explicitly ordered material type, ID,
file hash, MIME and byte count. The cache key also includes reimbursement ID
and merger version. Fix CreationDate, ModificationDate, Creator, Producer and
save options so identical input produces byte-identical output.
- Any visible merge-algorithm change, including corrected image page geometry,
increments the merger version. Byte determinism is required across repeated
generation within one version; different versions may intentionally produce
different bytes for the same ordered source collection.
- The in-memory bundle is bounded to 20 materials, 50 MiB source bytes, 40
pages, 40 megapixels per image, 50 MiB output and 100 MiB process cache.
Worker execution has a fixed timeout; concurrent requests for one collection
@@ -732,6 +740,7 @@ reply.header(
### 6. Tests Required
- Service tests assert deterministic bytes, all-page copy, image embedding,
EXIF orientation 6/8 output geometry, ordinary JPG/PNG/WebP geometry,
20–30-page worker behavior, concurrent deduplication, expiry and limits.
- Route tests assert authorization, reason gate, token binding, cache miss,
issue/access audit, audit-failure fail-closed behavior and exact PDF size.
@@ -748,6 +757,12 @@ reply.header(
const pages = await rasterizeEveryPdfOnThePhone(); // slow, blank-prone and duplicates PDF rendering
```
```js
const metadata = await sharp(source).rotate().metadata();
// metadata describes the input and can disagree with the rotated output.
const page = output.addPage([metadata.width, metadata.height]);
```
#### Correct
```tsx
@@ -755,6 +770,14 @@ const bundle = await fetchReimbursementMaterialPreviewBundle(reimbursementId);
await previewFileInWecom({ url: bundle.signedUrl, name: bundle.originalName, size: bundle.fileSize });
```
```js
const { data, info } = await sharp(source)
.rotate()
.jpeg()
.toBuffer({ resolveWithObject: true });
const page = output.addPage([info.width, info.height]);
```
## Scenario: Lightweight Travel Summary On Reimbursements
### 1. Scope / Trigger