Skip to content

Commit

Permalink
feat: user router 까지 생성
Browse files Browse the repository at this point in the history
  • Loading branch information
Hyun-git committed Jun 17, 2024
1 parent 6c6a40b commit d0a22fe
Show file tree
Hide file tree
Showing 4 changed files with 296 additions and 11 deletions.
143 changes: 143 additions & 0 deletions src/controller/user.controller.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { NextFunction, Request, Response } from 'express';
import _ from 'lodash';
import * as UserService from '../service/user.service';
import CustomError from '../errors/CustomError';
import { HttpCode } from '../errors/HttpCode';

Expand All @@ -15,3 +16,145 @@ const createUser = async (req: Request, res: Response, next: NextFunction) => {
return next(new CustomError(err.message, err.status));
}
};

const updateLastSeenMeme = async (req: Request, res: Response, next: NextFunction) => {
if (!_.has(req.body, 'deviceID')) {
return next(new CustomError(`'deviceID' field should be provided`, HttpCode.BAD_REQUEST));
}

if (!_.has(req.body, 'memeID')) {
return next(new CustomError(`'memeID' field should be provided`, HttpCode.BAD_REQUEST));
}

try {
const user = await UserService.updateLastSeenMeme(req.body.deviceID, req.body.memeID);
return res.json({ ...user });
} catch (err) {
return next(new CustomError(err.message, err.status));
}
}

const createMemeReaction = async (req: Request, res: Response, next: NextFunction) => {
if (!_.has(req.body, 'deviceID')) {
return next(new CustomError(`'deviceID' field should be provided`, HttpCode.BAD_REQUEST));
}

if (!_.has(req.body, 'memeID')) {
return next(new CustomError(`'memeID' field should be provided`, HttpCode.BAD_REQUEST));
}

try {
const meme = await UserService.createMemeReaction(req.body.deviceID, req.body.memeID);
return res.json({ ...meme });
} catch (err) {
return next(new CustomError(err.message, err.status));
}
}

const createMemeSave = async (req: Request, res: Response, next: NextFunction) => {
if (!_.has(req.body, 'deviceID')) {
return next(new CustomError(`'deviceID' field should be provided`, HttpCode.BAD_REQUEST));
}

if (!_.has(req.body, 'memeID')) {
return next(new CustomError(`'memeID' field should be provided`, HttpCode.BAD_REQUEST));
}

try {
const meme = await UserService.createMemeSave(req.body.deviceID, req.body.memeID);
return res.json({ ...meme });
} catch (err) {
return next(new CustomError(err.message, err.status));
}
}

const createMemeShare = async (req: Request, res: Response, next: NextFunction) => {
if (!_.has(req.body, 'deviceID')) {
return next(new CustomError(`'deviceID' field should be provided`, HttpCode.BAD_REQUEST));
}

if (!_.has(req.body, 'memeID')) {
return next(new CustomError(`'memeID' field should be provided`, HttpCode.BAD_REQUEST));
}

try {
const meme = await UserService.createMemeShare(req.body.deviceID, req.body.memeID);
return res.json({ ...meme });
} catch (err) {
return next(new CustomError(err.message, err.status));
}
}

const deleteMemeReaction = async (req: Request, res: Response, next: NextFunction) => {
if (!_.has(req.body, 'deviceID')) {
return next(new CustomError(`'deviceID' field should be provided`, HttpCode.BAD_REQUEST));
}

if (!_.has(req.body, 'memeID')) {
return next(new CustomError(`'memeID' field should be provided`, HttpCode.BAD_REQUEST));
}

try {
const meme = await UserService.deleteMemeReaction(req.body.deviceID, req.body.memeID);
return res.json({ ...meme });
} catch (err) {
return next(new CustomError(err.message, err.status));
}
}

const deleteMemeSave = async (req: Request, res: Response, next: NextFunction) => {
if (!_.has(req.body, 'deviceID')) {
return next(new CustomError(`'deviceID' field should be provided`, HttpCode.BAD_REQUEST));
}

if (!_.has(req.body, 'memeID')) {
return next(new CustomError(`'memeID' field should be provided`, HttpCode.BAD_REQUEST));
}

try {
const result = await UserService.deleteMemeSave(req.body.deviceID, req.body.memeID);
return res.json({ result });
} catch (err) {
return next(new CustomError(err.message, err.status));
}
}

