Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
8b35290
dev로 배포 변경 6/2
Vincentius7 Jun 1, 2025
21fff07
yml 수정
Vincentius7 Jun 1, 2025
0c5c559
Merge branch 'dev' of https://github.com/Pet-ner/AniDoc into dev
Vincentius7 Jun 1, 2025
4ac4516
Merge branch 'dev' of https://github.com/Pet-ner/AniDoc
codefish-sea Jun 1, 2025
dfec833
Feat : 진료 기록 수정
mordor8378 Jun 1, 2025
758efb6
dev로 배포 변경
Vincentius7 Jun 1, 2025
2c64135
Fix : 서스펜스
mordor8378 Jun 1, 2025
e2788bd
Fix : 관리자,의료진 계정 추가
mordor8378 Jun 1, 2025
fbbe8b3
Fix : 시간대 수정
mordor8378 Jun 1, 2025
fa4b789
chore : merge
codefish-sea Jun 1, 2025
f4a7573
chore : 복구
codefish-sea Jun 1, 2025
6038857
fix : staff page
codefish-sea Jun 1, 2025
997dabb
chore : 복구2
codefish-sea Jun 1, 2025
9c0b581
Chore : front 복구 작업
codefish-sea Jun 1, 2025
c480593
Fix : initializer 순서 설정
mordor8378 Jun 2, 2025
3c240a2
진료 예약 알림 개선
Vincentius7 Jun 2, 2025
d25a261
x
Vincentius7 Jun 2, 2025
d574f64
예약 알림 성능 개선
Vincentius7 Jun 2, 2025
92d6daf
chore : 병합
codefish-sea Jun 2, 2025
ffabe1a
fix : 직원 관리 탭
codefish-sea Jun 2, 2025
c319486
fix : profileSidebar
codefish-sea Jun 2, 2025
543cf1f
Fix : 의료진 조회 수정
mordor8378 Jun 2, 2025
20663d4
Refactor : 성공/에러 메시지 토스트로 변경
mordor8378 Jun 2, 2025
8d60e8d
Refactor : 토스트 메시지 추가 (회원기능)
mordor8378 Jun 2, 2025
6edf7f2
Merge branch 'main' of https://github.com/Pet-ner/AniDoc into dev
Vincentius7 Feb 5, 2026
46cf645
서버 통합을 위한 서버 설정 변경
Vincentius7 Feb 5, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ on:
- "backend/settings.gradle.kts"
- "backend/Dockerfile"
branches:
- main
- dev
jobs:
makeTagAndRelease:
runs-on: ubuntu-latest
Expand Down
Empty file added FETCH_HEAD
Empty file.
20 changes: 2 additions & 18 deletions backend/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,33 +1,17 @@
version: '3.8'

services:
mysql:
mysql-anidoc: # 서비스명을 DB 전용으로 명시
image: mysql:8.0
restart: always
environment:
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
MYSQL_DATABASE: ${MYSQL_DATABASE}
MYSQL_USER: ${MYSQL_USER}
MYSQL_PASSWORD: ${MYSQL_PASSWORD}

command:
--character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
networks:
- common
ports:
- "3306:3306"

app1:
build:
context: .
dockerfile: Dockerfile
depends_on:
- mysql
ports:
- "8080:8080"
networks:
- common

networks:
common:
driver: bridge
- "3307:3306" # 타 프로젝트 DB와 충돌 방지
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,14 @@
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;

import java.util.TimeZone;

@EnableJpaAuditing
@SpringBootApplication
public class AnidocApplication {

public static void main(String[] args) {
TimeZone.setDefault(TimeZone.getTimeZone("Asia/Seoul"));
SpringApplication.run(AnidocApplication.class, args);
}

}
Original file line number Diff line number Diff line change
@@ -1,25 +1,19 @@
package com.petner.anidoc.domain.user.notification.service;

import com.petner.anidoc.domain.user.notification.dto.PetInfoDto;
import com.petner.anidoc.domain.user.notification.dto.ReservationNotificationDto;
import com.petner.anidoc.domain.user.notification.dto.VaccinationNotificationDto;
import com.petner.anidoc.domain.user.notification.entity.Notification;
import com.petner.anidoc.domain.user.notification.entity.NotificationType;
import com.petner.anidoc.domain.user.notification.repository.NotificationRepository;
import com.petner.anidoc.domain.user.notification.util.NotificationMessageUtil;
import com.petner.anidoc.domain.user.notification.util.VaccinationNotificationHelper;
import com.petner.anidoc.domain.user.user.entity.User;
import com.petner.anidoc.domain.user.user.entity.UserRole;
import com.petner.anidoc.domain.user.user.repository.UserRepository;
import com.petner.anidoc.domain.vet.reservation.entity.Reservation;
import com.petner.anidoc.domain.vet.reservation.entity.ReservationStatus;
import com.petner.anidoc.domain.vet.reservation.repository.ReservationRepository;
import jakarta.transaction.Transactional;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;

