Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
75 commits
Select commit Hold shift + click to select a range
f14b991
Merge branch 'develop' into feature/hyoin
hyoinYang Jul 7, 2025
c504218
[feat] 대중교통 길찾기 api 세팅 + 스웨거 문서 작성
hyoinYang Jul 9, 2025
7a2034f
[feat] 엔티티 세팅
hyoinYang Jul 18, 2025
e23f067
[feat] 다익스트라 알고리즘 구현
hyoinYang Jul 21, 2025
16c4f2c
[fix] dto 수정
hyoinYang Jul 21, 2025
caf2ea6
[fix] cursor 형식 변경
hyoinYang Jul 22, 2025
41266eb
[refactor] 서비스 로직 분리
hyoinYang Jul 23, 2025
f1f54d2
[fix] 출발지, 도착지에 대해 임시 id 생성
hyoinYang Jul 24, 2025
c4d6d29
[fix] map의 null 에러 해결
hyoinYang Jul 24, 2025
2dd4bc3
[fix] 레포지토리의 id 변경
hyoinYang Jul 30, 2025
1605ecd
[fix] 노드를 비교하는 조건 변경
hyoinYang Jul 30, 2025
f6e2761
[refactor] 디버깅 메시지 삭제, 가중치 계산 추가, 중복되는 코드 삭제
hyoinYang Jul 30, 2025
78e26b5
[feat] 관리자 페이지 초안 구현 완료
KiSeungMin Jul 30, 2025
b33d662
[feat] 관리자 페이지 웨이블존 조회 로직 초안 구현 완료
KiSeungMin Jul 30, 2025
f44612d
[feat] 관리자 웨이블존 조회 페이지 구현 완료
KiSeungMin Jul 30, 2025
536c103
[fix] 환승시 패널티 강화 + null 에러 해결
hyoinYang Jul 30, 2025
a17dc10
[feat] CORS 구현 완료
KiSeungMin Aug 1, 2025
404ecb9
[feat] CORS 구현 완료
KiSeungMin Aug 1, 2025
636a8f7
[feat] 관리자 대시보드에 시스템 상태 정상 여부 표현 구현 완료
KiSeungMin Aug 1, 2025
1fe3017
[feat] 관리자 대시보드 메인 페이지 디자인 개선
KiSeungMin Aug 1, 2025
d222d74
[feat] 관리자 대시보드 메인 페이지에 실제 유저 수와 웨이블 존 개수가 뜨도록 기능 개선
KiSeungMin Aug 1, 2025
8db4eb2
[feat] 관리자 페이지 유저 목록 조회 기능 구현 완료
KiSeungMin Aug 1, 2025
1e364d6
[chore] admin 패키지 내부 구조 세분화
KiSeungMin Aug 1, 2025
06e632e
[chore] View 패키지 구조 세분화
KiSeungMin Aug 1, 2025
508da9e
[feat] soft delete된 유저 조회 로직 구현 완료
KiSeungMin Aug 1, 2025
7018246
[chore] User 관련 Admin Repository 로직을 분리
KiSeungMin Aug 1, 2025
6ed14a8
[chore] 웨이블존 관련 Admin Repository 로직을 분리
KiSeungMin Aug 1, 2025
8a3f8f1
[feat] 관리자 페이지 웨이블존 생성 기능 구현 완료
KiSeungMin Aug 1, 2025
018ac86
[feat] 관리자 페이지 웨이블존 생성 페이지 디자인 개선
KiSeungMin Aug 1, 2025
633315e
[fix] from, to 간소화
hyoinYang Aug 1, 2025
aa50661
[feat] 지하철 정보 추가
hyoinYang Aug 2, 2025
c9c5c83
[feat] 지하철 정보를 로직에 반영
hyoinYang Aug 2, 2025
55f07ff
[refactor] 불필요한 어노테이션 삭제
hyoinYang Aug 2, 2025
742ddc0
[feat] 관리자 페이지 웨이블존 수정 기능 구현
KiSeungMin Aug 2, 2025
14a2dfc
[feat] 관리자 페이지 웨이블존 삭제 기능 구현
KiSeungMin Aug 2, 2025
5e45154
[feat] 관리자 페이지 유저 복구 기능 구현
KiSeungMin Aug 2, 2025
918daae
[feat] 관리자 페이지 유저 리뷰, 좋아요 수 조회 기능 구현
KiSeungMin Aug 2, 2025
2fd6ff4
[feat] 관리자 페이지 위도 경도 편집 오류 해결
KiSeungMin Aug 2, 2025
ab7d325
[chore] Merge Conflict 해결
KiSeungMin Aug 2, 2025
8131c46
[test] CI/CD 임시 주석
KiSeungMin Aug 2, 2025
e443229
[fix] 관리자 페이지에서 유저 정보가 조회되지 않던 문제 해결
KiSeungMin Aug 2, 2025
659dbf1
[feat] BaseEntity에서 createdAt, updatedAt을 created_at, updated_at으로 변경
KiSeungMin Aug 2, 2025
82b8fbe
[chore] CI/CD workflow 원복
KiSeungMin Aug 2, 2025
be4878a
[fix] 서버에 요청이 가지 않던 문제 해결
KiSeungMin Aug 2, 2025
f7731de
[test] workflow 임시 수정
KiSeungMin Aug 2, 2025
7ecf278
[chore] workflow 원복
KiSeungMin Aug 2, 2025
0f9d4a4
[feat] 관리자 페이지 구현 완료
KiSeungMin Aug 3, 2025
5184dda
[feat] 지하철 시설 정보를 List 형식으로 분류
hyoinYang Aug 3, 2025
bfd4d9a
[feat] 지하철 시설 정보 정리 및 출력
hyoinYang Aug 3, 2025
9ae6a1c
[feat] 공공 api를 호출해 정보 출력
hyoinYang Aug 4, 2025
0dd9a19
feat: #95 spring log 확인
Aug 4, 2025
bbf32e1
Merge pull request #96 from Wayble-Project/feature/wonjun
wonjun-lee-fcwj245 Aug 4, 2025
977a004
.properties수정
Aug 4, 2025
6184ddf
Merge pull request #97 from Wayble-Project/feature/wonjun
wonjun-lee-fcwj245 Aug 4, 2025
73edc30
[refactor] 디렉토리 분류
hyoinYang Aug 4, 2025
cda390d
[feat] 유저 닉네임 중복 검증을 위한 메소드 추가
seung-in-Yoo Aug 4, 2025
79abc6a
[feat] 유저 닉네임 중복 검증을 위한 서비스 로직 추가
seung-in-Yoo Aug 4, 2025
217072b
[feat] 유저 닉네임 중복 검증 관련 에러 케이스 추가
seung-in-Yoo Aug 4, 2025
cba6c7b
[feat] 유저 닉네임 중복 검증 관련 응답 Dto 생성
seung-in-Yoo Aug 4, 2025
3a8c36c
[feat] 유저 컨트롤러에 닉네임 중복 확인 로직 추가
seung-in-Yoo Aug 4, 2025
f86b3d5
[feat] 중복된 닉네임일때의 에러 케이스 추가
seung-in-Yoo Aug 4, 2025
a47ccf2
[refactor] 유저 컨트롤러 닉네임 중복 확인 부분 코드 개선
seung-in-Yoo Aug 4, 2025
5d7574d
[refactor] 유저 관련 에러코드 숫자 변경
seung-in-Yoo Aug 4, 2025
b2072ae
Merge remote-tracking branch 'origin/develop' into feature/hyoin
hyoinYang Aug 4, 2025
9aebc27
[refactor] import 수정
hyoinYang Aug 4, 2025
0b3a1db
Merge pull request #98 from Wayble-Project/feature/seungin
seung-in-Yoo Aug 4, 2025
bb93d47
[refactor] 길찾기 api를 위해 config 수정
hyoinYang Aug 4, 2025
fc2e182
[refactor] 디버깅 메시지 삭제
hyoinYang Aug 4, 2025
80b65f9
[docs] 스웨거 문서 수정
hyoinYang Aug 4, 2025
c233e44
[refactor] 유저 조회 먼저 가능하도록 null 일때 에러 처리 로직 삭제
seung-in-Yoo Aug 5, 2025
f2368cb
[refactor] 유저타입 관련 null 가능하도록 기본값 삭제
seung-in-Yoo Aug 5, 2025
18dd220
[fix] 유저타입 관련 null 허용으로 NPE 오류 해결
seung-in-Yoo Aug 5, 2025
6ba090a
[refactor] 유저 닉네임 중복 확인 관련 리팩토링
seung-in-Yoo Aug 5, 2025
e954670
Merge pull request #100 from Wayble-Project/feature/seungin
seung-in-Yoo Aug 5, 2025
3b6bfb6
[feat] 대중교통 길찾기 구현
KiSeungMin Aug 5, 2025
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/cd-develop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: CI/CD

