diff --git a/.gitignore b/.gitignore index 64ba25c..81d09a1 100644 --- a/.gitignore +++ b/.gitignore @@ -131,3 +131,5 @@ dist .yarn/build-state.yml .yarn/install-state.gz .pnp.* + +.claude \ No newline at end of file diff --git a/src/controllers/DocumentController.ts b/src/controllers/DocumentController.ts index 3d1c952..cdc26f8 100644 --- a/src/controllers/DocumentController.ts +++ b/src/controllers/DocumentController.ts @@ -10,6 +10,7 @@ import { Route, SuccessResponse, Tags, + Security } from "tsoa"; import HttpStatus from "../interfaces/http-status"; import { @@ -29,6 +30,7 @@ import HttpStatusCode from "../interfaces/http-status"; @Route("api/v1/evaluation/document") @Tags("document") +@Security("bearerAuth") export class DocumentController extends Controller { /** * @example volume "เล่ม 1" diff --git a/src/controllers/EvaluationController.ts b/src/controllers/EvaluationController.ts index 622060e..5bd7ca2 100644 --- a/src/controllers/EvaluationController.ts +++ b/src/controllers/EvaluationController.ts @@ -2630,13 +2630,22 @@ export class EvaluationController { if (!evaluation) { throw new HttpError(HttpStatusCode.NOT_FOUND, "not found."); } - if (!evaluation.evaluation_directors_director) { - evaluation.evaluation_directors_director = []; - } - body.directors.forEach(async (directorId) => { + // ดึง directors เดิมที่มีอยู่ + const existingDirectors = await this.evaluation_directors_directorRepository.find({ + where: { evaluationId: evaluation.id }, + }); + const existingDirectorIds = new Set(existingDirectors.map((d) => d.directorId)); + + // เพิ่ม directors ใหม่ที่ยังไม่มี + const newDirectors = []; + for (const directorId of body.directors) { + // ข้ามถ้ามีอยู่แล้ว + if (existingDirectorIds.has(directorId)) { + continue; + } const director = await this.directorRepository.findOne({ where: { id: directorId } }); if (director != null) { - await this.evaluation_directors_directorRepository.save({ + newDirectors.push({ directorId: director.id, evaluationId: evaluation.id, createdUserId: request.user.sub, @@ -2646,8 +2655,12 @@ export class EvaluationController { createdAt: new Date(), lastUpdatedAt: new Date(), }); + existingDirectorIds.add(director.id); // เพิ่มเพื่อป้องกันซ้ำใน batch เดียวกัน } - }); + } + if (newDirectors.length > 0) { + await this.evaluation_directors_directorRepository.insert(newDirectors); + } evaluation.lastUpdateUserId = request.user.sub; evaluation.lastUpdateFullName = request.user.name; @@ -3348,4 +3361,72 @@ export class EvaluationController { } else throw new HttpError(HttpStatusCode.INTERNAL_SERVER_ERROR, error); } } + + /** + * API สำหรับลบกรรมการออกจากการประเมิน + * @summary ลบกรรมการในการประเมิน (ADMIN) + * @param {string} id id ของ evaluation_directors_director + */ + @Delete("del-director/{id}") + async removeEvaluationDirector( + @Path() id: string, + @Request() request: RequestWithUser, + ) { + try { + const evaluationDirector = + await this.evaluation_directors_directorRepository.findOneBy({ id }); + + if (!evaluationDirector) { + throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบข้อมูลกรรมการ"); + } + + await this.evaluation_directors_directorRepository.remove(evaluationDirector, { + data: request, + }); + + return new HttpSuccess(); + } catch (error: any) { + if (error instanceof HttpError) { + throw error; + } else throw new HttpError(HttpStatusCode.INTERNAL_SERVER_ERROR, error); + } + } + + /** + * API สำหรับลบการประชุมออกจากการประเมิน + * @summary ลบการประชุมในการประเมิน (ADMIN) + * @param {string} evaluationId id ของ evaluation + * @param {string} meetingId id ของ meeting + */ + @Delete("del-meeting/{evaluationId}/{meetingId}") + async removeMeetingFromEvaluation( + @Path() evaluationId: string, + @Path() meetingId: string, + @Request() request: RequestWithUser, + ) { + try { + const evaluation = await this.evaluationRepository.findOne({ + where: { id: evaluationId }, + relations: { meetings: true }, + }); + + if (!evaluation) { + throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบข้อมูลการประเมิน"); + } + + const meetingExists = evaluation.meetings.some((m) => m.id === meetingId); + if (!meetingExists) { + throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบข้อมูลการประชุมในการประเมินนี้"); + } + + evaluation.meetings = evaluation.meetings.filter((m) => m.id !== meetingId); + await this.evaluationRepository.save(evaluation, { data: request }); + + return new HttpSuccess(); + } catch (error: any) { + if (error instanceof HttpError) { + throw error; + } else throw new HttpError(HttpStatusCode.INTERNAL_SERVER_ERROR, error); + } + } } diff --git a/src/middlewares/logs.ts b/src/middlewares/logs.ts index 4bcf111..2ae251c 100644 --- a/src/middlewares/logs.ts +++ b/src/middlewares/logs.ts @@ -59,6 +59,17 @@ async function logMiddleware(req: Request, res: Response, next: NextFunction) { // Get rootId from token const rootId = req.app.locals.logData?.orgRootDnaId; + let _msg = data?.message; + if (!_msg) { + if (res.statusCode >= 500) { + _msg = "ไม่สำเร็จ"; + } else if (res.statusCode >= 400) { + _msg = "พบข้อผิดพลาด"; + } else if (res.statusCode >= 200) { + _msg = "สำเร็จ"; + } + } + const obj = { logType: res.statusCode >= 500 ? "error" : res.statusCode >= 400 ? "warning" : "info", ip: req.ip, @@ -71,7 +82,7 @@ async function logMiddleware(req: Request, res: Response, next: NextFunction) { method: req.method, endpoint: req.url, responseCode: String(res.statusCode === 304 ? 200 : res.statusCode), - responseDescription: data?.message, + responseDescription: _msg, input: level === 4 ? JSON.stringify(req.body, null, 2) : undefined, output: level === 4 ? JSON.stringify(data, null, 2) : undefined, ...req.app.locals.logData,