Expand Down Expand Up @@ -196,3 +190,6 @@ public long getUnreadCount(Long userId) {
return notificationRepository.countByUserIdAndIsReadFalse(userId);
}
}



Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@ public SseEmitter add(Long userId, SseEmitter emitter) {
if (list != null) list.remove(emitter);
});


emitter.onError(e -> {
List<SseEmitter> list = this.emitters.get(userId);
if (list != null) list.remove(emitter);
Expand Down Expand Up @@ -118,7 +117,8 @@ public void noti(Long userId, String eventName, Object data) {
}catch (ClientAbortException e){
deadEmitters.add(emitter);
}catch (IOException e) {
throw new RuntimeException(e);
log.debug("SSE 연결 끊어짐 - userId: {}, 메시지: {}", userId, e.getMessage());
deadEmitters.add(emitter);
}
}
userEmitters.removeAll(deadEmitters);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package com.petner.anidoc.domain.user.user.controller;


import com.petner.anidoc.domain.user.user.dto.UserResponseDto;
import com.petner.anidoc.domain.user.user.service.UserService;
import com.petner.anidoc.domain.user.user.service.UserStatusService;
import com.petner.anidoc.global.security.SecurityUser;
import io.swagger.v3.oas.annotations.Operation;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.*;

import java.security.Security;
import java.util.List;

@RestController
@RequestMapping("/api/admins")
@RequiredArgsConstructor

public class AdminController {

private final UserService userService;
private final UserStatusService userStatusService;

// ✅ 승인 대기 목록 조회
@Operation(summary = "승인 대기 목록 조회", description = "의료진 가입 승인 대기 중인 사용자 목록을 조회합니다.")
@GetMapping("/pending-approvals")
@PreAuthorize("hasRole('ROLE_ADMIN')")
public ResponseEntity<List<UserResponseDto>> getPendingApprovals(){
List<UserResponseDto> pendingUsers = userStatusService.getPendingApprovalUsers();
return ResponseEntity.ok(pendingUsers);
}

// ✅ 승인 완료 처리
@PostMapping("/approve/{id}")
@PreAuthorize("hasRole('ROLE_ADMIN')")
public ResponseEntity<String> approveUser(
@PathVariable Long id,
@AuthenticationPrincipal SecurityUser admin){

userStatusService.approveUser(id, admin.getId());
return ResponseEntity.ok("사용자 승인 완료");
}

// ✅ 승인 거절
@DeleteMapping("/reject/{id}")
@PreAuthorize("hasRole('ROLE_ADMIN')")
public ResponseEntity<String> rejectUser(
@PathVariable Long id,
@AuthenticationPrincipal SecurityUser admin) {
userStatusService.rejectUser(id, admin.getId());
return ResponseEntity.ok("사용자 승인 거부");
}


}


Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import jakarta.validation.Valid;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.*;

Expand Down Expand Up @@ -99,7 +100,6 @@ public ResponseEntity<LoginResponseDto> login(@Valid @RequestBody LoginRequestDt
throw new CustomException(ErrorCode.LOGIN_FAILED);
}

// TODO : 에러 코드 세분화(USER가 존재하지 않습니다, 비밀번호가 다릅니다 등)
}


