npm package: @gumin61/transfer-data-sdk
Description: SDK สำหรับโอนย้ายข้อมูลระหว่าง MySQL database (source → target) รองรับการ insert เฉพาะแถวใหม่ตาม PK, validation, restore user refs และจัดการ FK ที่ไม่มีในตารางอ้างอิง
Latest version: 1.2.1

# @gumin61/transfer-data-sdk

SDK สำหรับโอนย้ายข้อมูลระหว่าง MySQL database (source → target) รองรับการ insert เฉพาะแถวใหม่ตาม PK, validation, restore user refs และจัดการ FK ที่ไม่มีในตารางอ้างอิง

หมายเหตุ: structure schema ของฐานข้อมูลปลายทาง(target) จะต้องเหมือนกับฐานข้อมูลต้นทาง(source)

## การติดตั้ง

```bash
npm install @gumin61/transfer-data-sdk mysql2
```

## ความต้องการของระบบ

- Node.js 18+
- MySQL (ใช้ `mysql2` เป็น driver)

## การใช้งานพื้นฐาน

```ts
import mysql from "mysql2/promise";
import { copyDataTables } from "@gumin61/transfer-data-sdk";

const sourcePool = mysql.createPool({
  host: "localhost",
  port: 3306,
  user: "root",
  password: "password",
  database: "source_db",
});

const targetPool = mysql.createPool({
  host: "localhost",
  port: 3306,
  user: "root",
  password: "password",
  database: "target_db",
});

const result = await copyDataTables(
  { sourcePool, targetPool },
  {
    tableNames: ["users", "permissions"],
    options: { pageSize: 500, batchSize: 500 },
  }
);

console.table(result.tableResults);
console.log(`Total: ${result.totalRowsTransferred} rows`);
```

## API หลัก

### `copyDataTables(connectionPool, params)`

โอนข้อมูลจาก source ไป target ตาม PK (รันซ้ำจะ insert เฉพาะแถวใหม่)

**พารามิเตอร์**

| พารามิเตอร์ | ประเภท | คำอธิบาย |
|-------------|--------|----------|
| `tableNames` | `string[] \| string` | ชื่อตารางที่จะโอน (รองรับ alias `tablesName`) |
| `options.pageSize` | `number` | จำนวนแถวต่อหน้าการดึงข้อมูล (default: 500) |
| `options.batchSize` | `number` | จำนวนแถวต่อ batch insert (default: 500) |
| `setDataOptions` | `SetDataOptions[]` | กำหนดค่าแทนคอลัมน์ก่อน insert |
| `restoreUserRefsOptions` | `RestoreUserRefsTableOption[]` | ตารางที่ต้องคืนค่า created_by/updated_by หลังโอน |
| `insteadValueOptions` | `InsteadValueOptions[]` | จัดการ FK ที่ไม่มีในตารางอ้างอิง (ensure row หรือ fallback) |
| `validateAndUpdate` | `boolean` | `false` = validate เฉพาะอย่างเดียว, `true` = validate แล้วอัปเดตข้อมูลที่ไม่ตรงกัน |

**ผลลัพธ์**

```ts
{
  totalRowsTransferred: number;
  tableResults: { tableName: string; rowsTransferred: number }[];
  restoreUserRefsResults: { tableName: string; rowsUpdated: number }[];
  validateResults: {
    tableName: string;
    totalChecked: number;
    mismatchCount: number;
    insertedCount: number;
    updatedCount: number;
  }[];
}
```

---

### `restoreUserRefsInTable(sourcePool, targetPool, tableName, options?)`

คืนค่า `created_by`, `updated_by` ใน target ให้ตรงกับ source (ใช้หลังโอนตารางที่เคย insert ด้วยค่าสองตัวนี้เป็น `null`)

```ts
import { restoreUserRefsInTable } from "@gumin61/transfer-data-sdk";

await restoreUserRefsInTable(sourcePool, targetPool, "organizations", {
  batchSize: 100,
});
```

---

### `createRestoreUserRefsOptions(tableNames, batchSize?)`

