Refactor document versioning to extract fields from uploaded .docx files on the server side

- Removed the `fields` parameter from the version upload form, as it is now extracted from the uploaded .docx file using the `extractContentControls` function.
- Updated related types and interfaces to reflect the new structure of version fields.
- Modified the OpenAPI specification to document the changes in the versioning process.
- Adjusted tests to ensure compliance with the new behavior, including validation for the absence of the `fields` parameter.
- Implemented a new function `extractDocumentSnapshot` to handle the extraction of content controls and document properties from .docx files.
- Updated metadata and versioning services to align with the new extraction logic.
This commit is contained in:
Suphonchai Phoonsawat 2026-10-31 10:21:17 +07:00
parent bc61f0fd2e
commit cdae1ceedd
17 changed files with 380 additions and 179 deletions

View file

@ -16,13 +16,36 @@ export interface FieldSnapshotEntry {
missing: boolean;
}
/** fields ของ version รูปแบบปัจจุบัน — **ก้อนเดียวกับ return ของ `/api/template/extract`**
* ที่ server แกะจากไฟล์ .docx ฝั่ง server ตอนอัปโหลด (2026-10-30) · ค่าวันที่ใน
* properties มาเป็น ISO string ตาม DTO ของกลุ่ม docver */
export interface ExtractSnapshotFields {
/** ค่า Content Control `{ [tag]: text }` */
cc: Record<string, string>;
/** ข้อความใน bookmark `{ [name]: text }` */
bookmark: Record<string, string>;
/** document properties (standard/extended/custom) */
properties: {
standard?: Record<string, string | undefined>;
extended?: Record<string, number | string | undefined>;
custom?: Record<string, string | number | boolean | null>;
};
/** ผลตรวจ ticket ที่ stamp มากับไฟล์ (exp + HmacSha256 ด้วย secret ฝั่ง server) */
verify: Record<string, unknown>;
}
/**
* snapshot fields ต่อ version — backend เก็บ 2 รูปแบบ:
* snapshot fields ต่อ version — backend เก็บ 3 รูปแบบ:
* - ปัจจุบัน (2026-10-30): ก้อน extract snapshot `{cc, bookmark, properties, verify}`
* เหมือน return ของ /api/template/extract (server แกะจากไฟล์เอง)
* - เอกสารผูก template: `{ [fieldKey]: { value, missing } }`
* - เอกสารไม่ผูก template (track เองจาก add-in): map ดิบ `{ [tag]: text }`
* UI ต้อง handle ได้ทั้งคู่ (ใช้ readSnapshotField ช่วยอ่าน)
* UI ต้อง handle ได้ทั้งหมด (ใช้ readSnapshotField ช่วยอ่าน)
*/
export type FieldsSnapshot = Record<string, FieldSnapshotEntry> | Record<string, string>;
export type FieldsSnapshot =
| ExtractSnapshotFields
| Record<string, FieldSnapshotEntry>
| Record<string, string>;
/** item ของ GET /api/documents/{documentId}/versions */
export interface VersionRecord {
@ -41,7 +64,8 @@ export interface VersionRecord {
message: string | null;
}
/** อ่านค่า + สถานะ missing ของ field หนึ่งจาก snapshot ไม่ว่าจะเก็บรูปแบบไหน — ไม่มี field นี้คืน null */
/** อ่านค่า + สถานะ missing ของ field หนึ่งจาก snapshot ไม่ว่าจะเก็บรูปแบบไหน — ไม่มี field นี้คืน null
* รูปแบบ extract snapshot (2026-10-30) ค่า CC อยู่ใต้ `cc` — อ่านซ้ำเข้า map ดิบรอบเดียวกัน */
export function readSnapshotField(
snapshot: FieldsSnapshot | null,
key: string
@ -49,6 +73,13 @@ export function readSnapshotField(
if (!snapshot) {
return null;
}
// Record ธรรมดาไม่ถูก `in` ตัดออกจาก union (มี index signature ครอบทุก key) — เช็คแล้ว
// บอกชนิดตรง ๆ ว่าเป็นก้อน extract snapshot (key "cc" ชนกับ tag ชื่อเดียวกันยอมรับเป็น
// edge case ของการเดารูปจากข้อมูลเก่า)
if ("cc" in snapshot) {
const cc = (snapshot as ExtractSnapshotFields).cc;
return readSnapshotField(cc, key);
}
const raw = snapshot[key];
if (raw === undefined) {
return null;