const getLastSeenMeme = async (req: Request, res: Response, next: NextFunction) => {
if (!_.has(req.params, 'deviceID')) {
return next(new CustomError(`'deviceID' field should be provided`, HttpCode.BAD_REQUEST));
}

try {
const memeList = await UserService.getLastSeenMeme(req.params.deviceID);
return res.json({ memeList });
} catch (err) {
return next(new CustomError(err.message, err.status));
}
}

const getSavedMeme = async (req: Request, res: Response, next: NextFunction) => {
if (!_.has(req.params, 'deviceID')) {
return next(new CustomError(`'deviceID' field should be provided`, HttpCode.BAD_REQUEST));
}

try {
const memeList = await UserService.getSavedMeme(req.params.deviceID);
return res.json({ memeList });
} catch (err) {
return next(new CustomError(err.message, err.status));
}
}

export {
createUser,
updateLastSeenMeme,
createMemeReaction,
createMemeSave,
createMemeShare,
deleteMemeReaction,
deleteMemeSave,
getLastSeenMeme,
getSavedMeme,
};


6 changes: 6 additions & 0 deletions src/model/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@ export interface IUser {
isDeleted: boolean;
}

export interface IUserInfos extends IUser {
memeShareCount: number;
memeReactionCount: number;
memeSaveCount: number;
}

