hrms-api-org/plans/plan_cleanup-mock-seed.md

263 lines
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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';
```