Skip to content
Merged
1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
# GJGJ_BE
스위프 8기 "끄적끄적"
# Test (Local)
17 changes: 13 additions & 4 deletions src/main/java/com/example/ggj_be/domain/auth/AuthService.java
Original file line number Diff line number Diff line change
@@ -1,18 +1,24 @@
package com.example.ggj_be.domain.auth;

import com.example.ggj_be.domain.auth.dto.SignUpRequest;
import com.example.ggj_be.domain.auth.dto.TokenVo;
import com.example.ggj_be.domain.member.Member;
import com.example.ggj_be.domain.member.repository.MemberRepository;
import com.example.ggj_be.global.exception.ApiException;
import com.example.ggj_be.global.jwt.JwtProvider;
import com.example.ggj_be.global.response.code.status.ErrorStatus;
import com.example.ggj_be.global.util.RedisUtil;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.transaction.Transactional;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import com.example.ggj_be.domain.enums.Role;
import org.springframework.util.StringUtils;


import java.time.LocalDate;
import static com.example.ggj_be.global.util.JwtProperties.ACCESS_HEADER_STRING;
import static com.example.ggj_be.global.util.JwtProperties.TOKEN_PREFIX;

Expand All @@ -23,15 +29,17 @@ public class AuthService {

private final JwtProvider jwtProvider;
private final RedisUtil redisUtil;
private final MemberRepository memberRepository;
private final BCryptPasswordEncoder passwordEncoder;

public TokenVo generateATAndRT(Member member) {
String accessToken = jwtProvider.generateAccessToken(Long.valueOf(member.getAccountid()), member.getRole());
String refreshToken = jwtProvider.generateRefreshToken(Long.valueOf(member.getAccountid()),
String accessToken = jwtProvider.generateAccessToken(member.getId(), member.getRole());
String refreshToken = jwtProvider.generateRefreshToken(member.getId(),
member.getRole());
Long expiration = jwtProvider.getExpiration(refreshToken);

log.info("===================== Add RefreshToken In Redis");
redisUtil.set(member.getAccountid(), refreshToken, expiration);
redisUtil.set(String.valueOf(member.getAccountid()), refreshToken, expiration);

return new TokenVo(accessToken, refreshToken, member.getRole().toString());
}
Expand Down Expand Up @@ -74,4 +82,5 @@ public TokenVo reIssueToken(String refreshToken) {
return new TokenVo(newAccessToken, newRefreshToken, role.toString());
}

}
}

Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.example.ggj_be.domain.auth.AuthService;
import com.example.ggj_be.domain.auth.dto.AuthRequest;
import com.example.ggj_be.domain.auth.dto.SignUpRequest;
import com.example.ggj_be.domain.auth.dto.TokenVo;
import com.example.ggj_be.domain.member.Member;
import com.example.ggj_be.domain.member.service.MemberCommandService;
Expand Down Expand Up @@ -45,4 +46,12 @@ public ApiResponse<String> logout(@AuthMember Member member, HttpServletRequest

return ApiResponse.onSuccess("성공적으로 로그아웃되었습니다.");
}


