1 · Repository map — pms-api
บทนำสั้น
pms-api เป็น domain microservice ที่รองรับระบบบริหารผลการปฏิบัติงาน (Performance Management System) ของสภากาชาดไทย เป็น repo ที่ใหญ่ที่สุดในกลุ่ม Domain Microservices ทั้งแพลตฟอร์ม — 33 module, 66 entity, service ใหญ่สุดยาวเกือบ 3,000 บรรทัด และมีประวัติ commit ถึง 1,909 ครั้งกระจายอยู่บน 200+ branch (ตัวเลขจาก git log --oneline | wc -l และ git branch -a | wc -l บน canonical branch uat)
บทนี้ทำแผนที่ให้เห็นภาพรวมของ repo ทั้งหมด ก่อนที่จะลงรายละเอียด flow ในบทที่ 2
Stack และเวอร์ชัน
ทุกข้อมูลในตารางนี้ยืนยันโดยตรงจาก pms-api/package.json (branch: uat) และ pms-api/src/main.ts (branch: uat)
| ส่วน | ค่า | หลักฐาน |
|---|---|---|
| Framework | NestJS 9 (@nestjs/common ^9.0.0, @nestjs/core ^9.0.0) | pms-api/package.json (branch: uat):27,29 |
| ORM | TypeORM ^0.3.10 (ผ่าน @nestjs/typeorm ^9.0.1) | pms-api/package.json (branch: uat):35,64 |
| Database driver | mssql ^9.1.1 (MSSQL เป็นหลัก), mysql ^2.18.1 (ประกาศไว้แต่ไม่ได้ใช้กับ connection ใดจริง) | pms-api/package.json (branch: uat):49,51 |
| Rate limiting | @nestjs/throttler ^6.4.0 (global 100 req / 60 วินาที — ค่าที่ strict กว่า NestJS default) | pms-api/package.json (branch: uat):34, pms-api/src/app.module.ts (branch: uat):51-58,116 |
| Schedule/Cron | @nestjs/schedule ^6.0.0 | pms-api/package.json (branch: uat):32 |
| Auth | passport ^0.6.0 + passport-jwt ^4.0.0 + jsonwebtoken ^9.0.1 | pms-api/package.json (branch: uat):47,53,54 |
| PDF/Excel | puppeteer ^19.7.5 (PDF), exceljs ^4.4.0 + xlsx ^0.18.5 (ทั้งสอง library สำหรับ Excel อยู่คู่กัน) | pms-api/package.json (branch: uat):45,58,65 |
nodemailer ^7.0.5 | pms-api/package.json (branch: uat):52 | |
| Language | TypeScript ^4.7.4 | pms-api/package.json (branch: uat):92 |
| Package manager | yarn (มี yarn.lock, Dockerfile รัน yarn install + yarn build) | pms-api/Dockerfile (branch: uat):19,23 |
| Port | 9331 (จาก PORT=9331 ใน .env, fallback ใน main.ts) | pms-api/.env (branch: uat):6, pms-api/src/main.ts (branch: uat):27, pms-api/Dockerfile (branch: uat):25 |
ข้อสังเกตเรื่อง stack — TypeORM ไม่ใช่ Sequelize ตามที่บางเอกสารเก่าเขียนไว้ เห็นได้จากการ import { TypeOrmModule } from '@nestjs/typeorm' ใน pms-api/src/app.module.ts (branch: uat):6 และการใช้ @Column, @Entity, createQueryBuilder ทั่วโค้ดเช่น pms-api/src/entities/period.entity.ts (branch: uat):1,7 และ pms-api/src/modules/period/period.repositories.ts (branch: uat):39-44
ขนาดและสถานะ repo
| ตัวเลข | ค่า | ที่มา |
|---|---|---|
| Canonical branch | uat (default branch master เก็บไฟล์น้อยมาก) | git branch --show-current |
| Commit ทั้งหมด | 1,909 | `git log --oneline |
| Branch ทั้งหมด | 202 (รวม local + remote) | `git branch -a |
| โฟลเดอร์ module | 33 | ls src/modules/ |
| Feature module ที่ import ใน AppModule | 30 | นับ module class ใน pms-api/src/app.module.ts (branch: uat):83-113 |
| Entity ไฟล์ | 66 | `ls src/entities/*.entity.ts |
| Spec/test file | ~14 ไฟล์ .spec.ts | grep ทั่ว src/ |
| Commit ล่าสุด (ณ เวลาเขียน) | 426a9dd Merged in pre-uat (pull request #526) | git log -1 |
ตัวเลข branch 202 และ commit 1,909 บวกกับชื่อ branch ที่เห็นว่าเป็น feature/PR pattern (pre-uat, PR number) บ่งชี้ว่า pms-api อยู่ในช่วงพัฒนาอย่างหนัก (active development) มากกว่า repo อื่นในกลุ่มมาก ต้องระวังเวลาอ้างอิงโค้ดเพราะอาจเปลี่ยนเร็ว
โครงสร้างโฟลเดอร์ระดับบน
pms-api/
├── src/
│ ├── main.ts entry point — global prefix, CORS, Swagger, no-op console.log ใน prod
│ ├── app.module.ts root module — TypeORM ×4, ThrottlerModule, ScheduleModule, 30 feature module
│ ├── app.controller.ts health endpoint พื้นฐาน
│ ├── dto/ DTO (TypeScript class ที่ใช้ class-validator) รวม ~30+ ไฟล์
│ ├── entities/ 66 entity ของ TypeORM
│ ├── modules/ 33 โฟลเดอร์ module
│ ├── config/configuration.ts โหลดค่าจาก .env มาเป็น typed config object
│ └── docs/document.config.ts Swagger config
├── libs/
│ ├── jwt/ JwtAuthGuard, JwtStrategy, @Public() decorator, ApiKeyStrategy
│ ├── log/logs.middleware.ts I3GatewayLogger (custom logger)
│ ├── exception/ HttpExceptionFilter
│ ├── interceptors/ LoggingInterceptor
│ ├── utils/ StampAudit, list.util, date.util (Thai date formatter)
│ ├── services/emailService.ts ใช้ nodemailer ส่ง email ผ่าน ejs template
│ ├── constant/ enum/constant ที่ใช้ทั่ว repo
│ └── pdfTemplate/email-template/ 8 ไฟล์ ejs template สำหรับแจ้งเตือนตามรอบ period
├── docs/ Swagger config + PMS-Period-Notification-System.md (เอกสารเก่าที่ทีม dev เขียน)
├── sql/ สคริปต์ SQL manual (PMS01-PMS15, hotfix/, phase2/, report/)
├── vtrc-gcp-sa-key.json **GCP service account key committed** — ห้าม log/exfiltrate
├── .env ค่า UAT (NODE_ENV=uat), commit อยู่ใน repo
├── Dockerfile
└── package.jsonEntry point — main.ts
ลำดับการ bootstrap ของ pms-api ตาม pms-api/src/main.ts (branch: uat):20-75
No-op console.log ใน production — บรรทัด
pms-api/src/main.ts (branch: uat):16-18เขียนทับconsole.log = () => {}ทั้งฟังก์ชันเมื่อNODE_ENV === 'prod'เป็น repo เดียวในกลุ่มที่ใช้วิธีนี้ วิธีนี้หยาบเพราะปิดทั้ง global function แทนที่จะแทนที่ด้วย logger ที่เหมาะสม — ทำให้หา debug info ใน prod ได้ยากCrypto polyfill — บรรทัด
pms-api/src/main.ts (branch: uat):14,31ทำ(global as any).crypto ??= cryptoเพื่อให้ library ที่ require Web Crypto API ทำงานใน Node ได้Nest factory + logger —
NestFactory.create<NestExpressApplication>(AppModule, { logger: new I3GatewayLogger('System') })ที่บรรทัด 21-23Global pipes/filters/guards/interceptors — ลำดับการ register อยู่ที่บรรทัด 40-43:
HttpExceptionFilter(custom exception filter)LoggingInterceptorJwtAuthGuard(เป็น global — ทุก route ต้อง auth โดย default ยกเว้นที่ mark@Public())ValidationPipe({ whitelist: true, forbidNonWhitelisted: true, transform: true })ค่านี้ strict กว่า repo อื่น —forbidNonWhitelistedจะ reject request ที่ส่ง field นอกเหนือจาก DTO กำหนด
CORS callback-based —
pms-api/src/main.ts (branch: uat):45-66เขียน callback ที่ block origin ที่มีคำว่าlocalhostเฉพาะเมื่อNODE_ENV === 'prod'ดีกว่าorigin: '*'ของ repo อื่นในกลุ่ม แต่ก็ยังใช้allowedHeaders: '*'อยู่ (บรรทัด 64)Global prefix + static assets — prefix
${BASE_PATH}api/v1(ใน UAT จะเป็น/pms-api/api/v1) และ static assets/uploadsที่ prefix/pms-api/(บรรทัด 33-36)Swagger — mount ที่
${BASE_PATH}docs(บรรทัด 68-69)Listen —
app.listen(port, '0.0.0.0', ...)ที่บรรทัด 70 พอร์ต default 9331
AppModule — app.module.ts
pms-api/src/app.module.ts (branch: uat):45-118 เป็นที่ที่ wire ทุกอย่างเข้าด้วยกัน ส่วนสำคัญมี 4 อย่าง
TypeORM connections (4 ตัว, ใช้จริง 3)
TypeOrmModule.forRootAsync({
name: 'main',
useFactory: (configService) => configService.get('db'),
}),
TypeOrmModule.forRootAsync({
name: 'vtrc',
useFactory: (configService) => configService.get('vtrcDB'),
}),
TypeOrmModule.forRootAsync({
name: 'workforce',
useFactory: (configService) => configService.get('workforceDB'),
}),
TypeOrmModule.forRootAsync({
name: 'workforceTemp',
useFactory: (configService) => configService.get('workforceTempDB'),
}),ดูได้ที่ pms-api/src/app.module.ts (branch: uat):59-82 ตารางสถานะการใช้งาน
| Connection | env prefix | database | สถานะ |
|---|---|---|---|
main | DB_* | pms | ใช้งานจริง — เก็บ entity ของ PMS ทั้งหมด |
workforce | WORKFORCE_DB_* | dbWorkforce | ใช้งานจริง — อ่านข้อมูลองค์กร/พนักงานจาก database ส่วนกลาง |
workforceTemp | WORKFORCE_TEMP_DB_* | dbWorkforce_temp | ใช้งานจริง — เฉพาะ module rcc |
vtrc | VTRC_DB_* | vtrc | ประกาศไว้แต่ไม่พบการ inject ใช้งาน |
ความสำคัญ — pms-api อ่านข้อมูลพนักงาน/องค์กรจาก dbWorkforce โดยตรงผ่าน TypeORM query builder แทนที่จะเรียก workforce-api HTTP เห็นได้จาก module usagePermission, salaryPercentage, report, image, evaluations, evaluateUserAmount, employeeWorkforce, dropdown, budget, approver — ทั้ง 10 module inject @InjectEntityManager('workforce') ทำให้ pms-api coupling กับ database ส่วนกลางโดยตรง
Throttler
ThrottlerModule.forRoot({
throttlers: [{ ttl: 60000, limit: 100 }],
}),pms-api/src/app.module.ts (branch: uat):51-58 — 100 requests ต่อ 60 วินาทีต่อ IP และมี APP_GUARD provider ThrottlerGuard ที่บรรทัด 116 ทำให้ throttle ทำงานทุก endpoint โดยอัตโนมัติ เป็น repo เดียวในกลุ่มที่ตั้งค่านี้
ScheduleModule
pms-api/src/app.module.ts (branch: uat):104 — ScheduleModule.forRoot() เปิดใช้งาน @Cron() decorator ทั่วระบบ มี cron ที่ active จริง 2 ตัว (ดูรายละเอียดในบทที่ 2 และบทที่ 5)
Feature modules (30 ตัว)
ลำดับ import ตาม pms-api/src/app.module.ts (branch: uat):83-113
DropdownModule, UploadModule, AuthModule,
IndicatorTypeModule, IndicatorWeightModule, RatingScaleModule,
PeriodModule, GradingModule, IndicatorModule, GradeLimitModule,
ConfigNotificationModule, UsagePermissionModule,
EvaluateUserAmountModule, EvaluateFormModule, CreateFormModule,
ApproverModule, PermissionModule, MigrateDataModule,
SalaryPercentageModule, PerformanceRecordModule, EvaluationsModule,
SalaryRangeModule, ConfirmEvaluationModule, BudgetModule,
SalaryAdjustModule, RequestEditModule,
SalaryAdjustmentModule, SalaryAdjustToHRMIModule,
RccModule, ReportModuleสังเกตว่ามี 3 โฟลเดอร์ใน src/modules/ ที่ ไม่ถูก import เป็น standalone module — notify, image, employeeWorkforce — เป็นไปได้ว่าถูกใช้ผ่าน module อื่นหรือเป็น utility เล็ก ๆ ไม่ใช่ feature module เต็มรูปแบบ
รายการ module ทั้ง 33 จัดตาม domain
Period & Notification (4 modules)
| Module | Entity หลัก | บริการ |
|---|---|---|
period | PeriodEntity, PeriodExtendEntity, PeriodHistoryEntity | สร้าง/แก้ไข/ลบรอบการประเมิน ปีละ 1 รอบ มีช่วงเวลาย่อย 7 ช่วง (settingGoal, checkData, updateResult, evaluation, feedback ฯลฯ) |
configNotification | ConfigNotificationEntity, ConfigNotificationHistoryEntity, NotifyEntity | ตั้งค่าว่าจะแจ้งเตือนใครวันไหน + cron ส่ง email ทุกวัน 6 โมงเช้า |
notify | NotifyEntity | (ไม่ได้ import เป็น standalone) — เก็บประวัติการแจ้งเตือน |
migrateData | — | migrate ข้อมูลจาก workforce เข้า pms — cron ที่ถูก comment ปิดไว้ |
Indicator & Scoring (5 modules)
| Module | Entity หลัก | บริการ |
|---|---|---|
indicatorType | IndicatorTypeEntity, IndicatorTypeHistoryEntity | ประเภทตัวชี้วัด (เช่น KPI, Competency) |
indicator | IndicatorHdEntity, IndicatorDtEntity, IndicatorHdHistoryEntity | ตัวชี้วัดรายบุคคล/รายประเภท |
indicatorWeight | IndicatorWeightHdEntity, IndicatorWeightDtEntity, IndicatorWeightHistoryEntity | น้ำหนักตัวชี้วัด (เช่น KPI 70%, Competency 30%) |
ratingScale | RatingScaleEntity, RatingScaleHistoryEntity | scale การให้คะแนนตัวชี้วัด |
grading | GradingEntity, GradingDtEntity, GradingHistoryEntity | เกรดผลงานรวม (A, B+, B, C, D) |
Evaluation form (5 modules)
| Module | Entity หลัก | บรรทัดของ .service.ts | บริการ |
|---|---|---|---|
createForm | EvaluateFormEntity, EvaluateFormDtEntity, EvaluateFormHistoryEntity | 2,701 | สร้างแบบประเมินจากตัวชี้วัด — ใหญ่เป็นอันดับ 2 ของ repo |
evaluateForm | EvaluateFormEntity, EvaluateFormDtEntity, EvaluateFormKpiRatingEntity, EvaluateFormLearningEntity, EvaluateFormCourseEntity, EvaluateFormSummaryEntity, EvaluateFormDtHistoryEntity, EvaluateFormHistoryEntity, EvaluateFormNoQuotaHistoryEntity | 2,996 | กรอกผลการปฏิบัติงานจริง + export PDF (puppeteer) + export Excel — service ใหญ่สุดของ repo |
evaluateUserAmount | EvaluateUserAmountEntity, EvaluateUserAmountHistoryEntity, EvaluateUserEntity, EvaluateUserHistoryEntity | 1,147 | รายชื่อผู้ถูกประเมิน + จำนวนรวมต่อรอบ |
evaluations | EvaluateSummaryResultEntity, EvaluateFormEntity | 729 | สรุปผลประเมินระดับองค์กร |
confirmEvaluation | EvaluateFormEntity, OrgUnitConfirmHistoryEntity, ConfirmQuotaHistoryEntity | 1,240 | ยืนยันผลประเมินของหน่วยงานก่อนเข้า flow เงินเดือน |
Approval & permission (4 modules)
| Module | Entity หลัก | บริการ |
|---|---|---|
approver | ApproverHdEntity, ApproverDtEntity, ApproverHistoryEntity | สายอนุมัติ 5 ชั้น (admin1Email..admin5Email) + delegate |
permission | PMSUserEntity, PMSUserDepAdminEntity, PMSUserHistoryEntity | สิทธิ์ PMS (pms_admin, hrbp_department_admin, pms_salary_approver, pms_salary_viewer) |
usagePermission | — (อ่านจาก workforce connection) | 1,937 บรรทัด — sync ข้อมูลจาก workforce เข้า PMS ทุก 1 นาที — service ใหญ่เป็นอันดับ 3 |
auth | AuthDataEntity | JWT login สำหรับ candidate/user |
Salary (5 modules — ระวังชื่อคล้ายกัน)
ชื่อ module 5 ตัวในกลุ่มนี้สับสนง่ายมาก — salaryRange, salaryPercentage, salaryAdjust, salaryAdjustment, salaryAdjustToHRMI ต้องอ่านให้ดี
| Module | Entity หลัก | บริการ |
|---|---|---|
salaryRange | SalaryRangeHdEntity, SalaryRangeDtEntity, SalaryRangeHistoryEntity | ช่วงเงินเดือนตาม pay grade |
salaryPercentage | PercentSalaryEntity, PercentSalaryEmpLevelEntity, PercentSalaryEmpLevelDtEntity, PercentSalaryHistoryEntity | เปอร์เซ็นต์ปรับเงินเดือนตามเกรด + employee level |
salaryAdjust | SalaryAdjustBuEntity, SalaryAdjustDivEntity | สร้าง/preview การปรับเงินเดือน |
salaryAdjustment | SalaryAdjustHdEntity, SalaryAdjustBuEntity, SalaryAdjustDivEntity, SalaryAdjustDtEntity, + 4 History entity | 2,367 บรรทัด — บันทึก/อนุมัติการปรับจริง — service ใหญ่สุดของกลุ่ม |
salaryAdjustToHRMI | SalaryAdjustBuEntity, SalaryAdjustHdEntity | export ผลปรับเงินเดือนเป็นไฟล์ Excel ให้ HR นำเข้า HRMI ด้วยมือ (ต่างจาก recruitment-api ที่ export ผ่าน HTTP) |
Master data & utility (4 modules)
| Module | Entity หลัก | บริการ |
|---|---|---|
dropdown | MDropdownEntity | master data lookup (สถานะ, ประเภท, ฯลฯ) — เรียกใช้โดย module อื่นเกือบทุกตัว |
image | — (อ่านจาก workforce) | ดึงรูปพนักงานจาก workforce |
employeeWorkforce | — (อ่านจาก workforce) | query ข้อมูลพนักงานดิบจาก database ส่วนกลาง |
uploadFiles | PmsAttachFileEntity | จัดการไฟล์แนบ |
Operations & reporting (5 modules)
| Module | Entity หลัก | บริการ |
|---|---|---|
budget | BudgetEntity, BudgetDtEntity, BudgetHistoryEntity | งบประมาณปรับเงินเดือนต่อหน่วยงาน — มี GET /budget/getEmployeeSalary เป็น @Public() |
performanceRecord | — | บันทึกผลงานย้อนหลัง |
requestEditForm | EvaluateFormEntity (เขียนฟิลด์ isRequestEdit/requestEditStatus) | ขอแก้ไขแบบประเมินหลังปิดรอบ |
report | — (อ่านจากหลาย entity) | รายงานสรุปผลประเมิน/เงินเดือน |
rcc | — (อ่านจาก workforceTemp) | module เฉพาะ — ใช้ connection workforceTemp เท่านั้น |
รายการ entity ทั้ง 66 จัดตาม domain
Period (3)
PeriodEntity→tbPeriod—pms-api/src/entities/period.entity.ts (branch: uat):8PeriodExtendEntity→tbPeriodExtend— ส่วนขยายช่วงเวลา settingGoal ของแต่ละ BU —pms-api/src/entities/periodExtesnd.entity.ts (branch: uat):6PeriodHistoryEntity→tbPeriodHistory— บันทึก history ทุกการ update period —pms-api/src/entities/periodHistory.entity.ts (branch: uat):6
Notification (3)
ConfigNotificationEntity→tbConfigNotification—pms-api/src/entities/configNotification.entity.ts (branch: uat):6ConfigNotificationHistoryEntity→tbConfigNotificationHistoryNotifyEntity→ (notify log)
Indicator (10)
IndicatorTypeEntity,IndicatorTypeHistoryEntityIndicatorHdEntity,IndicatorDtEntity,IndicatorHdHistoryEntityIndicatorWeightHdEntity,IndicatorWeightDtEntity,IndicatorWeightHistoryEntityRatingScaleEntity,RatingScaleHistoryEntity
Grading (6)
GradingEntity,GradingDtEntity,GradingHistoryEntityGradeLimitHdEntity,GradeLimitDtEntity,GradeLimitHistoryEntity— forced distribution ต่อเกรด
EvaluateForm (11)
EvaluateFormEntity→tbEvaluateForm— entity หลัก 444 บรรทัด มี status ซ้อนกันหลายชั้น —pms-api/src/entities/evaluateForm.entity.ts (branch: uat):14EvaluateFormDtEntity,EvaluateFormDtHistoryEntity,EvaluateFormHistoryEntityEvaluateFormKpiRatingEntityEvaluateFormLearningEntity,EvaluateFormCourseEntityEvaluateFormSummaryEntity,EvaluateSummaryResultEntityEvaluateFormNoQuotaHistoryEntityEvaluateUserEntity,EvaluateUserHistoryEntity
EvaluateUserAmount (2)
EvaluateUserAmountEntity,EvaluateUserAmountHistoryEntity
Approver (3)
ApproverHdEntity→tbApprover—pms-api/src/entities/approver.entity.tsApproverDtEntity→tbApproverDt— 5 ชั้น admin1Email..admin5EmailApproverHistoryEntity→tbApproverHistory
PMS user (3)
PMSUserEntity,PMSUserDepAdminEntity,PMSUserHistoryEntity
Budget (3)
BudgetEntity,BudgetDtEntity,BudgetHistoryEntity
Salary range (3)
SalaryRangeHdEntity,SalaryRangeDtEntity,SalaryRangeHistoryEntity
Salary percentage (4)
PercentSalaryEntity,PercentSalaryEmpLevelEntity,PercentSalaryEmpLevelDtEntity,PercentSalaryHistoryEntity
Salary adjust (12)
SalaryAdjustHdEntity,SalaryAdjustHdHistoryEntitySalaryAdjustBuEntity,SalaryAdjustBuHistoryEntitySalaryAdjustDivEntity,SalaryAdjustDivHistoryEntitySalaryAdjustDtEntity,SalaryAdjustDtHistoryEntity
ConfirmEvaluation (2)
OrgUnitConfirmHistoryEntityConfirmQuotaHistoryEntity
Other (4)
AuthDataEntity(auth data),MDropdownEntity(master dropdown),PmsAttachFileEntity(file attach),EmailConfigEntity(email config)BaseEntity— base class ไม่ใช่ entity จริง —pms-api/src/entities/base.ts (branch: uat):3
BaseEntity — รากฐานของทุก entity
export class BaseEntity {
@PrimaryGeneratedColumn('increment', { name: 'id' })
id?: number;
@Column({ type: 'datetime', default: () => 'CURRENT_TIMESTAMP' })
createdAt?: Date;
@Column({ type: 'varchar', length: 500, nullable: true })
createdByName?: string;
@Column({ type: 'varchar', length: 50, nullable: true })
createdBy?: string;
@Column({ type: 'datetime', default: () => 'CURRENT_TIMESTAMP' })
updatedAt?: Date;
@Column({ type: 'varchar', length: 50, nullable: true })
updatedBy?: string;
@Column({ type: 'varchar', length: 500, nullable: true })
updatedByName?: string;
@Column({ type: 'bit', default: false, nullable: false })
isDeleted?: boolean;
}ดูได้ที่ pms-api/src/entities/base.ts (branch: uat):3-27 — pattern สำคัญทุก entity สืบทอดจาก BaseEntity และใช้ soft-delete ผ่าน isDeleted = true แทน DELETE จริง (ดู pms-api/src/modules/period/period.service.ts (branch: uat):135-145 ที่ทำ soft-delete ทั้ง header และ ref table)
Module-entity mapping (lookup สำคัญ)
ตารางนี้ช่วยให้แก้ bug ได้เร็ว — ถ้าเจอปัญหาที่ entity X ให้ไปดู module ที่เป็นเจ้าของ
| Entity (ตัวอย่าง) | Module เจ้าของ | หมายเหตุ |
|---|---|---|
tbPeriod | period | — |
tbEvaluateForm | evaluateForm + createForm (ทั้งคู่เขียน entity เดียวกัน) | ระวัง — createForm สร้าง, evaluateForm กรอกผล |
tbApprover, tbApproverDt | approver | — |
tbSalaryAdjustBu | salaryAdjust + salaryAdjustment | ทั้งคู่เขียน entity เดียวกัน — salaryAdjust สร้าง, salaryAdjustment อนุมัติ |
tbSalaryAdjustHd | salaryAdjustment | — |
tbConfigNotification | configNotification | — |
tbBudget | budget | — |
tbPMSUser | permission | — |
tbMDropdown | dropdown | ใช้โดย module อื่นเกือบทุกตัว — เป็น service กลาง |
Dockerfile analysis
pms-api/Dockerfile (branch: uat):1-28 ทั้งไฟล์
FROM node:20-alpine
WORKDIR /usr/src/app
ENV TZ="Asia/Bangkok"
ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true
ENV PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium
RUN apk add --no-cache \
nmap \
chromium \
harfbuzz \
freetype \
ttf-freefont \
nss
COPY package.json yarn.lock ./
RUN yarn install
COPY . .
RUN yarn build
EXPOSE 9331
CMD ["yarn", "start:prod"]ข้อสังเกตเมื่อเทียบกับ repo อื่นในกลุ่ม (benefit-api, recruitment-api)
- ไม่ใช้
--frozen-lockfile— บรรทัด 19 ใช้แค่yarn installทำให้แต่ละ build อาจได้ dependency version ต่างกัน ถ้าyarn.lockไม่ sync กับpackage.jsonเสี่ยงเกิด drift ระหว่าง build - ติดตั้ง chromium ระบบ — บรรทัด 9-15 ติดตั้ง
chromium,nss,harfbuzz,freetype,ttf-freefontเพื่อให้puppeteerทำงานได้ (ใช้สำหรับ export PDF ผลประเมิน) — ชุดเดียวกับbenefit-api - PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true — บรรทัด 6 — ป้องกัน puppeteer ดาวน์โหลด chromium ซ้ำเพราะใช้ของระบบ
- TZ="Asia/Bangkok" — timezone เป็นกรุงเทพฯ สำคัญเพราะมี cron 6 โมงเช้า (
@Cron('0 6 * * *', { timeZone: 'Asia/Bangkok' })ในpms-api/src/modules/configNotification/configNotification.controller.ts (branch: uat):30-32) - EXPOSE 9331 — ตรงกับ
PORT=9331ใน.envและ fallback ในmain.ts:27 - CMD yarn start:prod — รัน
node dist/src/main(ตามpackage.jsonscriptstart:prod)
Pattern ที่ใช้ทั่ว repo
Repository pattern (controller → service → repository)
ทุก module มีโครงสร้างไฟล์เดียวกัน
module/
├── module.controller.ts — HTTP route declaration
├── module.service.ts — business logic
├── module.repositories.ts — database query (TypeORM query builder)
└── module.module.ts — NestJS Module wiringตัวอย่าง — pms-api/src/modules/period/period.controller.ts (branch: uat):11-54 ประกาศ route, แล้วเรียก this.service.method() — service ในที่สุดเรียก this.repo.method() หรือ this.entityManager.save() โดยตรง
@InjectEntityManager('main') / 'workforce' / 'workforceTemp'
ทุก service ที่ต้องการ database access จะ inject EntityManager ตาม connection name
constructor(
@InjectEntityManager('main')
private readonly entityManager: EntityManager,
) {}ดูตัวอย่างได้ที่ pms-api/src/modules/period/period.service.ts (branch: uat):27-29 และ pms-api/src/modules/salaryAdjustment/salaryAdjustment.service.ts (branch: uat):50-52
StampAudit(req, body) — audit stamp
pms-api/libs/utils/audit.util.ts export StampAudit() ที่เพิ่มฟิลด์ createdBy, createdByName, updatedBy, updatedByName, updatedAt จาก JWT payload ของ request ใช้ทุกที่ที่ save entity เช่น pms-api/src/modules/period/period.service.ts (branch: uat):93
I3GatewayLogger — custom logger
ทุก service/repository สร้าง logger ผ่าน new I3GatewayLogger(ClassName.name) — เช่น pms-api/src/modules/period/period.service.ts (branch: uat):30 เป็น custom logger ที่ wrap logger ของ NestJS เพื่อให้มี prefix ชื่อ class
@Public() decorator สำหรับ override global JWT guard
pms-api/src/modules/configNotification/configNotification.controller.ts (branch: uat):8,44 import Public จาก libs/jwt/jwt-auth.guard เพื่อ mark endpoint @Post('/check-notifications') ว่าไม่ต้อง auth — pattern เดียวกับ benefit-api/recruitment-api
ข้อควรระวังก่อนแก้โค้ด
ชื่อ module คล้ายกันมาก
กลุ่ม salary 5 module — salaryRange, salaryPercentage, salaryAdjust, salaryAdjustment, salaryAdjustToHRMI — สับสนง่ายมาก ต้องดูให้แม่นยำก่อนแก้
salaryRange= ช่วงเงินเดือนตาม pay grade (master data)salaryPercentage= เปอร์เซ็นต์ปรับตามเกรด+level (master data)salaryAdjust= สร้าง/preview การปรับ (draft)salaryAdjustment= บันทึก/อนุมัติการปรับจริง (2,367 บรรทัด — ใหญ่สุดของกลุ่ม)salaryAdjustToHRMI= export Excel ส่ง HR
Entity เดียวกันเขียนโดยหลาย module
EvaluateFormEntityถูกเขียนโดยทั้งcreateFormและevaluateForm—createFormสร้าง header + ตัวชี้วัด,evaluateFormกรอกผล + คะแนนSalaryAdjustBuEntityถูกเขียนโดยทั้งsalaryAdjustและsalaryAdjustment
ก่อนแก้ entity ให้ grep หา service ทั้งหมดที่เขียนมันก่อน
usagePermission cron ที่ขับระบบทั้งหมด
pms-api/src/modules/usagePermission/usagePermission.service.ts (branch: uat):1929-1936 มี @Cron('*/1 * * * *') ที่เรียก getEmployeeTriggerQeue() ทุก 1 นาที — sync ข้อมูลองค์กร/พนักงานจาก dbWorkforce เข้า PMS ถ้า cron นี้หยุดทำงานเงียบ ๆ ข้อมูลที่ evaluateForm/confirmEvaluation/salaryAdjustment ใช้ (org unit, position, salary range) จะ drift โดยไม่มี alert
เอกสารเก่าที่ล้าสมัย
pms-api/docs/PMS-Period-Notification-System.md ระบุว่า notification cron อยู่ที่ period.controller.ts และเป็น comment ปิดไว้ แต่โค้ดจริงมี cron active อยู่ที่ configNotification.controller.ts:30-32 — เอกสารนี้ไม่ตรงกับโค้ดปัจจุบัน
ค่าที่ commit ใน .env เป็น UAT
pms-api/.env (branch: uat):1-2 ระบุ NODE_ENV=uat — ไม่ใช่ prod แต่ยังมี credential UAT ที่ใช้งานได้จริง + JWT secret ที่เหมือนกันทุกตัวอักษรกับ benefit-api/recruitment-api/ai-recruitment-api (shared JWT secret ข้าม 4+ microservice)
ไฟล์ GCP key ที่ commit ไว้
pms-api/vtrc-gcp-sa-key.json — GCP service account key commit อยู่ใน repo — ห้าม log, ห้าม paste, ห้าม exfiltrate ดูรายละเอียดใน scorecard ของแต่ละ repo (หน้า debt register)
สิ่งที่ไม่ได้ทำในบทนี้
บทนี้เป็นเพียง map — การวิเคราะห์เชิงลึกของแต่ละส่วนอยู่ในบทถัดไป
- flow period lifecycle ทั้งหมด → บทที่ 2 (Period & cycle)
- entity relationship diagram ละเอียด → บทที่ 3 (Persistence)
- auth guard + JWT + external integration → บทที่ 4
- cron jobs ทั้งหมด → บทที่ 5
- scorecard + debt register → บทที่ 6
ไป บท 2 Period & cycle →