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

@ -12,6 +12,9 @@ import {
uuid,
} from "drizzle-orm/pg-core";
// type-only — ไม่ลาก runtime ของ lib-tmpl มาที่ schema (ใช้เป็นชนิดของ fields jsonb)
import type { DocumentExtractSnapshot } from "@/lib/lib-tmpl";
// ─── Enum ────────────────────────────────────────────────────────────────────
export const annotationType = pgEnum("annotation_type", [
@ -275,6 +278,14 @@ export const documents = pgTable(
/** version ของเอกสาร — ไฟล์จริงอยู่ใน MinIO ตาม object key (docx + pdf ต่อ version)
* · pdf เป็น optional ตาม contract ของ add-in (best-effort — NULL = version นี้ไม่มี pdf)
* · object key เป็น uuid ล้วน `docver/<versionId>/document.{docx,pdf}` ไม่มี input ผู้ใช้ */
/** fields ของ version — **ก้อนเดียวกับ return ของ `/api/template/extract`** (ผ่าน
* `extractDocumentSnapshot` ของ lib/lib-tmpl.ts ตัวเดียวกัน): `{cc, bookmark, properties,
* verify}` แกะจากไฟล์ .docx ฝั่ง server ตอน upload (2026-10-30) · row เก่าก่อนวันนั้น
* เก็บ map ดิบ `{ [tag]: text }` / `{ [fieldKey]: {value, missing} }` — ฝั่งอ่านของ add-in
* (FieldsSnapshot + readSnapshotField) แยกรูปเอง จึงไม่ต้อง migrate ข้อมูลเก่า */
export type DocumentVersionFields = DocumentExtractSnapshot;
export const documentVersions = pgTable(
"document_versions",
{
@ -290,9 +301,9 @@ export const documentVersions = pgTable(
contentHash: text("content_hash").notNull(),
/** commit message (FR-13) — trim แล้วว่าง = NULL */
message: text("message"),
/** snapshot ตอน upload: map ดิบ `{ [sdtTag]: text }` จาก content controls
* (รูปแบบ {value,missing} ของเอกสารผูก template ยังไม่มีผู้ส่ง — วันหน้ามีค่อยขยาย $type) */
fields: jsonb("fields").$type<Record<string, string>>(),
/** snapshot ตอน upload: ก้อน extract snapshot `{cc, bookmark, properties, verify}`
* เหมือน return ของ /api/template/extract (ดู DocumentVersionFields เหนือตาราง) */
fields: jsonb("fields").$type<DocumentVersionFields>(),
templateId: text("template_id"),
/** ชื่อไฟล์ดั้งเดิมที่ browser ส่ง (ผ่าน decodeUploadFilename + strip path แล้ว) —
* ใช้ตั้ง Content-Disposition ของ object ไม่ใช่ชื่อ object */
@ -447,7 +458,7 @@ export type DocumentVersionDTO = {
createdByUserId: string;
createdByUserName: string;
contentHash: string;
fields: Record<string, string> | null;
fields: DocumentVersionFields | null;
message: string | null;
hasPdf: boolean;
};