Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/#144 #147

Open
wants to merge 18 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 16 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ class SecurityConfig {

private static final String STUDENT = "STUDENT";
private static final String TEACHER = "TEACHER";
private static final String PARENT = "PARENT";
private static final String ADMIN = "ADMIN";

private final TokenFilter tokenFilter;
Expand Down Expand Up @@ -116,6 +117,10 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
.requestMatchers(PATCH, "/divisions/**").authenticated()
.requestMatchers(DELETE, "/divisions/**").authenticated()

.requestMatchers(POST, "/notice").hasAnyRole(TEACHER, ADMIN)
.requestMatchers(PATCH, "/notice/{id}/create").hasAnyRole(TEACHER, ADMIN)
.requestMatchers(GET, "/notice/**").authenticated()

.anyRequest().authenticated()
.and()
.exceptionHandling()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
package b1nd.dodam.restapi.member.application;

import b1nd.dodam.domain.rds.member.entity.Member;
import b1nd.dodam.domain.rds.member.entity.Parent;
import b1nd.dodam.domain.rds.member.entity.Student;
import b1nd.dodam.domain.rds.member.entity.Teacher;
import b1nd.dodam.domain.rds.member.enumeration.ActiveStatus;
import b1nd.dodam.domain.rds.member.event.StudentRegisteredEvent;
import b1nd.dodam.domain.rds.member.exception.BroadcastClubMemberDuplicateException;
import b1nd.dodam.domain.rds.member.exception.MemberDuplicateException;
import b1nd.dodam.domain.rds.member.repository.BroadcastClubMemberRepository;
import b1nd.dodam.domain.rds.member.repository.MemberRepository;
import b1nd.dodam.domain.rds.member.repository.StudentRepository;
import b1nd.dodam.domain.rds.member.repository.TeacherRepository;
import b1nd.dodam.domain.rds.member.repository.*;
import b1nd.dodam.restapi.auth.infrastructure.security.support.MemberAuthenticationHolder;
import b1nd.dodam.restapi.member.application.data.req.*;
import b1nd.dodam.restapi.support.data.Response;
Expand All @@ -32,6 +30,7 @@ public class MemberCommandUseCase {
private final MemberRepository memberRepository;
private final StudentRepository studentRepository;
private final TeacherRepository teacherRepository;
private final ParentRepository parentRepository;
private final BroadcastClubMemberRepository broadcastClubMemberRepository;
private final MemberAuthenticationHolder memberAuthenticationHolder;
private final ApplicationEventPublisher eventPublisher;
Expand All @@ -55,6 +54,13 @@ public Response join(JoinTeacherReq req) {
return Response.created("선생님 회원가입 성공");
}

public Response join(JoinParentReq req) {
checkIfIdIsDuplicate(req.id());
Member member = memberRepository.save(req.mapToMember(encodePw(req.pw())));
parentRepository.save(req.mapToParent(member));
return Response.created("학부모 회원가입 성공");
}

private void checkIfIdIsDuplicate(String id) {
if(memberRepository.existsById(id)) {
throw new MemberDuplicateException();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package b1nd.dodam.restapi.member.application.data.req;

import b1nd.dodam.domain.rds.member.entity.Member;
import b1nd.dodam.domain.rds.member.entity.Parent;
import b1nd.dodam.domain.rds.member.entity.Teacher;
import b1nd.dodam.domain.rds.member.enumeration.ActiveStatus;
import b1nd.dodam.domain.rds.member.enumeration.MemberRole;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotEmpty;

public record JoinParentReq(@NotEmpty String id, @NotEmpty String pw, @NotEmpty String name, @NotEmpty @Email String email,
@NotEmpty String relation, @NotEmpty String phone) {
public Parent mapToParent(Member member) {
return Parent.builder()
.member(member)
.relation(relation)
.build();
}

public Member mapToMember(String encodedPw) {
return Member.builder()
.id(id)
.pw(encodedPw)
.email(email)
.name(name)
.role(MemberRole.PARENT)
.phone(phone)
.status(ActiveStatus.PENDING)
.build();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@ public Response join(@RequestBody @Valid JoinTeacherReq req) {
return commandUseCase.join(req);
}

@PostMapping("/join-parent")
public Response join(@RequestBody @Valid JoinParentReq req) {
return commandUseCase.join(req);
}

@PostMapping("/broadcast-club-member")
public Response apply(@RequestBody @Valid ApplyBroadcastClubMemberReq req) {
return commandUseCase.apply(req);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package b1nd.dodam.restapi.notice.application;

import b1nd.dodam.domain.rds.division.entity.Division;
import b1nd.dodam.domain.rds.division.entity.DivisionMember;
import b1nd.dodam.domain.rds.division.service.DivisionMemberService;
import b1nd.dodam.domain.rds.division.service.DivisionService;
import b1nd.dodam.domain.rds.member.entity.Member;
import b1nd.dodam.domain.rds.notice.entity.Notice;
import b1nd.dodam.domain.rds.notice.entity.NoticeDivision;
import b1nd.dodam.domain.rds.notice.enumration.NoticeStatus;
import b1nd.dodam.domain.rds.notice.service.NoticeDivisionService;
import b1nd.dodam.domain.rds.notice.service.NoticeService;
import b1nd.dodam.restapi.auth.infrastructure.security.support.MemberAuthenticationHolder;
import b1nd.dodam.restapi.notice.application.data.req.GenerateNoticeReq;
import b1nd.dodam.restapi.notice.application.data.res.NoticeRes;
import b1nd.dodam.restapi.support.data.ResponseData;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

@Component
@RequiredArgsConstructor
@Transactional(rollbackFor = Exception.class)
public class NoticeUseCase {

private final NoticeService noticeService;
private final NoticeDivisionService noticeDivisionService;
private final DivisionService divisionService;
private final DivisionMemberService divisionMemberService;
private final MemberAuthenticationHolder memberAuthenticationHolder;

public ResponseData<Long> register(GenerateNoticeReq generateNoticeReq){
Member member = memberAuthenticationHolder.current();
Notice notice = noticeService.save(generateNoticeReq.toEntity(member));

List<Division> divisions = generateNoticeReq.divisions().stream()
.map(divisionService::getById)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

division 개수만큼 SQL 조회하는 거 아닌가요?
SQL 한번만 쓸 수 있지 않나여?

.toList();

List<NoticeDivision> noticeDivisions = generateNoticeReq.toEntity(notice, divisions);

noticeService.changeStatus(notice.getId(), NoticeStatus.CREATED);
noticeDivisionService.saveAll(noticeDivisions);

return ResponseData.of(HttpStatus.OK, "공지 생성 성공", notice.getId());
}

@Transactional(readOnly = true)
public ResponseData<List<NoticeRes>> getNotices(Long lastId, int limit, NoticeStatus status) {
Member member = memberAuthenticationHolder.current();
List<DivisionMember> divisionMembers = divisionMemberService.getByMember(member);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

divisionMemberService.getByMember가 호출될 때 마다 getDivision으로 조직을 가져오는데 그냥 DivisionService에서 쿼리로 해결하는게 좋을 듯합니다


List<Long> divisionIds = divisionMembers.stream()
.map(divisionMember -> divisionMember.getDivision().getId())
.toList();

List<Notice> notices = noticeService.getAllByStatus(divisionIds, status, lastId, limit);

return ResponseData.of(HttpStatus.OK, "전체 공지 불러오기 성공", NoticeRes.of(notices, member));
}

@Transactional(readOnly = true)
public ResponseData<List<NoticeRes>> getNoticesByDivision(Long divisionId, Long lastId, int limit){
Member member = memberAuthenticationHolder.current();
Division division = divisionService.getById(divisionId);

List<Notice> notices = noticeService.getAllByDivision(member.getId(), division.getId(), lastId, limit)
.stream()
.toList();

return ResponseData.of(HttpStatus.OK, "카테고리별 공지 불러오기 성공", NoticeRes.of(notices, member));
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package b1nd.dodam.restapi.notice.application.data;

import b1nd.dodam.domain.rds.notice.entity.NoticeFile;
import b1nd.dodam.domain.rds.support.enumeration.FileType;
import jakarta.validation.constraints.NotEmpty;

import java.util.List;
import java.util.stream.Collectors;

public record File(@NotEmpty String url, @NotEmpty String name, @NotEmpty FileType fileType) {
public static List<File> of(List<NoticeFile> noticeFiles){
return noticeFiles.stream().map(
noticeFile ->
new File(noticeFile.getFileUrl(),
noticeFile.getFileName(),
noticeFile.getFileType())
).collect(Collectors.toList());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package b1nd.dodam.restapi.notice.application.data.req;

import b1nd.dodam.domain.rds.division.entity.Division;
import b1nd.dodam.domain.rds.notice.entity.Notice;
import b1nd.dodam.domain.rds.notice.entity.NoticeDivision;

public record AddNoticeDivisionReq(
Long divisionId
) {
public NoticeDivision toEntity(Notice notice, Division division){
return NoticeDivision.builder()
.notice(notice)
.division(division)
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package b1nd.dodam.restapi.notice.application.data.req;

import b1nd.dodam.domain.rds.division.entity.Division;
import b1nd.dodam.domain.rds.member.entity.Member;
import b1nd.dodam.domain.rds.notice.entity.Notice;
import b1nd.dodam.domain.rds.notice.entity.NoticeDivision;
import b1nd.dodam.domain.rds.notice.entity.NoticeFile;
import b1nd.dodam.domain.rds.notice.enumration.NoticeStatus;
import b1nd.dodam.restapi.notice.application.data.File;
import jakarta.validation.constraints.NotEmpty;

import java.util.List;
import java.util.stream.Collectors;

import static b1nd.dodam.domain.rds.notice.enumration.NoticeStatus.DRAFT;

public record GenerateNoticeReq (@NotEmpty String title, @NotEmpty String content, List<File> files,
List<Long> divisions){

public Notice toEntity(Member member) {
Notice notice = Notice.builder()
.title(title)
.content(content)
.noticeStatus(NoticeStatus.DRAFT)
.member(member)
.build();

if (files != null && !files.isEmpty()) {
files.forEach(file -> notice.addNoticeFiles(
NoticeFile.builder()
.fileUrl(file.url())
.fileName(file.name())
.fileType(file.fileType())
.build()
));
}

return notice;
}

public List<NoticeDivision> toEntity(Notice notice, List<Division> divisions) {
return divisions.stream()
.map(division -> {
NoticeDivision noticeDivision = GenerateNoticeReq.toEntity(notice, division);
notice.addNoticeDivision(noticeDivision);
return noticeDivision;
})
.collect(Collectors.toList());
}

public static NoticeDivision toEntity(Notice notice, Division division){
return NoticeDivision.builder()
.notice(notice)
.division(division)
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package b1nd.dodam.restapi.notice.application.data.res;

import b1nd.dodam.domain.rds.member.entity.Member;
import b1nd.dodam.domain.rds.notice.entity.Notice;
import b1nd.dodam.domain.rds.notice.enumration.NoticeStatus;
import b1nd.dodam.domain.rds.support.enumeration.FileType;
import b1nd.dodam.restapi.member.application.data.res.MemberInfoRes;

import java.time.LocalDateTime;
import java.util.List;
import java.util.stream.Collectors;

public record NoticeRes(
Long id,
String title,
String content,
String fileUrl,
FileType fileType,
NoticeStatus noticeStatus,
MemberInfoRes memberInfoRes,
LocalDateTime createdAt, LocalDateTime modifiedAt
) {
public static List<NoticeRes> of(List<Notice> notices, Member member) {
return notices.parallelStream()
.map(notice -> NoticeRes.of(notice, member))
.collect(Collectors.toList());
}

public static NoticeRes of(Notice notice, Member member) {
return new NoticeRes(
notice.getId(),
notice.getTitle(),
notice.getContent(),
notice.getFileUrl(),
notice.getFileType(),
notice.getNoticeStatus(),
MemberInfoRes.of(member, null, null),
notice.getCreatedAt(),
notice.getModifiedAt()
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package b1nd.dodam.restapi.notice.presentation;

import b1nd.dodam.domain.rds.notice.enumration.NoticeStatus;
import b1nd.dodam.restapi.notice.application.NoticeUseCase;
import b1nd.dodam.restapi.notice.application.data.req.GenerateNoticeReq;
import b1nd.dodam.restapi.notice.application.data.res.NoticeRes;
import b1nd.dodam.restapi.support.data.ResponseData;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping("/notice")
@RequiredArgsConstructor
public class NoticeController {

private final NoticeUseCase noticeUseCase;

@PostMapping
public ResponseData<Long> generate(@RequestBody GenerateNoticeReq generateNoticeReq){
return noticeUseCase.register(generateNoticeReq);
}

@GetMapping
public ResponseData<List<NoticeRes>> getByStatus(
@RequestParam Long lastId,
@RequestParam int limit,
@RequestParam NoticeStatus status

){
return noticeUseCase.getNotices(lastId, limit, status);
}

@GetMapping("/{id}/division")
public ResponseData<List<NoticeRes>> getBy(
@PathVariable Long id,
@RequestParam Long lastId,
@RequestParam int limit
){
return noticeUseCase.getNoticesByDivision(id, lastId, limit);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@

import b1nd.dodam.discord.webhook.client.DiscordWebhookClient;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.util.Arrays;

@Slf4j
@Component
@RequiredArgsConstructor
public class ErrorNoticeSender {
Expand All @@ -33,6 +35,7 @@ public void send(Exception e, RequestInfo request) {
+ "```\n"
+ getStackTrace(e)
+ "\n```";

discordWebHookClient.notice("", title, description);
}

Expand Down
Loading