Skip to content

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)

ส่วนค่าหลักฐาน
FrameworkNestJS 9 (@nestjs/common ^9.0.0, @nestjs/core ^9.0.0)pms-api/package.json (branch: uat):27,29
ORMTypeORM ^0.3.10 (ผ่าน @nestjs/typeorm ^9.0.1)pms-api/package.json (branch: uat):35,64
Database drivermssql ^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.0pms-api/package.json (branch: uat):32
Authpassport ^0.6.0 + passport-jwt ^4.0.0 + jsonwebtoken ^9.0.1pms-api/package.json (branch: uat):47,53,54
PDF/Excelpuppeteer ^19.7.5 (PDF), exceljs ^4.4.0 + xlsx ^0.18.5 (ทั้งสอง library สำหรับ Excel อยู่คู่กัน)pms-api/package.json (branch: uat):45,58,65
Emailnodemailer ^7.0.5pms-api/package.json (branch: uat):52
LanguageTypeScript ^4.7.4pms-api/package.json (branch: uat):92
Package manageryarn (มี yarn.lock, Dockerfile รัน yarn install + yarn build)pms-api/Dockerfile (branch: uat):19,23
Port9331 (จาก 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 branchuat (default branch master เก็บไฟล์น้อยมาก)git branch --show-current
Commit ทั้งหมด1,909`git log --oneline
Branch ทั้งหมด202 (รวม local + remote)`git branch -a
โฟลเดอร์ module33ls src/modules/
Feature module ที่ import ใน AppModule30นับ module class ใน pms-api/src/app.module.ts (branch: uat):83-113
Entity ไฟล์66`ls src/entities/*.entity.ts
Spec/test file~14 ไฟล์ .spec.tsgrep ทั่ว 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.json

Entry point — main.ts

ลำดับการ bootstrap ของ pms-api ตาม pms-api/src/main.ts (branch: uat):20-75

  1. 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 ได้ยาก

  2. Crypto polyfill — บรรทัด pms-api/src/main.ts (branch: uat):14,31 ทำ (global as any).crypto ??= crypto เพื่อให้ library ที่ require Web Crypto API ทำงานใน Node ได้

  3. Nest factory + loggerNestFactory.create<NestExpressApplication>(AppModule, { logger: new I3GatewayLogger('System') }) ที่บรรทัด 21-23

  4. Global pipes/filters/guards/interceptors — ลำดับการ register อยู่ที่บรรทัด 40-43:

    • HttpExceptionFilter (custom exception filter)
    • LoggingInterceptor
    • JwtAuthGuard (เป็น global — ทุก route ต้อง auth โดย default ยกเว้นที่ mark @Public())
    • ValidationPipe({ whitelist: true, forbidNonWhitelisted: true, transform: true }) ค่านี้ strict กว่า repo อื่น — forbidNonWhitelisted จะ reject request ที่ส่ง field นอกเหนือจาก DTO กำหนด
  5. CORS callback-basedpms-api/src/main.ts (branch: uat):45-66 เขียน callback ที่ block origin ที่มีคำว่า localhost เฉพาะเมื่อ NODE_ENV === 'prod' ดีกว่า origin: '*' ของ repo อื่นในกลุ่ม แต่ก็ยังใช้ allowedHeaders: '*' อยู่ (บรรทัด 64)

  6. Global prefix + static assets — prefix ${BASE_PATH}api/v1 (ใน UAT จะเป็น /pms-api/api/v1) และ static assets /uploads ที่ prefix /pms-api/ (บรรทัด 33-36)

  7. Swagger — mount ที่ ${BASE_PATH}docs (บรรทัด 68-69)

  8. Listenapp.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)

typescript
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 ตารางสถานะการใช้งาน

Connectionenv prefixdatabaseสถานะ
mainDB_*pmsใช้งานจริง — เก็บ entity ของ PMS ทั้งหมด
workforceWORKFORCE_DB_*dbWorkforceใช้งานจริง — อ่านข้อมูลองค์กร/พนักงานจาก database ส่วนกลาง
workforceTempWORKFORCE_TEMP_DB_*dbWorkforce_tempใช้งานจริง — เฉพาะ module rcc
vtrcVTRC_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

typescript
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):104ScheduleModule.forRoot() เปิดใช้งาน @Cron() decorator ทั่วระบบ มี cron ที่ active จริง 2 ตัว (ดูรายละเอียดในบทที่ 2 และบทที่ 5)

Feature modules (30 ตัว)

ลำดับ import ตาม pms-api/src/app.module.ts (branch: uat):83-113

typescript
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 modulenotify, image, employeeWorkforce — เป็นไปได้ว่าถูกใช้ผ่าน module อื่นหรือเป็น utility เล็ก ๆ ไม่ใช่ feature module เต็มรูปแบบ

รายการ module ทั้ง 33 จัดตาม domain

Period & Notification (4 modules)

ModuleEntity หลักบริการ
periodPeriodEntity, PeriodExtendEntity, PeriodHistoryEntityสร้าง/แก้ไข/ลบรอบการประเมิน ปีละ 1 รอบ มีช่วงเวลาย่อย 7 ช่วง (settingGoal, checkData, updateResult, evaluation, feedback ฯลฯ)
configNotificationConfigNotificationEntity, ConfigNotificationHistoryEntity, NotifyEntityตั้งค่าว่าจะแจ้งเตือนใครวันไหน + cron ส่ง email ทุกวัน 6 โมงเช้า
notifyNotifyEntity(ไม่ได้ import เป็น standalone) — เก็บประวัติการแจ้งเตือน
migrateDatamigrate ข้อมูลจาก workforce เข้า pms — cron ที่ถูก comment ปิดไว้

Indicator & Scoring (5 modules)

ModuleEntity หลักบริการ
indicatorTypeIndicatorTypeEntity, IndicatorTypeHistoryEntityประเภทตัวชี้วัด (เช่น KPI, Competency)
indicatorIndicatorHdEntity, IndicatorDtEntity, IndicatorHdHistoryEntityตัวชี้วัดรายบุคคล/รายประเภท
indicatorWeightIndicatorWeightHdEntity, IndicatorWeightDtEntity, IndicatorWeightHistoryEntityน้ำหนักตัวชี้วัด (เช่น KPI 70%, Competency 30%)
ratingScaleRatingScaleEntity, RatingScaleHistoryEntityscale การให้คะแนนตัวชี้วัด
gradingGradingEntity, GradingDtEntity, GradingHistoryEntityเกรดผลงานรวม (A, B+, B, C, D)

Evaluation form (5 modules)

ModuleEntity หลักบรรทัดของ .service.tsบริการ
createFormEvaluateFormEntity, EvaluateFormDtEntity, EvaluateFormHistoryEntity2,701สร้างแบบประเมินจากตัวชี้วัด — ใหญ่เป็นอันดับ 2 ของ repo
evaluateFormEvaluateFormEntity, EvaluateFormDtEntity, EvaluateFormKpiRatingEntity, EvaluateFormLearningEntity, EvaluateFormCourseEntity, EvaluateFormSummaryEntity, EvaluateFormDtHistoryEntity, EvaluateFormHistoryEntity, EvaluateFormNoQuotaHistoryEntity2,996กรอกผลการปฏิบัติงานจริง + export PDF (puppeteer) + export Excel — service ใหญ่สุดของ repo
evaluateUserAmountEvaluateUserAmountEntity, EvaluateUserAmountHistoryEntity, EvaluateUserEntity, EvaluateUserHistoryEntity1,147รายชื่อผู้ถูกประเมิน + จำนวนรวมต่อรอบ
evaluationsEvaluateSummaryResultEntity, EvaluateFormEntity729สรุปผลประเมินระดับองค์กร
confirmEvaluationEvaluateFormEntity, OrgUnitConfirmHistoryEntity, ConfirmQuotaHistoryEntity1,240ยืนยันผลประเมินของหน่วยงานก่อนเข้า flow เงินเดือน

Approval & permission (4 modules)

ModuleEntity หลักบริการ
approverApproverHdEntity, ApproverDtEntity, ApproverHistoryEntityสายอนุมัติ 5 ชั้น (admin1Email..admin5Email) + delegate
permissionPMSUserEntity, 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
authAuthDataEntityJWT login สำหรับ candidate/user

Salary (5 modules — ระวังชื่อคล้ายกัน)

ชื่อ module 5 ตัวในกลุ่มนี้สับสนง่ายมาก — salaryRange, salaryPercentage, salaryAdjust, salaryAdjustment, salaryAdjustToHRMI ต้องอ่านให้ดี

ModuleEntity หลักบริการ
salaryRangeSalaryRangeHdEntity, SalaryRangeDtEntity, SalaryRangeHistoryEntityช่วงเงินเดือนตาม pay grade
salaryPercentagePercentSalaryEntity, PercentSalaryEmpLevelEntity, PercentSalaryEmpLevelDtEntity, PercentSalaryHistoryEntityเปอร์เซ็นต์ปรับเงินเดือนตามเกรด + employee level
salaryAdjustSalaryAdjustBuEntity, SalaryAdjustDivEntityสร้าง/preview การปรับเงินเดือน
salaryAdjustmentSalaryAdjustHdEntity, SalaryAdjustBuEntity, SalaryAdjustDivEntity, SalaryAdjustDtEntity, + 4 History entity2,367 บรรทัด — บันทึก/อนุมัติการปรับจริง — service ใหญ่สุดของกลุ่ม
salaryAdjustToHRMISalaryAdjustBuEntity, SalaryAdjustHdEntityexport ผลปรับเงินเดือนเป็นไฟล์ Excel ให้ HR นำเข้า HRMI ด้วยมือ (ต่างจาก recruitment-api ที่ export ผ่าน HTTP)

Master data & utility (4 modules)

ModuleEntity หลักบริการ
dropdownMDropdownEntitymaster data lookup (สถานะ, ประเภท, ฯลฯ) — เรียกใช้โดย module อื่นเกือบทุกตัว
image— (อ่านจาก workforce)ดึงรูปพนักงานจาก workforce
employeeWorkforce— (อ่านจาก workforce)query ข้อมูลพนักงานดิบจาก database ส่วนกลาง
uploadFilesPmsAttachFileEntityจัดการไฟล์แนบ

Operations & reporting (5 modules)

ModuleEntity หลักบริการ
budgetBudgetEntity, BudgetDtEntity, BudgetHistoryEntityงบประมาณปรับเงินเดือนต่อหน่วยงาน — มี GET /budget/getEmployeeSalary เป็น @Public()
performanceRecordบันทึกผลงานย้อนหลัง
requestEditFormEvaluateFormEntity (เขียนฟิลด์ isRequestEdit/requestEditStatus)ขอแก้ไขแบบประเมินหลังปิดรอบ
report— (อ่านจากหลาย entity)รายงานสรุปผลประเมิน/เงินเดือน
rcc— (อ่านจาก workforceTemp)module เฉพาะ — ใช้ connection workforceTemp เท่านั้น

รายการ entity ทั้ง 66 จัดตาม domain

Period (3)

  • PeriodEntitytbPeriodpms-api/src/entities/period.entity.ts (branch: uat):8
  • PeriodExtendEntitytbPeriodExtend — ส่วนขยายช่วงเวลา settingGoal ของแต่ละ BU — pms-api/src/entities/periodExtesnd.entity.ts (branch: uat):6
  • PeriodHistoryEntitytbPeriodHistory — บันทึก history ทุกการ update period — pms-api/src/entities/periodHistory.entity.ts (branch: uat):6

Notification (3)

  • ConfigNotificationEntitytbConfigNotificationpms-api/src/entities/configNotification.entity.ts (branch: uat):6
  • ConfigNotificationHistoryEntitytbConfigNotificationHistory
  • NotifyEntity → (notify log)

Indicator (10)

  • IndicatorTypeEntity, IndicatorTypeHistoryEntity
  • IndicatorHdEntity, IndicatorDtEntity, IndicatorHdHistoryEntity
  • IndicatorWeightHdEntity, IndicatorWeightDtEntity, IndicatorWeightHistoryEntity
  • RatingScaleEntity, RatingScaleHistoryEntity

Grading (6)

  • GradingEntity, GradingDtEntity, GradingHistoryEntity
  • GradeLimitHdEntity, GradeLimitDtEntity, GradeLimitHistoryEntity — forced distribution ต่อเกรด

EvaluateForm (11)

  • EvaluateFormEntitytbEvaluateForm — entity หลัก 444 บรรทัด มี status ซ้อนกันหลายชั้น — pms-api/src/entities/evaluateForm.entity.ts (branch: uat):14
  • EvaluateFormDtEntity, EvaluateFormDtHistoryEntity, EvaluateFormHistoryEntity
  • EvaluateFormKpiRatingEntity
  • EvaluateFormLearningEntity, EvaluateFormCourseEntity
  • EvaluateFormSummaryEntity, EvaluateSummaryResultEntity
  • EvaluateFormNoQuotaHistoryEntity
  • EvaluateUserEntity, EvaluateUserHistoryEntity

EvaluateUserAmount (2)

  • EvaluateUserAmountEntity, EvaluateUserAmountHistoryEntity

Approver (3)

  • ApproverHdEntitytbApproverpms-api/src/entities/approver.entity.ts
  • ApproverDtEntitytbApproverDt — 5 ชั้น admin1Email..admin5Email
  • ApproverHistoryEntitytbApproverHistory

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, SalaryAdjustHdHistoryEntity
  • SalaryAdjustBuEntity, SalaryAdjustBuHistoryEntity
  • SalaryAdjustDivEntity, SalaryAdjustDivHistoryEntity
  • SalaryAdjustDtEntity, SalaryAdjustDtHistoryEntity

ConfirmEvaluation (2)

  • OrgUnitConfirmHistoryEntity
  • ConfirmQuotaHistoryEntity

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

typescript
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 เจ้าของหมายเหตุ
tbPeriodperiod
tbEvaluateFormevaluateForm + createForm (ทั้งคู่เขียน entity เดียวกัน)ระวัง — createForm สร้าง, evaluateForm กรอกผล
tbApprover, tbApproverDtapprover
tbSalaryAdjustBusalaryAdjust + salaryAdjustmentทั้งคู่เขียน entity เดียวกัน — salaryAdjust สร้าง, salaryAdjustment อนุมัติ
tbSalaryAdjustHdsalaryAdjustment
tbConfigNotificationconfigNotification
tbBudgetbudget
tbPMSUserpermission
tbMDropdowndropdownใช้โดย module อื่นเกือบทุกตัว — เป็น service กลาง

Dockerfile analysis

pms-api/Dockerfile (branch: uat):1-28 ทั้งไฟล์

dockerfile
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)

  1. ไม่ใช้ --frozen-lockfile — บรรทัด 19 ใช้แค่ yarn install ทำให้แต่ละ build อาจได้ dependency version ต่างกัน ถ้า yarn.lock ไม่ sync กับ package.json เสี่ยงเกิด drift ระหว่าง build
  2. ติดตั้ง chromium ระบบ — บรรทัด 9-15 ติดตั้ง chromium, nss, harfbuzz, freetype, ttf-freefont เพื่อให้ puppeteer ทำงานได้ (ใช้สำหรับ export PDF ผลประเมิน) — ชุดเดียวกับ benefit-api
  3. PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true — บรรทัด 6 — ป้องกัน puppeteer ดาวน์โหลด chromium ซ้ำเพราะใช้ของระบบ
  4. TZ="Asia/Bangkok" — timezone เป็นกรุงเทพฯ สำคัญเพราะมี cron 6 โมงเช้า (@Cron('0 6 * * *', { timeZone: 'Asia/Bangkok' }) ใน pms-api/src/modules/configNotification/configNotification.controller.ts (branch: uat):30-32)
  5. EXPOSE 9331 — ตรงกับ PORT=9331 ใน .env และ fallback ใน main.ts:27
  6. CMD yarn start:prod — รัน node dist/src/main (ตาม package.json script start: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

typescript
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 และ evaluateFormcreateForm สร้าง 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