12.10 · Publication + Conicle + Cron
บทนี้เจาะลึก business logic ของการเผยแพร่เนื้อหา (Post/Document/Course), Conicle LMS integration, และ cron jobs ที่ทำงานเบื้องหลัง
ภาพรวม
| ด้าน | ค่า |
|---|---|
| Publication controller | lib/controllers/{post,document,course,career}/*.js |
| Conicle controller | lib/controllers/conicle/conicle.js (~310 บรรทัด) |
| Cron | crons.js (72 บรรทัด) |
| Cron jobs active | 3 |
| Cron jobs disabled | 1 (auto-cancel leave) |
| Publication status | 3 (PUBLIC, INTERNAL, ALLINTERNAL) |
Part 1 · Publication (Post/Document/Course)
1. Publication status
// concept — conditionFilterPublic
if (context.userId == null || prasma.length > 0) {
status = { status: "PUBLIC" } // anonymous → เห็นเฉพาะ PUBLIC
} else {
typePost = ['ABOUTUS', 'ORIENTATION', 'PUBLIC_RELATIONS']
}Status values
| Status | ความหมาย | ใครเห็น |
|---|---|---|
PUBLIC | เผยแพร่สาธารณะ | ทุกคน (รวมผู้ที่ยังไม่ login) |
INTERNAL | ภายในหน่วยงาน | เฉพาะสมาชิก division |
ALLINTERNAL | ภายในทั้งหมด | ทุกพนักงานที่ login แล้ว |
2. Post workflow
Operations
| Operation | ที่อยู่ | ความหมาย |
|---|---|---|
createPost(args) | post.js:12 | สร้าง + nested postDivision |
updatePost(postId, args) | post.js:20 | แก้ไข |
highLightPost(postId, args) | post.js:28 | ตั้ง highlight (จำกัด ≤ 3/division) |
destroyPost(postId) | post.js:48 | ลบ |
Highlight limit
// post.js:28 (concept)
if (post.length < 3) {
// ตั้ง highlight ได้
} else {
throw responError('OUT_OF_LIMIT')
}จำกัด highlight สูงสุด 3 โพสต์ต่อ division
3. ไม่มี explicit review/publish state machine
ไม่เหมือน domain อื่น — post ไม่มี "draft → review → publish" workflow
status field (PUBLIC, INTERNAL, ALLINTERNAL) ถูกตั้งโดยตรงจาก backoffice — admin เป็นคนเลือก
4. Attachment handling
ไฟล์แนบเก็บใน Models.File (polymorphic):
| Field | ใช้กับ |
|---|---|
wdSlipId | Welfare slip |
relateId | ทั่วไป |
wdHospitalId | Welfare hospital |
postId | Post banner |
สำหรับ hospital: handleHospitalDocument + handleHospitalDocumentv2 (hospital.js:3076, 3025)
5. Read tracking — postRead.js
// concept — track userId + postId
await PostRead.create({
userId,
postId,
readAt: new Date()
})ใช้สำหรับแสดง "อ่านแล้ว" หรือนับยอด view
6. Permission scope
// concept
clouseFrontEnd(args, context, 'postId', includeRelation) // ฝั่ง user
clouseBackOffice(args, context, 'postId', includeRelation) // ฝั่ง adminPlasma branch
ถ้า user division มี KEY_PLASMA config — เห็นโพสต์ plasma ด้วย
Part 2 · Conicle (LMS)
7. Conicle integration — SSO + proxy
ที่อยู่: lib/controllers/conicle/conicle.js
Auth flow — authConicle
// concept
const authConicle = async () => {
// 1. ตรวจ ConicleSessions ใน DB — ถ้า sessionExpireDate > now ใช้ session เดิม
const existing = await ConicleSessions.findOne({
where: { sessionExpireDate: { [Op.gt]: new Date() } }
})
if (existing) return existing
// 2. ถ้าไม่มี/หมดอายุ → POST ${END_POINT_CONICLE}/api/integrate/account/login/
const response = await axios.post(loginUrl, {
username: CONINCLE_USERNAME,
password: CONINCLE_PASSWORD
})
// 3. parse set-cookie → extract sessionid, csrftoken, __cfduid
const cookies = parseCookies(response.headers['set-cookie'])
// 4. createConicle — save DB
await createConicle({
sessionid: cookies.sessionid,
csrftoken: cookies.csrftoken,
__cfduid: cookies.__cfduid,
sessionExpireDate: moment().add(1, 'day').toDate()
})
// 5. cleanup expired sessions
await destroyConicleSession({ sessionExpireDate: { [Op.lt]: new Date() } })
}User SSO — authUserConicle
// concept
const authUserConicle = async (cookie, context) => {
// POST ${END_POINT_CONICLE}/api/integrate/account/sso/generate/
// username = profileKey
// header: X-CSRFTOKEN + Cookie
return response.data.token
}8. Page redirect
// concept — generateUrlConicle
const generateUrlConicle = async (pageRequest, context) => {
const cookie = await authConicle()
const token = await authUserConicle(cookie, context)
// page: library, public-request, home
return `${END_POINT_CONICLE}/account/sso/validate/?redirect_path=${page}&token=${token}`
}CONICLE_PAGE config
| Index | Page |
|---|---|
0 | library |
1 | public-request |
2 | home |
9. List course
// concept
const conicleListCourse = async (context) => {
const cookie = await authConicle()
// GET ${END_POINT_CONICLE}/api/integrate/highlight/v2/
return response.data
}10. QR check-in
// concept
const readContentQrCode = async (code, context) => {
// GET ${END_POINT_CONICLE}/api/integrate/check-in/scan/?code=${code}&username=${profileKey}
}11. Conicle routes
| Route | บรรทัด | ความหมาย |
|---|---|---|
GET /api/conicle/downloadFile/:id | routes.js:902 | stream file from disk |
GET /api/conicle/getFileFromUrl | routes.js:920 | proxy fetch external URL → save → return local URL |
12. Long-term training export / SFTP
ไม่พบ logic SFTP หรือ long-term training export ใน vtrc-api
ค้นหา sftp|ssh|longterm|long_term|training ใน routes.js — ไม่ match
อาจอยู่ใน cu-central-api หรือไม่ได้ implement ใน vtrc-api
Part 3 · Cron jobs
13. Active cron jobs
if (+APP_NO === 1) {
jobs.push(schedule.scheduleJob('* 1 * * * *', async () => {
await updateSuccessFundChangeRateByJob()
}))
}
// Every day at 03:30.
jobs.push(schedule.scheduleJob('30 3 * * *', async () => {
updateDelegatesExpire();
}))
// Every day at 00:30.
jobs.push(schedule.scheduleJob('30 0 * * *', async () => {
statLog()
}))| Schedule | Job | ที่อยู่ | สิ่งที่ทำ |
|---|---|---|---|
* 1 * * * * (ทุกนาที ที่วินาที 1) | updateSuccessFundChangeRateByJob | fund.js:400 | flip PENDING → SUCCESS provident fund rate change (เฉพาะ APP_NO=1) |
30 3 * * * (03:30 ทุกวัน) | updateDelegatesExpire | (import) | ปิด delegate ที่เลย endDate |
30 0 * * * (00:30 ทุกวัน) | statLog | statLog/statLog.js:8 | sync activation log จาก log-event-api → reportActivateAccount |
14. Disabled cron — auto-cancel leave
// concept — ถูก comment ทิ้ง (RecurrenceRule: date=16, hour=0, minute=1)
// เทียบ cron: '1 0 16 * *' = ทุกวันที่ 16 เวลา 00:01
// schedule.scheduleJob(rule, async () => {
// // ยกเลิกเอกสารลาของสังกัด 0734 อัตโนมัติ
// await hrmi.updateLeaveDocument(..., {
// approveStatus: 'C',
// noteApprove: 'ยกเลิกโดยระบบ'
// })
// })ถูกปิดไว้ — แต่ยังอยู่ในโค้ด ควรยืนยันกับ HR ก่อนลบ
15. statLog — cross-service
// statLog/statLog.js:8 (concept)
const statLog = async () => {
// 1. ดึง activation events จาก log-event-api
const activationEvents = await axios.get(
`${END_POINT_LOG_EVENT}/log-event-api/api/v1/logEventActive`
)
// 2. ดึง activated events
const activatedEvents = await axios.get(
`${END_POINT_LOG_EVENT}/log-event-api/api/v1/logEventActivated`
)
// 3. ดึง employee org info จาก cu-central-api
const employees = await statLogCD()
// 4. INSERT ลง reportActivateAccount (db2 = vtrc-centralize)
await ReportActivateAccount.bulkInsert(...)
}16. ข้อระวัง cron
1. ไม่มี error handling
schedule.scheduleJob ไม่ catch error — ถ้า throw จะเป็น unhandledRejection
2. APP_NO gate
cron fund rate ทำงานเฉพาะ APP_NO=1 — ถ้า deploy หลาย instance โดยไม่ตั้งค่า อาจทำซ้ำหรือไม่ทำ
3. ไม่มี lock
ถ้า cron รันนานเกิน schedule ตัวถัดไปอาจเริ่มซ้อนทับ
4. Dead code
auto-cancel leave ถูกปิดไว้ — ควรลบหรือใส่ comment อธิบายว่าทำไมปิด
สรุปทั้ง 3 ส่วน
| ด้าน | ทำใน vtrc-api | ส่งต่อ cu-central-api / ภายนอก |
|---|---|---|
| Publication status | ใช่ | — |
| Read tracking | ใช่ | — |
| Permission scope (plasma) | ใช่ | — |
| Conicle auth + SSO | ใช่ | Conicle LMS API |
| Conicle cookie management | ใช่ | — |
| Cron fund rate | ใช่ | — |
| Cron delegate expire | ใช่ | — |
| Cron stat log | ใช่ | log-event-api + cu-central-api |
| Long-term training export | ไม่พบ | (ไม่พบใน vtrc-api) |