@Operation(summary = "회원가입 API", description = "새로운 사용자를 등록합니다.")
@PostMapping("/signup")
public ApiResponse<Member> register(@RequestBody SignUpRequest request) {
Member registeredMember = memberCommandService.signUp(request);
return ApiResponse.onSuccess(registeredMember);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ public class AuthRequest {
@Schema(description = " 로그인 요청 DTO")
@Getter
public static class LoginRequest{
@Schema(description = "로그인 아이디", example = "gjgj1234")
@Schema(description = "로그인 아이디(이메일)", example = "example@mail.com")
@NotBlank(message = "아이디가 입력되지 않았습니다.")
private String accountId;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package com.example.ggj_be.domain.auth.dto;

import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Getter;

import java.time.LocalDate;

@Getter
public class SignUpRequest {

@Schema(description = "사용자 이메일 (아이디)", example = "user@example.com")
@NotBlank(message = "아이디를 입력해주세요. (이메일 형식)")
@Email(message = "이메일 형식이 올바르지 않습니다.")
private String accountId;

@Schema(description = "비밀번호", example = "securepassword123")
@NotBlank(message = "비밀번호를 입력해주세요.")
private String password;

@Schema(description = "사용자 이름", example = "홍길동")
@NotBlank(message = "이름을 입력해주세요.")
private String nameKo;

@Schema(description = "전화번호", example = "010-0000-0000")
@NotBlank(message = "전화번호를 입력해주세요.")
private String memberNo;

@Schema(description = "생년월일 (YYYY-MM-DD)", example = "2000-01-01")
@NotNull(message = "생년월일을 입력해주세요.")
private LocalDate userBirth;

@Schema(description = "프로필 이미지 URL", example = "https://example.com/profile.jpg")
private String userImg;

@Schema(description = "서비스 이용 약관 동의 여부", example = "true")
@NotNull(message = "서비스 이용 약관 동의 여부를 입력해주세요.")
private Boolean agreeService;

@Schema(description = "개인정보 처리방침 동의 여부", example = "true")
@NotNull(message = "개인정보 처리방침 동의 여부를 입력해주세요.")
private Boolean agreeInfo;

}
54 changes: 54 additions & 0 deletions src/main/java/com/example/ggj_be/domain/board/Board.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package com.example.ggj_be.domain.board;


import jakarta.persistence.*;
import lombok.*;
import org.hibernate.annotations.ColumnDefault;

import java.time.LocalDateTime;

@Entity
@Builder
@Getter
@AllArgsConstructor
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Table(name = "board_tb",
uniqueConstraints = {
@UniqueConstraint(name = "UniqueBoardId", columnNames = {"board_id"})
}
)
public class Board {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "board_id")
private String boardId;

@Column(nullable = false)
private String category;

@Column(nullable = false)
private String title;

@Column(nullable = false)
private String content;

@Column(nullable = false)
private String author;

@Column(nullable = false)
@ColumnDefault("'0'")
private int views;

@Column(nullable = false)
@ColumnDefault("'0'")
private int replies;

@Column(nullable = false)
private LocalDateTime createdAt;

@Column(nullable = false)
private LocalDateTime updatedAt;



}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package com.example.ggj_be.domain.board.indivisual.controller;

import com.example.ggj_be.domain.auth.AuthService;
import com.example.ggj_be.domain.auth.dto.AuthRequest;
import com.example.ggj_be.domain.auth.dto.TokenVo;
import com.example.ggj_be.domain.member.Member;
import com.example.ggj_be.domain.member.service.MemberCommandService;
import com.example.ggj_be.domain.member.service.MemberQueryService;
import com.example.ggj_be.global.annotation.AuthMember;
import com.example.ggj_be.global.response.ApiResponse;
import io.swagger.v3.oas.annotations.Operation;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;

import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import io.swagger.v3.oas.annotations.tags.Tag;
@Tag(name = "개인 게시판 관련 API 명세서", description = "")
@RestController
@RequiredArgsConstructor
@RequestMapping("/api")
public class IndivisualController {

private final MemberQueryService memberQueryService;
private final MemberCommandService memberCommandService;
private final AuthService authService;

// @Operation(summary = "사용자/관리자 로그인 API", description = "일반 직원인 경우 EMPLOYEE, 관리자인 경우 ADMIN을 반환합니다.")
// @PostMapping("/post")
// public ApiResponse<TokenVo> login(@RequestBody AuthRequest.LoginRequest request) {
//
// Member member = memberQueryService.checkAccountIdAndPwd(request);
// TokenVo tokenVo = authService.generateATAndRT(member);
//
// return ApiResponse.onSuccess(tokenVo);
// }

}
2 changes: 1 addition & 1 deletion src/main/java/com/example/ggj_be/domain/enums/Role.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.example.ggj_be.domain.enums;

public enum Role {
EMPLOYEE, // 일반 직원
MEMBER, // 일반 회원
ADMIN, // 관리자
BUS // 사업체
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package com.example.ggj_be.domain.mail.controller;

import com.example.ggj_be.domain.mail.service.MailService;
import com.example.ggj_be.domain.member.dto.MemberRequest;
import com.example.ggj_be.global.response.ApiResponse;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.mail.MessagingException;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;


@Tag(name = "Email 관련 API 명세서", description = "인증 코드 전송, 검증 처리하는 API")
@RestController
@RequestMapping("/api/emails")
@RequiredArgsConstructor
public class MailController {

private final MailService mailService;

@Operation(summary = "이메일 인증 코드 전송", description = "비밀번호 변경 시 이메일 인증 단계가 필요함 / 인증코드 숫자 6자리를 전송합니다.")
@PostMapping
public ApiResponse<String> sendAuthCode(@RequestBody @Valid MemberRequest.AuthAccountId request)
throws MessagingException {

mailService.sendEmailMessage(request.getAccountId());

return ApiResponse.onSuccess("성공적으로 인증코드가 전송되었습니다.");
}

@Operation(summary = "이메일 인증 코드 검증", description = "전송받은 인증코드 6자리와 서버에서 관리하는 6자리를 비교합니다.")
@PostMapping("/verify")
public ApiResponse<String> verifyCode(@RequestBody @Valid MemberRequest.VerifyCode request) {

return ApiResponse.onSuccess(mailService.verifyAuthCode(request));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.example.ggj_be.domain.mail.service;

import com.example.ggj_be.domain.member.dto.MemberRequest;
import jakarta.mail.MessagingException;

public interface MailService {


void sendEmailMessage(String email) throws MessagingException;

String verifyAuthCode(MemberRequest.VerifyCode request);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package com.example.ggj_be.domain.mail.service;

import com.example.ggj_be.domain.member.dto.MemberRequest;
import com.example.ggj_be.global.exception.ApiException;
import com.example.ggj_be.global.response.code.status.ErrorStatus;
import com.example.ggj_be.global.util.RedisUtil;
import jakarta.mail.MessagingException;
import jakarta.mail.internet.MimeMessage;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.thymeleaf.spring6.SpringTemplateEngine;

import org.thymeleaf.context.Context;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Optional;

@Service
@RequiredArgsConstructor
@Slf4j
public class MailServiceImpl implements MailService {

private final JavaMailSender javaMailSender;
private final SpringTemplateEngine templateEngine;
private final RedisUtil redisUtil;

@Override
@Async
public void sendEmailMessage(String accountId) throws MessagingException {

String authCode = createCode();

MimeMessage message = javaMailSender.createMimeMessage();
String email = accountId;
message.addRecipients(MimeMessage.RecipientType.TO, email);
message.setSubject("[끄적끄적] 이메일 인증 - 인증 코드 전송");
message.setText(setContext(authCode), "UTF-8", "html");
javaMailSender.send(message);

redisUtil.setEmailCode(accountId, authCode);
}

@Override
public String verifyAuthCode(MemberRequest.VerifyCode request) {

String authCode = redisUtil.getEmailCode(request.getAccountId());

return Optional.ofNullable(authCode)
.filter(code -> code.equals(request.getAuthCode()))
.map(code -> "이메일 인증에 성공하였습니다.")
.orElseThrow(() -> new ApiException(ErrorStatus._MAIL_WRONG_CODE));
}

private String createCode() {
try {
SecureRandom random = SecureRandom.getInstanceStrong();
int authCode = random.nextInt(1000000);
log.info("===================== authCode: " + authCode);
return String.format("%06d", authCode);
} catch (NoSuchAlgorithmException e) {
throw new ApiException(ErrorStatus._MAIL_CREATE_CODE_ERROR);
}
}

private String setContext(String code) {
Context context = new Context();
context.setVariable("code", code);
return templateEngine.process("mailCode", context);
}


}
Loading