a
This commit is contained in:
commit
40266cc6e5
191 changed files with 5022 additions and 0 deletions
0
Backend/services/__init__.py
Normal file
0
Backend/services/__init__.py
Normal file
74
Backend/services/avatar_service.py
Normal file
74
Backend/services/avatar_service.py
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
from typing import Optional
|
||||
from ..schemas.avatar import (
|
||||
AvatarUpdate,
|
||||
Avatar,
|
||||
AvatarOptions,
|
||||
AvatarType,
|
||||
TopClothesType,
|
||||
BottomClothesType,
|
||||
)
|
||||
from ..utils.db import execute, fetch_one
|
||||
from ..utils.queries.avatar import AvatarQueries
|
||||
|
||||
|
||||
class AvatarService:
|
||||
|
||||
@staticmethod
|
||||
async def init_db():
|
||||
await execute(AvatarQueries.CREATE_TABLE)
|
||||
|
||||
async def get_or_create_avatar(self, user_id: int) -> Avatar:
|
||||
avatar = await self.get_user_avatar(user_id)
|
||||
if not avatar:
|
||||
avatar = await self.create_default_avatar(user_id)
|
||||
return avatar
|
||||
|
||||
async def get_user_avatar(self, user_id: int) -> Optional[Avatar]:
|
||||
row = await fetch_one(AvatarQueries.SELECT_USER_AVATAR, (user_id,))
|
||||
if not row:
|
||||
return None
|
||||
return Avatar(**row)
|
||||
|
||||
async def create_default_avatar(self, user_id: int) -> Avatar:
|
||||
await execute(
|
||||
AvatarQueries.INSERT_AVATAR,
|
||||
(
|
||||
user_id,
|
||||
AvatarType.MALE.value,
|
||||
TopClothesType.ANA_CLOTHES.value,
|
||||
BottomClothesType.JEANS.value,
|
||||
),
|
||||
)
|
||||
return await self.get_user_avatar(user_id)
|
||||
|
||||
async def update_avatar(self, user_id: int, avatar_data: AvatarUpdate) -> Avatar:
|
||||
|
||||
update_fields = []
|
||||
params = []
|
||||
|
||||
if avatar_data.avatar_type is not None:
|
||||
update_fields.append("avatar_type = ?")
|
||||
params.append(avatar_data.avatar_type.value)
|
||||
if avatar_data.top_clothe_type is not None:
|
||||
update_fields.append("top_clothe_type = ?")
|
||||
params.append(avatar_data.top_clothe_type.value)
|
||||
if avatar_data.bottom_clothe_type is not None:
|
||||
update_fields.append("bottom_clothe_type = ?")
|
||||
params.append(avatar_data.bottom_clothe_type.value)
|
||||
|
||||
if update_fields:
|
||||
query = AvatarQueries.UPDATE_AVATAR.format(fields=", ".join(update_fields))
|
||||
params.append(user_id)
|
||||
await execute(query, tuple(params))
|
||||
|
||||
return await self.get_user_avatar(user_id)
|
||||
|
||||
async def get_avatar_options(self) -> AvatarOptions:
|
||||
return AvatarOptions(
|
||||
avatar_types=list(AvatarType),
|
||||
top_clothe_types=list(TopClothesType),
|
||||
bottom_clothe_types=list(BottomClothesType),
|
||||
)
|
||||
|
||||
async def get_avatar_by_userId(self, user_id: int) -> Optional[Avatar]:
|
||||
return await self.get_user_avatar(user_id)
|
||||
144
Backend/services/diary_service.py
Normal file
144
Backend/services/diary_service.py
Normal file
|
|
@ -0,0 +1,144 @@
|
|||
import os
|
||||
from datetime import datetime
|
||||
from typing import List, Optional
|
||||
from fastapi import UploadFile
|
||||
from ..schemas.diary import DiaryCreate, DiaryUpdate, Diary
|
||||
from ..utils.db import execute, fetch_one, fetch_all
|
||||
from ..utils.image_processor import ImageProcessor
|
||||
from ..utils.queries.diary import DiaryQueries
|
||||
|
||||
|
||||
class DiaryService:
|
||||
def __init__(self):
|
||||
self.image_processor = ImageProcessor()
|
||||
self.upload_dir = "uploads/diary"
|
||||
os.makedirs(self.upload_dir, exist_ok=True)
|
||||
|
||||
@staticmethod
|
||||
async def init_db():
|
||||
await execute(DiaryQueries.CREATE_TABLE)
|
||||
|
||||
async def create_diary(
|
||||
self, user_id: int, diary_data: DiaryCreate, files: List[UploadFile]
|
||||
) -> Diary:
|
||||
# image_path 는 ,로 구분 되어 있음
|
||||
image_path = ""
|
||||
if files is not None:
|
||||
for file in files:
|
||||
image_path += (
|
||||
","
|
||||
+ await self.image_processor.write_file_and_get_image_path(
|
||||
file, self.upload_dir
|
||||
)
|
||||
)
|
||||
image_path = image_path[1:]
|
||||
|
||||
query = DiaryQueries.INSERT_DIARY
|
||||
|
||||
created_at = datetime.now()
|
||||
await execute(
|
||||
query,
|
||||
(
|
||||
user_id,
|
||||
diary_data.title,
|
||||
diary_data.content,
|
||||
image_path,
|
||||
diary_data.category,
|
||||
created_at,
|
||||
False,
|
||||
False,
|
||||
),
|
||||
)
|
||||
|
||||
row = await fetch_one(
|
||||
DiaryQueries.SELECT_LATEST_USER_DIARY,
|
||||
(user_id,),
|
||||
)
|
||||
|
||||
return Diary(**row)
|
||||
|
||||
async def get_user_diaries(
|
||||
self, user_id: int, skip: int = 0, limit: int = 20, category: str = None
|
||||
) -> List[Diary]:
|
||||
if category:
|
||||
query = DiaryQueries.SELECT_USER_DIARIES_BY_CATEGORY
|
||||
rows = await fetch_all(query, (user_id, category, limit, skip))
|
||||
else:
|
||||
query = DiaryQueries.SELECT_USER_DIARIES
|
||||
rows = await fetch_all(query, (user_id, limit, skip))
|
||||
|
||||
return [Diary(**row) for row in rows]
|
||||
|
||||
async def get_diary_by_id(self, diary_id: int) -> Optional[Diary]:
|
||||
query = DiaryQueries.SELECT_BY_ID
|
||||
row = await fetch_one(query, (diary_id,))
|
||||
|
||||
if not row:
|
||||
return None
|
||||
|
||||
return Diary(**row)
|
||||
|
||||
async def get_diary_with_user_id(
|
||||
self, diary_id: int, user_id: int
|
||||
) -> Optional[Diary]:
|
||||
query = DiaryQueries.SELECT_BY_ID_WITH_USER_ID
|
||||
row = await fetch_one(query, (diary_id, user_id))
|
||||
|
||||
if not row:
|
||||
return None
|
||||
|
||||
return Diary(**row)
|
||||
|
||||
async def update_diary(
|
||||
self,
|
||||
diary_id: int,
|
||||
user_id: int,
|
||||
diary_data: DiaryUpdate,
|
||||
files: List[UploadFile],
|
||||
) -> Optional[Diary]:
|
||||
diary = await self.get_diary_with_user_id(diary_id, user_id)
|
||||
if not diary:
|
||||
return None
|
||||
|
||||
update_fields = []
|
||||
params = []
|
||||
|
||||
if diary_data.title is not None:
|
||||
update_fields.append("title = ?")
|
||||
params.append(diary_data.title)
|
||||
|
||||
if diary_data.content is not None:
|
||||
update_fields.append("content = ?")
|
||||
params.append(diary_data.content)
|
||||
|
||||
if diary_data.category is not None:
|
||||
update_fields.append("category = ?")
|
||||
params.append(diary_data.category)
|
||||
|
||||
if files is not None:
|
||||
update_fields.append("images = ?")
|
||||
image_paths = ""
|
||||
for file in files:
|
||||
image_paths += (
|
||||
","
|
||||
+ await self.image_processor.write_file_and_get_image_path(
|
||||
file, self.upload_dir
|
||||
)
|
||||
)
|
||||
image_paths = image_paths[1:]
|
||||
params.append(image_paths)
|
||||
|
||||
if update_fields:
|
||||
query = DiaryQueries.UPDATE_DIARY.format(fields=", ".join(update_fields))
|
||||
params.extend([diary_id, user_id])
|
||||
await execute(query, tuple(params))
|
||||
|
||||
return await self.get_diary_with_user_id(diary_id, user_id)
|
||||
|
||||
async def delete_diary(self, diary_id: int, user_id: int) -> bool:
|
||||
try:
|
||||
query = DiaryQueries.DELETE_DIARY
|
||||
await execute(query, (diary_id, user_id))
|
||||
return True
|
||||
except Exception:
|
||||
return False
|
||||
142
Backend/services/friendship_service.py
Normal file
142
Backend/services/friendship_service.py
Normal file
|
|
@ -0,0 +1,142 @@
|
|||
from typing import List, Optional
|
||||
from datetime import datetime
|
||||
from ..schemas.friendship import Friendship, FriendshipResponse, FriendshipStatus
|
||||
from ..utils.db import execute, fetch_one, fetch_all
|
||||
from ..utils.queries.friendship import FriendshipQueries
|
||||
|
||||
|
||||
class FriendshipService:
|
||||
|
||||
@staticmethod
|
||||
async def init_db():
|
||||
await execute(FriendshipQueries.CREATE_TABLE)
|
||||
|
||||
async def send_friendship_request(
|
||||
self, user_id: int, friend_username: str
|
||||
) -> FriendshipResponse:
|
||||
friend_query = FriendshipQueries.SELECT_USER_BY_USERNAME
|
||||
friend_row = await fetch_one(friend_query, (friend_username,))
|
||||
|
||||
if not friend_row:
|
||||
raise ValueError("User not found")
|
||||
|
||||
friend_id = friend_row["id"]
|
||||
|
||||
if user_id == friend_id:
|
||||
raise ValueError("Cannot send friendship request to yourself")
|
||||
|
||||
existing_query = FriendshipQueries.SELECT_EXISTING_FRIENDSHIP
|
||||
existing_row = await fetch_one(
|
||||
existing_query, (user_id, friend_id, friend_id, user_id)
|
||||
)
|
||||
|
||||
if existing_row:
|
||||
raise ValueError("Friendship request already exists")
|
||||
|
||||
created_at = datetime.now()
|
||||
|
||||
query = FriendshipQueries.INSERT_FRIENDSHIP
|
||||
|
||||
await execute(
|
||||
query, (user_id, friend_id, FriendshipStatus.PENDING.value, created_at)
|
||||
)
|
||||
|
||||
friendship_row = await fetch_one(
|
||||
FriendshipQueries.SELECT_FRIENDSHIP_BY_IDS,
|
||||
(user_id, friend_id),
|
||||
)
|
||||
|
||||
if not friendship_row:
|
||||
raise ValueError("Friendship not found after creation")
|
||||
|
||||
friendship = Friendship(
|
||||
id=friendship_row["id"],
|
||||
user_id=friendship_row["user_id"],
|
||||
friend_id=friendship_row["friend_id"],
|
||||
status=friendship_row["status"],
|
||||
created_at=friendship_row["created_at"],
|
||||
)
|
||||
|
||||
return friendship.to_response(friend_username)
|
||||
|
||||
async def accept_friendship_request(
|
||||
self, friendship_id: int, user_id: int
|
||||
) -> Optional[FriendshipResponse]:
|
||||
friendship_query = FriendshipQueries.SELECT_FRIENDSHIP_FOR_ACCEPT
|
||||
|
||||
friendship_row = await fetch_one(
|
||||
friendship_query,
|
||||
(friendship_id, user_id, FriendshipStatus.PENDING.value),
|
||||
)
|
||||
|
||||
if not friendship_row:
|
||||
return None
|
||||
|
||||
update_query = FriendshipQueries.UPDATE_FRIENDSHIP_STATUS
|
||||
await execute(update_query, (FriendshipStatus.ACCEPTED.value, friendship_id))
|
||||
|
||||
friendship = Friendship(
|
||||
id=friendship_row["id"],
|
||||
user_id=friendship_row["user_id"],
|
||||
friend_id=friendship_row["friend_id"],
|
||||
status=FriendshipStatus.ACCEPTED.value,
|
||||
created_at=friendship_row["created_at"],
|
||||
)
|
||||
|
||||
return friendship.to_response(friendship_row["username"])
|
||||
|
||||
async def get_user_friendships(
|
||||
self, user_id: int, status: Optional[str] = None
|
||||
) -> List[FriendshipResponse]:
|
||||
if status:
|
||||
query = FriendshipQueries.SELECT_USER_FRIENDSHIPS_BY_STATUS
|
||||
rows = await fetch_all(query, (user_id, user_id, user_id, status))
|
||||
else:
|
||||
query = FriendshipQueries.SELECT_USER_FRIENDSHIPS
|
||||
rows = await fetch_all(
|
||||
query, (user_id, user_id, user_id, FriendshipStatus.ACCEPTED.value)
|
||||
)
|
||||
|
||||
friendships = []
|
||||
for row in rows:
|
||||
friendship = Friendship(
|
||||
id=row["id"],
|
||||
user_id=row["user_id"],
|
||||
friend_id=row["friend_id"],
|
||||
status=row["status"],
|
||||
created_at=row["created_at"],
|
||||
)
|
||||
friendships.append(friendship.to_response(row["username"]))
|
||||
|
||||
return friendships
|
||||
|
||||
async def delete_friendship(self, friendship_id: int, user_id: int) -> bool:
|
||||
query = FriendshipQueries.DELETE_FRIENDSHIP
|
||||
await execute(query, (friendship_id, user_id, user_id))
|
||||
return True
|
||||
|
||||
async def get_pending_requests(self, user_id: int) -> List[FriendshipResponse]:
|
||||
query = FriendshipQueries.SELECT_PENDING_REQUESTS
|
||||
|
||||
rows = await fetch_all(query, (user_id, FriendshipStatus.PENDING.value))
|
||||
|
||||
friendships = []
|
||||
for row in rows:
|
||||
friendship = Friendship(
|
||||
id=row["id"],
|
||||
user_id=row["user_id"],
|
||||
friend_id=row["friend_id"],
|
||||
status=row["status"],
|
||||
created_at=row["created_at"],
|
||||
)
|
||||
friendships.append(friendship.to_response(row["username"]))
|
||||
|
||||
return friendships
|
||||
|
||||
async def check_friendship(self, user_id1: int, user_id2: int) -> bool:
|
||||
friendship_query = FriendshipQueries.CHECK_FRIENDSHIP_STATUS
|
||||
friendship_row = await fetch_one(
|
||||
friendship_query, (user_id1, user_id2, user_id2, user_id1)
|
||||
)
|
||||
|
||||
return friendship_row is not None
|
||||
110
Backend/services/guestbook_service.py
Normal file
110
Backend/services/guestbook_service.py
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
from datetime import datetime
|
||||
from typing import List
|
||||
from fastapi import HTTPException
|
||||
from Backend.utils.db import execute, fetch_one, fetch_all
|
||||
from Backend.utils.queries.guestbook import GuestBookQueries
|
||||
from Backend.schemas.guestbook import GuestBook, GuestBookCreate, GuestbookResponse
|
||||
from Backend.schemas.user import User
|
||||
from Backend.utils.queries.user import UserQueries
|
||||
|
||||
|
||||
class GuestbookService:
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
async def init_db():
|
||||
await execute(GuestBookQueries.CREATE_TABLE)
|
||||
|
||||
async def create_guestbook(
|
||||
self, data: GuestBookCreate, user: User
|
||||
) -> GuestbookResponse:
|
||||
user_exist = await fetch_one(UserQueries.SELECT_BY_ID, (data.target_user_id,))
|
||||
if user_exist is None:
|
||||
raise HTTPException(status_code=404, detail="User not found")
|
||||
|
||||
ex_row = await fetch_one(
|
||||
GuestBookQueries.SELECT_GUEST_BOOK_BY_USER_ID, (user.id,)
|
||||
)
|
||||
created_at = datetime.now()
|
||||
query = GuestBookQueries.INSERT_GUEST_BOOK
|
||||
await execute(query, (data.target_user_id, user.id, data.content, created_at))
|
||||
|
||||
query = GuestBookQueries.SELECT_GUEST_BOOK_BY_USER_ID
|
||||
row = await fetch_one(query, (user.id,))
|
||||
|
||||
if not (ex_row is None):
|
||||
if row is None or ex_row["id"] == row["id"]:
|
||||
raise HTTPException(
|
||||
status_code=400, detail="Failed to create guest book"
|
||||
)
|
||||
|
||||
return GuestbookResponse(
|
||||
id=row["id"],
|
||||
content=row["content"],
|
||||
target_user_id=row["target_user_id"],
|
||||
user_id=row["user_id"],
|
||||
user_profile_path=user.profile_image_path,
|
||||
username=user.username,
|
||||
created_at=row["created_at"],
|
||||
)
|
||||
|
||||
async def get_target_user_guestbooks(
|
||||
self, target_user_id: int, limit: int = 20, offset: int = 0
|
||||
) -> List[GuestbookResponse]:
|
||||
query = GuestBookQueries.SELECT_TARGET_USER_GUEST_BOOKS
|
||||
|
||||
rows = await fetch_all(query, (target_user_id, limit, offset))
|
||||
|
||||
response_list = []
|
||||
for row in rows:
|
||||
user = await fetch_one(UserQueries.SELECT_BY_ID, (row["user_id"],))
|
||||
|
||||
response_list.append(
|
||||
GuestbookResponse(
|
||||
id=row["id"],
|
||||
content=row["content"],
|
||||
target_user_id=row["target_user_id"],
|
||||
user_id=row["user_id"],
|
||||
user_profile_path=user["profile_image_path"],
|
||||
username=user["username"],
|
||||
created_at=row["created_at"],
|
||||
)
|
||||
)
|
||||
|
||||
return response_list
|
||||
|
||||
async def update_guestbook_by_id(self, id: int, content: str) -> GuestbookResponse:
|
||||
query = GuestBookQueries.SELECT_GUEST_BOOK_BY_ID
|
||||
|
||||
row = await fetch_one(query, (id,))
|
||||
|
||||
if row is None:
|
||||
raise HTTPException(status_code=404, detail="Guest book not found")
|
||||
|
||||
query = GuestBookQueries.UPDATE_GUEST_BOOK_BY_ID
|
||||
await execute(query, (content, id))
|
||||
|
||||
query = GuestBookQueries.SELECT_GUEST_BOOK_BY_ID
|
||||
|
||||
row = await fetch_one(query, (id,))
|
||||
|
||||
user = await fetch_one(UserQueries.SELECT_BY_ID, (row["user_id"],))
|
||||
|
||||
return GuestbookResponse(
|
||||
id=row["id"],
|
||||
content=row["content"],
|
||||
target_user_id=row["target_user_id"],
|
||||
user_id=row["user_id"],
|
||||
user_profile_path=user["profile_image_path"],
|
||||
username=user["username"],
|
||||
created_at=row["created_at"],
|
||||
)
|
||||
|
||||
async def delete_guestbook_by_id(self, id: int, user_id: int) -> bool:
|
||||
try:
|
||||
query = GuestBookQueries.DELETE_GUEST_BOOK
|
||||
await execute(query, (id, user_id))
|
||||
return True
|
||||
except Exception:
|
||||
return False
|
||||
81
Backend/services/letter_service.py
Normal file
81
Backend/services/letter_service.py
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
# from typing import List, Optional
|
||||
# from ..schemas.letter import LetterCreate, Letter, EmailRequest
|
||||
# from ..utils.db import execute, fetch_one, fetch_all
|
||||
# from ..utils.default_queries import LetterQueries
|
||||
# from ..utils.email_processor import EmailProcessor
|
||||
#
|
||||
#
|
||||
# class LetterService:
|
||||
# def __init__(self):
|
||||
# self.email_processor = EmailProcessor()
|
||||
#
|
||||
# @staticmethod
|
||||
# async def init_db():
|
||||
# await execute(LetterQueries.CREATE_TABLE)
|
||||
#
|
||||
# async def create_letter(self, sender_id: int, letter_data: LetterCreate) -> Letter:
|
||||
# query = LetterQueries.INSERT_LETTER
|
||||
#
|
||||
# await execute(
|
||||
# query,
|
||||
# (sender_id, letter_data.content),
|
||||
# )
|
||||
#
|
||||
# row = await fetch_one(
|
||||
# LetterQueries.SELECT_LATEST_USER_LETTER,
|
||||
# (sender_id,),
|
||||
# )
|
||||
#
|
||||
# return Letter(**row)
|
||||
#
|
||||
# async def get_user_letters(
|
||||
# self, sender_id: int, skip: int = 0, limit: int = 20
|
||||
# ) -> List[Letter]:
|
||||
# query = LetterQueries.SELECT_USER_LETTERS
|
||||
# rows = await fetch_all(query, (sender_id, limit, skip))
|
||||
# return [Letter(**row) for row in rows]
|
||||
#
|
||||
# async def get_letter_by_id(
|
||||
# self, letter_id: int, sender_id: int
|
||||
# ) -> Optional[Letter]:
|
||||
# query = LetterQueries.SELECT_LETTER_BY_ID
|
||||
# row = await fetch_one(query, (letter_id, sender_id))
|
||||
# if not row:
|
||||
# return None
|
||||
# return Letter(**row)
|
||||
#
|
||||
# async def delete_letter(self, letter_id: int, sender_id: int) -> bool:
|
||||
# try:
|
||||
# query = LetterQueries.DELETE_LETTER
|
||||
# await execute(
|
||||
# query,
|
||||
# (letter_id, sender_id),
|
||||
# )
|
||||
# return True
|
||||
# except Exception:
|
||||
# return False
|
||||
#
|
||||
# async def update_letter(
|
||||
# self, letter_id: int, sender_id: int, content: str
|
||||
# ) -> Optional[Letter]:
|
||||
# query = LetterQueries.UPDATE_LETTER
|
||||
# await execute(
|
||||
# query,
|
||||
# (content, letter_id, sender_id),
|
||||
# )
|
||||
#
|
||||
# row = await fetch_one(
|
||||
# LetterQueries.SELECT_LETTER_BY_ID,
|
||||
# (letter_id, sender_id),
|
||||
# )
|
||||
# if row is None:
|
||||
# return None
|
||||
#
|
||||
# return Letter(**row)
|
||||
#
|
||||
# async def send_letter(self, letter: Letter, data: EmailRequest):
|
||||
# subject = f"2025_SSF_LETTER_{data.sender_name}"
|
||||
# content = letter.content
|
||||
# await self.email_processor.send_email(
|
||||
# subject, content, data.sender_email, data.sender_password
|
||||
# )
|
||||
150
Backend/services/photo_service.py
Normal file
150
Backend/services/photo_service.py
Normal file
|
|
@ -0,0 +1,150 @@
|
|||
# import os
|
||||
# from datetime import datetime
|
||||
# from typing import List
|
||||
# from fastapi import UploadFile
|
||||
# from ..schemas.photo import (
|
||||
# PhotoUpload,
|
||||
# Photo,
|
||||
# PhotoComment,
|
||||
# CommentCreate,
|
||||
# CommentResponse,
|
||||
# )
|
||||
# from ..utils.db import execute, fetch_one, fetch_all
|
||||
# from ..utils.default_queries import PhotoQueries
|
||||
# from ..utils.image_processor import ImageProcessor
|
||||
#
|
||||
#
|
||||
# class PhotoService:
|
||||
# def __init__(self):
|
||||
# self.image_processor = ImageProcessor()
|
||||
# self.upload_dir = "uploads/photos"
|
||||
# os.makedirs(self.upload_dir, exist_ok=True)
|
||||
#
|
||||
# @staticmethod
|
||||
# async def init_db():
|
||||
# await execute(PhotoQueries.CREATE_TABLE)
|
||||
# await execute(PhotoQueries.CREATE_COMMENTS_TABLE)
|
||||
#
|
||||
# async def upload_photo(
|
||||
# self, user_id: int, photo_data: PhotoUpload, file: UploadFile
|
||||
# ) -> Photo:
|
||||
# if not file.content_type.startswith("image/"):
|
||||
# raise ValueError("File must be an image")
|
||||
#
|
||||
# self.image_processor.validate_image_file(file.filename, file.size)
|
||||
#
|
||||
# created_at = datetime.now()
|
||||
#
|
||||
# image_path = await self.image_processor.write_file_and_get_image_path(
|
||||
# file, self.upload_dir
|
||||
# )
|
||||
#
|
||||
# query = PhotoQueries.INSERT_PHOTO
|
||||
#
|
||||
# await execute(
|
||||
# query,
|
||||
# (user_id, photo_data.album_name, image_path, photo_data.title, created_at),
|
||||
# )
|
||||
#
|
||||
# row = await fetch_one(
|
||||
# PhotoQueries.SELECT_LATEST_USER_PHOTO,
|
||||
# (user_id,),
|
||||
# )
|
||||
#
|
||||
# return Photo(**row)
|
||||
#
|
||||
# async def get_user_photos(
|
||||
# self, user_id: int, skip: int = 0, limit: int = 20, album_name: str = None
|
||||
# ) -> List[Photo]:
|
||||
# if album_name:
|
||||
# query = PhotoQueries.SELECT_USER_PHOTOS_BY_ALBUM
|
||||
# rows = await fetch_all(query, (user_id, album_name, limit, skip))
|
||||
# else:
|
||||
# query = PhotoQueries.SELECT_USER_PHOTOS
|
||||
# rows = await fetch_all(query, (user_id, limit, skip))
|
||||
#
|
||||
# return [Photo(**row) for row in rows]
|
||||
#
|
||||
# async def check_friendship(self, user_id: int, photo_id: int) -> bool:
|
||||
# photo_query = PhotoQueries.SELECT_PHOTO_OWNER
|
||||
# photo_row = await fetch_one(photo_query, (photo_id,))
|
||||
#
|
||||
# if not photo_row:
|
||||
# return False
|
||||
#
|
||||
# photo_owner_id = photo_row["user_id"]
|
||||
#
|
||||
# if user_id == photo_owner_id:
|
||||
# return True
|
||||
#
|
||||
# from ..services.friendship_service import FriendshipService
|
||||
#
|
||||
# friendship_service = FriendshipService()
|
||||
# return await friendship_service.check_friendship(user_id, photo_owner_id)
|
||||
#
|
||||
# async def add_comment(
|
||||
# self, photo_id: int, user_id: int, comment_data: CommentCreate
|
||||
# ) -> PhotoComment:
|
||||
# if not await self.check_friendship(user_id, photo_id):
|
||||
# raise ValueError("Cannot add comment before being friends")
|
||||
# created_at = datetime.now()
|
||||
#
|
||||
# query = PhotoQueries.INSERT_COMMENT
|
||||
#
|
||||
# await execute(query, (photo_id, user_id, comment_data.content, created_at))
|
||||
#
|
||||
# row = await fetch_one(
|
||||
# PhotoQueries.SELECT_LATEST_COMMENT,
|
||||
# (photo_id, user_id),
|
||||
# )
|
||||
#
|
||||
# return PhotoComment(**row)
|
||||
#
|
||||
# async def get_photo_comments(self, photo_id: int) -> List[CommentResponse]:
|
||||
# query = PhotoQueries.SELECT_PHOTO_COMMENTS
|
||||
#
|
||||
# rows = await fetch_all(query, (photo_id,))
|
||||
#
|
||||
# return [CommentResponse(**row) for row in rows]
|
||||
#
|
||||
# async def apply_filter(
|
||||
# self,
|
||||
# photo_id: int,
|
||||
# filter_type: str,
|
||||
# user_id: int,
|
||||
# cover: bool = False,
|
||||
# title: str = None,
|
||||
# ) -> str:
|
||||
# photo_query = PhotoQueries.SELECT_PHOTO_BY_ID
|
||||
# row = await fetch_one(photo_query, (photo_id, user_id))
|
||||
#
|
||||
# if not row:
|
||||
# raise ValueError("Photo not found")
|
||||
#
|
||||
# original_path = row["image_path"]
|
||||
# filtered_path = await self.image_processor.apply_filter(
|
||||
# original_path, filter_type
|
||||
# )
|
||||
#
|
||||
# if cover:
|
||||
# photo_update_query = PhotoQueries.UPDATE_PHOTO_PATH
|
||||
# await execute(photo_update_query, (filtered_path, photo_id, user_id))
|
||||
# else:
|
||||
# row = await fetch_one(
|
||||
# PhotoQueries.SELECT_PHOTO_ALBUM_NAME, (photo_id, user_id)
|
||||
# )
|
||||
# photo_create_query = PhotoQueries.INSERT_PHOTO
|
||||
# await execute(
|
||||
# photo_create_query,
|
||||
# (user_id, row["album_name"], filtered_path, title, datetime.now()),
|
||||
# )
|
||||
#
|
||||
# return filtered_path
|
||||
#
|
||||
# async def delete_photo(self, photo_id: int, user_id: int) -> bool:
|
||||
# try:
|
||||
# query = PhotoQueries.DELETE_PHOTO
|
||||
# await execute(query, (photo_id, user_id))
|
||||
# return True
|
||||
# except Exception:
|
||||
# return False
|
||||
149
Backend/services/room_service.py
Normal file
149
Backend/services/room_service.py
Normal file
|
|
@ -0,0 +1,149 @@
|
|||
from fastapi import HTTPException
|
||||
from typing import List
|
||||
from Backend.utils.queries.room import RoomQueries
|
||||
from Backend.schemas.room import (
|
||||
FurnitureItem,
|
||||
RoomFurniturePlacement,
|
||||
FurniturePlacementRequest,
|
||||
Room,
|
||||
RoomTypes,
|
||||
RoomTypeResponse,
|
||||
room_path,
|
||||
Furniture,
|
||||
furniture_path,
|
||||
RoomFurnitureResponse,
|
||||
FurniturePlacementResponse,
|
||||
RoomResponse,
|
||||
)
|
||||
from Backend.utils.db import fetch_all, fetch_one, execute
|
||||
|
||||
|
||||
class RoomService:
|
||||
|
||||
@staticmethod
|
||||
async def init_db():
|
||||
await execute(RoomQueries.CREATE_TABLE)
|
||||
await execute(RoomQueries.CREATE_TABLE_ROOM_FURNITURE)
|
||||
await execute(RoomQueries.CREATE_TABLE_USER_FURNITURE)
|
||||
|
||||
async def get_or_create_room(self, user_id: int) -> int:
|
||||
row = await fetch_one(RoomQueries.SELECT_ROOM_ID_BY_USER_ID, (user_id,))
|
||||
if row:
|
||||
return row["id"]
|
||||
|
||||
await execute(RoomQueries.INSERT_ROOM, (user_id, "My Room", "room_1"))
|
||||
new_row = await fetch_one(RoomQueries.SELECT_ROOM_ID_BY_USER_ID, (user_id,))
|
||||
|
||||
return new_row["id"]
|
||||
|
||||
async def get_room_by_id(self, id: int) -> Room:
|
||||
row = await fetch_one(RoomQueries.SELECT_ROOM_BY_ID, (id,))
|
||||
if row is None:
|
||||
raise HTTPException(status_code=404, detail="Room not found")
|
||||
|
||||
return Room(**row)
|
||||
|
||||
async def get_room_by_userId(self, id: int) -> Room:
|
||||
row = await fetch_one(RoomQueries.SELECT_ROOM_BY_USER_ID, (id,))
|
||||
if row is None:
|
||||
raise HTTPException(status_code=404, detail="Room not found")
|
||||
|
||||
return Room(**row)
|
||||
|
||||
async def get_room_types(self) -> List[RoomTypeResponse]:
|
||||
return [
|
||||
RoomTypeResponse(type=rt.value, image_path=room_path[rt.value])
|
||||
for rt in RoomTypes
|
||||
]
|
||||
|
||||
async def update_room_name(self, room_id: int, new_name: str):
|
||||
await execute(RoomQueries.UPDATE_ROOM_NAME, (new_name, room_id))
|
||||
|
||||
async def update_room_type(self, room_id: int, new_type: RoomTypes):
|
||||
query = RoomQueries.UPDATE_ROOM_TYPE
|
||||
await execute(query, (new_type.value, room_id))
|
||||
|
||||
row = await fetch_one(RoomQueries.SELECT_ROOM_BY_ID, (room_id,))
|
||||
return Room(**row)
|
||||
|
||||
# furniture
|
||||
async def get_furniture_catalog(self) -> List[FurnitureItem]:
|
||||
furniture_list = []
|
||||
for f in list(Furniture):
|
||||
furniture_list.append(
|
||||
FurnitureItem(
|
||||
name=f,
|
||||
image_path=furniture_path[f],
|
||||
width=int(f[-1]),
|
||||
)
|
||||
)
|
||||
|
||||
return furniture_list
|
||||
|
||||
async def get_room_furnitures(self, room_id: int) -> RoomFurnitureResponse:
|
||||
rows = await fetch_all(RoomQueries.SELECT_ROOM_FURNITURE, (room_id,))
|
||||
furniture_placement_response = []
|
||||
for row in rows:
|
||||
furniture_placement_response.append(
|
||||
FurniturePlacementResponse(
|
||||
furniture_name=row["furniture_name"],
|
||||
x=row["x"],
|
||||
y=row["y"],
|
||||
image_path=furniture_path[row["furniture_name"]],
|
||||
)
|
||||
)
|
||||
row = await fetch_one(RoomQueries.SELECT_ROOM_BY_ID, (room_id,))
|
||||
return RoomFurnitureResponse(
|
||||
furniture=furniture_placement_response,
|
||||
room=RoomResponse(
|
||||
id=row["id"],
|
||||
user_id=row["user_id"],
|
||||
room_name=row["room_name"],
|
||||
room_type=row["room_type"],
|
||||
room_image_path=room_path[row["room_type"]],
|
||||
),
|
||||
)
|
||||
|
||||
async def place_furniture(self, room_id: int, request: FurniturePlacementRequest):
|
||||
is_oneone = furniture_path.get(request.furniture_name + "1") is not None
|
||||
|
||||
placed_furnitures = await fetch_all(
|
||||
RoomQueries.SELECT_ROOM_FURNITURE, (room_id,)
|
||||
)
|
||||
|
||||
for f in placed_furnitures:
|
||||
if f["x"] == request.x and f["y"] == request.y:
|
||||
raise HTTPException(status_code=409, detail="Furniture already placed")
|
||||
|
||||
await execute(
|
||||
RoomQueries.INSERT_ROOM_FURNITURE,
|
||||
(room_id, request.furniture_name, request.x, request.y),
|
||||
)
|
||||
if not is_oneone:
|
||||
await execute(
|
||||
RoomQueries.INSERT_ROOM_FURNITURE,
|
||||
(room_id, "1", request.x - 1, request.y),
|
||||
)
|
||||
|
||||
async def remove_furniture(self, room_id: int, x: int, y: int, furniture_name: str):
|
||||
is_oneone = furniture_path.get(furniture_name + "1") is not None
|
||||
await execute(RoomQueries.DELETE_FURNITURE, (room_id, x, y))
|
||||
if not is_oneone:
|
||||
await execute(RoomQueries.DELETE_FURNITURE, (room_id, x - 1, y))
|
||||
|
||||
async def add_furniture(self, user_id: int, furniture_name: str):
|
||||
await execute(RoomQueries.INSERT_USER_FURNITURE, (user_id, furniture_name))
|
||||
|
||||
async def get_user_furniture(self, user_id: int):
|
||||
rows = await fetch_all(RoomQueries.SELECT_USER_FURNITURE, (user_id,))
|
||||
furniture_list = []
|
||||
for row in rows:
|
||||
furniture_list.append(
|
||||
FurnitureItem(
|
||||
name=row["furniture_name"],
|
||||
image_path=furniture_path[row["furniture_name"]],
|
||||
width=int(row["furniture_name"][-1]),
|
||||
)
|
||||
)
|
||||
|
||||
return furniture_list
|
||||
33
Backend/services/store_service.py
Normal file
33
Backend/services/store_service.py
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
import httpx
|
||||
|
||||
|
||||
class StoreService:
|
||||
def __init__(self):
|
||||
self.SERVER_URL = "https://dotory.ana.st"
|
||||
|
||||
async def get_dotory_by_id(self, user_id: int):
|
||||
async with httpx.AsyncClient() as client:
|
||||
response = await client.get(f"{self.SERVER_URL}/")
|
||||
response_json = response.json()
|
||||
return
|
||||
|
||||
async def register_user(self, user_id: int):
|
||||
async with httpx.AsyncClient() as client:
|
||||
response = await client.post(
|
||||
f"{self.SERVER_URL}/", json={"user_id": user_id}
|
||||
)
|
||||
return
|
||||
|
||||
async def buy_product(self, product_id: int, user_id: int):
|
||||
async with httpx.AsyncClient() as client:
|
||||
response = await client.post(
|
||||
f"{self.SERVER_URL}/", json={"user_id": user_id}
|
||||
)
|
||||
return
|
||||
|
||||
async def update_user_dotory(self, user_id: int, dotoryNum: int):
|
||||
async with httpx.AsyncClient() as client:
|
||||
response = await client.put(
|
||||
f"{self.SERVER_URL}/", json={"num": dotoryNum}
|
||||
)
|
||||
return
|
||||
178
Backend/services/user_service.py
Normal file
178
Backend/services/user_service.py
Normal file
|
|
@ -0,0 +1,178 @@
|
|||
from datetime import datetime, timezone, timedelta
|
||||
from typing import Optional, List
|
||||
import os
|
||||
from fastapi import UploadFile
|
||||
from Backend.utils.db import execute, fetch_one, fetch_all
|
||||
from Backend.utils.image_processor import ImageProcessor
|
||||
from Backend.utils.queries.user import UserQueries
|
||||
from Backend.schemas.user import User, UserCreate, UserResponse, UserUpdate
|
||||
from Backend.services.store_service import StoreService
|
||||
|
||||
store_service = StoreService()
|
||||
|
||||
class UserService:
|
||||
def __init__(self):
|
||||
self.image_processor = ImageProcessor()
|
||||
self.upload_dir = "uploads/profile"
|
||||
os.makedirs(self.upload_dir, exist_ok=True)
|
||||
|
||||
@staticmethod
|
||||
async def init_db():
|
||||
await execute(UserQueries.CREATE_TABLE)
|
||||
|
||||
async def create_user(
|
||||
self, user_data: UserCreate, profile_file: UploadFile = None
|
||||
) -> User:
|
||||
password_hash, salt = User.hash_password(user_data.password)
|
||||
|
||||
if profile_file is not None:
|
||||
await self.image_processor.validate_image_file(
|
||||
|
||||
)
|
||||
image_path = await self.image_processor.write_file_and_get_image_path(
|
||||
profile_file, upload_dir=self.upload_dir
|
||||
)
|
||||
query = UserQueries.INSERT_USER_WITH_PROFILE
|
||||
params = (
|
||||
user_data.username,
|
||||
user_data.email,
|
||||
password_hash,
|
||||
salt,
|
||||
image_path,
|
||||
)
|
||||
else:
|
||||
query = UserQueries.INSERT_USER_WITHOUT_PROFILE
|
||||
params = (user_data.username, user_data.email, password_hash, salt)
|
||||
|
||||
await execute(
|
||||
query,
|
||||
params,
|
||||
)
|
||||
|
||||
row = await fetch_one(
|
||||
UserQueries.SELECT_BY_USERNAME,
|
||||
(user_data.username,),
|
||||
)
|
||||
|
||||
if row is None:
|
||||
raise Exception("User creation failed")
|
||||
|
||||
row = dict(row)
|
||||
|
||||
if isinstance(row["created_at"], str):
|
||||
datetime.fromisoformat(row["created_at"].replace("Z", "+09:00"))
|
||||
|
||||
row["is_active"] = bool(row["is_active"])
|
||||
|
||||
await store_service.register_user(row["id"])
|
||||
|
||||
|
||||
return User(**row)
|
||||
|
||||
async def get_user_by_username(self, username: str) -> Optional[User]:
|
||||
row = await fetch_one(
|
||||
UserQueries.SELECT_BY_USERNAME,
|
||||
(username,),
|
||||
)
|
||||
|
||||
if row is None:
|
||||
return None
|
||||
|
||||
row = dict(row)
|
||||
|
||||
if isinstance(row["created_at"], str):
|
||||
datetime.fromisoformat(row["created_at"].replace("Z", "+09:00"))
|
||||
|
||||
row["is_active"] = bool(row["is_active"])
|
||||
|
||||
return User(**row)
|
||||
|
||||
async def get_user_by_email(self, email: str) -> Optional[User]:
|
||||
row = await fetch_one(
|
||||
UserQueries.SELECT_BY_EMAIL,
|
||||
(email,),
|
||||
)
|
||||
|
||||
if row is None:
|
||||
return None
|
||||
|
||||
row = dict(row)
|
||||
|
||||
if isinstance(row["created_at"], str):
|
||||
datetime.fromisoformat(row["created_at"].replace("Z", "+09:00"))
|
||||
|
||||
row["is_active"] = bool(row["is_active"])
|
||||
|
||||
return User(**row)
|
||||
|
||||
async def authenticate_user(self, username: str, password: str) -> Optional[User]:
|
||||
user =
|
||||
if user and :
|
||||
return user
|
||||
return None
|
||||
|
||||
async def delete_user(self, username: str) -> bool:
|
||||
try:
|
||||
query = UserQueries.DELETE_USER_BY_USERNAME
|
||||
await execute(query, (username,))
|
||||
return True
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
async def find_user(self, username: str) -> List[UserResponse]:
|
||||
query = UserQueries.SELECT_BY_USERNAME_LIKE
|
||||
rows = await fetch_all(
|
||||
query,
|
||||
("%" + username + "%",),
|
||||
)
|
||||
|
||||
return [User(**row).to_response() for row in rows]
|
||||
|
||||
async def get_user_by_id(self, user_id: int) -> Optional[User]:
|
||||
row = await fetch_one(UserQueries.SELECT_BY_ID, (user_id,))
|
||||
if row is None:
|
||||
return None
|
||||
row = dict(row)
|
||||
if isinstance(row["created_at"], str):
|
||||
datetime.fromisoformat(row["created_at"].replace("Z", "+09:00"))
|
||||
row["is_active"] = bool(row["is_active"])
|
||||
return User(**row)
|
||||
|
||||
async def update_user(
|
||||
self, user: User, user_data: UserUpdate, profile_file: UploadFile = None
|
||||
) -> User:
|
||||
update_fields = {}
|
||||
if user_data.email:
|
||||
existing_user = await fetch_one(
|
||||
UserQueries.SELECT_USER_BY_EMAIL_AND_NOT_ID,
|
||||
(user_data.email, user.id),
|
||||
)
|
||||
if existing_user:
|
||||
raise ValueError("Email already registered")
|
||||
update_fields["email"] = user_data.email
|
||||
|
||||
if user_data.password:
|
||||
password_hash, salt = User.hash_password(user_data.password)
|
||||
update_fields["password_hash"] = password_hash
|
||||
update_fields["salt"] = salt
|
||||
|
||||
if profile_file:
|
||||
await self.image_processor.validate_image_file(
|
||||
profile_file.filename, profile_file.size
|
||||
)
|
||||
image_path = await self.image_processor.write_file_and_get_image_path(
|
||||
profile_file, upload_dir=self.upload_dir
|
||||
)
|
||||
update_fields["profile_image_path"] = image_path
|
||||
|
||||
if not update_fields:
|
||||
return user
|
||||
|
||||
set_clause = ", ".join(f"{key} = ?" for key in update_fields.keys())
|
||||
query = UserQueries.UPDATE_USER_BY_ID.format(set_clause)
|
||||
params = list(update_fields.values())
|
||||
params.append(user.id)
|
||||
|
||||
await execute(query, tuple(params))
|
||||
|
||||
return await self.get_user_by_id(user.id)
|
||||
Loading…
Add table
Add a link
Reference in a new issue