Skip to content

Commit

Permalink
feat: 연/월별 행사글 route & controller 추가, package 업데이트
Browse files Browse the repository at this point in the history
  • Loading branch information
ptyoiy committed May 18, 2024
1 parent ce4a3d8 commit 285f1d5
Show file tree
Hide file tree
Showing 17 changed files with 1,973 additions and 1,852 deletions.
119 changes: 87 additions & 32 deletions adminPage/handlers/event.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ import { redisClient } from "../../redis/connect.js";
import { EventActionQueryParameters } from "./index.js";
import { IEvent } from "../../models/types.js";
import { delEventSchedule, setEventSchedule } from "../../redis/schedule.js";
import {
cacheYearMonthData,
calculateMonthsBetween,
} from "../../redis/caching.js";

const list: ActionHandler<any> = async (request, response, context) => {
const { query } = request; // 요청 url의 query 부분 추출
Expand Down Expand Up @@ -83,64 +87,115 @@ const after = (action: "edit" | "new") => async (originalResponse, request, cont
const {
currentAdmin: { role },
} = context;
const hasRecord = originalResponse?.record?.params;
const hasRecord: IEvent = originalResponse?.record?.params;
const hasError = Object.keys(originalResponse.record.errors).length;
// checking if object doesn't have any errors or is a edit action
if (isPost && isEdit && hasRecord && !hasError) {
const {id, end} = hasRecord
const { id, start, end } = hasRecord;
const promises = [];
// 학과는 로그인한 관리자의 것으로 적용
if (role != "관리자") {
hasRecord.major_advisor = role;
}
// redis 캐싱
const redisKeyEach = `event:${id}`;
const redisKeyAll = "allEvents";
await redisClient.set(redisKeyEach, JSON.stringify(hasRecord));
promises.push(redisClient.set(redisKeyEach, JSON.stringify(hasRecord)));
// end날이 아직 안 지났다면
if (end > new Date()) {
// 종료 변경하는 스케쥴 등록
console.log("has update::?",hasRecord.update);
const recordModel = (await Event.findOne({
where: {id}
})) as IEvent;
// end날 이후 종료 상태로 변경하는 스케쥴 등록
const recordModel = await Event.findOne({
where: { id },
});
setEventSchedule(recordModel);
}
// 전체 목록 캐싱
const allEventsFromDb = await Event.findAll({
where: {
expired: false, // 진행중인 행사만 가져오기
},
// 캐싱
const allEvents = await Event.findAll({
order: [["start", "ASC"]],
});
await redisClient.set(redisKeyAll, JSON.stringify(allEventsFromDb));
// 수정한 글의 시작~종료일 사이 연-월 리스트 추출
const ranges = calculateMonthsBetween(start, end);

// 진행 중인 행사글, 수정한 글의 시작~종료일 사이에 있는 모든 행사글(=관련글) 추출
const [onGoings, relations] = allEvents.reduce(
(acc, event) => {
if (!event.expired) acc[0].push(event);
if (
ranges.some((range) => {
const [year, month] = range.split("-");
const startOfMonth = new Date(+year, +month - 1, 1);
const endOfMonth = new Date(+year, +month, 0, 23, 59, 59);
return (
(event.start >= startOfMonth && event.start <= endOfMonth) ||
(event.end >= startOfMonth && event.end <= endOfMonth) ||
(event.start <= startOfMonth && event.end >= endOfMonth)
);
})
)
acc[1].push(event);
return acc;
},
[[], []] as IEvent[][]
);
// 전체 목록 캐싱
promises.push(redisClient.set(redisKeyAll, JSON.stringify(onGoings)));
console.log({ relations: relations.map((v) => v.id) });
// 관련글들을 연:월로 grouping
promises.push(...cacheYearMonthData(relations));
await Promise.all(promises);
}

return originalResponse;
};

const deleteAfter = () => async (originalResponse, request, context) => {
const isPost = request?.method === "post";
const isAction = context?.action.name === "bulkDelete";
const isAction = context?.action.name === "delete";
const { record } = originalResponse;

console.log({isPost, action: context?.action.name, record});
// checking if object doesn't have any errors or is a edit action
if (isPost && isAction && record) {
const {id, end} = record;
const { id, start, end } = record;
const redisKeyAll = "allEvents";

await redisClient.del(`event:${id}`);
const promises = [];
promises.push(redisClient.del(`event:${id}`));

if (end > new Date()) {
delEventSchedule(record);
}
// 전체 목록 캐싱
const allEventsFromDb = await Event.findAll({
where: {
expired: false, // 진행중인 행사만 가져오기
},
// 캐싱
const allEvents = await Event.findAll({
order: [["start", "ASC"]],
});
await redisClient.set(redisKeyAll, JSON.stringify(allEventsFromDb));
// 수정한 글의 시작~종료일 사이 연-월 리스트 추출
const ranges = calculateMonthsBetween(start, end);

// 진행 중인 행사글, 수정한 글의 시작~종료일 사이에 있는 모든 행사글(=관련글) 추출
const [onGoings, relations] = allEvents.reduce(
(acc, event) => {
if (!event.expired) acc[0].push(event);
if (
ranges.some((range) => {
const [year, month] = range.split("-");
const startOfMonth = new Date(+year, +month - 1, 1);
const endOfMonth = new Date(+year, +month, 0, 23, 59, 59);
return (
(event.start >= startOfMonth && event.start <= endOfMonth) ||
(event.end >= startOfMonth && event.end <= endOfMonth) ||
(event.start <= startOfMonth && event.end >= endOfMonth)
);
})
)
acc[1].push(event);
return acc;
},
[[], []] as IEvent[][]
);
// 전체 목록 캐싱
promises.push(redisClient.set(redisKeyAll, JSON.stringify(onGoings)));
// 관련글들을 연:월로 grouping
promises.push(...cacheYearMonthData(relations));
await Promise.all(promises);
}

return originalResponse;
Expand All @@ -153,23 +208,23 @@ const bulkDelete = () => async (originalResponse, request, context) => {

// checking if object doesn't have any errors or is a edit action
if (isPost && isAction && records) {
records.forEach(async ({params: record}) => {
const promises = records.map(async ({ params: record }) => {
// redis 캐싱 제거
await redisClient.del(`event:${record.id}`);
if (record.end > new Date()) {
// 스케쥴에 등록된 행사들 제거
delEventSchedule(record);
}
return redisClient.del(`event:${record.id}`);
});
const redisKeyAll = "allEvents";
// 전체 목록 캐싱
const allEventsFromDb = await Event.findAll({
where: {
expired: false, // 진행중인 행사만 가져오기
},
const allEvents = await Event.findAll({
order: [["start", "ASC"]],
});
await redisClient.set(redisKeyAll, JSON.stringify(allEventsFromDb));

promises.push(redisClient.set(redisKeyAll, JSON.stringify(allEvents.filter(e => !e.expired))));
promises.push(...cacheYearMonthData(allEvents));
await Promise.all(promises);
}
return originalResponse;
};
Expand Down
147 changes: 108 additions & 39 deletions controllers/event/event_search.ts
Original file line number Diff line number Diff line change
@@ -1,56 +1,125 @@
import * as express from "express";
import { Event } from "../../models/index.js";
import { Event, sequelize } from "../../models/index.js";
import { IEvent } from "../../models/types.js";
import { redisClient } from "../../redis/connect.js";
import { redisGetAndParse } from "../common_method/utils.js";
import { Op } from "sequelize";

const EXPIRE = 3600; // 유효시간 1시간

// GET /posts/events/${행사글id}
export const getEvent = async (
req: express.Request<any, any>,
res: express.Response,
next: express.NextFunction
req: express.Request<any, any>,
res: express.Response,
next: express.NextFunction
) => {
try {
const eventId = req.params.eventId;
const redisKey = `event:${eventId}`;
let event: IEvent;

// Redis 먼저 조회
const eventRedis = await redisClient.get(redisKey);

if (eventRedis) {
event = JSON.parse(eventRedis);
}
else {
// Redis에 없는 경우, DB에서 조회
event = await Event.findByPk(eventId) as IEvent;

if (event) {
await redisClient.set(redisKey, JSON.stringify(event), { EX: EXPIRE });
} else {
return res.status(400).json({ message: "해당 행사를 찾을 수 없습니다." });
}
}
return res.status(200).json(event);
} catch (error) {
console.log(error);
return res.status(500).json({ message: "서버 내부 에러" });
try {
const eventId = req.params.eventId;
if (eventId == "date") return next();
const redisKey = `event:${eventId}`;
let event: IEvent;

// Redis 먼저 조회
const eventRedis = await redisClient.get(redisKey);

if (eventRedis) {
event = JSON.parse(eventRedis);
} else {
// Redis에 없는 경우, DB에서 조회
event = (await Event.findByPk(eventId)) as IEvent;

if (event) {
await redisClient.set(redisKey, JSON.stringify(event), { EX: EXPIRE });
} else {
return res
.status(400)
.json({ message: "해당 행사를 찾을 수 없습니다." });
}
}
return res.status(200).json(event);
} catch (error) {
// console.log(error);
return res.status(500).json({ message: "서버 내부 에러", error });
}
};

// GET /events
export const getEventAll = async (
req: express.Request<any, any>,
res: express.Response,
next: express.NextFunction
req: express.Request<any, any>,
res: express.Response,
next: express.NextFunction
) => {
try {
const eventAll = await redisGetAndParse("allEvents");
return res.status(200).json(eventAll);
} catch (error) {
console.error(error);
res.status(500).json({ error: "서버 내부 에러" });
}
};

// GET /events/date
export const getEventsByDate = async (
req: express.Request<any, any>,
res: express.Response,
next: express.NextFunction
) => {
try {
const eventAll = await redisGetAndParse('allEvents');
return res.status(200).json(eventAll);
} catch (error) {
console.error(error);
res.status(500).json({ error: '서버 내부 에러' });
try {
const { year, month } = req.query;
console.log({year, month});
if (
!year ||
!month ||
!Number.isInteger(+year) ||
!Number.isInteger(+month) ||
(+year <= 2020 || +year >= 2040) ||
(+month < 1 || +month > 12)
) {
throw new Error(`query string error: year:${year} month:${month}`);
}
}
// redis 캐싱 확인
const cachedEvents = await redisClient.hGet(`events:${year}`, month.toString());
return res.status(200).json(JSON.parse(cachedEvents));
// // DB 쿼리
// const startOfMonth = new Date(+year, +month - 1, 1);
// const endOfMonth = new Date(+year, +month, 0, 23, 59, 59);

// const events = await Event.findAll({
// where: {
// [Op.or]: [
// {
// start: {
// [Op.between]: [startOfMonth, endOfMonth],
// },
// },
// {
// end: {
// [Op.between]: [startOfMonth, endOfMonth],
// },
// },
// {
// [Op.and]: [
// {
// start: {
// [Op.lte]: startOfMonth,
// },
// },
// {
// end: {
// [Op.gte]: endOfMonth,
// },
// },
// ],
// },
// ],
// },
// });
// // redis 캐싱
// // TODO: 서버 시작 시 캐싱하므로 상관 없는 코드지만,
// // 사용해야 할 경우 연관된 연/월 key들 전부 캐싱 (시작 캐싱 함수처럼)
// await redisClient.hSet(`events:${year}`, month.toString(), JSON.stringify(events));
// return res.status(200).json(events);
} catch (error) {
res.status(500).json({ error: "서버 내부 에러", message: error.message });
}
};
4 changes: 2 additions & 2 deletions controllers/event/index.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { deleteBookmark, setBookmark } from "./event_bookmark.js";
import { setLike } from "./event_like.js";
import { getEvent, getEventAll } from "./event_search.js";
import { getEvent, getEventAll, getEventsByDate } from "./event_search.js";

export {
deleteBookmark, getEvent,
deleteBookmark, getEvent, getEventsByDate,
getEventAll, setBookmark, setLike
};
8 changes: 4 additions & 4 deletions models/admin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ interface AdminAttributes {
}

class Admin extends Model<AdminAttributes> implements AdminAttributes {
public id!: number;
public account!: string;
public password?: string;
public role!: '컴퓨터' | '소프트' | '관리자';
declare id: number;
declare account: string;
declare password: string;
declare role: '컴퓨터' | '소프트' | '관리자';
}

Admin.init(
Expand Down
Loading

0 comments on commit 285f1d5

Please sign in to comment.