สร้าง `RestoreUserRefsTableOption[]` สำหรับใช้กับ `restoreUserRefsOptions`

```ts
import { createRestoreUserRefsOptions } from "@gumin61/transfer-data-sdk";

const options = createRestoreUserRefsOptions(
  ["organizations"],
  2500
);
```

---

## ตัวอย่างการใช้งานขั้นสูง

### โอนตารางที่มี FK ต้องใส่ null ก่อน

กรณีโอน `organizations` ก่อน `users` → ยังไม่มี user ใน target จึงต้องใส่ `created_by`/`updated_by` เป็น `null` ก่อน แล้วคืนค่าหลังโอน `users` เสร็จ

```ts
const result = await copyDataTables(
  { sourcePool, targetPool },
  {
    tableNames: ["organizations", "users"],
    setDataOptions: [
      { tableName: "organizations", columnName: "created_by", value: null },
      { tableName: "organizations", columnName: "updated_by", value: null },
    ],
    restoreUserRefsOptions: createRestoreUserRefsOptions(
      ["organizations"],
      100
    ),
    options: { pageSize: 500, batchSize: 500 },
  }
);
```

### จัดการ FK ที่ไม่มีในตารางอ้างอิง

เมื่อ source มี FK ชี้ไปยังแถวที่ไม่มีใน target สามารถกำหนด `insteadValueOptions` ให้:
- หาแถวในตารางอ้างอิงจาก `lookup` ก่อน
- ถ้าไม่พบ จะ INSERT `createData` แล้วใช้ `insertId` เป็นค่าของ FK

```ts
const result = await copyDataTables(
  { sourcePool, targetPool },
  {
    tableNames: ["pii_master"],
    insteadValueOptions: [
      {
        tableName: "pii_master",  // ตารางที่มี FK ที่ไม่มีอยู่จริง(ทำให้เกิดข้อผิดพลาด)
        columnName: "data_type_id",  // Column FK ของตาราง
        referenceTableName: "data_type_master",  // ตารางที่จะต้องกำหนดข้อมูลใหม่แทนไปก่อน
        referenceColumnName: "data_type_id",  // Column PK
        ensureReferenceRow: {  // Mock FK Data ขึ้นมา โดย fields จะต้องตรงกับในตารางที่จะ insert
          lookup: { company_id: 1, data_type_name_en: "Mock FK Data" },
          createData: {
            company_id: 1,
            data_type_name_th: "ข้อมูลทดแทน FK",
            data_type_name_en: "Mock FK Data",
            is_deleted: 0,
            created_by: null,
            updated_by: null,
          },
        },
      },
    ],
    options: { pageSize: 500, batchSize: 500 },
  }
);
```

### Validate และอัปเดตข้อมูลที่ไม่ตรงกัน

- `validateAndUpdate: false` — ตรวจสอบและแสดง log เฉพาะอย่างเดียว ไม่แก้ไขข้อมูล
- `validateAndUpdate: true` — ตรวจสอบแล้ว INSERT/UPDATE ข้อมูลที่ขาดหรือไม่ตรงกันให้ target ตรงกับ source

```ts
const result = await copyDataTables(
  { sourcePool, targetPool },
  {
    tableNames: ["users", "permissions"],
    validateAndUpdate: true, // true = validate + update
    options: { pageSize: 500, batchSize: 500 },
  }
);

if (result.validateResults.length > 0) {
  console.table(result.validateResults);
}
```

---

## สรุป Types ที่ Export

```ts
// ฟังก์ชัน
copyDataTables(connectionPool, params): Promise<CopyDataTablesResult>
restoreUserRefsInTable(sourcePool, targetPool, tableName, options?): Promise<{ rowsUpdated: number }>
createRestoreUserRefsOptions(tableNames, batchSize?): RestoreUserRefsTableOption[]

// Types
ConnectionPool
CopyDataTablesResult
TableTransferResult
RestoreUserRefsResult
RestoreUserRefsTableOption
ValidateTableResult
SetDataOptions
InsteadValueOptions
TransferOptions
```

---