Merge branch 'backend' into 'master'

Revert "Merge branch 'Be/Report' into 'backend'"

See merge request s11-webmobile1-sub2/S11P12A701!191
This commit is contained in:
박정민 2024-08-13 15:42:53 +09:00
commit 464d1558f4
124 changed files with 5392 additions and 22 deletions

23
backend/.gitlab-ci.yml Normal file
View File

@ -0,0 +1,23 @@
deploy-to-server:
stage: deploy
only:
- backend
before_script:
- echo 'start deployment'
- whoami
script:
- cd /home/ubuntu/gitlab_runner/
- git pull origin backend
- cd backend
# - kill $(lsof -t -i:8000)
- ./gradlew build
# - cd /home/ubuntu/s03p12a112/backend/target/
# - setsid nohup java -jar backend-0.0.1-SNAPSHOT.jar > /dev/null 2>&1 &
# - cd /home/ubuntu/s03p12a112/frontend/
# - sudo npm install
# - sudo npm run build
# - sudo service nginx restart
after_script:
- echo 'deployment is done'
tags:
- edu

View File

@ -1,38 +1,50 @@
plugins {
id 'java'
id 'org.springframework.boot' version '3.3.1'
id 'io.spring.dependency-management' version '1.1.5'
id 'org.springframework.boot' version '3.2.7'
id 'io.spring.dependency-management' version '1.1.0'
}
group = 'com.edufocus'
group = 'io.openvidu'
version = '0.0.1-SNAPSHOT'
java {
toolchain {
languageVersion = JavaLanguageVersion.of(17)
}
}
configurations {
compileOnly {
extendsFrom annotationProcessor
}
}
description = 'Basic server application built for Java with Spring Boot'
sourceCompatibility = '17'
targetCompatibility = '17'
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-web'
compileOnly 'org.projectlombok:lombok'
runtimeOnly 'com.mysql:mysql-connector-j'
annotationProcessor 'org.projectlombok:lombok'
implementation 'org.openpnp:opencv:4.9.0-0'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'io.livekit:livekit-server:0.5.11'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'mysql:mysql-connector-java:8.0.33'
implementation 'org.springframework.boot:spring-boot-starter-websocket'
implementation 'org.springframework.boot:spring-boot-starter-amqp'
implementation 'org.springframework.boot:spring-boot-starter-reactor-netty'
testImplementation 'org.springframework.amqp:spring-rabbit-test'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
implementation group: 'io.livekit', name: 'livekit-server', version: '0.6.1'
implementation 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.4.0'
runtimeOnly group: 'io.jsonwebtoken', name: 'jjwt-jackson', version: '0.11.5'
runtimeOnly group: 'io.jsonwebtoken', name: 'jjwt-impl', version: '0.11.5'
implementation group: 'io.jsonwebtoken', name: 'jjwt-api', version: '0.11.5'
implementation group: 'org.springframework.boot', name: 'spring-boot-starter-mail', version: '3.2.5'
implementation 'org.springframework.boot:spring-boot-starter-mail'
implementation 'org.mindrot:jbcrypt:0.4'
implementation 'org.springframework.boot:spring-boot-starter-actuator'
implementation 'org.springframework.boot:spring-boot-starter-data-redis'
implementation group: 'org.glassfish.jaxb', name: 'jaxb-runtime', version: '4.0.5'
}
tasks.named('test') {
test {
useJUnitPlatform()
}

View File

@ -2,7 +2,11 @@ package com.edufocus.edufocus;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
import org.springframework.scheduling.annotation.EnableAsync;
@EnableAsync
@EnableJpaAuditing
@SpringBootApplication
public class EdufocusApplication {

View File

@ -0,0 +1,139 @@
package com.edufocus.edufocus.board.controller;
import com.edufocus.edufocus.board.entity.dto.*;
import com.edufocus.edufocus.board.entity.vo.Board;
import com.edufocus.edufocus.board.entity.vo.Comment;
import com.edufocus.edufocus.board.service.BoardService;
import com.edufocus.edufocus.user.util.JWTUtil;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.validation.constraints.Positive;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* @author Haneol Kim
*/
@RestController
@RequestMapping("/board")
public class BoardController {
private final JWTUtil jwtUtil;
private final BoardService boardService;
public BoardController(BoardService boardService, JWTUtil jwtUtil){
this.boardService = boardService;
this.jwtUtil = jwtUtil;
}
@GetMapping()
public ResponseEntity<List<ResponseBoardSummaryDto>> searchBoards(
@RequestParam(value = "category", required = false, defaultValue = "announcement") String category,
@RequestParam(value = "lectureId") long lectureId,
@RequestParam(value = "pageNo", required = false, defaultValue = "0") int pageNo
){
List<ResponseBoardSummaryDto> boardSummaries = boardService.findBoards(pageNo, category, lectureId);
return new ResponseEntity<>(boardSummaries, HttpStatus.OK);
}
@GetMapping(value = "/{boardId}")
public ResponseEntity<ResponseBoardDetailDto> getBoardDetail(
@PathVariable int boardId,
HttpServletRequest request
){
String token = request.getHeader("Authorization");
long userId = Long.parseLong(jwtUtil.getUserId(token));
ResponseBoardDetailDto responseBoardDetailDto = boardService.findBoardDetail(userId, boardId);
return new ResponseEntity<>(responseBoardDetailDto, HttpStatus.OK);
}
@PostMapping
public ResponseEntity<?> addBoard(
@RequestBody RequestBoardDto requestBoardDto,
HttpServletRequest request
){
String token = request.getHeader("Authorization");
long userId = Long.parseLong(jwtUtil.getUserId(token));
boardService.createBoard(userId, requestBoardDto);
return new ResponseEntity<>(HttpStatus.OK);
}
@PutMapping(value = "/{boardId}")
public ResponseEntity<?> updateBoard(
@PathVariable long boardId,
@RequestBody RequestBoardUpdateDto requestBoardUpdateDto
){
boardService.updateBoard(boardId, requestBoardUpdateDto);
return new ResponseEntity<>(HttpStatus.OK);
}
@DeleteMapping(value = "/{boardId}")
public ResponseEntity<?> deleteBoard(
@PathVariable int boardId
){
boardService.deleteBoard(boardId);
return new ResponseEntity<>(HttpStatus.OK);
}
@GetMapping(value = "/comment/{boardId}")
public ResponseEntity<List<ResponseCommentDto>> getComments(
@PathVariable int boardId,
HttpServletRequest request
){
String token = request.getHeader("Authorization");
long userId = Long.parseLong(jwtUtil.getUserId(token));
List<ResponseCommentDto> comments = boardService.findComments(userId, boardId);
return new ResponseEntity<>(comments, HttpStatus.OK);
}
@PostMapping(value = "/comment/{boardId}")
public ResponseEntity<?> addComment(
@PathVariable int boardId,
@RequestBody RequestCommentDto requestCommentDto,
HttpServletRequest request
){
String token = request.getHeader("Authorization");
long userId = Long.parseLong(jwtUtil.getUserId(token));
boardService.createComment(userId, boardId, requestCommentDto);
return new ResponseEntity<>(HttpStatus.OK);
}
@PutMapping(value = "/comment/{commentId}")
public ResponseEntity<?> updateComment(
@PathVariable int commentId,
@RequestBody RequestCommentDto requestCommentDto
){
boardService.updateComment(commentId, requestCommentDto);
return new ResponseEntity<>(HttpStatus.OK);
}
@DeleteMapping(value = "/comment/{commentId}")
public ResponseEntity<?> deleteComment(
@PathVariable int commentId
){
boardService.deleteComment(commentId);
return new ResponseEntity<>(HttpStatus.OK);
}
@ExceptionHandler()
public ResponseEntity<?> NoContentException(Exception e){
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
}

View File

@ -0,0 +1,15 @@
package com.edufocus.edufocus.board.entity.dto;
import com.edufocus.edufocus.board.entity.vo.Board;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class RequestBoardDto {
private long lectureId;
private String title;
private String category;
private String content;
}

View File

@ -0,0 +1,12 @@
package com.edufocus.edufocus.board.entity.dto;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class RequestBoardUpdateDto {
private String title;
private String content;
}

View File

@ -0,0 +1,10 @@
package com.edufocus.edufocus.board.entity.dto;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class RequestCommentDto {
private String content;
}

View File

@ -0,0 +1,23 @@
package com.edufocus.edufocus.board.entity.dto;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import java.time.LocalDateTime;
import java.util.Date;
@Builder
@Getter
@Setter
@AllArgsConstructor
public class ResponseBoardDetailDto {
private long id;
private String name;
private String title;
private String content;
private boolean isMine;
private Date createdAt;
private Date modifiedAt;
}

View File

@ -0,0 +1,19 @@
package com.edufocus.edufocus.board.entity.dto;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import java.time.LocalDateTime;
import java.util.Date;
@Builder
@Getter
@Setter
public class ResponseBoardSummaryDto {
private long id;
private String name;
private String title;
private Date createdAt;
}

View File

@ -0,0 +1,21 @@
package com.edufocus.edufocus.board.entity.dto;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.Date;
@Builder
@Getter
@Setter
public class ResponseCommentDto {
private long id;
private String name;
private String content;
private boolean isMine;
private Date createAt;
private Date modifiedAt;
}

View File

@ -0,0 +1,90 @@
package com.edufocus.edufocus.board.entity.vo;
import com.edufocus.edufocus.board.entity.dto.ResponseBoardDetailDto;
import com.edufocus.edufocus.board.entity.dto.ResponseBoardSummaryDto;
import com.edufocus.edufocus.lecture.entity.Lecture;
import com.edufocus.edufocus.user.model.entity.vo.User;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import jakarta.persistence.*;
import lombok.*;
import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.OnDelete;
import org.hibernate.annotations.OnDeleteAction;
import org.hibernate.annotations.UpdateTimestamp;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
import java.time.LocalDateTime;
import java.util.Date;
import java.util.List;
@Entity
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Setter
@EntityListeners(AuditingEntityListener.class)
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY)
public class Board {
@Id
@GeneratedValue
private long id;
@Column(nullable = false)
private String title;
@Column(nullable = false)
private String category;
@Column(columnDefinition = "TEXT", nullable = false)
private String content;
@Column(nullable = true)
private int viewCount;
@Column
@CreationTimestamp
@Temporal(TemporalType.TIMESTAMP)
private Date createdAt;
@Column
@UpdateTimestamp
@Temporal(TemporalType.TIMESTAMP)
private Date modifiedAt;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id")
private User user;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "lecture_id")
@OnDelete(action = OnDeleteAction.CASCADE)
private Lecture lecture;
@OneToMany(mappedBy = "board")
private List<Comment> comments;
public ResponseBoardSummaryDto makeSummaryDto() {
return ResponseBoardSummaryDto.builder()
.id(id)
.title(title)
.name(user.getName())
.createdAt(createdAt)
.build();
}
public ResponseBoardDetailDto makeDetailDto(long userId) {
return ResponseBoardDetailDto.builder()
.id(id)
.name(user.getName())
.title(title)
.content(content)
.isMine(user.getId() == userId)
.createdAt(createdAt)
.modifiedAt(modifiedAt)
.build();
}
}

View File

@ -0,0 +1,65 @@
package com.edufocus.edufocus.board.entity.vo;
import com.edufocus.edufocus.board.entity.dto.ResponseCommentDto;
import com.edufocus.edufocus.user.model.entity.vo.User;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.OnDelete;
import org.hibernate.annotations.OnDeleteAction;
import org.hibernate.annotations.UpdateTimestamp;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import java.time.LocalDateTime;
import java.util.Date;
@Entity
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Setter
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY)
public class Comment {
@Id
@GeneratedValue
private long id;
@Column(columnDefinition = "TEXT", nullable = false)
private String content;
@Column
@CreationTimestamp
@Temporal(TemporalType.TIMESTAMP)
private Date createdAt;
@Column
@Temporal(TemporalType.TIMESTAMP)
private Date modifiedAt;
@ManyToOne
@JoinColumn(name = "user_id")
User user;
@ManyToOne
@JoinColumn(name = "board_id")
@OnDelete(action = OnDeleteAction.CASCADE)
Board board;
public ResponseCommentDto makeCommentDto(long userId) {
return ResponseCommentDto.builder()
.id(id)
.name(user.getName())
.content(content)
.isMine(user.getId() == userId)
.createAt(createdAt)
.modifiedAt(modifiedAt)
.build();
}
}

View File

@ -0,0 +1,15 @@
package com.edufocus.edufocus.board.repository;
import com.edufocus.edufocus.board.entity.vo.Board;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
public interface BoardRepository extends JpaRepository<Board, Long> {
@Query("select b from Board b join fetch b.user where b.category =:category and b.lecture.id=:lectureId")
Page<Board> findByLectureIdAndCategory(Long lectureId, String category, Pageable pageable);
}

View File

@ -0,0 +1,14 @@
package com.edufocus.edufocus.board.repository;
import com.edufocus.edufocus.board.entity.vo.Comment;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import java.util.List;
public interface CommentRepository extends JpaRepository<Comment, Long> {
@Query("select c from Comment c join fetch c.user where c.board.id =:boardId")
List<Comment> findByBoardId(long boardId);
}

View File

@ -0,0 +1,27 @@
package com.edufocus.edufocus.board.service;
import com.edufocus.edufocus.board.entity.dto.*;
import java.util.List;
public interface BoardService {
public void createBoard(long userId, RequestBoardDto requestBoardDto);
public List<ResponseBoardSummaryDto> findBoards(int pageNo, String category, long lectureId);
public ResponseBoardDetailDto findBoardDetail(long userId, long boardId);
public void updateBoard(long boardId, RequestBoardUpdateDto requestBoardUpdateDto);
public void deleteBoard(long boardId);
public void createComment(long userId, long boardId, RequestCommentDto requestCommentDto);
public List<ResponseCommentDto> findComments(long userId, long boardId);
public void updateComment(long commentId, RequestCommentDto requestCommentDto);
public void deleteComment(long commentId);
}

View File

