From aca8dcdde5362ed57d4a1052635e2fd97932dbe8 Mon Sep 17 00:00:00 2001 From: sooieese Date: Wed, 6 Nov 2024 14:43:08 +0900 Subject: [PATCH 1/4] =?UTF-8?q?feat:=EC=82=AC=EC=A7=84=EC=A1=B0=ED=9A=8Cap?= =?UTF-8?q?i?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- controllers/photoController.js | 82 +++++++++++++++++++++++++++++++++- middlewares/location.js | 58 ++++++++++++++++++++++++ routes/photoRouter.js | 7 ++- 3 files changed, 145 insertions(+), 2 deletions(-) create mode 100644 middlewares/location.js diff --git a/controllers/photoController.js b/controllers/photoController.js index 7c04d92..ff3bea4 100644 --- a/controllers/photoController.js +++ b/controllers/photoController.js @@ -1,8 +1,10 @@ const Photo = require('../models/photo'); const PhotoTemp = require('../models/photoTemp'); const User = require('../models/user'); +const Photobooth = require('../models/photobooth'); const { deleteTemp } = require('../middlewares/uploadPhoto'); const { deleteImages } = require('../middlewares/s3'); +const { Sequelize, Op, fn, col } = require('sequelize'); const createTemp = async (req, res) => { try{ @@ -98,5 +100,83 @@ const deletePhoto = async (req, res) => { } }; +const getPhoto = async (req, res) => { + try { + const user_id = req.params.user_id; + const date = req.query.date ? new Date(req.query.date) : null; + const brand = req.query.brand ? req.query.brand : null; + const location = req.query.location ? req.query.location : null; + + let photos; + + if (date) { // 날짜 필터링 + console.log("date로 들어옴"); + const year = date.getFullYear(); + const month = date.getMonth() + 1; + + photos = await Photo.findAll({ + where: { + user_id: user_id, + [Op.and]: [ + Sequelize.where(fn('YEAR', col('date')), year), + Sequelize.where(fn('MONTH', col('date')), month) + ], + }, + order: [['date', 'DESC']], + attributes: ['image_url', 'photo_like'], + }); + } else if (brand) { // 브랜드 이름 필터링 + console.log("brand로 들어옴"); + photos = await Photo.findAll({ + where: { user_id: user_id }, + include: [ + { + model: Photobooth, + where: { brand: brand }, + attributes: [], + }, + ], + order: [['date', 'DESC']], + attributes: ['image_url', 'photo_like'], + }); + } else if (location && req.nearbyBoothIds && req.nearbyBoothIds.length > 0) { + // location 필터링 - 미들웨어 반환값 이용, 현재위치 시에는 location=true로 받아야함 + console.log("location으로 들어옴"); + photos = await Photo.findAll({ + where: { + user_id: user_id, + photobooth_id: { [Op.in]: req.nearbyBoothIds }, + }, + order: [['date', 'DESC']], + attributes: ['image_url', 'photo_like'], + }); + } else { + console.log("마지막 else로"); + // 필터링 없이 전체 조회 (날짜페이지에서 아무 선택 안했을 때) + photos = await Photo.findAll({ + where: { user_id: user_id }, + order: [['date', 'DESC']], + attributes: ['image_url', 'photo_like'], + }); + } + + // 데이터가 없는 경우 + if (photos.length === 0) { + return res.status(200).json({ + photonum: 0, + }); + } + + const response = photos.map((photo) => ({ + images: photo.image_url, + photo_like: photo.photo_like, + })); + + return res.status(200).json(response); + } catch (error) { + console.error("getPhoto error", error); + return res.status(500).json({ error: "Internal Server Error" }); + } + }; -module.exports = { createTemp, updateInfo, updateRecord, savePhoto, deletePhoto }; \ No newline at end of file +module.exports = { createTemp, updateInfo, updateRecord, savePhoto, deletePhoto, getPhoto }; \ No newline at end of file diff --git a/middlewares/location.js b/middlewares/location.js new file mode 100644 index 0000000..84fa81a --- /dev/null +++ b/middlewares/location.js @@ -0,0 +1,58 @@ +const { Sequelize, Op } = require('sequelize'); +const axios = require('axios'); +const Photobooth = require('../models/photobooth'); + +// 미들웨어로 근처 포토부스 위치를 반환 +const getNearbyBooths = async (req, res, next) => { + const { latitude = '37.6329741', longitude = '127.0798802', query } = req.query; // 기본 위치는 학교 위치로 설정 + const radius = 1000; // 반경 1km + + try { + let searchLatitude = parseFloat(latitude); + let searchLongitude = parseFloat(longitude); + + if (query) { + console.log('Search Query:', query); + // 키워드 검색 요청이 들어온 경우 카카오 지도 API를 사용하여 위치 검색 + const kakaoUrl = `https://dapi.kakao.com/v2/local/search/keyword.json?query=${query}`; + const kakaoSecretKey = process.env.KAKAO_ID; + + const response = await axios.get(kakaoUrl, { + headers: { + Authorization: `KakaoAK ${kakaoSecretKey}` + } + }); + + // 키워드와 가장 근접한 장소의 위경도를 중심으로 설정 + if (response.data.documents && response.data.documents.length > 0) { + const { x, y } = response.data.documents[0]; + searchLongitude = parseFloat(x); + searchLatitude = parseFloat(y); + console.log('Updated Coordinates from Keyword:', searchLongitude, searchLatitude); + } else { + console.log('No results found for the query, using default coordinates.'); + } + } + + // 반경 내 포토부스 조회 + const nearbyBooths = await Photobooth.findAll({ + where: Sequelize.where( + Sequelize.fn( + 'ST_Distance_Sphere', + Sequelize.literal(`POINT(${searchLongitude}, ${searchLatitude})`), + Sequelize.literal(`POINT(longitude, latitude)`) + ), + { [Op.lte]: radius } + ) + }); + + req.nearbyBoothIds = nearbyBooths.map((booth) => booth.id); // 포토부스 ID 목록만 저장 + console.log('Nearby Booth IDs:', req.nearbyBoothIds); + next(); + } catch (error) { + console.error('Error fetching nearby booths:', error); + res.status(500).json({ message: '서버 오류가 발생했습니다.' }); + } +}; + +module.exports = { getNearbyBooths }; diff --git a/routes/photoRouter.js b/routes/photoRouter.js index 9af611d..8e4813e 100644 --- a/routes/photoRouter.js +++ b/routes/photoRouter.js @@ -2,7 +2,8 @@ const express = require('express'); const router = express.Router(); const { uploadOneImageUrl, uploadOneImage } = require('../middlewares/s3'); const { uploadImageByQR } = require('../middlewares/uploadPhoto'); -const { createTemp, updateInfo, updateRecord, savePhoto, deletePhoto } = require('../controllers/photoController'); +const { getNearbyBooths } = require('../middlewares/location'); +const { createTemp, updateInfo, updateRecord, savePhoto, deletePhoto, getPhoto } = require('../controllers/photoController'); // 사진 등록용 라우트 1: 사용자id와 사진url 저장 (photoTemp 테이블) router.post('/temp/upload/qr', uploadImageByQR, uploadOneImageUrl, createTemp); // 1) QR 업로드 @@ -20,4 +21,8 @@ router.post('/save/:photoTemp_id', savePhoto); // 사진 삭제용 라우트 router.delete('/delete/:photo_id', deletePhoto); +// 앨범 조회용 라우트 +router.get('/:user_id', getNearbyBooths , getPhoto); + + module.exports = router; From eafe6e9e8d6d82fdf5095731029b9ccf986ca89f Mon Sep 17 00:00:00 2001 From: sooieese Date: Wed, 6 Nov 2024 14:59:31 +0900 Subject: [PATCH 2/4] =?UTF-8?q?fix:=20=EC=95=A8=EB=B2=94=EC=A1=B0=ED=9A=8C?= =?UTF-8?q?api?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- controllers/albumController.js | 84 ++++++++++++++++++++++++++++++++++ controllers/photoController.js | 82 +-------------------------------- index.js | 3 ++ routes/albumRouter.js | 9 ++++ routes/photoRouter.js | 6 +-- 5 files changed, 98 insertions(+), 86 deletions(-) create mode 100644 controllers/albumController.js create mode 100644 routes/albumRouter.js diff --git a/controllers/albumController.js b/controllers/albumController.js new file mode 100644 index 0000000..873a3d7 --- /dev/null +++ b/controllers/albumController.js @@ -0,0 +1,84 @@ +const Photo = require('../models/photo'); +const Photobooth = require('../models/photobooth'); +const { Sequelize, Op, fn, col } = require('sequelize'); + +const getPhoto = async (req, res) => { + try { + const user_id = req.params.user_id; + const date = req.query.date ? new Date(req.query.date) : null; + const brand = req.query.brand ? req.query.brand : null; + const location = req.query.location ? req.query.location : null; + + let photos; + + if (date) { // 날짜 필터링 + console.log("date로 들어옴"); + const year = date.getFullYear(); + const month = date.getMonth() + 1; + + photos = await Photo.findAll({ + where: { + user_id: user_id, + [Op.and]: [ + Sequelize.where(fn('YEAR', col('date')), year), + Sequelize.where(fn('MONTH', col('date')), month) + ], + }, + order: [['date', 'DESC']], + attributes: ['image_url', 'photo_like'], + }); + } else if (brand) { // 브랜드 이름 필터링 + console.log("brand로 들어옴"); + photos = await Photo.findAll({ + where: { user_id: user_id }, + include: [ + { + model: Photobooth, + where: { brand: brand }, + attributes: [], + }, + ], + order: [['date', 'DESC']], + attributes: ['image_url', 'photo_like'], + }); + } else if (location && req.nearbyBoothIds && req.nearbyBoothIds.length > 0) { + // location 필터링 - 미들웨어 반환값 이용, 현재위치 시에는 location=true로 받아야함 + console.log("location으로 들어옴"); + photos = await Photo.findAll({ + where: { + user_id: user_id, + photobooth_id: { [Op.in]: req.nearbyBoothIds }, + }, + order: [['date', 'DESC']], + attributes: ['image_url', 'photo_like'], + }); + } else { + console.log("마지막 else로"); + // 필터링 없이 전체 조회 (날짜페이지에서 아무 선택 안했을 때) + photos = await Photo.findAll({ + where: { user_id: user_id }, + order: [['date', 'DESC']], + attributes: ['image_url', 'photo_like'], + }); + } + + // 데이터가 없는 경우 + if (photos.length === 0) { + return res.status(200).json({ + photonum: 0, + }); + } + + const response = photos.map((photo) => ({ + images: photo.image_url, + photo_like: photo.photo_like, + })); + + return res.status(200).json(response); + } catch (error) { + console.error("getPhoto error", error); + return res.status(500).json({ error: "Internal Server Error" }); + } + }; + + module.exports = {getPhoto} \ No newline at end of file diff --git a/controllers/photoController.js b/controllers/photoController.js index ff3bea4..3ac337e 100644 --- a/controllers/photoController.js +++ b/controllers/photoController.js @@ -4,7 +4,6 @@ const User = require('../models/user'); const Photobooth = require('../models/photobooth'); const { deleteTemp } = require('../middlewares/uploadPhoto'); const { deleteImages } = require('../middlewares/s3'); -const { Sequelize, Op, fn, col } = require('sequelize'); const createTemp = async (req, res) => { try{ @@ -100,83 +99,4 @@ const deletePhoto = async (req, res) => { } }; -const getPhoto = async (req, res) => { - try { - const user_id = req.params.user_id; - const date = req.query.date ? new Date(req.query.date) : null; - const brand = req.query.brand ? req.query.brand : null; - const location = req.query.location ? req.query.location : null; - - let photos; - - if (date) { // 날짜 필터링 - console.log("date로 들어옴"); - const year = date.getFullYear(); - const month = date.getMonth() + 1; - - photos = await Photo.findAll({ - where: { - user_id: user_id, - [Op.and]: [ - Sequelize.where(fn('YEAR', col('date')), year), - Sequelize.where(fn('MONTH', col('date')), month) - ], - }, - order: [['date', 'DESC']], - attributes: ['image_url', 'photo_like'], - }); - } else if (brand) { // 브랜드 이름 필터링 - console.log("brand로 들어옴"); - photos = await Photo.findAll({ - where: { user_id: user_id }, - include: [ - { - model: Photobooth, - where: { brand: brand }, - attributes: [], - }, - ], - order: [['date', 'DESC']], - attributes: ['image_url', 'photo_like'], - }); - } else if (location && req.nearbyBoothIds && req.nearbyBoothIds.length > 0) { - // location 필터링 - 미들웨어 반환값 이용, 현재위치 시에는 location=true로 받아야함 - console.log("location으로 들어옴"); - photos = await Photo.findAll({ - where: { - user_id: user_id, - photobooth_id: { [Op.in]: req.nearbyBoothIds }, - }, - order: [['date', 'DESC']], - attributes: ['image_url', 'photo_like'], - }); - } else { - console.log("마지막 else로"); - // 필터링 없이 전체 조회 (날짜페이지에서 아무 선택 안했을 때) - photos = await Photo.findAll({ - where: { user_id: user_id }, - order: [['date', 'DESC']], - attributes: ['image_url', 'photo_like'], - }); - } - - // 데이터가 없는 경우 - if (photos.length === 0) { - return res.status(200).json({ - photonum: 0, - }); - } - - const response = photos.map((photo) => ({ - images: photo.image_url, - photo_like: photo.photo_like, - })); - - return res.status(200).json(response); - } catch (error) { - console.error("getPhoto error", error); - return res.status(500).json({ error: "Internal Server Error" }); - } - }; - -module.exports = { createTemp, updateInfo, updateRecord, savePhoto, deletePhoto, getPhoto }; \ No newline at end of file +module.exports = { createTemp, updateInfo, updateRecord, savePhoto, deletePhoto }; \ No newline at end of file diff --git a/index.js b/index.js index 149e728..87d530c 100644 --- a/index.js +++ b/index.js @@ -62,6 +62,9 @@ app.use('/api/photo', photoRouter); const boothLikeRouter = require('./routes/boothLikeRouter'); app.use('/api/booth', boothLikeRouter); +const albumRouter = require('./routes/albumRouter'); +app.use('/api/album', albumRouter); + // 스웨거 세팅 const swaggerUi = require('swagger-ui-express'); const YAML = require('yamljs'); diff --git a/routes/albumRouter.js b/routes/albumRouter.js new file mode 100644 index 0000000..00d19a4 --- /dev/null +++ b/routes/albumRouter.js @@ -0,0 +1,9 @@ +const express = require('express'); +const router = express.Router(); +const { getNearbyBooths } = require('../middlewares/location'); +const {getPhoto} = require('../controllers/albumController'); + +// 앨범 조회용 라우트 +router.get('/:user_id', getNearbyBooths , getPhoto); + +module.exports = router; \ No newline at end of file diff --git a/routes/photoRouter.js b/routes/photoRouter.js index 8e4813e..ce871b9 100644 --- a/routes/photoRouter.js +++ b/routes/photoRouter.js @@ -3,7 +3,7 @@ const router = express.Router(); const { uploadOneImageUrl, uploadOneImage } = require('../middlewares/s3'); const { uploadImageByQR } = require('../middlewares/uploadPhoto'); const { getNearbyBooths } = require('../middlewares/location'); -const { createTemp, updateInfo, updateRecord, savePhoto, deletePhoto, getPhoto } = require('../controllers/photoController'); +const { createTemp, updateInfo, updateRecord, savePhoto, deletePhoto } = require('../controllers/photoController'); // 사진 등록용 라우트 1: 사용자id와 사진url 저장 (photoTemp 테이블) router.post('/temp/upload/qr', uploadImageByQR, uploadOneImageUrl, createTemp); // 1) QR 업로드 @@ -21,8 +21,4 @@ router.post('/save/:photoTemp_id', savePhoto); // 사진 삭제용 라우트 router.delete('/delete/:photo_id', deletePhoto); -// 앨범 조회용 라우트 -router.get('/:user_id', getNearbyBooths , getPhoto); - - module.exports = router; From 8edfb80ba10ac79f757609a5f020e6836b9bc66c Mon Sep 17 00:00:00 2001 From: sooieese Date: Wed, 6 Nov 2024 15:13:23 +0900 Subject: [PATCH 3/4] =?UTF-8?q?feat:=EC=82=AC=EC=A7=84=EC=A1=B0=ED=9A=8C?= =?UTF-8?q?=20api?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- controllers/albumController.js | 4 +-- controllers/photoController.js | 49 +++++++++++++++++++++++++++++++++- routes/albumRouter.js | 4 +-- routes/photoRouter.js | 6 +++-- 4 files changed, 56 insertions(+), 7 deletions(-) diff --git a/controllers/albumController.js b/controllers/albumController.js index 873a3d7..17b8996 100644 --- a/controllers/albumController.js +++ b/controllers/albumController.js @@ -2,7 +2,7 @@ const Photo = require('../models/photo'); const Photobooth = require('../models/photobooth'); const { Sequelize, Op, fn, col } = require('sequelize'); -const getPhoto = async (req, res) => { +const getAlbum = async (req, res) => { try { const user_id = req.params.user_id; const date = req.query.date ? new Date(req.query.date) : null; @@ -81,4 +81,4 @@ const getPhoto = async (req, res) => { } }; - module.exports = {getPhoto} \ No newline at end of file + module.exports = {getAlbum} \ No newline at end of file diff --git a/controllers/photoController.js b/controllers/photoController.js index 3ac337e..7c46b3e 100644 --- a/controllers/photoController.js +++ b/controllers/photoController.js @@ -99,4 +99,51 @@ const deletePhoto = async (req, res) => { } }; -module.exports = { createTemp, updateInfo, updateRecord, savePhoto, deletePhoto }; \ No newline at end of file +const getPhoto = async (req, res) => { + try { + const { user_id, photo_id } = req.params; + + const photo = await Photo.findOne({ + where: { + user_id: user_id, + id: photo_id, + }, + include: [ + { + model: Photobooth, + attributes: ['name'], + }, + ], + attributes: [ + 'date', + 'image_url', + 'record', + 'hashtag_1', + 'hashtag_2', + 'hashtag_3', + 'photo_like', + ], + }); + + // 사진이 없는 경우 + if (!photo) { + return res.status(404).json({ message: '사진을 찾을 수 없습니다.' }); + } + + const response = { + date: photo.date, + photobooth_name: photo.Photobooth ? photo.Photobooth.name : null, + hashtags: [photo.hashtag_1, photo.hashtag_2, photo.hashtag_3].filter(Boolean), // 빈 해시태그는 제외 + image_url: photo.image_url, + record: photo.record, + photo_like : photo.photo_like, + }; + + return res.status(200).json(response); + } catch (error) { + console.error('Error photo details', error); + return res.status(500).json({ error: 'Internal Server Error' }); + } + }; + +module.exports = { createTemp, updateInfo, updateRecord, savePhoto, deletePhoto, getPhoto }; \ No newline at end of file diff --git a/routes/albumRouter.js b/routes/albumRouter.js index 00d19a4..ce061cd 100644 --- a/routes/albumRouter.js +++ b/routes/albumRouter.js @@ -1,9 +1,9 @@ const express = require('express'); const router = express.Router(); const { getNearbyBooths } = require('../middlewares/location'); -const {getPhoto} = require('../controllers/albumController'); +const {getAlbum} = require('../controllers/albumController'); // 앨범 조회용 라우트 -router.get('/:user_id', getNearbyBooths , getPhoto); +router.get('/:user_id', getNearbyBooths , getAlbum); module.exports = router; \ No newline at end of file diff --git a/routes/photoRouter.js b/routes/photoRouter.js index ce871b9..23b92cf 100644 --- a/routes/photoRouter.js +++ b/routes/photoRouter.js @@ -2,8 +2,7 @@ const express = require('express'); const router = express.Router(); const { uploadOneImageUrl, uploadOneImage } = require('../middlewares/s3'); const { uploadImageByQR } = require('../middlewares/uploadPhoto'); -const { getNearbyBooths } = require('../middlewares/location'); -const { createTemp, updateInfo, updateRecord, savePhoto, deletePhoto } = require('../controllers/photoController'); +const { createTemp, updateInfo, updateRecord, savePhoto, deletePhoto, getPhoto } = require('../controllers/photoController'); // 사진 등록용 라우트 1: 사용자id와 사진url 저장 (photoTemp 테이블) router.post('/temp/upload/qr', uploadImageByQR, uploadOneImageUrl, createTemp); // 1) QR 업로드 @@ -21,4 +20,7 @@ router.post('/save/:photoTemp_id', savePhoto); // 사진 삭제용 라우트 router.delete('/delete/:photo_id', deletePhoto); +// 사진 조회용 라우트 +router.get('/:user_id/:photo_id', getPhoto); + module.exports = router; From 57b9bb026c4316c3e0abd762136e09dc88fe1e0e Mon Sep 17 00:00:00 2001 From: sooieese Date: Wed, 6 Nov 2024 15:48:23 +0900 Subject: [PATCH 4/4] =?UTF-8?q?fix=20:=20=EC=82=AC=EC=A7=84=EC=A1=B0?= =?UTF-8?q?=ED=9A=8Capi=20=EA=B2=BD=EB=A1=9C=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- controllers/photoController.js | 3 +-- routes/photoRouter.js | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/controllers/photoController.js b/controllers/photoController.js index 7c46b3e..76fc3e1 100644 --- a/controllers/photoController.js +++ b/controllers/photoController.js @@ -101,11 +101,10 @@ const deletePhoto = async (req, res) => { const getPhoto = async (req, res) => { try { - const { user_id, photo_id } = req.params; + const {photo_id} = req.params; const photo = await Photo.findOne({ where: { - user_id: user_id, id: photo_id, }, include: [ diff --git a/routes/photoRouter.js b/routes/photoRouter.js index 23b92cf..2357b8f 100644 --- a/routes/photoRouter.js +++ b/routes/photoRouter.js @@ -21,6 +21,6 @@ router.post('/save/:photoTemp_id', savePhoto); router.delete('/delete/:photo_id', deletePhoto); // 사진 조회용 라우트 -router.get('/:user_id/:photo_id', getPhoto); +router.get('/:photo_id', getPhoto); module.exports = router;