Expand All @@ -111,6 +111,8 @@ public ResponseEntity<String> logout(@CookieValue(value = "accessToken", require
if (accessToken == null || !authTokenService.isValid(accessToken)){
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("유효하지 않은 토큰입니다.");
}


userService.logout(accessToken);


Expand Down Expand Up @@ -151,7 +153,7 @@ public ResponseEntity<UserUpdateResponseDto> getUserProfile(@AuthenticationPrinc


// ✅ 의료진 조회
@Operation(summary = "의료진 목록 조회", description = "근무 중인 의료진 목록을 조회합니다.")
@Operation(summary = "의료진 목록 조회", description = "의료진 목록을 조회합니다.")
@GetMapping("/staff")
public ResponseEntity<List<StaffResponseDto>> getStaffList(
@RequestParam(value = "onlyAvailable", defaultValue = "false") boolean onlyAvailable) {
Expand All @@ -160,14 +162,13 @@ public ResponseEntity<List<StaffResponseDto>> getStaffList(
return ResponseEntity.ok(staffList);
}


//✅ 비밀번호 일치 확인
@PostMapping("/verify-password")
public ResponseEntity<Map<String, Object>> verifyCurrentPassword(
@RequestBody PasswordVerificationRequest request,
@AuthenticationPrincipal SecurityUser securityUser) {
User user = userService.getUserById(securityUser.getId());
try {
User user = userService.getUserById(securityUser.getId());
try {
boolean isValid = userService.verifyCurrentPassword(user, request.getPassword());

Map<String, Object> response = new HashMap<>();
Expand Down Expand Up @@ -200,7 +201,7 @@ public ResponseEntity<UserStatus> getMyStatus(@AuthenticationPrincipal SecurityU

@PutMapping("/me/status")
public ResponseEntity<String> updateMyStatus(@AuthenticationPrincipal SecurityUser securityUser,
@RequestParam UserStatus status){
@RequestParam UserStatus status){
userService.updateMyStatus(securityUser.getId(),status);
return ResponseEntity.ok("상태 변경이 반영되었습니다.");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,16 @@
public class StaffResponseDto {
private Long id;
private String name;
private String email;
private String phoneNumber;
private UserStatus status;

public static StaffResponseDto fromEntity(User user) {
return StaffResponseDto.builder()
.id(user.getId())
.name(user.getName())
.email(user.getEmail())
.phoneNumber(user.getPhoneNumber())
.status(user.getStatus())
.build();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.petner.anidoc.domain.user.user.entity;

public enum ApprovalStatus {
PENDING, // 승인 대기
APPROVED, // 승인됨
REJECTED // 승인 거부
}
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@ public class User extends BaseEntity implements UserDetails {
@Column(name = "status")
private UserStatus status;

@Enumerated(EnumType.STRING)
@Column(name ="approval_status")
private ApprovalStatus approvalStatus;

@Enumerated(EnumType.STRING)
@Column(name = "sso_provider")
private SsoProvider ssoProvider;
Expand Down Expand Up @@ -133,4 +137,14 @@ public void updateRefreshToken(String refreshToken){
public void updateStatus(UserStatus status) {
this.status = status;
}

public void updateBasicInfo(String name, String phoneNumber, String emergencyContact, UserRole role, VetInfo vetInfo) {
this.name = name;
this.phoneNumber = phoneNumber;
this.emergencyContact = emergencyContact;
this.role = role;
this.vetInfo = vetInfo;
}


}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
package com.petner.anidoc.domain.user.user.entity;

public enum UserStatus {
ON_DUTY, AWAY,OFF
ON_DUTY, AWAY,OFFLINE
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.petner.anidoc.domain.user.user.repository;

import com.petner.anidoc.domain.user.user.entity.ApprovalStatus;
import com.petner.anidoc.domain.user.user.entity.User;
import com.petner.anidoc.domain.user.user.entity.UserRole;
import com.petner.anidoc.domain.user.user.entity.UserStatus;
Expand All @@ -25,20 +26,14 @@ public interface UserRepository extends JpaRepository<User, Long> {
@Query("SELECT u FROM User u WHERE u.role = :role AND u.status = :status")
List<User> findByRoleAndStatus(@Param("role") UserRole role, @Param("status") UserStatus status);

// 승인된 특정 역할의 사용자 조회
List<User> findByRoleAndApprovalStatus(UserRole role, ApprovalStatus approvalStatus);

// 승인된 특정 역할과 상태의 사용자 조회
List<User> findByRoleAndApprovalStatusAndStatus(UserRole role, ApprovalStatus approvalStatus, UserStatus status);

List<User> findByRoleAndApprovalStatusAndStatusIn(UserRole role, ApprovalStatus approvalStatus, List<UserStatus> statuses);

@Modifying
@Transactional
@Query("UPDATE User u SET u.name = :name, u.phoneNumber = :phoneNumber, " +
"u.emergencyContact = :emergencyContact," +
"u.role = :role, " +
" u.vetInfo = :vetInfo, u.updatedAt = CURRENT_TIMESTAMP " +
"WHERE u.id = :id")
void updateUserBasicInfo(@Param("id") Long id,
@Param("name") String name,
@Param("phoneNumber") String phoneNumber,
@Param("emergencyContact") String emergencyContact,
@Param("role") UserRole role,
@Param("vetInfo") VetInfo vetInfo);
}


Loading
Loading