on:
# push:
# branches: [ "develop" ]
# branches: [ "feature/seungmin" ]
pull_request:
branches: [ "main" ]

Expand Down
1 change: 1 addition & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ dependencies {
implementation 'jakarta.persistence:jakarta.persistence-api:3.1.0'
implementation 'org.springframework.boot:spring-boot-starter-data-elasticsearch'
implementation 'org.springframework.boot:spring-boot-starter-validation'
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'

implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'io.jsonwebtoken:jjwt-api:0.11.5'
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
package com.wayble.server.admin.controller;

import com.wayble.server.admin.dto.SystemStatusDto;
import com.wayble.server.admin.service.AdminSystemService;
import com.wayble.server.admin.service.AdminUserService;
import com.wayble.server.admin.service.AdminWaybleZoneService;
import jakarta.servlet.http.HttpSession;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

@Slf4j
@Controller
@RequestMapping("/admin")
@RequiredArgsConstructor
public class AdminController {

private final AdminSystemService adminSystemService;
private final AdminUserService adminUserService;
private final AdminWaybleZoneService adminWaybleZoneService;

@Value("${spring.admin.username}")
private String adminUsername;

@Value("${spring.admin.password}")
private String adminPassword;

@GetMapping("")
public String adminLoginPage(HttpSession session, Model model) {
// 이미 로그인된 경우 대시보드로 리다이렉트
if (session.getAttribute("adminLoggedIn") != null) {
return "redirect:/admin/dashboard";
}
return "admin/login";
}

@PostMapping("/login")
public String adminLogin(
@RequestParam String username,
@RequestParam String password,
HttpSession session,
Model model
) {
if (adminUsername.equals(username) && adminPassword.equals(password)) {
session.setAttribute("adminLoggedIn", true);
session.setAttribute("adminUsername", username);
log.info("관리자 로그인 성공: {}", username);
return "redirect:/admin/dashboard";
} else {
model.addAttribute("error", "잘못된 사용자명 또는 비밀번호입니다.");
log.warn("관리자 로그인 실패 시도: {}", username);
return "admin/login";
}
}

@GetMapping("/dashboard")
public String adminDashboard(HttpSession session, Model model) {
// 로그인 확인
if (session.getAttribute("adminLoggedIn") == null) {
return "redirect:/admin";
}

// 시스템 상태 조회
SystemStatusDto systemStatus = SystemStatusDto.of(
adminSystemService.isApiServerHealthy(),
adminSystemService.isDatabaseHealthy(),
adminSystemService.isElasticsearchHealthy(),
adminSystemService.isFileStorageHealthy()
);

// 통계 데이터 조회
long totalUserCount = adminUserService.getTotalUserCount();
long totalDeletedUserCount = adminUserService.getTotalDeletedUserCount();
long totalWaybleZoneCount = adminWaybleZoneService.getTotalWaybleZoneCounts();

model.addAttribute("adminUsername", session.getAttribute("adminUsername"));
model.addAttribute("systemStatus", systemStatus);
model.addAttribute("totalUserCount", totalUserCount);
model.addAttribute("totalDeletedUserCount", totalDeletedUserCount);
model.addAttribute("totalWaybleZoneCount", totalWaybleZoneCount);
return "admin/dashboard";
}

@PostMapping("/logout")
public String adminLogout(HttpSession session) {
session.invalidate();
log.info("관리자 로그아웃");
return "redirect:/admin";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package com.wayble.server.admin.controller.user;

import com.wayble.server.admin.dto.user.AdminUserDetailDto;
import com.wayble.server.admin.dto.user.AdminUserThumbnailDto;
import com.wayble.server.admin.service.AdminUserService;
import com.wayble.server.common.response.CommonResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import java.util.List;
import java.util.Optional;

@RestController
@RequiredArgsConstructor
@Validated
@RequestMapping("/api/v1/admin/users")
public class AdminUserController {

private final AdminUserService adminUserService;

@GetMapping("/count")
public CommonResponse<Long> getTotalUserCount() {
return CommonResponse.success(adminUserService.getTotalUserCount());
}

@GetMapping()
public CommonResponse<List<AdminUserThumbnailDto>> findByCondition(
@RequestParam(name = "page", defaultValue = "0") int page,
@RequestParam(name = "size", defaultValue = "100") int size)
{
return CommonResponse.success(adminUserService.findUsersByPage(page, size));
}

@GetMapping("/{userId}")
public CommonResponse<Optional<AdminUserDetailDto>> findUserById(@PathVariable("userId") long userId) {
return CommonResponse.success(adminUserService.findUserById(userId));
}

@GetMapping("/deleted/count")
public CommonResponse<Long> getTotalDeletedUserCount() {
return CommonResponse.success(adminUserService.getTotalDeletedUserCount());
}

@GetMapping("/deleted")
public CommonResponse<List<AdminUserThumbnailDto>> findDeletedUsers(
@RequestParam(name = "page", defaultValue = "0") int page,
@RequestParam(name = "size", defaultValue = "100") int size)
{
return CommonResponse.success(adminUserService.findDeletedUsersByPage(page, size));
}

@GetMapping("/deleted/{userId}")
public CommonResponse<Optional<AdminUserDetailDto>> findDeletedUserById(@PathVariable("userId") long userId) {
return CommonResponse.success(adminUserService.findDeletedUserById(userId));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
package com.wayble.server.admin.controller.user;

import com.wayble.server.admin.dto.user.AdminUserDetailDto;
import com.wayble.server.admin.dto.user.AdminUserPageDto;
import com.wayble.server.admin.service.AdminUserService;
import jakarta.servlet.http.HttpSession;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

import java.util.Optional;

@Slf4j
@Controller
@RequestMapping("/admin/users")
@RequiredArgsConstructor
public class AdminUserViewController {

private final AdminUserService adminUserService;

@GetMapping
public String getUsers(HttpSession session, Model model,
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "100") int size) {
// 로그인 확인
if (session.getAttribute("adminLoggedIn") == null) {
return "redirect:/admin";
}

// 페이징 데이터 조회
AdminUserPageDto pageData = adminUserService.getUsersWithPaging(page, size);

model.addAttribute("pageData", pageData);
model.addAttribute("adminUsername", session.getAttribute("adminUsername"));

log.debug("사용자 목록 조회 - 페이지: {}, 전체: {}", page, pageData.totalElements());

return "admin/user/users";
}

@GetMapping("/{id}")
public String getUserDetail(HttpSession session, Model model, @PathVariable Long id) {
// 로그인 확인
if (session.getAttribute("adminLoggedIn") == null) {
return "redirect:/admin";
}

Optional<AdminUserDetailDto> userOpt = adminUserService.findUserById(id);
if (userOpt.isEmpty()) {
return "redirect:/admin/user/users?error=notfound";
}

model.addAttribute("user", userOpt.get());
model.addAttribute("adminUsername", session.getAttribute("adminUsername"));

log.debug("사용자 상세 조회 - ID: {}", id);

return "admin/user/user-detail";
}

@GetMapping("/deleted")
public String getDeletedUsers(HttpSession session, Model model,
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "100") int size) {
// 로그인 확인
if (session.getAttribute("adminLoggedIn") == null) {
return "redirect:/admin";
}

// 페이징 데이터 조회
AdminUserPageDto pageData = adminUserService.getDeletedUsersWithPaging(page, size);

model.addAttribute("pageData", pageData);
model.addAttribute("adminUsername", session.getAttribute("adminUsername"));

log.debug("삭제된 사용자 목록 조회 - 페이지: {}, 전체: {}", page, pageData.totalElements());

return "admin/user/deleted-users";
}

@GetMapping("/deleted/{id}")
public String getDeletedUserDetail(HttpSession session, Model model, @PathVariable Long id) {
// 로그인 확인
if (session.getAttribute("adminLoggedIn") == null) {
return "redirect:/admin";
}

Optional<AdminUserDetailDto> userOpt = adminUserService.findDeletedUserById(id);
if (userOpt.isEmpty()) {
return "redirect:/admin/users/deleted?error=notfound";
}

model.addAttribute("user", userOpt.get());
model.addAttribute("adminUsername", session.getAttribute("adminUsername"));

log.debug("삭제된 사용자 상세 조회 - ID: {}", id);

return "admin/user/deleted-user-detail";
}

@PostMapping("/deleted/{id}/restore")
public String restoreUser(HttpSession session,
@PathVariable Long id,
RedirectAttributes redirectAttributes) {
// 로그인 확인
if (session.getAttribute("adminLoggedIn") == null) {
return "redirect:/admin";
}

try {
adminUserService.restoreUser(id);
redirectAttributes.addFlashAttribute("successMessage", "사용자가 성공적으로 복원되었습니다.");
log.info("사용자 복원 완료 - ID: {}, 관리자: {}", id, session.getAttribute("adminUsername"));
return "redirect:/admin/users/" + id;
} catch (Exception e) {
log.error("사용자 복원 실패 - ID: {}", id, e);
redirectAttributes.addFlashAttribute("errorMessage", "사용자 복원에 실패했습니다: " + e.getMessage());
return "redirect:/admin/users/deleted/" + id;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package com.wayble.server.admin.controller.wayblezone;

import com.wayble.server.admin.dto.wayblezone.AdminWaybleZoneCreateDto;
import com.wayble.server.admin.dto.wayblezone.AdminWaybleZoneDetailDto;
import com.wayble.server.admin.dto.wayblezone.AdminWaybleZoneThumbnailDto;
import com.wayble.server.admin.dto.wayblezone.AdminWaybleZoneUpdateDto;
import com.wayble.server.admin.service.AdminWaybleZoneService;
import com.wayble.server.common.response.CommonResponse;

import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;

import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import java.util.List;
import java.util.Optional;

@RestController
@RequiredArgsConstructor
@Validated
@RequestMapping("/api/v1/admin/wayble-zones")
public class AdminWaybleZoneController {

private final AdminWaybleZoneService adminWaybleZoneService;

@GetMapping()
public CommonResponse<List<AdminWaybleZoneThumbnailDto>> findByCondition(
@RequestParam(name = "page", defaultValue = "0") int page,
@RequestParam(name = "size", defaultValue = "20") int size)
{
return CommonResponse.success(adminWaybleZoneService.findWaybleZonesByPage(page, size));
}

@GetMapping("/count")
public CommonResponse<Long> findAllWaybleZoneCount() {
return CommonResponse.success(adminWaybleZoneService.getTotalWaybleZoneCounts());
}

@GetMapping("/{waybleZoneId}")
public CommonResponse<Optional<AdminWaybleZoneDetailDto>> findWaybleZoneById(@PathVariable("waybleZoneId") long waybleZoneId) {
return CommonResponse.success(adminWaybleZoneService.findWaybleZoneById(waybleZoneId));
}

@PostMapping
public CommonResponse<Long> createWaybleZone(@Valid @RequestBody AdminWaybleZoneCreateDto createDto) {
Long waybleZoneId = adminWaybleZoneService.createWaybleZone(createDto);
return CommonResponse.success(waybleZoneId);
}

@PutMapping("/{waybleZoneId}")
public CommonResponse<Long> updateWaybleZone(@PathVariable("waybleZoneId") Long waybleZoneId,
@Valid @RequestBody AdminWaybleZoneUpdateDto updateDto) {
// DTO의 ID와 URL의 ID가 일치하는지 확인
AdminWaybleZoneUpdateDto validatedDto = new AdminWaybleZoneUpdateDto(
waybleZoneId,
updateDto.zoneName(),
updateDto.contactNumber(),
updateDto.zoneType(),
updateDto.state(),
updateDto.city(),
updateDto.district(),
updateDto.streetAddress(),
updateDto.detailAddress(),
updateDto.latitude(),
updateDto.longitude(),
updateDto.mainImageUrl()
);

Long updatedWaybleZoneId = adminWaybleZoneService.updateWaybleZone(validatedDto);
return CommonResponse.success(updatedWaybleZoneId);
}
}
Loading