const UserSchema: Schema = new Schema(
{
deviceID: { type: String, required: true },
Expand Down
17 changes: 14 additions & 3 deletions src/routes/user.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,21 @@
import express from 'express';
import * as UserController from '../controller/user.controller';

const router = express.Router();

router.post('/'); // user 생성
router.get('/:userId'); // user 조회
router.post('/',UserController.createUser); // user 생성
router.put('/lastSeenMeme',UserController.updateLastSeenMeme); // user가 본 meme 업데이트

router.get('/:userId/save'); // user가 저장한 meme 조회

router.post('/reaction',UserController.createMemeReaction); // user의 reaction 생성
router.post('/save',UserController.createMemeSave); // user의 save 생성
router.post('/share',UserController.createMemeShare); // user의 share 생성


router.delete('/reaction',UserController.deleteMemeReaction); // user의 reaction 삭제
router.delete('/save',UserController.deleteMemeSave); // user의 save 삭제

router.get('/:userId/lastSeenMeme', UserController.getLastSeenMeme); // user가 본 meme 조회
router.get('/:userId/save', UserController.getSavedMeme); // user가 저장한 meme 조회

export default router;
141 changes: 133 additions & 8 deletions src/service/user.service.ts
Original file line number Diff line number Diff line change
@@ -1,29 +1,32 @@
import { logger } from '../util/logger';
import { IUser, UserModel } from '../model/user';
import { IUser, IUserInfos, UserModel } from '../model/user';
import _ from 'lodash';
import CustomError from '../errors/CustomError';
import { HttpCode } from '../errors/HttpCode';
import { IMeme, MemeModel } from 'src/model/meme';
import { MemeReactionModel } from 'src/model/memeReaction';
import { MemeSaveModel } from 'src/model/memeSave';
import { MemeShareModel } from 'src/model/memeShare';
import { IMeme, MemeModel } from '../model/meme';
import { MemeReactionModel } from '../model/memeReaction';
import { MemeSaveModel } from '../model/memeSave';
import { MemeShareModel } from '../model/memeShare';

async function createUser(deviceID: string): Promise<IUser> {
async function createUser(deviceID: string): Promise<IUserInfos> {
try {
const existedUser = await UserModel.findOne(
{ deviceID, isDeleted: false },
{ _id: 0, createdAt: 0, updatedAt: 0 },
);
if (existedUser) {
return existedUser.toObject();
const memeShareCount = await MemeShareModel.countDocuments({ deviceID, isDeleted: false });
const memeReactionCount = await MemeReactionModel.countDocuments({ deviceID, isDeleted: false });
const memeSaveCount = await MemeSaveModel.countDocuments({ deviceID, isDeleted: false });
return { ...existedUser.toObject(), memeShareCount, memeReactionCount, memeSaveCount };
}
const user = await UserModel.create({
deviceID,
});

await user.save();
logger.info(`Created user - deviceID(${JSON.stringify(user.toObject)})`);
return user.toObject();
return { ...user.toObject(), memeShareCount: 0, memeReactionCount: 0, memeSaveCount: 0 };
} catch (err) {
logger.info(`Failed to create User`);
}
Expand Down Expand Up @@ -147,3 +150,125 @@ async function createMemeShare(deviceID: string, memeID: string): Promise<Boolea
logger.error(`Failed create memeSave`, err.message);
}
}

async function deleteMemeReaction(deviceID: string, memeID: string): Promise<IMeme> {
try {
const meme = await MemeModel.findOne({ memeID, isDeleted: false });
const user = await UserModel.findOne({ deviceID, isDeleted: false });

if (_.isNull(meme)) {
throw new CustomError(`Failed to get Meme - memeID(${memeID})`, HttpCode.NOT_FOUND);
}

if (_.isNull(user)) {
throw new CustomError(`Failed to get User - deviceID(${deviceID})`, HttpCode.NOT_FOUND);
}

const memeReaction = await MemeReactionModel.findOne({ deviceID, memeID, isDeleted: false });
if (_.isNull(memeReaction)) {
logger.info(`Already delete memeReaction - deviceID(${deviceID}), memeID(${memeID}`);
return meme;
}
await MemeReactionModel.findOneAndUpdate(
{ deviceID, memeID },
{
isDeleted: true,
},
).lean();

const newReactionCount = meme.reaction - 1;

const updatedMeme = await MemeModel.findOneAndUpdate(
{ memeID },
{
reaction: newReactionCount,
},
{
projection: { _id: 0, createdAt: 0, updatedAt: 0 },
returnDocument: 'after',
},
).lean();

return updatedMeme;
} catch (err) {
logger.error(`Failed delete memeReaction`, err.message);
}
}

async function deleteMemeSave(deviceID: string, memeID: string): Promise<Boolean> {
try {
const meme = await MemeModel.findOne({ memeID, isDeleted: false });
const user = await UserModel.findOne({ deviceID, isDeleted: false });

if (_.isNull(meme)) {
throw new CustomError(`Failed to get Meme - memeID(${memeID})`, HttpCode.NOT_FOUND);
}

if (_.isNull(user)) {
throw new CustomError(`Failed to get User - deviceID(${deviceID})`, HttpCode.NOT_FOUND);
}

const memeSave = await MemeSaveModel.findOne({ deviceID, memeID, isDeleted: false });

if (_.isNull(memeSave)) {
logger.info(`Already delete memeSave - deviceID(${deviceID}), memeID(${memeID}`);
return false;
}
await MemeSaveModel.findOneAndUpdate(
{ deviceID, memeID },
{
isDeleted: true,
},
).lean();

return true;
}
catch (err) {
logger.error(`Failed delete memeSave`, err.message);
}
}

async function getLastSeenMeme(deviceID: string): Promise<IMeme[]> {
try {
const user = await UserModel.findOne({ deviceID, isDeleted: false });
if (_.isNull(user)) {
throw new CustomError(`Cannot find User`, HttpCode.NOT_FOUND);
}

const lastSeenMeme = user.lastSeenMeme;
const memeList = await MemeModel.find({ memeID: { $in: lastSeenMeme } }).lean();

return memeList;
} catch (err) {
logger.error(`Failed get lastSeenMeme`, err.message);
}
}

async function getSavedMeme(deviceID: string): Promise<IMeme[]> {
try {
const user = await UserModel.findOne({ deviceID, isDeleted: false });
if (_.isNull(user)) {
throw new CustomError(`Cannot find User`, HttpCode.NOT_FOUND);
}

const savedMeme = await MemeSaveModel.find({ deviceID, isDeleted: false }).lean();

const memeList = await MemeModel.find({ memeID: { $in: savedMeme.map((meme) => meme.memeID) } }).lean();

return memeList;
} catch (err) {
logger.error(`Failed get savedMeme`, err.message);
}
}

export {
createUser,
updateLastSeenMeme,
createMemeReaction,
createMemeSave,
createMemeShare,
deleteMemeReaction,
deleteMemeSave,
getLastSeenMeme,
getSavedMeme,
};

0 comments on commit d0a22fe

Please sign in to comment.