@ -0,0 +1,132 @@
package com.edufocus.edufocus.board.service;
import com.edufocus.edufocus.board.entity.dto.*;
import com.edufocus.edufocus.board.entity.vo.Board;
import com.edufocus.edufocus.board.entity.vo.Comment;
import com.edufocus.edufocus.board.repository.BoardRepository;
import com.edufocus.edufocus.board.repository.CommentRepository;
import com.edufocus.edufocus.lecture.entity.Lecture;
import com.edufocus.edufocus.lecture.repository.LectureRepository;
import com.edufocus.edufocus.user.model.entity.vo.User;
import com.edufocus.edufocus.user.model.repository.UserRepository;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.stream.Collectors;
@Service
public class BoardServiceImpl implements BoardService {
private static final int PAGE_SIZE = 10;
private final BoardRepository boardRepository;
private final CommentRepository commentRepository;
private final UserRepository userRepository;
private final LectureRepository lectureRepository;
public BoardServiceImpl(BoardRepository boardRepository, CommentRepository commentRepository, UserRepository userRepository, LectureRepository lectureRepository){
this.boardRepository = boardRepository;
this.commentRepository = commentRepository;
this.userRepository = userRepository;
this.lectureRepository = lectureRepository;
}
@Transactional
public List<ResponseBoardSummaryDto> findBoards(int pageNo, String category, long lectureId) {
Pageable pageable = PageRequest.of(pageNo, PAGE_SIZE, Sort.by("id").descending());
List<Board> boards = boardRepository.findByLectureIdAndCategory(lectureId, category, pageable).getContent();
return boards.stream().map(Board::makeSummaryDto)
.collect(Collectors.toList());
}
@Transactional
public ResponseBoardDetailDto findBoardDetail(long userId, long boardId) {
return boardRepository.findById(boardId)
.orElseThrow(NoSuchElementException::new)
.makeDetailDto(userId);
}
@Transactional
public void createBoard(long userId, RequestBoardDto requestBoardDto) {
User user = userRepository.getReferenceById(userId);
Lecture lecture = lectureRepository.getReferenceById(requestBoardDto.getLectureId());
Board board = Board.builder()
.title(requestBoardDto.getTitle())
.category(requestBoardDto.getCategory())
.content(requestBoardDto.getContent())
.user(user)
.lecture(lecture)
.build();
boardRepository.save(board);
}
@Transactional
public void updateBoard(long boardId, RequestBoardUpdateDto requestBoardUpdateDto) {
Board board = boardRepository.getReferenceById(boardId);
board.setTitle(requestBoardUpdateDto.getTitle());
board.setContent(requestBoardUpdateDto.getContent());
boardRepository.save(board);
}
@Transactional
public void deleteBoard(long boardId) {
Board board = boardRepository.getReferenceById(boardId);
boardRepository.delete(board);
}
@Transactional
public List<ResponseCommentDto> findComments(long userId, long boardId) {
return commentRepository.findByBoardId(boardId).stream()
.map((Comment c) -> c.makeCommentDto(userId))
.collect(Collectors.toList());
}
@Transactional
public void createComment(long userId, long boardId, RequestCommentDto requestCommentDto) {
User user = userRepository.getReferenceById(userId);
Board board = boardRepository.getReferenceById(boardId);
Comment comment = Comment.builder()
.content(requestCommentDto.getContent())
.board(board)
.user(user)
.build();
commentRepository.save(comment);
}
@Transactional
public void updateComment(long commentId, RequestCommentDto requestCommentDto) {
Comment comment = commentRepository.getReferenceById(commentId);
comment.setContent(requestCommentDto.getContent());
commentRepository.save(comment);
}
@Transactional
public void deleteComment(long commentId) {
Comment comment = commentRepository.getReferenceById(commentId);
commentRepository.delete(comment);
}
}

View File

@ -0,0 +1,32 @@
package com.edufocus.edufocus.global.config;
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import java.util.concurrent.Executor;
@Configuration
@EnableWebMvc
public class AsyncConfig implements AsyncConfigurer {
@Override
@Bean(name = "mailExecutor")
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(5);
executor.setMaxPoolSize(10);
executor.setQueueCapacity(500);
executor.setThreadNamePrefix("MailExecutor-");
executor.initialize();
return executor;
}
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return AsyncConfigurer.super.getAsyncUncaughtExceptionHandler();
}
}

View File

@ -0,0 +1,17 @@
package com.edufocus.edufocus.global.config;
import com.edufocus.edufocus.global.properties.ImagePathProperties;
import com.edufocus.edufocus.global.properties.MailProperties;
import com.edufocus.edufocus.global.properties.RabbitMQProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;
@Configuration
@EnableConfigurationProperties({
RabbitMQProperties.class,
ImagePathProperties.class,
MailProperties.class
})
public class PropertiesConfig {
}

View File

@ -0,0 +1,22 @@
package com.edufocus.edufocus.global.config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
@Configuration
public class RedisConfig {
@Value("${spring.data.redis.host}")
private String host;
@Value("${spring.data.redis.port}")
private int port;
@Bean
public RedisConnectionFactory redisConnectionFactory() {
return new LettuceConnectionFactory(host, port);
}
}

View File

@ -0,0 +1,15 @@
package com.edufocus.edufocus.global.constant;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
@Getter
@RequiredArgsConstructor
public enum RabbitMQConstant {
CHAT_QUEUE_NAME("chat.queue"),
CHAT_EXCHANGE("chat.exchange"),
ROUTING_KEY("*.room.*"),
ROUTING_KEY_PREFIX("*.room.");;
private final String constant;
}

View File

@ -0,0 +1,12 @@
package com.edufocus.edufocus.global.properties;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.springframework.boot.context.properties.ConfigurationProperties;
@Getter
@RequiredArgsConstructor
@ConfigurationProperties(prefix = "image")
public class ImagePathProperties {
private final String path;
}

View File

@ -0,0 +1,15 @@
package com.edufocus.edufocus.global.properties;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.springframework.boot.context.properties.ConfigurationProperties;
@Getter
@RequiredArgsConstructor
@ConfigurationProperties(prefix = "spring.mail")
public class MailProperties {
private final String host;
private final Integer port;
private final String name;
private final String password;
}

View File

@ -0,0 +1,16 @@
package com.edufocus.edufocus.global.properties;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.springframework.boot.context.properties.ConfigurationProperties;
@Getter
@RequiredArgsConstructor
@ConfigurationProperties(prefix = "spring.rabbitmq")
public class RabbitMQProperties {
private final String host;
private final Integer port;
private final String username;
private final String password;
}

View File

