diff --git a/src/controllers/photo-controller.js b/src/controllers/photo-controller.js index 35112c7..d819c8f 100644 --- a/src/controllers/photo-controller.js +++ b/src/controllers/photo-controller.js @@ -1,4 +1,6 @@ const PhotoService = require('../services/photo-service'); +const ReviewService = require('../services/review-service'); + const resSuccess = require('./helpers/response'); const getUser = require('./helpers/get-user'); const { paginationLimitMap } = require('../constants/pagination-constant'); @@ -43,6 +45,32 @@ const photoController = { next(err); } }, + postReview: async (req, res, next) => { + try { + const { useful } = req.body; + const userId = getUser(req).id; + const photoId = req.params.id; + + const reviewService = new ReviewService({ logger: req.logger }); + const review = await reviewService.postReview(userId, photoId, useful); + res.status(200).json(resSuccess(review)); + } catch (err) { + next(err); + } + }, + postUnreview: async (req, res, next) => { + try { + const { useful } = req.body; + const userId = getUser(req).id; + const photoId = req.params.id; + + const reviewService = new ReviewService({ logger: req.logger }); + const review = await reviewService.postUnreview(userId, photoId, useful); + res.status(200).json(resSuccess(review)); + } catch (err) { + next(err); + } + }, }; module.exports = photoController; diff --git a/src/errors/review-error.js b/src/errors/review-error.js new file mode 100644 index 0000000..2b2a977 --- /dev/null +++ b/src/errors/review-error.js @@ -0,0 +1,20 @@ +const reviewErrorMap = { + alreadyReview: { + message: '你已經評價過此照片', + code: 're001', + }, + notReviewYet: { + message: '你未曾評價過此照片', + code: 're002', + }, + alreadyUnReview: { + message: '你早已經取消此照片的評價', + code: 're003', + }, + badUnReviewRequest: { + message: '你想取消的評價與之前評價的不同', + code: 're004', + }, +}; + +module.exports = reviewErrorMap; diff --git a/src/models/review/index.js b/src/models/review/index.js index fa7d4d0..0d0b39b 100644 --- a/src/models/review/index.js +++ b/src/models/review/index.js @@ -30,6 +30,40 @@ class ReviewModel { return photoWithReviewCount; } + async updateReview(userId, photoId, useful) { + const review = await prisma.reviews.update({ + where: { + userId_photoId: { + userId: Number(userId), + photoId: Number(photoId), + }, + }, + data: { useful }, + select: { + id: true, + userId: true, + photoId: true, + useful: true, + }, + }); + return review; + } + async getReview(userId, photoId) { + const review = await prisma.reviews.findUnique({ + where: { + userId_photoId: { + userId: Number(userId), + photoId: Number(photoId), + }, + }, + select: { + userId: true, + photoId: true, + useful: true, + }, + }); + return review; + } async _truncate() { await prisma.reviews.deleteMany({}); } diff --git a/src/routes/modules/photo.js b/src/routes/modules/photo.js index c064fdb..f4ee665 100644 --- a/src/routes/modules/photo.js +++ b/src/routes/modules/photo.js @@ -5,6 +5,8 @@ const { authenticated } = require('../../middleware/auth'); const router = express.Router(); +router.post('/:id/reviews', authenticated, photoController.postReview); +router.post('/:id/unreviews', authenticated, photoController.postUnreview); router.get('/', photoController.getPhotos); router.post('/', authenticated, uploadImages, photoController.postPhotos); diff --git a/src/services/review-service.js b/src/services/review-service.js new file mode 100644 index 0000000..b3438d6 --- /dev/null +++ b/src/services/review-service.js @@ -0,0 +1,74 @@ +const { isEmpty, isNil } = require('ramda'); +const BaseService = require('./base'); +const ReviewModel = require('../models/review/index'); +const PhotoModel = require('../models/photo'); + +const GeneralError = require('../errors/error/general-error'); +const getPhotoErrorMap = require('../errors/get-photo-error'); +const reviewErrorMap = require('../errors//review-error'); + +class ReviewService extends BaseService { + async postReview(userId, photoId, useful) { + // check space exist + const photoModel = new PhotoModel(); + const photoCheck = await photoModel.getPhoto(photoId); + if (!photoCheck) throw new GeneralError(getPhotoErrorMap['photoNotFound']); + + // check existed review + const reviewModel = new ReviewModel(); + const review = await reviewModel.getReview(userId, photoId); + + // if already review (not cancel) + if (review && !isEmpty(review.useful)) + throw new GeneralError(reviewErrorMap['alreadyReview']); + + // if already review but cancel + if (review && isEmpty(review.useful)) { + const updateReview = await reviewModel.updateReview( + userId, + photoId, + useful + ); + + this.logger.debug('update a review', { updateReview }); + + return updateReview; + } + + // if review not exist + const newReview = await reviewModel.createReview(userId, photoId, useful); + + this.logger.debug('create a review', { newReview }); + + return newReview; + } + async postUnreview(userId, photoId, useful) { + // check space exist + const photoModel = new PhotoModel(); + const photoCheck = await photoModel.getPhoto(photoId); + if (!photoCheck) throw new GeneralError(getPhotoErrorMap['photoNotFound']); + + // check existed review + const reviewModel = new ReviewModel(); + const review = await reviewModel.getReview(userId, photoId); + + // if review not existed + if (isNil(review)) throw new GeneralError(reviewErrorMap['notReviewYet']); + + // if review already be canceled + if (review && isEmpty(review.useful)) + throw new GeneralError(reviewErrorMap['alreadyUnReview']); + + // if unreview's useful not equal reviewed before + if (review && useful !== review.useful) + throw new GeneralError(reviewErrorMap['badUnReviewRequest']); + + const unReview = await reviewModel.updateReview(userId, photoId, ''); + + this.logger.debug('update a review', { unReview }); + + return unReview; + } +} + +module.exports = ReviewService;