# Plan + สรุปตาราง + Drop Script — Cleanup & Mock Seed `hrms_organization` > **ไฟล์เดียวจบ:** Part A สรุปตาราง DB · Part B plan checklist (CP0-CP7) · Part C drop script SQL > **Status:** ✅ CP0-CP6 done (drop 54 tables + seed mock org/pos/profile) · ⏳ CP7 (Keycloak) defer > **Seed mock:** แยกที่ `src/seed/` → `npm run seed:run` (ไม่กระทบ `migration:run` หลัก) > **วิธีใช้:** เช็ค `- [x]` ทีละขั้น, ทุก CP destructive ต้องแจ้ง user ก่อน + รอยืนยัน (DB test `192.168.1.68` เท่านั้น) --- # Part A — สรุปตาราง DB | รายการ | ค่า | |---|---| | Database | `hrms_organization` (MySQL 8.4.6) | | Host | test `192.168.1.68` / prod `192.168.1.63` (ห้ามแตะ prod) | | รวมตาราง | **211 → 157** (drop 54 แล้ว @ test) | สถานะการใช้งาน: | สถานะ | จำนวน | รายละเอียด | |---|---:|---| | ✅ ใช้งานจริง (เก็บไว้) | **147** | ข้อมูลธุรกิจหลัก + Master Data + ตารางเสริมที่มี controller | | 🗑️ เตรียมลบ/ลบแล้ว | **54** | ดู §5 | | ⚙️ ระบบ/ตัวเชื่อม | **10** | junction + system tables | | **รวม** | **211** | | ## A.1 ข้อมูลหลัก | กลุ่ม | ตัวอย่างตาราง | |---|---| | โครงสร้างองค์กร | `orgRoot`, `orgChild1`–`orgChild4`, `orgRevision` | | ประวัติพนักงาน | `profile`, `profileEmployee`, `profileSalary`, `profile*` + `*History` | | ตำแหน่ง/อัตรากำลัง | `position`, `posMaster`, `posMasterHistory`, `employeePosition` | | คำสั่ง | `command`, `commandSend`, `commandRecive`, `commandOperator` | | สิทธิ์/การเข้าถึง | `authRole`, `permissionOrg`, `permissionProfile`, `roleKeycloak` | | อายุงาน/วิทยฐานะ | `tenure*` (5 ตาราง) | ## A.2 Master Data (ข้อมูลอ้างอิง — lookup, เปลี่ยนน้อย) | กลุ่ม | ตาราง (จำนวนแถว exact) | |---|---| | ที่อยู่ | `province` (77), `district` (1,051), `subDistrict` (8,585), `province_master`, `district_master`, `subdistrict_master` | | ประวัติบุคคล | `gender` (3), `bloodGroup` (4), `religion` (5), `relationship` (4), `prefixe` (5), `rank` (9), `educationLevel` (14) | | ตำแหน่ง | `posType` (4), `posLevel` (15), `posDict` (910), `posExecutive` (40), `employeePosType`, `employeePosLevel`, `employeePosDict` | | เครื่องราช | `insignia` (14), `insigniaType` (3) | | คำสั่ง/การลา | `commandType` (47), `commandCode` (21), `commandSys` (12), `leaveType` (11) | > 💡 ที่อยู่มี 2 ชุดซ้ำซ้อน: `province`/`district`/`subDistrict` (ใช้จริง) vs `*_master` (ชุดสำรอง) — ตรวจสอบว่าจำเป็นไหม ## A.3 ตารางคำนวณอัตโนมัติ (cron เขียนทุกคืน — ห้ามแก้ด้วยมือ) | เวลา | ตาราง | สิ่งที่ทำ | |---|---|---| | 00:30 ทุกคืน | `profile`, `profileEmployee` | ประมวลผลคำสั่ง | | 04:00 ทุกคืน | `registry`, `registryEmployee`, `tenure*` | **ล้างและสร้างใหม่** (full refresh) | | 02:00 วันที่ 1 ต.ค. | `profile`, `profileEmployee`, `profileSalary`, `posMaster` | อัปเดตสถานะเกษียณ | ## A.4 ข้อมูลต้นทางจากระบบเดิม (MIS) ชื่อตารางตัวใหญ่ (`HR_*`, `EMPLOYEE`, `Profile*`): | สถานะ | จำนวน | รายละเอียด | |---|---:|---| | ใช้อยู่ (สำหรับนำเข้าข้อมูล) | 22 | `EMPLOYEE`, `OFFICER`, `Profile*` (ตัวใหญ่), `*Import` | | ไม่ได้ใช้แล้ว (dead entity) | 25 | `HR_*` (24) + `IMPORT_ORG` — เคยมีข้อมูลจริง >1.4M แถว | > ⚠️ ทั้ง 47 ตารางอยู่ในกลุ่มเตรียมลบ (§5) — **drop แล้ว @ test** ## A.5 กลุ่มที่เตรียมลบ/ลบแล้ว (54 ตาราง) | กลุ่ม | จำนวน | ตัวอย่าง / เหตุผล | |---|---:|---| | ข้อมูลระบบเดิม (MIS) | 47 | `HR_*`, `EMPLOYEE`, `OFFICER`, `Profile*`, `*Import`, `IMPORT_ORG` | | ตารางพักนำเข้า (ว่าง) | 2 | `HR_CHILDEN_EMPTEMP`, `HR_INSIGNIA_EMPTEMP` (0 แถว) | | ตารางซ้ำ/ทดสอบ/เก่า | 5 | `Profiles`, `new_table`, `new_table_emp`, `entity_base`, `MET_PREFIX` | หลังลบครบเหลือ **157 ตาราง**. Script อยู่ Part C. > ⚠️ `profile*Employee*` (profileDisciplineEmployee, profileInformation, profileSalaryEmployee + *History) **ยังใช้** (มี controller: ProfileDisciplineEmployeeController, ProfileSalaryEmployeeController, ProfileEmployeeController) — อย่า drop ## A.6 ข้อควรระวัง - ตารางเก็บ **ชื่อ + เลขบัตรประชาชน**: ~23 ตาราง (หลัก `profile`, `profileEmployee`, `registry`) — ข้อมูลส่วนบุคคล - `migrations`, `typeorm_metadata` เป็นตารางระบบ **ห้ามลบเด็ดขาด** - จำนวนแถวใน §A.1/A.4 เป็นค่าประมาณ (ยกเว้น §A.2 Master Data = exact) ## A.7 Seed / Mock data (สำหรับ dev/test) mock data แยกที่ `src/seed/` (Node/TS) → `npm run seed:show` / `seed:run` / `seed:revert` seed **แยกจาก migration หลัก** → `migration:run` ไม่เจอ seed → ไม่ทำลายข้อมูล prod. อ่าน `src/seed/README.md` --- # Part B — Plan checklist (CP0-CP7) ## Context DB `hrms_organization` (test) มีข้อมูลจริง + ตารางระบบเดิมไม่ได้ใช้ → ทำ environment สะอาด: ลดตารางเหลือใช้จริง + แทนข้อมูลจริงด้วย mock 30 คน + โครงสร้าง mock (revision 2569) + (defer) Keycloak ## Decisions | | | |---|---| | Target DB | `192.168.1.68` (test) — **แจ้ง user ทุกครั้งก่อนจัดการ data** | | Mock | 30 คน = 20 ข้าราชการ (Profile) + 10 ลูกจ้าง (ProfileEmployee) | | citizenId | สุ่ม 13 หลักผ่าน checksum จริง + ห้ามตรงของเดิม (block list) | | HR_* (24) | drop เลย (ไม่รอ .NET) | | Keycloak | clear `keycloak` column ก่อนลบ profile → CP6 clear orphan → CP7 ผูก KC ใหม่ | | CP7 password | `ddMMyyyy` พ.ศ. จาก birthDate mock | ## Checklist ### CP0 — Pre-flight ✅ - [x] หยุด service (กัน cron 00:30/03:00/04:00 ทับ) + ยืนยันต่อ DB `192.168.1.68` (clone: 211 tables, profile 20,501) - [x] verify junction/child table names + NOT NULL columns ของ target tables ### เตรียมไฟล์ ✅ - [x] `src/seed/mockData.ts` (ชื่อไทย + genValidCitizenId checksum + deterministic UUID + genBirthDate) - [x] 5 seed migrations (CP3-CP5c) + birthDate ใน CP5b; typecheck + test ผ่าน ### CP1 — Full Backup ✅ SKIP (user มี backup อยู่แล้ว) ### CP2 — Drop 54 unused tables (211 → 157) ✅ done - [x] comment `ImportDataController.ts` @Route/@Security → `npm run build` → verify `/api/v1/org/upload/*` หาย - [x] execute DROP 54 tables ใต้ `SET FOREIGN_KEY_CHECKS=0/1` (ดู Part C) - [x] **verify**: tables = **157**; `migrations`/`typeorm_metadata` ยังอยู่ ### CP3 — Seed orgRevision 2569 ✅ done - [x] run `1782100000001-seed_org_revision_2569.ts` (ล้าง org/pos เดิม + INSERT "โครงสร้าง 2569") - [x] **verify**: `orgRevision`=1, `orgRevisionIsCurrent`=1 ### CP4 — Seed mock org structure ✅ done - [x] run `1782100000002-seed_mock_org_structure.ts` - [x] **verify**: สำนักA/หน่วยงานA/ฝ่ายA/กลุ่มงานA; FK chain ครบ ### CP5a — Seed mock positions ✅ done (seed ตรง — TypeORM INSERT quirk) - [x] run `1782100000003-seed_mock_positions.ts` (20 posMaster + 10 employeePosMaster) - [x] **verify**: posMaster=20, employeePosMaster=10; current_holderId NULL ### CP5b — Seed mock profiles + clear keycloak ✅ done - [x] run `1782100000004-seed_mock_profiles.ts` (snapshot citizenId block list, clear keycloak, DELETE child + profile, INSERT 30 mock + birthDate) - [x] **verify**: profile=20, profileEmployee=10; citizenId checksum + ไม่ตรง blocklist; birthDate ครบ ### CP5c — Link holders ✅ done - [x] run `1782100000005-seed_link_holders.ts` - [x] **verify**: posMaster holder=20 join profile ครบ; employeePosMaster holder=10 ### CP6 — Clear Keycloak orphans ✅ SKIP (จัด KC ท้ายสุดพร้อม CP7) ### CP7 — ผูก Keycloak ให้ mock ⏳ defer - [ ] เปลี่ยน `.env` `KC_SERVICE_ACCOUNT_*` ชี้ Keycloak **test** - [ ] seed migration (`1782100000006-seed_keycloak_users.ts`) เรียก helper `src/keycloak/index.ts` (createUser/addUserRoles/getRoles/getUserByUsername) - [ ] role `WHERE name='USER'` (แทน hardcode UUID `8a1a0dc9-...`) - [ ] loop mock 30 คน: createUser(citizenId, password `ddMMyyyy` พ.ศ. จาก birthDate) + addUserRoles(USER) + เซ็ต `profile.keycloak` + junction - [ ] **verify**: KC user = mock count; login mock ได้; super_admin ยังอยู่ ## ไฟล์ที่สร้างแล้ว **Seed (แยกจาก migration หลัก):** `src/seed/README.md`, `src/seed/data-source.ts`, `src/seed/mockData.ts`, `src/seed/migrations/178210000000{1..5}-seed_*.ts` **npm scripts:** `npm run seed:show` / `seed:run` / `seed:revert` (ใช้ `src/seed/data-source.ts`) **Code:** `src/controllers/ImportDataController.ts` (@Route/@Security comment — CP2) ## End-to-end verify (หลัง CP6) 1. `npm run migration:run` ผ่านไม่ error (migration หลัก) + `npm run seed:show` เห็น seed 5 2. tables = 157 3. profile=20, profileEmployee=10, orgRevision=1 4. citizenId ผ่าน checksum + ไม่ตรง blocklist 5. posMaster holder=20 join profile ครบ 6. cron 04:00 รอบแรก → registry/tenure rebuild จาก mock 30 คน 7. login admin ได้ (super_admin ยังอยู่) ## Risks - migration + `SET FOREIGN_KEY_CHECKS` session-level ไม่ rollback → **backup เป็นหลัก ไม่พึ่ง down()** - cron ทับ → หยุด service หรือทำ CP3-5c ในกรอบเวลาแคบ - CP7 env ต้องชี้ KC test ก่อน - TypeORM quirk: CP5a INSERT SELECT WHERE NOT EXISTS อาจไม่ execute ใน transaction → seed ตรงถ้า posMaster ว่าง --- # Part C — Drop script SQL (CP2) > ⚠️⚠️⚠️ DROP TABLE ลบถาวร ย้อนกลับไม่ได้. ทุก DROP comment (`--`) ไว้ — uncomment เฉพาะที่ตรวจสอบแล้ว. > ก่อนรัน: (1) backup DB (2) comment `ImportDataController` + `npm run build` (3) รันบน test เท่านั้น (4) ปรึกษาทีม .NET เรื่อง HR_* > 🚫 ห้าม DROP: `migrations`, `typeorm_metadata` ```sql USE hrms_organization; -- นับตารางก่อนรัน (ควรได้ 211) SELECT COUNT(*) AS tables_before FROM information_schema.tables WHERE table_schema = 'hrms_organization' AND table_type = 'BASE TABLE'; SET FOREIGN_KEY_CHECKS = 0; -- ============================================================ -- กลุ่ม 1 — MIS tables (47) — ⚠️ ImportDataController พังหลัง drop → comment controller ก่อน -- ============================================================ -- -- บุคลากรต้นทาง (4) -- DROP TABLE IF EXISTS EMPLOYEE; -- DROP TABLE IF EXISTS EMPLOYEETEMP; -- DROP TABLE IF EXISTS OFFICER; -- DROP TABLE IF EXISTS positionOfficer; -- -- Profile* staging ตัว P ใหญ่ (14) -- DROP TABLE IF EXISTS ProfileAbilitys; -- DROP TABLE IF EXISTS ProfileCertificates; -- DROP TABLE IF EXISTS ProfileChangeNames; -- DROP TABLE IF EXISTS ProfileChildrens; -- DROP TABLE IF EXISTS ProfileDisciplines; -- DROP TABLE IF EXISTS ProfileDutys; -- DROP TABLE IF EXISTS ProfileEducations; -- DROP TABLE IF EXISTS ProfileHonors; -- DROP TABLE IF EXISTS ProfileInsignias; -- DROP TABLE IF EXISTS ProfileLeaveSummary; -- DROP TABLE IF EXISTS ProfileNopaids; -- DROP TABLE IF EXISTS ProfileOthers; -- DROP TABLE IF EXISTS ProfileSalaries; -- DROP TABLE IF EXISTS ProfileTrainings; -- -- Import master (4) -- DROP TABLE IF EXISTS amphurImport; -- DROP TABLE IF EXISTS educationMis; -- DROP TABLE IF EXISTS provinceImport; -- DROP TABLE IF EXISTS subDistrictImport; -- -- HR_* dead entity (24) — ⛔ มีข้อมูลจริง >1.4M แถว ปรึกษาทีม .NET ก่อน! -- DROP TABLE IF EXISTS HR_CHANGENAME; -- DROP TABLE IF EXISTS HR_CHANGENAME_EMP; -- DROP TABLE IF EXISTS HR_CHANGENAME_EMPTEMP; -- DROP TABLE IF EXISTS HR_CHILDEN; -- DROP TABLE IF EXISTS HR_CHILDEN_EMP; -- DROP TABLE IF EXISTS HR_DISCIPLINE; -- DROP TABLE IF EXISTS HR_DISCIPLINE_EMP; -- DROP TABLE IF EXISTS HR_DISCIPLINE_EMPTEMP; -- DROP TABLE IF EXISTS HR_EDUCATION; -- DROP TABLE IF EXISTS HR_EDUCATION_EMP; -- DROP TABLE IF EXISTS HR_EDUCATION_EMPTEMP; -- DROP TABLE IF EXISTS HR_FUND_COURSE_CODE; -- DROP TABLE IF EXISTS HR_INSIGNIA; -- DROP TABLE IF EXISTS HR_INSIGNIA_EMP; -- DROP TABLE IF EXISTS HR_MAJOR_CODE; -- DROP TABLE IF EXISTS HR_PERSONAL_EMPTEMP_ADDRESS; -- DROP TABLE IF EXISTS HR_PERSONAL_EMPTEMP_FAMILY; -- DROP TABLE IF EXISTS HR_PERSONAL_EMP_ADDRESS; -- DROP TABLE IF EXISTS HR_PERSONAL_EMP_FAMILY; -- DROP TABLE IF EXISTS HR_PERSONAL_OFFICER_ADDRESS; -- DROP TABLE IF EXISTS HR_PERSONAL_OFFICER_FAMILY; -- DROP TABLE IF EXISTS HR_POSITION_EMPLOYEE; -- DROP TABLE IF EXISTS HR_POSITION_EMPLOYEETEMP; -- DROP TABLE IF EXISTS HR_POSITION_OFFICER; -- -- IMPORT_ORG (0 แถว) -- DROP TABLE IF EXISTS IMPORT_ORG; -- ============================================================ -- กลุ่ม 2 — Import staging ไม่มี entity (2, 0 แถว) -- ============================================================ -- DROP TABLE IF EXISTS HR_CHILDEN_EMPTEMP; -- DROP TABLE IF EXISTS HR_INSIGNIA_EMPTEMP; -- ============================================================ -- กลุ่ม 3 — ซ้ำ/ทดสอบ/เก่า (5) -- ⚠️ profile*Employee* ยังใช้ (มี controller) — อย่า drop -- ============================================================ -- DROP TABLE IF EXISTS Profiles; -- DROP TABLE IF EXISTS new_table; -- DROP TABLE IF EXISTS new_table_emp; -- DROP TABLE IF EXISTS entity_base; -- DROP TABLE IF EXISTS MET_PREFIX; SET FOREIGN_KEY_CHECKS = 1; -- นับตารางหลังรัน (drop ครบ 54 → เหลือ 157) SELECT COUNT(*) AS tables_after FROM information_schema.tables WHERE table_schema = 'hrms_organization' AND table_type = 'BASE TABLE'; ```