@ -0,0 +1,107 @@
package com.edufocus.edufocus.lecture.controller;
import com.edufocus.edufocus.lecture.entity.Lecture;
import com.edufocus.edufocus.lecture.entity.LectureCreateRequest;
import com.edufocus.edufocus.lecture.entity.LectureSearchResponse;
import com.edufocus.edufocus.lecture.entity.LectureDetailResponse;
import com.edufocus.edufocus.lecture.service.LectureService;
import com.edufocus.edufocus.user.util.JWTUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.util.ArrayList;
import java.util.List;
@RestController
@RequestMapping("/lecture")
@Slf4j
@RequiredArgsConstructor
public class LectureController {
private final LectureService lectureService;
private final JWTUtil jwtUtil;
@PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public ResponseEntity<?> createLecture(@RequestHeader("Authorization") String accessToken, @RequestPart LectureCreateRequest lectureCreateRequest
, @RequestPart(value = "image", required = false) MultipartFile image) throws Exception {
Long userId = Long.parseLong(jwtUtil.getUserId(accessToken));
Lecture lecture = lectureService.findLectureByTitle(lectureCreateRequest.getTitle());
if (lecture != null) {
return new ResponseEntity<>(HttpStatus.CONFLICT);
}
if (image != null && !image.getContentType().startsWith("image")) {
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
}
lectureService.createLecture(userId, lectureCreateRequest, image);
return new ResponseEntity<>(HttpStatus.CREATED);
}
@PutMapping("/{lectureId}")
public ResponseEntity<?> updateLecture(@RequestHeader("Authorization") String accessToken, @PathVariable Long lectureId, @RequestBody LectureCreateRequest lectureCreateRequest) {
Long userId = Long.parseLong(jwtUtil.getUserId(accessToken));
if (!lectureService.updateLecture(userId, lectureId, lectureCreateRequest)) {
return new ResponseEntity<>(HttpStatus.UNAUTHORIZED);
}
return new ResponseEntity<>(HttpStatus.OK);
}
@DeleteMapping("/{lectureId}")
public ResponseEntity<?> deleteLecture(@RequestHeader("Authorization") String accessToken, @PathVariable long lectureId) {
Long userId = Long.parseLong(jwtUtil.getUserId(accessToken));
if (!lectureService.deleteLecture(userId, lectureId)) {
return new ResponseEntity<>(HttpStatus.UNAUTHORIZED);
}
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
@GetMapping
public ResponseEntity<?> findAllLecture() {
List<LectureSearchResponse> lectures = lectureService.findAllLecture();
return new ResponseEntity<>(lectures, HttpStatus.OK);
}
@GetMapping("/{lectureId}")
public ResponseEntity<?> findById(@RequestHeader(value = "Authorization", required = false) String accessToken, @PathVariable long lectureId) {
Long userId = null;
if (accessToken != null) {
userId = Long.parseLong(jwtUtil.getUserId(accessToken));
}
LectureDetailResponse lectureDetailResponse = lectureService.findLectureById(userId, lectureId);
return new ResponseEntity<>(lectureDetailResponse, HttpStatus.OK);
}
@GetMapping("/mylecture")
public ResponseEntity<?> findMyLecture(@RequestHeader(value = "Authorization", required = false) String accessToken) {
if (accessToken == null) {
return new ResponseEntity<>(new ArrayList<>(), HttpStatus.OK);
}
Long userId = Long.parseLong(jwtUtil.getUserId(accessToken));
List<LectureSearchResponse> myLectures = lectureService.findMyLecture(userId);
return new ResponseEntity<>(myLectures, HttpStatus.OK);
}
@GetMapping("/isLive/{lectureId}")
public ResponseEntity<Boolean> checkIsLive(@PathVariable long lectureId) {
return new ResponseEntity<>(lectureService.getState(lectureId), HttpStatus.OK);
}
}

View File

@ -0,0 +1,53 @@
package com.edufocus.edufocus.lecture.entity;
import com.edufocus.edufocus.user.model.entity.vo.User;
import jakarta.persistence.*;
import lombok.*;
import java.util.Date;
@Entity
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Lecture {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column
private Long id;
@ManyToOne
@JoinColumn(name = "user_id")
private User user;
@Column
private String title;
@Column(columnDefinition = "text")
private String description;
@Column(columnDefinition = "text")
private String plan;
@Column
private String image;
@Column(name = "start_date")
@Temporal(TemporalType.DATE)
private Date startDate;
@Column(name = "end_date")
@Temporal(TemporalType.DATE)
private Date endDate;
@Column
private String time;
@Column(columnDefinition = "boolean default false")
private boolean online;
}

View File

@ -0,0 +1,26 @@
package com.edufocus.edufocus.lecture.entity;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import java.util.Date;
@Getter
@NoArgsConstructor
@AllArgsConstructor
public class LectureCreateRequest {
private String title;
private String description;
private String plan;
private Date startDate;
private Date endDate;
private String time;
}

View File

@ -0,0 +1,37 @@
package com.edufocus.edufocus.lecture.entity;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import java.util.Date;
@Getter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class LectureDetailResponse {
private Long id;
private String teacherName;
private String title;
private String description;
private String plan;
private String image;
private Date startDate;
private Date endDate;
private String time;
private boolean online;
private String status;
}

View File

@ -0,0 +1,19 @@
package com.edufocus.edufocus.lecture.entity;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
@Getter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class LectureSearchResponse {
private Long id;
private String title;
private String image;
}

View File

@ -0,0 +1,9 @@
package com.edufocus.edufocus.lecture.entity;
public enum UserStatus {
MANAGED_BY_ME, // 내가 관리하는 강의 - 강사
MANAGED_BY_OTHERS, // 내가 관리하지 않은 강의 - 강사
ENROLLED, // 내가 수강 중인 강의 - 학생
PENDING, // 내가 수강신청하고 승인 대기 중인 강의 - 학생
NOT_ENROLLED // 내가 수강신청하지 않은 강의 - 학생/비로그인
}

View File

@ -0,0 +1,20 @@
package com.edufocus.edufocus.lecture.repository;
import com.edufocus.edufocus.lecture.entity.Lecture;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public interface LectureRepository extends JpaRepository<Lecture, Long> {
Lecture findByTitle(String title);
List<Lecture> findAllByUserId(Long userId);
List<Lecture> findLecturesByUserIdOrderById(Long userId);
Lecture findByIdAndUserId(long id, long userId);
}

View File

@ -0,0 +1,39 @@
package com.edufocus.edufocus.lecture.service;
import com.edufocus.edufocus.lecture.entity.Lecture;
import com.edufocus.edufocus.lecture.entity.LectureCreateRequest;
import com.edufocus.edufocus.lecture.entity.LectureSearchResponse;
import com.edufocus.edufocus.lecture.entity.LectureDetailResponse;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import java.util.List;
@Service
public interface LectureService {
void createLecture(long userId, LectureCreateRequest lectureCreateRequest, MultipartFile image) throws Exception;
boolean updateLecture(long userId, long lectureId, LectureCreateRequest lectureCreateRequest);
boolean deleteLecture(long userId, long LectureId);
List<LectureSearchResponse> findAllLecture();
String getUserStatus(Long userId, Lecture lecture);
LectureDetailResponse findLectureById(Long userId, long lectureId);
List<LectureSearchResponse> findMyLecture(long userId);
Lecture findLectureByTitle(String title);
void startClass(Long lectureId);
void stopClass(Long lectureId);
boolean getState(Long lectureId);
boolean checkTeacher(Long userId, Long lectureId);
}

View File

@ -0,0 +1,302 @@
package com.edufocus.edufocus.lecture.service;
import com.edufocus.edufocus.global.properties.ImagePathProperties;
import com.edufocus.edufocus.lecture.entity.*;
import com.edufocus.edufocus.lecture.repository.LectureRepository;
import com.edufocus.edufocus.registration.entity.Registration;
import com.edufocus.edufocus.registration.entity.RegistrationStatus;
import com.edufocus.edufocus.registration.repository.RegistrationRepository;
import com.edufocus.edufocus.user.model.entity.vo.User;
import com.edufocus.edufocus.user.model.entity.vo.UserRole;
import com.edufocus.edufocus.user.model.repository.UserRepository;
import jakarta.transaction.Transactional;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.IOException;
import java.util.*;
import java.util.List;
@Service
@Transactional
@RequiredArgsConstructor
public class LectureServiceImpl implements LectureService {
private final ImagePathProperties imagePathProperties;
private final LectureRepository lectureRepository;
private final UserRepository userRepository;
private final RegistrationRepository registrationRepository;
@Override
public void createLecture(long userId, LectureCreateRequest lectureCreateRequest, MultipartFile image) throws IOException {
User user = userRepository.findById(userId).orElse(null);
Lecture lecture = Lecture.builder()
.user(user)
.title(lectureCreateRequest.getTitle())
.description(lectureCreateRequest.getDescription())
.plan(lectureCreateRequest.getPlan())
.startDate(lectureCreateRequest.getStartDate())
.endDate(lectureCreateRequest.getEndDate())
.time(lectureCreateRequest.getTime())
.build();
if (image != null) {
String uid = UUID.randomUUID().toString();
String imagePath = imagePathProperties.getPath();
File checkPathFile = new File(imagePath);
if (!checkPathFile.exists()) {
checkPathFile.mkdirs();
}
File savingImage = new File(imagePath + "/" + uid + "_" + image.getOriginalFilename());
image.transferTo(savingImage.toPath());
String savePath = savingImage.toPath().toString();
lecture.setImage(savePath);
}
lectureRepository.save(lecture);
}
@Override
public boolean updateLecture(long userId, long lectureId, LectureCreateRequest lectureCreateRequest) {
Optional<Lecture> findLecture = lectureRepository.findById(lectureId);
if (findLecture.isEmpty() || findLecture.get().getUser().getId() != userId) {
return false;
}
Lecture lecture = findLecture.get();
if (lectureCreateRequest.getTitle() != null) {
lecture.setTitle(lectureCreateRequest.getTitle());
}
if (lectureCreateRequest.getDescription() != null) {
lecture.setDescription(lectureCreateRequest.getDescription());
}
if (lectureCreateRequest.getPlan() != null) {
lecture.setPlan(lectureCreateRequest.getPlan());
}
if (lectureCreateRequest.getStartDate() != null) {
lecture.setStartDate(lectureCreateRequest.getStartDate());
}
if (lectureCreateRequest.getEndDate() != null) {
lecture.setEndDate(lectureCreateRequest.getEndDate());
}
if (lectureCreateRequest.getTime() != null) {
lecture.setTime(lectureCreateRequest.getTime());
}
lectureRepository.save(lecture);
return true;
}
@Override
public boolean deleteLecture(long userId, long lectureId) {
Optional<Lecture> findLecture = lectureRepository.findById(lectureId);
if (findLecture.isEmpty()) {
return false;
}
Lecture lecture = findLecture.get();
if (lecture.getUser().getId() != userId) {
return false;
}
String image = lecture.getImage();
if (image != null) {
File file = new File(lecture.getImage());
file.delete();
}
lectureRepository.deleteById(lectureId);
return true;
}
@Override
public List<LectureSearchResponse> findAllLecture() {
List<Lecture> lectureList = lectureRepository.findAll(Sort.by(Sort.Direction.DESC, "id"));
List<LectureSearchResponse> lectureSearchResponseList = new ArrayList<>();
for (Lecture lecture : lectureList) {
LectureSearchResponse lectureSearchResponse = LectureSearchResponse.builder()
.id(lecture.getId())
.title(lecture.getTitle())
.image(lecture.getImage()).build();
lectureSearchResponseList.add(lectureSearchResponse);
}
return lectureSearchResponseList;
}
@Override
public String getUserStatus(Long userId, Lecture lecture) {
if (userId == null) {
return String.valueOf(UserStatus.NOT_ENROLLED);
}
User user = userRepository.findById(userId).orElseThrow(NoSuchElementException::new);
UserRole userRole = user.getRole();
if (userRole == UserRole.ADMIN) {
if (lecture.getUser() == user) {
return String.valueOf(UserStatus.MANAGED_BY_ME);
}
return String.valueOf(UserStatus.MANAGED_BY_OTHERS);
}
Registration registration = registrationRepository.findByUserIdAndLectureId(userId, lecture.getId());
if (registration == null) {
return String.valueOf(UserStatus.NOT_ENROLLED);
}
if (registration.getStatus() == RegistrationStatus.ACCEPTED) {
return String.valueOf(UserStatus.ENROLLED);
}
return String.valueOf(UserStatus.PENDING);
}
@Override
public LectureDetailResponse findLectureById(Long userId, long lectureId) {
Lecture lecture = lectureRepository.findById(lectureId).orElseThrow(NoSuchElementException::new);
String userStatus = getUserStatus(userId, lecture);
return LectureDetailResponse.builder()
.id(lecture.getId())
.title(lecture.getTitle())
.description(lecture.getDescription())
.plan(lecture.getPlan())
.image(lecture.getImage())
.startDate(lecture.getStartDate())
.endDate(lecture.getEndDate())
.time(lecture.getTime())
.online(lecture.isOnline())
.teacherName(lecture.getUser().getName())
.status(userStatus)
.build();
}
public List<LectureSearchResponse> findMyLecture(long userId) {
User user = userRepository.findById(userId).orElseThrow(NoSuchElementException::new);
List<LectureSearchResponse> myLectureList = new ArrayList<>();
if (user.getRole() == UserRole.ADMIN) {
List<Lecture> lectureList = lectureRepository.findLecturesByUserIdOrderById(userId);
for (Lecture lecture : lectureList) {
LectureSearchResponse lectureSearchResponse = LectureSearchResponse.builder()
.id(lecture.getId())
.title(lecture.getTitle())
.image(lecture.getImage()).build();
myLectureList.add(lectureSearchResponse);
}
} else {
List<Registration> registrationList = registrationRepository.findAllByUserId(userId);
for (Registration registration : registrationList) {
Lecture lecture = registration.getLecture();
if (registration.getStatus() == RegistrationStatus.ACCEPTED) {
LectureSearchResponse lectureSearchResponse = LectureSearchResponse.builder()
.id(lecture.getId())
.title(lecture.getTitle())
.image(lecture.getImage()).build();
myLectureList.add(lectureSearchResponse);
}
}
Collections.sort(myLectureList, new Comparator<LectureSearchResponse>() {
@Override
public int compare(LectureSearchResponse lsr1, LectureSearchResponse lsr2) {
long lsr1Id = lsr1.getId();
long lsr2Id = lsr2.getId();
if (lsr2Id > lsr1Id)
return 1;
else if (lsr2Id == lsr1Id)
return 0;
return -1;
}
});
}
return myLectureList;
}
@Override
public Lecture findLectureByTitle(String title) {
return lectureRepository.findByTitle(title);
}
@Override
public void startClass(Long id) {
Optional<Lecture> lecture = lectureRepository.findById(id);
if (!lecture.isPresent()) {
throw new RuntimeException("Lecture not found with id: " + id);
}
Lecture l;
l = lecture.get();
if (l.isOnline() == false) {
l.setOnline(true);
}
lectureRepository.save(l);
}
@Override
public void stopClass(Long id) {
Optional<Lecture> lecture = lectureRepository.findById(id);
if (!lecture.isPresent()) {
throw new RuntimeException("Lecture not found with id: " + id);
}
Lecture l;
l = lecture.get();
if (l.isOnline()) {
l.setOnline(false);
}
lectureRepository.save(l);
}
@Override
public boolean getState(Long lectureId) {
Lecture lecture = lectureRepository.findById(lectureId).orElse(null);
return lecture.isOnline();
}
@Override
public boolean checkTeacher(Long userId, Long lectureId) {
Optional<Lecture> lecture = lectureRepository.findById(lectureId);
return lecture.isPresent() && lecture.get().getUser().getId() == userId;
}
}

View File

@ -0,0 +1,39 @@
package com.edufocus.edufocus.mail.controller;
import com.edufocus.edufocus.mail.service.MailService;
import com.edufocus.edufocus.user.model.service.UserService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
@Slf4j
@RestController
@RequestMapping("/mail")
@RequiredArgsConstructor
public class MailController {
private final MailService mailService;
private final UserService userService;
@PostMapping("/sendcode")
public ResponseEntity<?> sendMail(@RequestParam String email) {
if (!userService.isEmailExist(email)) {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
mailService.sendMail(email);
return new ResponseEntity<>(HttpStatus.OK);
}
@GetMapping("/verify")
public ResponseEntity<?> verifyCode(@RequestParam String code, @RequestParam String email) {
if (!mailService.verifyCode(code, email)) {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
return new ResponseEntity<>(HttpStatus.OK);
}
}

View File

@ -0,0 +1,13 @@
package com.edufocus.edufocus.mail.service;
import org.springframework.stereotype.Service;
@Service
public interface MailService {
void sendMail(String to);
String createRandomCode();
boolean verifyCode(String code, String email);
}

View File

@ -0,0 +1,76 @@
package com.edufocus.edufocus.mail.service;
import com.edufocus.edufocus.redis.util.RedisUtil;
import com.edufocus.edufocus.user.model.repository.UserRepository;
import com.edufocus.edufocus.user.model.service.UserService;
import lombok.RequiredArgsConstructor;
import lombok.ToString;
import lombok.extern.slf4j.Slf4j;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import java.util.Random;
@Service
@Slf4j
@ToString
@RequiredArgsConstructor
public class MailServiceImpl implements MailService {
private final JavaMailSender mailSender;
private final UserRepository userRepository;
private final UserService userService;
private final RedisUtil redisUtil;
@Override
@Async("mailExecutor")
public void sendMail(String email) {
String code = createRandomCode();
if (redisUtil.exists(email)) {
redisUtil.deleteData(redisUtil.getData(email));
}
redisUtil.setDataExpire(code, email, 60 * 5L);
redisUtil.setDataExpire(email, code, 60 * 5L);
SimpleMailMessage mail = createEmail(email, "[EDUFOCUS] 비밀번호 찾기 안내", code);
mailSender.send(mail);
}
@Override
public boolean verifyCode(String code, String email) {
String registedEmail = redisUtil.getData(code);
return registedEmail != null && registedEmail.equals(email);
}
private SimpleMailMessage createEmail(String to, String title, String code) {
SimpleMailMessage message = new SimpleMailMessage();
message.setFrom("EDUFOCUS");
message.setTo(to);
message.setSubject(title);
message.setText("인증번호 6자리입니다 : " + code);
return message;
}
@Override
public String createRandomCode() {
StringBuilder sb = new StringBuilder();
Random random = new Random();
for (int i = 0; i < 3; i++) {
sb.append((char) (random.nextInt(26) + 65));
}
for (int i = 0; i < 3; i++) {
sb.append(random.nextInt(10));
}
return sb.toString();
}
}

View File

@ -0,0 +1,157 @@
package com.edufocus.edufocus.qna.controller;
import com.edufocus.edufocus.qna.entity.Qna;
import com.edufocus.edufocus.qna.entity.QnaRequestDto;
import com.edufocus.edufocus.qna.entity.QnaResponseDto;
import com.edufocus.edufocus.qna.service.QnaService;
import com.edufocus.edufocus.user.model.entity.vo.User;
import com.edufocus.edufocus.user.model.entity.vo.UserRole;
import com.edufocus.edufocus.user.model.repository.UserRepository;
import com.edufocus.edufocus.user.util.JWTUtil;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import okhttp3.Response;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.sql.SQLException;
import java.util.List;
@RestController
@RequestMapping("/qna")
@Slf4j
@RequiredArgsConstructor
public class QnaController {
private final QnaService qnaService;
private final JWTUtil jwtUtil;
private static int PAGE_SIZE = 10;
private final UserRepository userRepository;
@PostMapping("/{lecture_id}")
public ResponseEntity<QnaResponseDto> createQna(@PathVariable("lecture_id") Long lecture_id, @RequestBody QnaRequestDto qnaRequestDto, HttpServletRequest request) {
try {
String token = request.getHeader("Authorization");
Long userId = Long.parseLong(jwtUtil.getUserId(token));
QnaResponseDto qnaResponseDto = qnaService.createQna(userId, qnaRequestDto, lecture_id);
return new ResponseEntity<>(qnaResponseDto, HttpStatus.CREATED);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
@PostMapping({"/answer/create/{qna_id}"})
public ResponseEntity<QnaResponseDto> createAnswer(@PathVariable("qna_id") Long qna_id, @RequestBody QnaRequestDto qnaRequestDto, HttpServletRequest request) {
try {
String token = request.getHeader("Authorization");
System.out.println(token);
Long userId = Long.parseLong(jwtUtil.getUserId(token));
User findUser = userRepository.findById(userId).orElse(null);
if (findUser.getRole() != UserRole.ADMIN) {
return new ResponseEntity<>(HttpStatus.NOT_ACCEPTABLE);
}
QnaResponseDto responseDto = qnaService.createAnswer(qna_id, qnaRequestDto);
return new ResponseEntity<>(responseDto, HttpStatus.ACCEPTED);
} catch (Exception e) {
return new ResponseEntity<>(HttpStatus.NOT_ACCEPTABLE);
}
}
@PutMapping({"/answer/update/{qna_id}"})
public ResponseEntity<QnaResponseDto> updateAnswer(@PathVariable("qna_id") Long qna_id, @RequestBody QnaRequestDto qnaRequestDto, HttpServletRequest request) {
try {
String token = request.getHeader("Authorization");
Long userId = Long.parseLong(jwtUtil.getUserId(token));
User findUser = userRepository.findById(userId).orElse(null);
if (findUser.getRole() != UserRole.ADMIN) {
System.out.println("role 안맞음");
return new ResponseEntity<>(HttpStatus.NOT_ACCEPTABLE);
}
QnaResponseDto responseDto = qnaService.updateAnswer(qna_id, qnaRequestDto);
return new ResponseEntity<>(responseDto, HttpStatus.ACCEPTED);
} catch (Exception e) {
return new ResponseEntity<>(HttpStatus.NOT_ACCEPTABLE);
}
}
@PostMapping("/answer/delete/{qna_id}")
public ResponseEntity<QnaResponseDto> deleteAnswer(@PathVariable("qna_id") Long qna_id, HttpServletRequest request) {
try {
String token = request.getHeader("Authorization");
Long userId = Long.parseLong(jwtUtil.getUserId(token));
User findUser = userRepository.findById(userId).orElse(null);
System.out.println("delete answer");
if (findUser.getRole() != UserRole.ADMIN) {
return new ResponseEntity<>(HttpStatus.NOT_ACCEPTABLE);
}
qnaService.deleteAnswer(qna_id);
return new ResponseEntity<>(HttpStatus.ACCEPTED);
} catch (Exception e) {
return new ResponseEntity<>(HttpStatus.NOT_ACCEPTABLE);
}
}
@PutMapping("/{id}")
public ResponseEntity<QnaResponseDto> updateQna(@PathVariable Long id, @RequestBody QnaRequestDto qnaRequestDto, HttpServletRequest request) {
String token = request.getHeader("Authorization");
Long userId = Long.parseLong(jwtUtil.getUserId(token));
try {
QnaResponseDto qnaResponseDto = qnaService.updateQna(id, qnaRequestDto, userId);
return new ResponseEntity<>(qnaResponseDto, HttpStatus.ACCEPTED);
} catch (Exception e) {
return new ResponseEntity<>(HttpStatus.NOT_ACCEPTABLE);
}
}
@DeleteMapping("/{id}")
public ResponseEntity<Qna> deleteQna(@PathVariable Long id, HttpServletRequest request) {
try {
String token = request.getHeader("Authorization");
Long userId = Long.parseLong(jwtUtil.getUserId(token));
qnaService.deleteQna(id, userId);
return new ResponseEntity<>(HttpStatus.ACCEPTED);
} catch (SQLException e) {
return new ResponseEntity<>(HttpStatus.NOT_ACCEPTABLE);
}
}
@GetMapping("/{id}")
public ResponseEntity<QnaResponseDto> getQna(@PathVariable Long id, HttpServletRequest request) {
try {
String token = request.getHeader("Authorization");
long userId = Long.parseLong(jwtUtil.getUserId(token));
QnaResponseDto findQna = qnaService.getQna(id, userId);
return new ResponseEntity<>(findQna, HttpStatus.ACCEPTED);
} catch (SQLException e) {
return new ResponseEntity<>(HttpStatus.NOT_ACCEPTABLE);
}
}
@GetMapping("/all/{id}")
public ResponseEntity<List<QnaResponseDto>> getAllQna(
@PathVariable Long id,
@RequestParam(defaultValue = "0") int pageNo) {
try {
List<QnaResponseDto> qnaList = qnaService.getAllQnasByLecture(id, pageNo, PAGE_SIZE);
return new ResponseEntity<>(qnaList, HttpStatus.ACCEPTED);
} catch (SQLException e) {
return new ResponseEntity<>(HttpStatus.NOT_ACCEPTABLE);
}
}
}

View File

@ -0,0 +1,63 @@
package com.edufocus.edufocus.qna.entity;
import com.edufocus.edufocus.lecture.entity.Lecture;
import com.edufocus.edufocus.user.model.entity.vo.User;
import jakarta.persistence.*;
import lombok.*;
import org.hibernate.annotations.OnDelete;
import org.hibernate.annotations.OnDeleteAction;
import java.util.Date;
@Entity
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Qna {
// 연관관계 주인
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "qna_id")
private Long id;
@Column
private String title;
@Column(columnDefinition = "TEXT")
private String content;
@Column(name = "created_at")
@Temporal(TemporalType.TIMESTAMP)
private Date createdAt;
@Column(name = "modified_at")
@Temporal(TemporalType.DATE)
private Date modifiedAt;
@Column(columnDefinition = "TEXT")
private String answer;
private String name;
@ManyToOne
@JoinColumn(name = "id")
private User user;
@ManyToOne
@JoinColumn(name = "lecture_id")
@OnDelete(action = OnDeleteAction.CASCADE)
private Lecture lecture;
private boolean isMine;
}

View File

@ -0,0 +1,24 @@
package com.edufocus.edufocus.qna.entity;
import jakarta.persistence.Column;
import lombok.*;
@Getter
public class QnaRequestDto {
private String title;
private String content;
private String answer;
public static Qna toEntity(QnaRequestDto qnaRequestDto) {
{
return Qna.builder()
.content(qnaRequestDto.getContent())
.title(qnaRequestDto.getTitle())
.answer(qnaRequestDto.getAnswer())
.build();
}
}
}

View File

@ -0,0 +1,43 @@
package com.edufocus.edufocus.qna.entity;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import org.springframework.stereotype.Service;
import java.util.Date;
@Getter
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class QnaResponseDto {
private Long id;
private String title;
private String username;
private String content;
private Date createtAt;
private String answer;
private boolean isMine;
public static QnaResponseDto toEntity(Qna qna) {
return new QnaResponseDto(
qna.getId(),
qna.getTitle(),
qna.getUser().getName(),
qna.getContent(),
qna.getCreatedAt(),
qna.getAnswer(),
qna.isMine()
);
}
}

View File

@ -0,0 +1,20 @@
package com.edufocus.edufocus.qna.repository;
import com.edufocus.edufocus.qna.entity.Qna;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public interface QnaRepository extends JpaRepository<Qna, Long> {
//List<Qna> findByLectureId(Long lecturerId);
@Query("SELECT q FROM Qna q WHERE q.lecture.id = :lectureId ORDER BY q.createdAt DESC")
Page<Qna> findByLectureId(Long lectureId, Pageable pageable);
}

View File

@ -0,0 +1,33 @@
package com.edufocus.edufocus.qna.service;
import com.edufocus.edufocus.lecture.entity.Lecture;
import com.edufocus.edufocus.qna.entity.Qna;
import com.edufocus.edufocus.qna.entity.QnaRequestDto;
import com.edufocus.edufocus.qna.entity.QnaResponseDto;
import jakarta.transaction.Transactional;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import java.sql.SQLException;
import java.util.List;
@Service
public interface QnaService {
QnaResponseDto createQna(Long id, QnaRequestDto qnaRequestDto, Long lecture_id) throws SQLException;
QnaResponseDto updateQna(Long id, QnaRequestDto qnaRequestDto, Long userId) throws SQLException;
void deleteQna(Long id, Long userId) throws SQLException;
QnaResponseDto getQna(Long id, Long userId) throws SQLException;
List<QnaResponseDto> getAllQnasByLecture(Long lectureId, int pageNo, int pageNumber) throws SQLException;
QnaResponseDto createAnswer(Long id, QnaRequestDto qnaRequestDto) throws SQLException;
QnaResponseDto updateAnswer(Long id, QnaRequestDto qnaRequestDto) throws SQLException;
void deleteAnswer(Long id) throws SQLException;
}

View File

@ -0,0 +1,169 @@
package com.edufocus.edufocus.qna.service;
import com.edufocus.edufocus.lecture.entity.Lecture;
import com.edufocus.edufocus.lecture.repository.LectureRepository;
import com.edufocus.edufocus.qna.entity.Qna;
import com.edufocus.edufocus.qna.entity.QnaRequestDto;
import com.edufocus.edufocus.qna.entity.QnaResponseDto;
import com.edufocus.edufocus.qna.repository.QnaRepository;
import com.edufocus.edufocus.user.model.entity.vo.User;
import com.edufocus.edufocus.user.model.entity.vo.UserRole;
import com.edufocus.edufocus.user.model.repository.UserRepository;
import jakarta.transaction.Transactional;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import java.sql.SQLException;
import java.util.Date;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
@Service
@Transactional
@RequiredArgsConstructor
public class QnaServiceImpl implements QnaService {
private final QnaRepository qnaRepository;
private final LectureRepository lectureRepository;
private final UserRepository userRepository;
@Override
public QnaResponseDto createQna(Long id, QnaRequestDto qnaRequestDto, Long lecture_id) {
Lecture lecture = lectureRepository.findById(lecture_id).orElse(null);
User user = userRepository.findById(id).orElse(null);
Qna qna = QnaRequestDto.toEntity(qnaRequestDto);
qna.setLecture(lecture);
qna.setUser(user);
qna.setCreatedAt(new Date());
qnaRepository.save(qna);
return QnaResponseDto.toEntity(qna);
}
@Override
public QnaResponseDto updateQna(Long id, QnaRequestDto qnaRequestDto, Long userId) {
System.out.println("userId:" + userId);
Qna findQna = qnaRepository.findById(id).orElse(null);
System.out.println("quesiton에 있는거: " + findQna.getUser().getId());
User user = userRepository.findById(userId).orElse(null);
if (findQna.getUser().getId() != userId || user.getRole() != UserRole.STUDENT) {
throw new RuntimeException();
}
findQna.setModifiedAt(new Date());
findQna.setTitle(qnaRequestDto.getTitle());
findQna.setContent(qnaRequestDto.getContent());
qnaRepository.save(findQna);
return QnaResponseDto.toEntity(findQna);
}
@Override
public void deleteQna(Long id, Long userId) {
Qna qna = qnaRepository.findById(id).orElse(null);
User user = userRepository.findById(userId).orElse(null);
if (qna.getUser().getId() == userId || user.getRole() == UserRole.ADMIN) {
qnaRepository.delete(qna);
} else {
throw new RuntimeException();
}
}
@Override
public QnaResponseDto getQna(Long id, Long userId) {
Qna qna;
try {
qna = qnaRepository.findById(id).orElse(null);
} catch (Exception e) {
throw new RuntimeException("Qna 없음 " + id, e);
}
QnaResponseDto dto = QnaResponseDto.builder()
.id(qna.getId())
.title(qna.getTitle())
.username(qna.getUser().getName())
.content(qna.getContent())
.createtAt(qna.getCreatedAt())
.answer(qna.getAnswer())
.isMine(userId == qna.getUser().getId())
.build();
return dto;
}
@Override
public List<QnaResponseDto> getAllQnasByLecture(Long lectureId, int pageNo, int pageSize) {
Pageable pageable = PageRequest.of(pageNo, pageSize);
Page<Qna> qnaPage = qnaRepository.findByLectureId(lectureId, pageable);
return qnaPage.getContent().stream()
.map(QnaResponseDto::toEntity)
.collect(Collectors.toList());
}
@Override
public QnaResponseDto createAnswer(Long id, QnaRequestDto qnaRequestDto) throws SQLException {
Qna findQna = qnaRepository.findById(id).orElse(null);
if (findQna.getAnswer() != null) {
throw new RuntimeException();
}
findQna.setAnswer(qnaRequestDto.getAnswer());
qnaRepository.save(findQna);
return QnaResponseDto.toEntity(findQna);
}
@Override
public QnaResponseDto updateAnswer(Long id, QnaRequestDto qnaRequestDto) throws SQLException {
Qna findQna = qnaRepository.findById(id).orElse(null);
findQna.setAnswer(qnaRequestDto.getAnswer());
qnaRepository.save(findQna);
return QnaResponseDto.toEntity(findQna);
}
@Override
public void deleteAnswer(Long id) throws SQLException {
Qna findQna = qnaRepository.findById(id).orElse(null);
findQna.setAnswer(null);
qnaRepository.save(findQna);
}
}

View File

@ -0,0 +1,145 @@
package com.edufocus.edufocus.quiz.controller;
import com.edufocus.edufocus.quiz.entity.*;
import com.edufocus.edufocus.quiz.service.QuizService;
import com.edufocus.edufocus.quiz.service.QuizSetService;
import com.edufocus.edufocus.user.util.JWTUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping("/quiz")
@Slf4j
@RequiredArgsConstructor
public class QuizController {
private final QuizService quizService;
private final QuizSetService quizSetService;
private final JWTUtil jwtUtil;
@PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public ResponseEntity<?> createQuizSet(@RequestHeader("Authorization") String accessToken, @RequestPart QuizSetCreateRequest quizSetCreateRequest
, @RequestPart(value = "images", required = false) List<MultipartFile> images) throws IOException {
long userId = Long.parseLong(jwtUtil.getUserId(accessToken));
if (quizSetCreateRequest.getQuizzes().isEmpty()) {
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
}
for (MultipartFile image : images) {
if (image != null && !image.getContentType().startsWith("image")) {
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
}
}
QuizSet quizSet = quizSetService.createQuizSet(userId, quizSetCreateRequest.getTitle());
int imageIdx = 0;
for (QuizCreateRequest quizCreateRequest : quizSetCreateRequest.getQuizzes()) {
quizService.createQuiz(quizSet, quizCreateRequest, images.get(imageIdx++));
}
return new ResponseEntity<>(HttpStatus.CREATED);
}
@GetMapping("/student/{quizsetId}")
public ResponseEntity<?> getQuizzes(@PathVariable Long quizsetId) {
QuizSetResponse quizSet = quizSetService.findQuizSetResponse(quizsetId);
return new ResponseEntity<>(quizSet, HttpStatus.OK);
}
@GetMapping("/teacher/{quizsetId}")
public ResponseEntity<?> getQuizzesIncludeAnswer(@RequestHeader("Authorization") String accessToken, @PathVariable Long quizsetId) {
long userId = Long.parseLong(jwtUtil.getUserId(accessToken));
QuizSet quizSet = quizSetService.findQuizSet(quizsetId);
if (quizSet.getUser().getId() != userId) {
return new ResponseEntity<>(HttpStatus.UNAUTHORIZED);
}
return new ResponseEntity<>(quizSet, HttpStatus.OK);
}
@PutMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public ResponseEntity<?> updateQuizSet(@RequestHeader("Authorization") String accessToken, @RequestPart QuizSetUpdateRequest quizSetUpdateRequest
, @RequestPart(value = "images", required = false) List<MultipartFile> images) throws IOException {
long userId = Long.parseLong(jwtUtil.getUserId(accessToken));
QuizSet quizset = quizSetService.findQuizSet(quizSetUpdateRequest.getId());
if (userId != quizset.getUser().getId()) {
return new ResponseEntity<>(HttpStatus.UNAUTHORIZED);
}
if (quizset.isTested()) {
return new ResponseEntity<>(HttpStatus.CONFLICT);
}
for (MultipartFile image : images) {
if (image != null && !image.getContentType().startsWith("image")) {
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
}
}
quizSetService.updateQuizSet(quizSetUpdateRequest.getId(), quizSetUpdateRequest.getTitle());
Map<Long, Boolean> quizUpdatedCheckMap = new HashMap<>();
for (Quiz quiz : quizset.getQuizzes()) {
quizUpdatedCheckMap.put(quiz.getId(), false);
}
int imageIdx = 0;
for (QuizUpdateRequest quizUpdateRequest : quizSetUpdateRequest.getQuizzes()) {
if (quizUpdateRequest.getId() == null) {
QuizCreateRequest quizCreateRequest = QuizCreateRequest.builder()
.question(quizUpdateRequest.getQuestion())
.answer(quizUpdateRequest.getAnswer())
.choices(quizUpdateRequest.getChoices())
.build();
quizService.createQuiz(quizset, quizCreateRequest, images.get(imageIdx++));
} else {
quizService.updateQuiz(quizUpdateRequest, images.get(imageIdx++));
quizUpdatedCheckMap.put(quizUpdateRequest.getId(), true);
}
}
for (Long quizId : quizUpdatedCheckMap.keySet()) {
if (!quizUpdatedCheckMap.get(quizId)) {
quizService.removeQuiz(quizId);
}
}
return new ResponseEntity<>(HttpStatus.OK);
}
@DeleteMapping("/{quizsetId}")
public ResponseEntity<?> deleteQuizSet(@RequestHeader("Authorization") String accessToken, @PathVariable long quizsetId) {
long userId = Long.parseLong(jwtUtil.getUserId(accessToken));
if (!quizSetService.deleteQuizSet(userId, quizsetId)) {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
@GetMapping
public ResponseEntity<?> getQuizSets(@RequestHeader("Authorization") String accessToken) {
long userId = Long.parseLong(jwtUtil.getUserId(accessToken));
return new ResponseEntity<>(quizSetService.findMyQuizSetResponses(userId), HttpStatus.OK);
}
}

View File

@ -0,0 +1,37 @@
package com.edufocus.edufocus.quiz.entity;
import com.edufocus.edufocus.report.entity.dto.ChoiceDto;
import com.fasterxml.jackson.annotation.JsonBackReference;
import jakarta.persistence.*;
import lombok.*;
@Entity
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Choice {
@Id
@GeneratedValue
private long id;
@ManyToOne
@JoinColumn(name = "QuizId")
@JsonBackReference
private Quiz quiz;
@Column
private int num;
@Column
private String content;
public ChoiceDto makeChoiceDto(){
return ChoiceDto.builder()
.num(num)
.content(content)
.build();
}
}

View File

@ -0,0 +1,15 @@
package com.edufocus.edufocus.quiz.entity;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
@Getter
@NoArgsConstructor
@AllArgsConstructor
public class ChoiceCreateRequest {
private int num;
private String content;
}

View File

@ -0,0 +1,14 @@
package com.edufocus.edufocus.quiz.entity;
import lombok.*;
@Getter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class MyQuizSetResponse {
private long quizSetId;
private String title;
}

View File

@ -0,0 +1,55 @@
package com.edufocus.edufocus.quiz.entity;
import com.fasterxml.jackson.annotation.JsonBackReference;
import com.fasterxml.jackson.annotation.JsonManagedReference;
import jakarta.persistence.*;
import lombok.*;
import java.util.List;
@Entity
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Quiz {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
@ManyToOne
@JoinColumn(name = "quizset_id")
@JsonBackReference
private QuizSet quizSet;
@Column
private String question;
@Column
private String image;
@Column
private String answer;
@OneToMany(mappedBy = "quiz", orphanRemoval = true, cascade = CascadeType.ALL)
@JsonManagedReference
private List<Choice> choices;
public void setQuizSet(QuizSet quizSet) {
this.quizSet = quizSet;
if (!quizSet.getQuizzes().contains(this)) {
quizSet.getQuizzes().remove(this);
}
}
public void addChoice(Choice choice) {
this.choices.add(choice);
if (choice.getQuiz() != this) {
choice.setQuiz(this);
}
}
}

View File

@ -0,0 +1,21 @@
package com.edufocus.edufocus.quiz.entity;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import java.util.List;
@Builder
@Getter
@NoArgsConstructor
@AllArgsConstructor
public class QuizCreateRequest {
private String question;
private String answer;
private List<ChoiceCreateRequest> choices;
}

View File

@ -0,0 +1,21 @@
package com.edufocus.edufocus.quiz.entity;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import java.util.List;
@Getter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class QuizResponse {
private String question;
private String image;
private List<Choice> choices;
}

View File

@ -0,0 +1,45 @@
package com.edufocus.edufocus.quiz.entity;
import com.edufocus.edufocus.user.model.entity.vo.User;
import com.fasterxml.jackson.annotation.JsonBackReference;
import com.fasterxml.jackson.annotation.JsonManagedReference;
import jakarta.persistence.*;
import lombok.*;
import java.util.List;
@Entity
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class QuizSet {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
@ManyToOne
@JoinColumn(name = "user_id")
@JsonBackReference
private User user;
@Column
private String title;
@Column
private boolean tested;
@OneToMany(mappedBy = "quizSet", orphanRemoval = true)
@JsonManagedReference
private List<Quiz> quizzes;
public void addQuiz(Quiz quiz) {
this.quizzes.add(quiz);
if (quiz.getQuizSet() != this) {
quiz.setQuizSet(this);
}
}
}

View File

@ -0,0 +1,18 @@
package com.edufocus.edufocus.quiz.entity;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import java.util.List;
@Getter
@NoArgsConstructor
@AllArgsConstructor
public class QuizSetCreateRequest {
private String title;
private List<QuizCreateRequest> quizzes;
}

View File

@ -0,0 +1,17 @@
package com.edufocus.edufocus.quiz.entity;
import lombok.*;
import java.util.List;
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class QuizSetResponse {
private String title;
private List<QuizResponse> quizzes;
}

View File

@ -0,0 +1,20 @@
package com.edufocus.edufocus.quiz.entity;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import java.util.List;
@Getter
@NoArgsConstructor
@AllArgsConstructor
public class QuizSetUpdateRequest {
private long id;
private String title;
private List<QuizUpdateRequest> quizzes;
}

View File

@ -0,0 +1,21 @@
package com.edufocus.edufocus.quiz.entity;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import java.util.List;
@Getter
@NoArgsConstructor
@AllArgsConstructor
public class QuizUpdateRequest {
private Long id;
private String question;
private String answer;
private List<ChoiceCreateRequest> choices;
}

View File

@ -0,0 +1,7 @@
package com.edufocus.edufocus.quiz.repository;
import com.edufocus.edufocus.quiz.entity.Choice;
import org.springframework.data.jpa.repository.JpaRepository;
public interface ChoiceRepository extends JpaRepository<Choice, Long> {
}

View File

@ -0,0 +1,8 @@
package com.edufocus.edufocus.quiz.repository;
import com.edufocus.edufocus.quiz.entity.Quiz;
import org.springframework.data.jpa.repository.JpaRepository;
public interface QuizRepository extends JpaRepository<Quiz, Long> {
}

View File

@ -0,0 +1,11 @@
package com.edufocus.edufocus.quiz.repository;
import com.edufocus.edufocus.quiz.entity.QuizSet;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
public interface QuizSetRepository extends JpaRepository<QuizSet, Long> {
List<QuizSet> findQuizSetsByUserId(long userId);
}

View File

@ -0,0 +1,23 @@
package com.edufocus.edufocus.quiz.service;
import com.edufocus.edufocus.quiz.entity.Quiz;
import com.edufocus.edufocus.quiz.entity.QuizCreateRequest;
import com.edufocus.edufocus.quiz.entity.QuizSet;
import com.edufocus.edufocus.quiz.entity.QuizUpdateRequest;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.util.List;
@Service
public interface QuizService {
void createQuiz(QuizSet quizSet, QuizCreateRequest quizCreateRequest, MultipartFile quizImage) throws IOException;
void updateQuiz(QuizUpdateRequest quizUpdateRequest, MultipartFile quizImage) throws IOException;
Quiz findQuiz(long quizId);
void removeQuiz(long quizId);
}

View File

@ -0,0 +1,136 @@
package com.edufocus.edufocus.quiz.service;
import com.edufocus.edufocus.global.properties.ImagePathProperties;
import com.edufocus.edufocus.quiz.entity.*;
import com.edufocus.edufocus.quiz.repository.ChoiceRepository;
import com.edufocus.edufocus.quiz.repository.QuizRepository;
import jakarta.transaction.Transactional;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.UUID;
@Service
@Transactional
@RequiredArgsConstructor
public class QuizServiceImpl implements QuizService {
private final ImagePathProperties imagePathProperties;
private final QuizRepository quizRepository;
private final ChoiceRepository choiceRepository;
@Override
public void createQuiz(QuizSet quizSet, QuizCreateRequest quizCreateRequest, MultipartFile quizImage) throws IOException {
Quiz quiz = Quiz.builder()
.quizSet(quizSet)
.question(quizCreateRequest.getQuestion())
.answer(quizCreateRequest.getAnswer())
.build();
List<Choice> choices = new ArrayList<>();
for (ChoiceCreateRequest choiceCreateRequest : quizCreateRequest.getChoices()) {
Choice choice = Choice.builder()
.quiz(quiz)
.num(choiceCreateRequest.getNum())
.content(choiceCreateRequest.getContent())
.build();
choices.add(choice);
}
quiz.setChoices(choices);
if (quizImage != null && !quizImage.isEmpty()) {
String uid = UUID.randomUUID().toString();
String imagePath = imagePathProperties.getPath();
File checkPathFile = new File(imagePath);
if (!checkPathFile.exists()) {
checkPathFile.mkdirs();
}
File savingImage = new File(imagePath + "/" + uid + "_" + quizImage.getOriginalFilename());
quizImage.transferTo(savingImage.toPath());
String savePath = savingImage.toPath().toString();
quiz.setImage(savePath);
}
quizRepository.save(quiz);
}
@Override
public void updateQuiz(QuizUpdateRequest quizUpdateRequest, MultipartFile quizImage) throws IOException {
Quiz quiz = quizRepository.findById(quizUpdateRequest.getId()).orElseThrow(NoSuchElementException::new);
quiz.setQuestion(quizUpdateRequest.getQuestion());
quiz.setAnswer(quizUpdateRequest.getAnswer());
quiz.getChoices().clear();
quizRepository.save(quiz);
for (ChoiceCreateRequest choiceCreateRequest : quizUpdateRequest.getChoices()) {
Choice choice = Choice.builder()
.quiz(quiz)
.num(choiceCreateRequest.getNum())
.content(choiceCreateRequest.getContent())
.build();
quiz.addChoice(choice);
}
if (quizImage != null && !quizImage.isEmpty()) {
if (quiz.getImage() != null) {
File file = new File(quiz.getImage());
file.delete();
}
String uid = UUID.randomUUID().toString();
String imagePath = imagePathProperties.getPath();
File checkPathFile = new File(imagePath);
if (!checkPathFile.exists()) {
checkPathFile.mkdirs();
}
File savingImage = new File(imagePath + "/" + uid + "_" + quizImage.getOriginalFilename());
quizImage.transferTo(savingImage.toPath());
String savePath = savingImage.toPath().toString();
quiz.setImage(savePath);
}
quizRepository.save(quiz);
}
@Override
public Quiz findQuiz(long quizId) {
return quizRepository.findById(quizId).orElseThrow(NoSuchElementException::new);
}
@Override
public void removeQuiz(long quizId) {
Quiz quiz = quizRepository.findById(quizId).orElseThrow(NoSuchElementException::new);
String image = quiz.getImage();
if (image != null) {
File file = new File(quiz.getImage());
file.delete();
quiz.setImage(null);
}
quizRepository.delete(quiz);
}
}

View File

@ -0,0 +1,26 @@
package com.edufocus.edufocus.quiz.service;
import com.edufocus.edufocus.quiz.entity.MyQuizSetResponse;
import com.edufocus.edufocus.quiz.entity.QuizSet;
import com.edufocus.edufocus.quiz.entity.QuizSetResponse;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public interface QuizSetService {
QuizSet createQuizSet(long userId, String title);
void updateQuizSet(long quizSetId, String title);
boolean deleteQuizSet(long userId, long quizSetId);
QuizSet findQuizSet(long quizSetId);
QuizSetResponse findQuizSetResponse(long quizSetId);
List<MyQuizSetResponse> findMyQuizSetResponses(long userId);
void updateQuizSetTested(long quizSetId);
}

View File

@ -0,0 +1,109 @@
package com.edufocus.edufocus.quiz.service;
import com.edufocus.edufocus.quiz.entity.*;
import com.edufocus.edufocus.quiz.repository.QuizSetRepository;
import com.edufocus.edufocus.user.model.entity.vo.User;
import com.edufocus.edufocus.user.model.exception.UnAuthorizedException;
import com.edufocus.edufocus.user.model.repository.UserRepository;
import jakarta.transaction.Transactional;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
import java.util.NoSuchElementException;
@Service
@Transactional
@RequiredArgsConstructor
public class QuizSetServiceImpl implements QuizSetService {
private final QuizSetRepository quizSetRepository;
private final UserRepository userRepository;
@Override
public QuizSet createQuizSet(long userId, String title) {
User user = userRepository.findById(userId).orElseThrow(NoSuchElementException::new);
QuizSet quizSet = QuizSet.builder()
.user(user)
.title(title)
.build();
return quizSetRepository.save(quizSet);
}
@Override
public void updateQuizSet(long quizSetId, String title) {
QuizSet quizSet = quizSetRepository.findById(quizSetId).orElseThrow(NoSuchElementException::new);
quizSet.setTitle(title);
quizSetRepository.save(quizSet);
}
@Override
public boolean deleteQuizSet(long userId, long quizSetId) {
QuizSet quizSet = quizSetRepository.findById(quizSetId).orElseThrow(NoSuchElementException::new);
if (userId != quizSet.getUser().getId()) {
return false;
}
quizSetRepository.deleteById(quizSetId);
return true;
}
@Override
public QuizSet findQuizSet(long quizSetId) {
return quizSetRepository.findById(quizSetId).orElseThrow(NoSuchElementException::new);
}
@Override
public QuizSetResponse findQuizSetResponse(long quizSetId) {
QuizSet quizSet = quizSetRepository.findById(quizSetId).orElseThrow(NoSuchElementException::new);
List<QuizResponse> quizResponses = new ArrayList<>();
for (Quiz quiz : quizSet.getQuizzes()) {
QuizResponse quizResponse = QuizResponse.builder()
.question(quiz.getQuestion())
.image(quiz.getImage())
.choices(quiz.getChoices())
.build();
quizResponses.add(quizResponse);
}
return QuizSetResponse.builder()
.title(quizSet.getTitle())
.quizzes(quizResponses)
.build();
}
@Override
public List<MyQuizSetResponse> findMyQuizSetResponses(long userId) {
List<QuizSet> quizSetList = quizSetRepository.findQuizSetsByUserId(userId);
List<MyQuizSetResponse> myQuizSetResponses = new ArrayList<>();
for (QuizSet quizSet : quizSetList) {
MyQuizSetResponse myQuizSetResponse = MyQuizSetResponse.builder()
.quizSetId(quizSet.getId())
.title(quizSet.getTitle())
.build();
myQuizSetResponses.add(myQuizSetResponse);
}
return myQuizSetResponses;
}
@Override
public void updateQuizSetTested(long quizSetId) {
QuizSet quizSet = quizSetRepository.findById(quizSetId).orElseThrow(NoSuchElementException::new);
quizSet.setTested(true);
quizSetRepository.save(quizSet);
}
}

View File

@ -0,0 +1,40 @@
package com.edufocus.edufocus.redis.util;
import lombok.RequiredArgsConstructor;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;
import java.time.Duration;
@Component
@RequiredArgsConstructor
public class RedisUtil {
private final StringRedisTemplate stringRedisTemplate;
public String getData(String key) {
ValueOperations<String, String> valueOperations = stringRedisTemplate.opsForValue();
return valueOperations.get(key);
}
public void setData(String key, String value) {
ValueOperations<String, String> valueOperations = stringRedisTemplate.opsForValue();
valueOperations.set(key, value);
}
public void setDataExpire(String key, String value, long duration) {
ValueOperations<String, String> valueOperations = stringRedisTemplate.opsForValue();
Duration expireDuration = Duration.ofSeconds(duration);
valueOperations.set(key, value, expireDuration);
}
public void deleteData(String key) {
stringRedisTemplate.delete(key);
}
public boolean exists(String key) {
return stringRedisTemplate.hasKey(key);
}
}

View File

@ -0,0 +1,71 @@
package com.edufocus.edufocus.registration.controller;
import com.edufocus.edufocus.lecture.service.LectureService;
import com.edufocus.edufocus.registration.service.RegistrationService;
import com.edufocus.edufocus.user.util.JWTUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.Map;
@RestController
@RequestMapping("/registration")
@Slf4j
@RequiredArgsConstructor
public class RegistrationController {
private final RegistrationService registrationService;
private final LectureService lectureService;
private final JWTUtil jwtUtil;
@PostMapping
public ResponseEntity<?> register(@RequestHeader("Authorization") String accessToken, @RequestBody Map<String, Long> map) {
Long userId = Long.parseLong(jwtUtil.getUserId(accessToken));
Long lectureId = map.get("lectureId");
if (!registrationService.createRegistration(userId, lectureId)) {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
return new ResponseEntity<>(HttpStatus.CREATED);
}
@PutMapping("/{registrationId}")
public ResponseEntity<?> acceptRegistration(@RequestHeader("Authorization") String accessToken, @PathVariable long registrationId) {
Long userId = Long.parseLong(jwtUtil.getUserId(accessToken));
if (!registrationService.acceptRegistration(userId, registrationId)) {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
return new ResponseEntity<>(HttpStatus.OK);
}
@DeleteMapping("/{registrationId}")
public ResponseEntity<?> deleteRegistration(@RequestHeader("Authorization") String accessToken, @PathVariable long registrationId) {
Long userId = Long.parseLong(jwtUtil.getUserId(accessToken));
if (!registrationService.deleteRegistration(userId, registrationId)) {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
@GetMapping("/{lectureId}")
public ResponseEntity<?> getRegistrations(@RequestHeader("Authorization") String accessToken, @PathVariable long lectureId) {
Long userId = Long.parseLong(jwtUtil.getUserId(accessToken));
if (!lectureService.checkTeacher(userId, lectureId)) {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
return new ResponseEntity<>(registrationService.searchRegistrations(lectureId), HttpStatus.OK);
}
}

View File

@ -0,0 +1,54 @@
package com.edufocus.edufocus.registration.entity;
import com.edufocus.edufocus.lecture.entity.Lecture;
import com.edufocus.edufocus.quiz.entity.QuizSet;
import com.edufocus.edufocus.report.entity.vo.Report;
import com.edufocus.edufocus.report.entity.vo.ReportSet;
import com.edufocus.edufocus.user.model.entity.vo.User;
import jakarta.persistence.*;
import lombok.*;
import org.hibernate.annotations.OnDelete;
import org.hibernate.annotations.OnDeleteAction;
import java.util.UUID;
@Entity
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Registration {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
@ManyToOne
@JoinColumn(name = "user_id")
private User user;
@ManyToOne
@OnDelete(action = OnDeleteAction.CASCADE)
@JoinColumn(name = "lecture_id")
private Lecture lecture;
@Enumerated(EnumType.STRING)
private RegistrationStatus status;
public boolean isAccepted() {
return status == RegistrationStatus.ACCEPTED;
}
public Report makeReport(ReportSet reportSet, QuizSet quizSet, long lectureId) {
return Report.builder()
.allCount(0)
.correctCount(-1)
.reportSet(reportSet)
.quizSet(quizSet)
.lectureId(lectureId)
.user(user)
.build();
}
}

View File

@ -0,0 +1,18 @@
package com.edufocus.edufocus.registration.entity;
import com.edufocus.edufocus.lecture.entity.Lecture;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
@Getter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class RegistrationSearchResponse {
private Long id;
private String userName;
}

View File

@ -0,0 +1,5 @@
package com.edufocus.edufocus.registration.entity;
public enum RegistrationStatus {
WAITING, ACCEPTED
}

View File

@ -0,0 +1,21 @@
package com.edufocus.edufocus.registration.repository;
import com.edufocus.edufocus.registration.entity.Registration;
import com.edufocus.edufocus.registration.entity.RegistrationStatus;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public interface RegistrationRepository extends JpaRepository<Registration, Long> {
List<Registration> findAllByUserId(@Param("userId") Long userId);
List<Registration> findAllByLectureId(Long lectureId);
Registration findByUserIdAndLectureId(Long userId, Long lectureId);
List<Registration> findByLectureIdAndStatus(Long lectureId, RegistrationStatus status);
}

View File

@ -0,0 +1,22 @@
package com.edufocus.edufocus.registration.service;
import com.edufocus.edufocus.registration.entity.Registration;
import com.edufocus.edufocus.registration.entity.RegistrationSearchResponse;
import com.edufocus.edufocus.registration.entity.RegistrationStatus;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public interface RegistrationService {
boolean createRegistration(Long userId, Long registrationId);
boolean acceptRegistration(Long userId, Long RegistrationId);
boolean deleteRegistration(Long userId, Long registrationId);
List<RegistrationSearchResponse>[] searchRegistrations(Long LectureId);
RegistrationStatus getStatus(Long userId, Long lectureId);
}

View File

@ -0,0 +1,107 @@
package com.edufocus.edufocus.registration.service;
import com.edufocus.edufocus.lecture.repository.LectureRepository;
import com.edufocus.edufocus.registration.entity.Registration;
import com.edufocus.edufocus.registration.entity.RegistrationSearchResponse;
import com.edufocus.edufocus.registration.entity.RegistrationStatus;
import com.edufocus.edufocus.registration.repository.RegistrationRepository;
import com.edufocus.edufocus.user.model.entity.vo.User;
import com.edufocus.edufocus.user.model.entity.vo.UserRole;
import com.edufocus.edufocus.user.model.repository.UserRepository;
import jakarta.transaction.Transactional;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
@Service
@Transactional
@RequiredArgsConstructor
public class RegistrationServiceImpl implements RegistrationService {
private final RegistrationRepository registrationRepository;
private final UserRepository userRepository;
private final LectureRepository lectureRepository;
@Override
public boolean createRegistration(Long userId, Long lectureId) {
Optional<User> user = userRepository.findById(userId);
if (user.isEmpty() || user.get().getRole().equals(UserRole.ADMIN)) {
return false;
}
if (registrationRepository.findByUserIdAndLectureId(userId, lectureId) != null) {
return false;
}
Registration registration = Registration.builder()
.user(userRepository.findById(userId).get())
.lecture(lectureRepository.findById(lectureId).get())
.status(RegistrationStatus.WAITING)
.build();
registrationRepository.save(registration);
return true;
}
@Override
public boolean acceptRegistration(Long userId, Long registrationId) {
Optional<Registration> registration = registrationRepository.findById(registrationId);
if (registration.isEmpty() || registration.get().getLecture().getUser().getId() != userId) {
return false;
}
registration.get().setStatus(RegistrationStatus.valueOf("ACCEPTED"));
registrationRepository.save(registration.get());
return true;
}
@Override
public boolean deleteRegistration(Long userId, Long registrationId) {
Optional<Registration> registration = registrationRepository.findById(registrationId);
if (registration.isEmpty() || registration.get().getLecture().getUser().getId() != userId) {
return false;
}
registrationRepository.deleteById(registrationId);
return true;
}
@Override
public List<RegistrationSearchResponse>[] searchRegistrations(Long lectureId) {
List<Registration> registrations = registrationRepository.findAllByLectureId(lectureId);
List<RegistrationSearchResponse>[] responses = new ArrayList[2];
responses[0] = new ArrayList<>();
responses[1] = new ArrayList<>();
for (Registration registration : registrations) {
RegistrationSearchResponse response = RegistrationSearchResponse.builder()
.id(registration.getId())
.userName(registration.getUser().getName())
.build();
if (registration.getStatus() == RegistrationStatus.ACCEPTED) {
responses[0].add(response);
} else {
responses[1].add(response);
}
}
return responses;
}
@Override
public RegistrationStatus getStatus(Long userId, Long lectureId) {
Registration registration = registrationRepository.findByUserIdAndLectureId(userId, lectureId);
return registration.getStatus();
}
}

View File

@ -0,0 +1,96 @@
package com.edufocus.edufocus.report.controller;
import com.edufocus.edufocus.report.entity.dto.*;
import com.edufocus.edufocus.report.service.ReportService;
import com.edufocus.edufocus.user.model.service.UserService;
import com.edufocus.edufocus.user.util.JWTUtil;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.sql.SQLException;
import java.util.List;
import java.util.UUID;
@RestController
@RequestMapping("/report")
@Slf4j
@RequiredArgsConstructor
public class ReportController {
private final ReportService reportService;
private final JWTUtil jwtUtil;
private final UserService userService;
@PostMapping("/submit/quizSet/{reportSetId}")
public ResponseEntity<?> submit(@PathVariable("reportSetId") UUID reportSetId, @RequestBody ReportRequest reportRequest, HttpServletRequest request) {
String token = request.getHeader("Authorization");
long userId = Long.parseLong(jwtUtil.getUserId(token));
if (userService.isTeacher(userId))
return new ResponseEntity<>("강사는 퀴즈제출을 할수 없습니다", HttpStatus.FORBIDDEN);
reportService.grade(userId, reportSetId, reportRequest);
return new ResponseEntity<>(HttpStatus.OK);
}
@GetMapping("/student/{lectureId}")
public ResponseEntity<List<ReportResponse>> searchMyReport(@PathVariable long lectureId, HttpServletRequest request) {
String token = request.getHeader("Authorization");
long userId = Long.parseLong(jwtUtil.getUserId(token));
List<ReportResponse> reportResponses = reportService.findReports(lectureId, userId);
if (reportResponses.isEmpty())
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
return new ResponseEntity<>(reportResponses, HttpStatus.OK);
}
@GetMapping("/reportDetail/{reportId}")
public ResponseEntity<ReportDetailResponseDto> searchDetailReport(@PathVariable("reportId") long reportId) {
ReportDetailResponseDto detailReport = reportService.reportDetail(reportId);
return new ResponseEntity<>(detailReport, HttpStatus.OK);
}
@GetMapping("/teacher/reportSet/{lectureId}")
public ResponseEntity<List<ReportSetResponse>> searchReportSets(@PathVariable("lectureId") long lectureId) {
List<ReportSetResponse> reportSetResponses = reportService.findReportSets(lectureId);
if (reportSetResponses.isEmpty())
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
return new ResponseEntity<>(reportSetResponses, HttpStatus.OK);
}
@GetMapping("/teacher/report/{reportSetId}")
public ResponseEntity<?> searchReports(@PathVariable("reportSetId") UUID reportSetId) {
List<ReportResponse> reportResponses = reportService.findReports(reportSetId);
if (reportResponses.isEmpty())
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
return new ResponseEntity<>(reportResponses, HttpStatus.OK);
}
@DeleteMapping("/teacher/report/{reportSetId}")
public ResponseEntity<?> deleteReportSet(@PathVariable("reportSetId") UUID reportSetId, HttpServletRequest request) {
String token = request.getHeader("Authorization");
long userId = Long.parseLong(jwtUtil.getUserId(token));
reportService.deleteReportSet(reportSetId, userId);
return new ResponseEntity<>(HttpStatus.OK);
}
}

View File

@ -0,0 +1,12 @@
package com.edufocus.edufocus.report.entity.dto;
import lombok.Builder;
import lombok.Data;
import lombok.Setter;
@Data
@Builder
public class ChoiceDto {
int num;
String content;
}

View File

@ -0,0 +1,23 @@
package com.edufocus.edufocus.report.entity.dto;
import com.edufocus.edufocus.quiz.entity.Choice;
import com.edufocus.edufocus.quiz.entity.QuizSet;
import com.fasterxml.jackson.annotation.JsonBackReference;
import com.fasterxml.jackson.annotation.JsonManagedReference;
import jakarta.persistence.*;
import lombok.Builder;
import lombok.Data;
import java.util.List;
@Data
@Builder
public class QuizDto {
private long id;
private String question;
private String image;
private String answer;
private String userAnswer;
private boolean isCorrect;
private List<ChoiceDto> choices;
}

View File

@ -0,0 +1,19 @@
package com.edufocus.edufocus.report.entity.dto;
import com.edufocus.edufocus.quiz.entity.Quiz;
import lombok.Builder;
import lombok.Data;
import java.time.LocalDateTime;
import java.util.Date;
import java.util.List;
@Data
@Builder
public class ReportDetailResponseDto {
private int allCount;
private String title;
private int correctCount;
private Date testAt;
private List<QuizDto> quizzes;
}

View File

@ -0,0 +1,23 @@
package com.edufocus.edufocus.report.entity.dto;
import lombok.Getter;
import lombok.Setter;
import java.util.List;
@Getter
@Setter
public class ReportRequest {
List<String> answer;
// [1,2,,3]
// List<answerDto> a
// userID :
// quizSetId :
// answerList : [
// { ans1 : 1},
// {ans2 : 2}
// ]
}

View File

@ -0,0 +1,20 @@
package com.edufocus.edufocus.report.entity.dto;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import java.time.LocalDateTime;
import java.util.Date;
@Getter
@Setter
@Builder
public class ReportResponse {
private long reportId;
private String name;
private String title;
private int allCount;
private int correctCount;
private Date date;
}

View File

@ -0,0 +1,19 @@
package com.edufocus.edufocus.report.entity.dto;
import lombok.*;
import java.time.LocalDateTime;
import java.util.Date;
import java.util.UUID;
@Builder
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class ReportSetResponse {
private UUID reportSetId;
private String quizSetTitle;
private Date testAt;
}

View File

@ -0,0 +1,39 @@
package com.edufocus.edufocus.report.entity.vo;
import com.edufocus.edufocus.quiz.entity.Quiz;
import jakarta.persistence.*;
import lombok.*;
import org.hibernate.annotations.OnDelete;
import org.hibernate.annotations.OnDeleteAction;
@Entity
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class Answer {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
@Column
private String userAnswer;
@Column
private boolean isCorrect;
@ManyToOne
@JoinColumn(name = "report_id")
@OnDelete(action = OnDeleteAction.CASCADE)
private Report report;
@ManyToOne
@JoinColumn(name = "quiz_id")
@OnDelete(action = OnDeleteAction.CASCADE)
private Quiz quiz;
}

View File

@ -0,0 +1,70 @@
package com.edufocus.edufocus.report.entity.vo;
import com.edufocus.edufocus.quiz.entity.QuizSet;
import com.edufocus.edufocus.report.entity.dto.ReportResponse;
import com.edufocus.edufocus.user.model.entity.vo.User;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import jakarta.persistence.*;
import lombok.*;
import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.OnDelete;
import org.hibernate.annotations.OnDeleteAction;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
import java.time.LocalDateTime;
import java.util.Date;
import java.util.List;
@Entity
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Builder
@EntityListeners(AuditingEntityListener.class)
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY)
public class Report {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private int allCount;
private int correctCount;
@Column
@CreationTimestamp
@Temporal(TemporalType.TIMESTAMP)
private Date testAt;
private Long lectureId;
@ManyToOne
@JoinColumn(name = "user_id")
private User user;
@ManyToOne
@JoinColumn(name = "quizset_id")
@OnDelete(action = OnDeleteAction.CASCADE)
private QuizSet quizSet;
@ManyToOne
@JoinColumn(name = "reportset_id")
@OnDelete(action = OnDeleteAction.CASCADE)
private ReportSet reportSet;
@OneToMany(mappedBy = "report")
private List<Answer> answers;
public ReportResponse makeReportResponse() {
return ReportResponse.builder()
.reportId(id)
.name(user.getName())
.title(quizSet.getTitle())
.allCount(allCount)
.correctCount(correctCount)
.date(testAt)
.build();
}
}

View File

@ -0,0 +1,66 @@
package com.edufocus.edufocus.report.entity.vo;
import com.edufocus.edufocus.lecture.entity.Lecture;
import com.edufocus.edufocus.quiz.entity.QuizSet;
import com.edufocus.edufocus.report.entity.dto.ReportSetResponse;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import jakarta.persistence.*;
import jakarta.persistence.CascadeType;
import lombok.*;
import org.hibernate.annotations.*;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
import java.time.LocalDateTime;
import java.util.Date;
import java.util.List;
import java.util.UUID;
@Entity
@Getter
@Builder
@NoArgsConstructor
@AllArgsConstructor
@EntityListeners(AuditingEntityListener.class)
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY)
public class ReportSet {
@Id
@GeneratedValue(generator = "UUID")
@GenericGenerator(
name = "UUID",
strategy = "org.hibernate.id.UUIDGenerator"
)
private UUID id;
@Column
@CreationTimestamp
@Temporal(TemporalType.TIMESTAMP)
private Date createAt;
@OneToMany(mappedBy = "reportSet")
private List<Report> reports;
@ManyToOne
@JoinColumn(name = "lecture_id")
@OnDelete(action = OnDeleteAction.CASCADE)
private Lecture lecture;
@ManyToOne
@JoinColumn(name = "quizSet_id")
@OnDelete(action = OnDeleteAction.CASCADE)
private QuizSet quizSet;
public ReportSetResponse makeReportSetResponse() {
return ReportSetResponse.builder()
.reportSetId(id)
.quizSetTitle(quizSet.getTitle())
.testAt(createAt)
.build();
}
public long findUserId() {
return lecture.getUser().getId();
}
}

View File

@ -0,0 +1,11 @@
package com.edufocus.edufocus.report.repository;
import com.edufocus.edufocus.report.entity.vo.Answer;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
public interface AnswerRepository extends JpaRepository<Answer, Long> {
List<Answer> findByReportId(Long reportId);
}

View File

@ -0,0 +1,17 @@
package com.edufocus.edufocus.report.repository;
import com.edufocus.edufocus.report.entity.vo.Report;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.List;
import java.util.UUID;
@Repository
public interface ReportRepository extends JpaRepository<Report, Long> {
Report findByReportSetIdAndUserId(UUID reportSetId, long userId);
List<Report> findByReportSetIdOrderById(UUID reportSetId);
List<Report> findByLectureIdAndUserIdOrderById(long lectureId, long userId);
}

View File

@ -0,0 +1,13 @@
package com.edufocus.edufocus.report.repository;
import com.edufocus.edufocus.report.entity.vo.ReportSet;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.List;
import java.util.UUID;
@Repository
public interface ReportSetRepository extends JpaRepository<ReportSet, UUID> {
List<ReportSet> findByLectureIdOrderById(long userId);
}

View File

@ -0,0 +1,8 @@
package com.edufocus.edufocus.report.service;
import com.edufocus.edufocus.report.entity.vo.Answer;
public interface AnswerService {
void save(Answer answer);
}

View File

@ -0,0 +1,22 @@
package com.edufocus.edufocus.report.service;
import com.edufocus.edufocus.report.entity.vo.Answer;
import com.edufocus.edufocus.report.repository.AnswerRepository;
import jakarta.transaction.Transactional;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
@Service
@Transactional
@RequiredArgsConstructor
public class AnswerServiceImpl implements AnswerService{
private final AnswerRepository answerRepository;
@Override
public void save(Answer answer) {
answerRepository.save(answer);
}
}

View File

@ -0,0 +1,25 @@
package com.edufocus.edufocus.report.service;
import com.edufocus.edufocus.report.entity.dto.*;
import org.springframework.stereotype.Service;
import java.sql.SQLException;
import java.util.List;
import java.util.UUID;
@Service
public interface ReportService {
void grade(long userId, UUID reportSetId, ReportRequest reportRequest);
ReportDetailResponseDto reportDetail(long userId);
List<ReportSetResponse> findReportSets(long lectureId);
List<ReportResponse> findReports(UUID reportSetId);
List<ReportResponse> findReports(long lectureId, long userid);
UUID initReportSet(long lectureId, long quizSetId);
void deleteReportSet(UUID reportSetId, long userId);
}

View File

@ -0,0 +1,182 @@
package com.edufocus.edufocus.report.service;
import com.edufocus.edufocus.lecture.repository.LectureRepository;
import com.edufocus.edufocus.quiz.entity.Choice;
import com.edufocus.edufocus.quiz.entity.Quiz;
import com.edufocus.edufocus.quiz.entity.QuizSet;
import com.edufocus.edufocus.quiz.repository.QuizRepository;
import com.edufocus.edufocus.quiz.repository.QuizSetRepository;
import com.edufocus.edufocus.registration.entity.Registration;
import com.edufocus.edufocus.registration.entity.RegistrationStatus;
import com.edufocus.edufocus.registration.repository.RegistrationRepository;
import com.edufocus.edufocus.report.entity.dto.*;
import com.edufocus.edufocus.report.entity.vo.Answer;
import com.edufocus.edufocus.report.entity.vo.Report;
import com.edufocus.edufocus.report.entity.vo.ReportSet;
import com.edufocus.edufocus.report.repository.AnswerRepository;
import com.edufocus.edufocus.report.repository.ReportRepository;
import com.edufocus.edufocus.report.repository.ReportSetRepository;
import jakarta.transaction.Transactional;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import java.util.*;
@Service
@Transactional
@RequiredArgsConstructor
public class ReportServiceImpl implements ReportService {
private final ReportRepository reportRepository;
private final QuizRepository quizRepository;
private final RegistrationRepository registrationRepository;
private final AnswerRepository answerRepository;
private final ReportSetRepository reportSetRepository;
private final LectureRepository lectureRepository;
private final QuizSetRepository quizSetRepository;
@Override
public void grade(long userId, UUID reportSetId, ReportRequest reportRequest){
ReportSet reportSet = reportSetRepository.findById(reportSetId).orElseThrow(NoSuchElementException::new);
Report report = reportRepository.findByReportSetIdAndUserId(reportSetId, userId);
List<Quiz> quizList = report.getQuizSet().getQuizzes();
List<String> answerInputList = reportRequest.getAnswer();
List<Answer> answerList = new ArrayList<>();
int correctCount = 0;
for (int idx = 0; idx < answerInputList.size(); idx++) {
Quiz quiz = quizList.get(idx);
String inputAnswer = answerInputList.get(idx);
boolean isCorrect;
Answer answer;
if (quiz.getAnswer().equalsIgnoreCase(inputAnswer)) {
correctCount++;
isCorrect = true;
} else {
isCorrect = false;
}
answer = Answer.builder()
.userAnswer(inputAnswer)
.isCorrect(isCorrect)
.report(report)
.quiz(quiz)
.build();
answerList.add(answer);
}
report.setAllCount(quizList.size());
report.setCorrectCount(correctCount);
reportRepository.save(report);
answerRepository.saveAll(answerList);
}
@Override
public ReportDetailResponseDto reportDetail(long reportId) {
Report report = reportRepository.findById(reportId).orElseThrow(NoSuchElementException::new);
QuizSet quizSet = quizSetRepository.findById(report.getQuizSet().getId()).orElseThrow(NoSuchElementException::new);
List<QuizDto> quizDtos = new ArrayList<>();
List<Answer> myAnswer = answerRepository.findByReportId(report.getId());
for (Answer answer : myAnswer) {
Quiz quiz = quizRepository.findById(answer.getQuiz().getId()).orElse(null);
List<ChoiceDto> choiceDtos = quiz.getChoices()
.stream()
.map(Choice::makeChoiceDto)
.toList();
QuizDto quizDto = QuizDto.builder()
.id(quiz.getId())
.question(quiz.getQuestion())
.image(quiz.getImage())
.question(quiz.getQuestion())
.answer(quiz.getAnswer())
.userAnswer(answer.getUserAnswer())
.isCorrect(answer.isCorrect())
.choices(choiceDtos)
.build();
quizDtos.add(quizDto);
}
return ReportDetailResponseDto.builder()
.title(quizSet.getTitle())
.testAt(report.getTestAt())
.allCount(report.getAllCount())
.correctCount(report.getCorrectCount())
.quizzes(quizDtos)
.build();
}
@Override
public List<ReportSetResponse> findReportSets(long lectureId) {
List<ReportSet> reportSets = reportSetRepository.findByLectureIdOrderById(lectureId);
return reportSets.stream()
.map(ReportSet::makeReportSetResponse)
.toList();
}
@Override
public List<ReportResponse> findReports(UUID reportSetId) {
List<Report> reports = reportRepository.findByReportSetIdOrderById(reportSetId);
return reports.stream()
.map(Report::makeReportResponse)
.toList();
}
@Override
public List<ReportResponse> findReports(long lectureId, long userId) {
List<Report> reports = reportRepository.findByLectureIdAndUserIdOrderById(lectureId, userId);
return reports.stream()
.map(Report::makeReportResponse)
.toList();
}
@Override
public UUID initReportSet(long lectureId, long quizSetId) {
ReportSet reportSet = ReportSet.builder()
.lecture(lectureRepository.getReferenceById(lectureId))
.quizSet(quizSetRepository.getReferenceById(quizSetId))
.build();
reportSetRepository.save(reportSet);
QuizSet quizSet = quizSetRepository.getReferenceById(quizSetId);
List<Registration> registrations = registrationRepository.findByLectureIdAndStatus(lectureId, RegistrationStatus.ACCEPTED);
List<Report> reports = registrations.stream()
.map((Registration registration)-> registration.makeReport(reportSet, quizSet, lectureId))
.toList();
reportRepository.saveAll(reports);
return reportSet.getId();
}
@Override
public void deleteReportSet(UUID reportSetId, long userId) {
ReportSet reportSet = reportSetRepository.findById(reportSetId).orElseThrow(NoSuchElementException::new);
if(reportSet.findUserId() == userId)
reportSetRepository.delete(reportSet);
}
}

View File

@ -0,0 +1,21 @@
package com.edufocus.edufocus.swagger;
import io.swagger.v3.oas.models.Components;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Info;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class SwaggerConfig {
@Bean
public OpenAPI openAPI() {
return new OpenAPI()
.info(new Info()
.title("에듀포커스 API")
.description("")
.version("1.0.0"));
}
}

View File

@ -0,0 +1,52 @@
package com.edufocus.edufocus.user.config;
import com.edufocus.edufocus.user.intercepter.JWTInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
@EnableWebMvc
public class WebConfiguration implements WebMvcConfigurer {
private JWTInterceptor jwtInterceptor;
public WebConfiguration(JWTInterceptor jwtInterceptor) {
super();
this.jwtInterceptor = jwtInterceptor;
}
//
@Override
public void addCorsMappings(CorsRegistry registry) {
registry
.addMapping("/**")
.allowedOriginPatterns("https://i11a701.p.ssafy.io/", "https://localhost:5173", "http://localhost:5173", "http://localhost:4173", "http://localhost:5080", "http://192.168.*.*:5173")
.allowedMethods("GET", "POST", HttpMethod.GET.name(), HttpMethod.POST.name(), HttpMethod.PUT.name(),
HttpMethod.DELETE.name(), HttpMethod.HEAD.name(), HttpMethod.OPTIONS.name(),
HttpMethod.PATCH.name())
.allowCredentials(true)
.allowedHeaders("*")
.maxAge(1800); // Pre-flight Caching
}
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/img/**").addResourceLocations("classpath:/static/assets/img/");
registry.addResourceHandler("/*.html**").addResourceLocations("classpath:/static/");
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(jwtInterceptor)
.addPathPatterns("/**") // 모든 경로에 대해 인터셉터 적용
.excludePathPatterns("/v3/api-docs/**", "/swagger-resources/**", "/webjars/**", "/swagger-ui/**", "/auth/**", "/board/**", "/user/**", "/lecture/**", "/qna/**", "/quiz/**", "/video/**", "/registration/**", "/report/**", "/mail/**"); // 인증 없이 접근 가능한 경로 설정
}
}

View File

@ -0,0 +1,203 @@
package com.edufocus.edufocus.user.controller;
import com.edufocus.edufocus.user.model.entity.dto.InfoDto;
import com.edufocus.edufocus.user.model.entity.dto.PasswordDto;
import com.edufocus.edufocus.user.model.entity.dto.RequestJoinDto;
import com.edufocus.edufocus.user.model.entity.vo.User;
import com.edufocus.edufocus.user.model.exception.InvalidTokenException;
import com.edufocus.edufocus.user.model.service.UserService;
import com.edufocus.edufocus.user.util.JWTUtil;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.Map;
@RestController
@RequestMapping("/user")
@Slf4j
@RequiredArgsConstructor
@CrossOrigin(origins = "http://localhost:5173", allowedHeaders = "*", allowCredentials = "true")
public class UserController {
private final UserService userService;
private final JWTUtil jwtUtil;
@PostMapping("/join")
public ResponseEntity<String> join(@RequestBody RequestJoinDto requestJoinDto) {
if (userService.isUserIdExist(requestJoinDto.getUserId()))
return new ResponseEntity<>("아이디가 중복 됐습니다.", HttpStatus.CONFLICT);
if (userService.isEmailExist(requestJoinDto.getEmail()))
return new ResponseEntity<>("이메일이 중복 됐습니다.", HttpStatus.CONFLICT);
userService.join(requestJoinDto);
return ResponseEntity.ok("User registered successfully");
}
@PutMapping("/updateinfo")
public ResponseEntity<String> updateUserInfo(@RequestBody InfoDto infoDto, HttpServletRequest request) {
try {
String token = request.getHeader("Authorization");
Long userId = Long.parseLong(jwtUtil.getUserId(token));
userService.changeUserInfo(infoDto, userId);
return ResponseEntity.ok("Password changed successfully");
} catch (Exception e) {
return ResponseEntity.status(HttpStatus.NOT_ACCEPTABLE).body(e.getMessage());
}
}
// 비밀번호 변경
@PutMapping("/updatepassword")
public ResponseEntity<String> updatePassword(@RequestBody PasswordDto passwordDto, HttpServletRequest request) {
try {
String token = request.getHeader("Authorization");
Long userId = Long.parseLong(jwtUtil.getUserId(token));
userService.changePassword(passwordDto, userId);
return ResponseEntity.ok("Password changed successfully");
} catch (Exception e) {
return ResponseEntity.status(HttpStatus.NOT_ACCEPTABLE).body(e.getMessage());
}
}
// 비밀번호 찾기를 통한 변경
@PutMapping("/updateforgottenpassword")
public ResponseEntity<String> updatePassword(@RequestParam String email,
@RequestParam String newPassword) {
userService.changeForgottenPassword(email, newPassword);
return new ResponseEntity<>(HttpStatus.OK);
}
@Operation(summary = "로그인", description = "아이디와 비밀번호를 이용하여 로그인 처리.")
@PostMapping("/login")
public ResponseEntity<Map<String, Object>> login(
@RequestBody @Parameter(description = "로그인 시 필요한 회원정보(아이디, 비밀번호).", required = true) User user, HttpServletRequest request, HttpServletResponse response) {
Map<String, Object> resultMap = new HashMap<>();
HttpStatus status = HttpStatus.ACCEPTED;
User loginUser = userService.login(user);
if (loginUser == null)
return new ResponseEntity<>(HttpStatus.UNAUTHORIZED);
String accessToken = jwtUtil.createAccessToken(String.valueOf(loginUser.getId()));
String refreshToken = jwtUtil.createRefreshToken(String.valueOf(loginUser.getId()));
userService.saveRefreshToken(loginUser.getId(), refreshToken);
resultMap.put("name", loginUser.getName());
resultMap.put("role", loginUser.getRole());
resultMap.put("access-token", accessToken);
setCookies(response, refreshToken);
status = HttpStatus.CREATED;
return new ResponseEntity<>(resultMap, status);
}
@PostMapping("/logout")
public ResponseEntity<?> removeToken(HttpServletRequest request) {
String token = request.getHeader("Authorization");
Long userId = Long.parseLong(jwtUtil.getUserId(token));
userService.deleteRefreshToken(userId);
return new ResponseEntity<Map<String, Object>>(HttpStatus.ACCEPTED);
}
@Operation(summary = "Access Token 재발급", description = "만료된 access token 을 재발급 받는다.")
@PostMapping("/refresh")
public ResponseEntity<?> refreshToken(HttpServletRequest request, HttpServletResponse response) {
Cookie[] cookies = request.getCookies();
String token = null;
if (cookies != null) {
for (Cookie cookie : cookies) {
if (cookie.getName().equals("refresh-token")) {
token = cookie.getValue();
break;
}
}
}
try {
jwtUtil.checkToken(token);
} catch (Exception e) {
throw new InvalidTokenException();
}
Long userId = Long.parseLong(jwtUtil.getUserId(token));
if (!token.equals(userService.getRefreshToken(userId))) {
throw new InvalidTokenException();
}
String accessToken = jwtUtil.createAccessToken(String.valueOf(userId));
String refreshToken = jwtUtil.createRefreshToken(String.valueOf(userId));
Map<String, Object> resultMap = new HashMap<>();
resultMap.put("access-token", accessToken);
userService.saveRefreshToken(userId, refreshToken);
setCookies(response, refreshToken);
return new ResponseEntity<Map<String, Object>>(resultMap, HttpStatus.CREATED);
}
@Operation(summary = "회원 정보 조회", description = "토큰을 이용하여 회원 정보를 조회한다.")
@GetMapping("/userinfo")
public ResponseEntity<Map<String, Object>> getMember(HttpServletRequest request) {
Map<String, Object> resultMap = new HashMap<>();
HttpStatus status = HttpStatus.ACCEPTED;
String token = request.getHeader("Authorization");
if (jwtUtil.checkToken(token)) {
String userId = jwtUtil.getUserId(token);
log.info("사용 가능한 토큰!!! userId: {}", userId);
try {
User user = userService.userInfo(Long.parseLong(userId));
resultMap.put("userInfo", user);
status = HttpStatus.OK;
} catch (Exception e) {
log.error("정보조회 실패 : {}", e);
resultMap.put("message", e.getMessage());
status = HttpStatus.INTERNAL_SERVER_ERROR;
}
} else {
log.error("사용 불가능 토큰!!!");
status = HttpStatus.UNAUTHORIZED;
}
return new ResponseEntity<Map<String, Object>>(resultMap, status);
}
private void setCookies(HttpServletResponse response, String refreshToken) {
Cookie refreshCookie = new Cookie("refresh-token", refreshToken);
refreshCookie.setPath("/");
refreshCookie.setHttpOnly(true);
refreshCookie.setSecure(true);
response.addCookie(refreshCookie);
}
}

View File

@ -0,0 +1,32 @@
package com.edufocus.edufocus.user.intercepter;
import com.edufocus.edufocus.user.model.exception.UnAuthorizedException;
import com.edufocus.edufocus.user.util.JWTUtil;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
@Component
@Slf4j
public class JWTInterceptor implements HandlerInterceptor {
private final String HEADER_AUTH = "Authorization";
private JWTUtil jwtUtil;
public JWTInterceptor(JWTUtil jwtUtil) {
super();
this.jwtUtil = jwtUtil;
}
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler){
final String token = request.getHeader(HEADER_AUTH);
jwtUtil.checkToken(token);
return true;
}
}

View File

@ -0,0 +1,15 @@
package com.edufocus.edufocus.user.model.entity.dto;
import lombok.Getter;
import lombok.Setter;
@Setter
@Getter
public class InfoDto {
String name;
String email;
}

View File

@ -0,0 +1,19 @@
package com.edufocus.edufocus.user.model.entity.dto;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@ToString
@Data
public class MemberChangeDto {
Long id;
String password;
}

View File

@ -0,0 +1,12 @@
package com.edufocus.edufocus.user.model.entity.dto;
import lombok.Getter;
import lombok.Setter;
@Setter
@Getter
public class PasswordDto {
String currentPassword;
String newPassword;
String newPasswordCheck;
}

View File

@ -0,0 +1,11 @@
package com.edufocus.edufocus.user.model.entity.dto;
import lombok.AllArgsConstructor;
import lombok.Setter;
@Setter
@AllArgsConstructor
public class RefreshTokenDto {
private String Token;
}

View File

@ -0,0 +1,17 @@
package com.edufocus.edufocus.user.model.entity.dto;
import com.edufocus.edufocus.user.model.entity.vo.UserRole;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;
@Setter
@Getter
@AllArgsConstructor
public class RequestJoinDto {
private String userId;
private String email;
private String password;
private UserRole role;
private String name;
}

View File

@ -0,0 +1,40 @@
package com.edufocus.edufocus.user.model.entity.vo;
import jakarta.persistence.*;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Pattern;
import lombok.*;
@Entity
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY) // 자동 증가 설정
@Column(name = "id")
private Long id;
@Column(name = "user_id", unique = true, nullable = false)
@Pattern(regexp = "(?=.*[0-9])(?=.*[a-zA-Z])(?=.*\\W)(?=\\S+$).{8,16}", message = "비밀번호는 8~16자 영문 대 소문자, 숫자, 특수문자를 사용하세요.")
@NotBlank(message = "아이디는 필수 입력 값입니다.")
private String userId;
@Pattern(regexp = "^(?:\\w+\\.?)*\\w+@(?:\\w+\\.)+\\w+$", message = "이메일 형식이 올바르지 않습니다.")
private String email;
private String password;
@Enumerated(EnumType.STRING) // 혹은 EnumType.ORDINAL
private UserRole role;
private String refreshToken;
private String name;
}

View File

@ -0,0 +1,5 @@
package com.edufocus.edufocus.user.model.entity.vo;
public enum UserRole {
STUDENT,ADMIN
}

View File

@ -0,0 +1,11 @@
package com.edufocus.edufocus.user.model.exception;
public class ExpriedTokenException extends RuntimeException {
private static final long serialVersionUID = 1L;
public ExpriedTokenException() {
super("계정 권한이 유효하지 않습니다.\n다시 로그인을 하세요.");
}
}

View File

@ -0,0 +1,26 @@
package com.edufocus.edufocus.user.model.exception;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(InvalidTokenException.class)
public ResponseEntity<String> handleInvalidTokenException(InvalidTokenException e) {
return new ResponseEntity<>(e.getMessage(), HttpStatus.FORBIDDEN);
}
//
@ExceptionHandler(ExpriedTokenException.class)
public ResponseEntity<String> handleExpiredTokenException(ExpriedTokenException e) {
return new ResponseEntity<>(e.getMessage(), HttpStatus.UNAUTHORIZED);
}
@ExceptionHandler(UserException.class)
public ResponseEntity<String> handleUserException(UserException e) {
return new ResponseEntity<>(e.getMessage(), HttpStatus.FORBIDDEN);//수정해야함
}
}

View File

@ -0,0 +1,7 @@
package com.edufocus.edufocus.user.model.exception;
public class InvalidTokenException extends RuntimeException {
public InvalidTokenException() {
super("Token is invalid");
}
}

Some files were not shown because too many files have changed in this diff Show More