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: Viewer 및 CCTV 그룹 참가 구현 #130

Merged
merged 7 commits into from
Nov 15, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
@@ -0,0 +1,13 @@
package org.ioteatime.meonghanyangserver.cctv.controller;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.ioteatime.meonghanyangserver.cctv.dto.request.CreateCctvRequest;
import org.ioteatime.meonghanyangserver.common.api.Api;
import org.springframework.web.bind.annotation.RequestBody;

@Tag(name = "CCTV Device Api", description = "CCTV 기기 관련 API 목록입니다.")
public interface CctvDeviceApi {
@Operation(summary = "CCTV 기기 생성 요청", description = "담당자: 임지인")
Api<?> createCctv(@RequestBody CreateCctvRequest createCctvRequest);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package org.ioteatime.meonghanyangserver.cctv.controller;

import lombok.RequiredArgsConstructor;
import org.ioteatime.meonghanyangserver.cctv.dto.request.CreateCctvRequest;
import org.ioteatime.meonghanyangserver.cctv.service.CctvService;
import org.ioteatime.meonghanyangserver.common.api.Api;
import org.ioteatime.meonghanyangserver.common.type.CctvSuccessType;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequiredArgsConstructor
@RequestMapping("/open-api/cctv")
public class CctvDeviceController implements CctvDeviceApi {
private final CctvService cctvService;

@PostMapping
public Api<?> createCctv(@RequestBody CreateCctvRequest createCctvRequest) {
cctvService.createCctv(createCctvRequest);
return Api.success(CctvSuccessType.CREATE_CCTV);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package org.ioteatime.meonghanyangserver.cctv.dto.request;

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

public record CreateCctvRequest(
@NotNull @Schema(description = "그룹 ID", example = "1") Long groupId,
@NotBlank @Schema(description = "Thing ID", example = "cf27414r3b22c023") String thingId,
@NotBlank
@Schema(
description = "Kvs Channel 이름",
example = "kvs-9bc02f62-df22-40b0-bdc1-610d8fd293b4")
String kvsChannelName) {}
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
package org.ioteatime.meonghanyangserver.cctv.dto.response;

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

@Schema(description = "CCTV 초대 응답")
public record CctvInviteResponse(
@NotNull @Schema(description = "그룹 ID", example = "1") Long groupId,
@NotNull
@NotBlank
@Schema(
description = "KVS 채널 이름",
example = "kvs-9bc02f62-df22-40b0-bdc1-610d8fd293b4")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
public interface CctvRepository {
boolean existsByKvsChannelName(String kvsChannelName);

boolean existsByThingId(String thingId);

void deleteById(Long cctvId);

CctvEntity save(CctvEntity cctv);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ public boolean existsByKvsChannelName(String kvsChannelName) {
return jpaCctvRepository.existsByKvsChannelName(kvsChannelName);
}

@Override
public boolean existsByThingId(String thingId) {
return jpaCctvRepository.existsByThingId(thingId);
}

@Override
public void deleteById(Long cctvId) {
jpaCctvRepository.deleteById(cctvId);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,7 @@
public interface JpaCctvRepository extends JpaRepository<CctvEntity, Long> {
boolean existsByKvsChannelName(String kvsChannelName);

boolean existsByThingId(String thingId);

void deleteByGroupId(Long groupId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,15 @@
import jakarta.transaction.Transactional;
import lombok.RequiredArgsConstructor;
import org.ioteatime.meonghanyangserver.cctv.domain.CctvEntity;
import org.ioteatime.meonghanyangserver.cctv.dto.request.CreateCctvRequest;
import org.ioteatime.meonghanyangserver.cctv.repository.CctvRepository;
import org.ioteatime.meonghanyangserver.clients.kvs.KvsClient;
import org.ioteatime.meonghanyangserver.common.exception.BadRequestException;
import org.ioteatime.meonghanyangserver.common.exception.NotFoundException;
import org.ioteatime.meonghanyangserver.common.type.CctvErrorType;
import org.ioteatime.meonghanyangserver.common.type.GroupErrorType;
import org.ioteatime.meonghanyangserver.group.domain.GroupEntity;
import org.ioteatime.meonghanyangserver.group.repository.GroupRepository;
import org.ioteatime.meonghanyangserver.groupmember.repository.GroupMemberRepository;
import org.springframework.stereotype.Service;

Expand All @@ -17,6 +21,32 @@ public class CctvService {
private final KvsClient kvsClient;
private final CctvRepository cctvRepository;
private final GroupMemberRepository groupMemberRepository;
private final GroupRepository groupRepository;

public void createCctv(CreateCctvRequest createCctvRequest) {

if (cctvRepository.existsByThingId(createCctvRequest.thingId())) {
throw new BadRequestException(CctvErrorType.ALREADY_EXIST);
}
GroupEntity groupEntity =
groupRepository
.findById(createCctvRequest.groupId())
.orElseThrow(() -> new NotFoundException((GroupErrorType.NOT_FOUND)));

String cctvNickname = "CCTV-" + createCctvRequest.thingId().substring(1, 3);

// CctvEntity 객체 생성
CctvEntity cctv =
CctvEntity.builder()
.cctvNickname(cctvNickname)
.kvsChannelName(createCctvRequest.kvsChannelName())
.thingId(createCctvRequest.thingId())
.group(groupEntity)
.build();

// 저장
cctvRepository.save(cctv);
}

@Transactional
public void outById(Long memberId, Long cctvId) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.ioteatime.meonghanyangserver.common.type;

public enum CctvErrorType implements ErrorTypeCode {
ALREADY_EXIST("CCTV ALREADY EXIST", "이미 존재하는 CCTV 기기입니다."),
ONLY_MASTER_CAN_DELETE("BAD REQUEST", "CCTV는 MASTER만 삭제할 수 있습니다."),
NOT_FOUND("NOT FOUND", "CCTV를 찾을 수 없습니다.");

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.ioteatime.meonghanyangserver.common.type;

public enum CctvSuccessType implements SuccessTypeCode {
CREATE_CCTV(201, "CREATE", "CCTV 생성에 성공하였습니다."),
DELETE_CCTV(200, "OK", "CCTV 삭제(퇴출)에 성공하였습니다.");

private final Integer code;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ public enum GroupErrorType implements ErrorTypeCode {
ALREADY_EXISTS("BAD REQUEST", "그룹이 이미 존재합니다."),
GROUP_MEMBER_ALREADY_EXISTS("BAD REQUEST", "유저가 그룹에 이미 속해 있습니다."),
NOT_FOUND("NOT FOUND", "그룹이 존재하지 않습니다."),
GROUP_MEMBER_NOT_FOUND("NOT FOUND", "그룹 회원 정보를 찾을 수 없습니다."),
ONLY_MASTER_REMOVE_GROUP_MEMBER("BAD REQUEST", "방장만 그룹에서 참여자를 제외시킬 수 있습니다."),
ONLY_MASTER_REMOVE_GROUP_MASTER("BAD REQUEST", "방장은 자신을 제외시킬 수 있습니다.");
ONLY_MASTER_REMOVE_GROUP_MASTER("BAD REQUEST", "방장은 자신을 제외시킬 수 있습니다."),
GROUP_MEMBER_NOT_FOUND("GROUP MEMBER NOT FOUND", "그룹 회원 정보를 찾을 수 없습니다.");

private final String message;
private final String description;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ public enum GroupSuccessType implements SuccessTypeCode {
GET_GROUP_ID(200, "OK", "로그인한 회원이 참여하고 있는 그룹 ID 조회에 성공하였습니다."),
GET_CHANNEL_INFO(200, "OK", "그룹 ID와 KVS 채널 이름 조회에 성공하였습니다."),
GET_GROUP_TOTAL_INFO(200, "OK", "그룹 통합 데이터 조회에 성공하였습니다."),
DELETE_GROUP_MEMBER(200, "OK", "그룹에서 참여자 제외를 성공하였습니다.");
DELETE_GROUP_MEMBER(200, "OK", "그룹에서 참여자 제외를 성공하였습니다."),
JOIN_GROUP_MEMBER(201, "CREATE", "그룹에 참여자 등록을 성공하였습니다");

private final Integer code;
private final String message;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
package org.ioteatime.meonghanyangserver.group.repository;

import java.util.Optional;
import org.ioteatime.meonghanyangserver.group.domain.GroupEntity;

public interface GroupRepository {
GroupEntity save(GroupEntity groupEntity);

Optional<GroupEntity> findById(Long groupId);

void deleteById(Long groupId);
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.ioteatime.meonghanyangserver.group.repository;

import java.util.Optional;
import lombok.RequiredArgsConstructor;
import org.ioteatime.meonghanyangserver.group.domain.GroupEntity;
import org.springframework.stereotype.Repository;
Expand All @@ -14,6 +15,11 @@ public GroupEntity save(GroupEntity groupEntity) {
return jpaGroupRepository.save(groupEntity);
}

@Override
public Optional<GroupEntity> findById(Long groupId) {
return jpaGroupRepository.findById(groupId);
}

@Override
public void deleteById(Long groupId) {
jpaGroupRepository.deleteById(groupId);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,17 @@
import io.swagger.v3.oas.annotations.tags.Tag;
import org.ioteatime.meonghanyangserver.common.api.Api;
import org.ioteatime.meonghanyangserver.common.utils.LoginMember;
import org.ioteatime.meonghanyangserver.groupmember.dto.request.JoinGroupMemberRequest;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;

@Tag(name = "Group Member Api", description = "Group Member 관련 API 목록입니다.")
public interface GroupMemberApi {

@Operation(summary = "참여자가 그룹에 입장을 요청합니다.", description = "담당자: 임지인")
public Api<Void> joinGroupAsMember(
@LoginMember Long memberId, @RequestBody JoinGroupMemberRequest joinGroupMemberRequest);

@Operation(summary = "방장이 그룹에서 참여자를 제외합니다.", description = "담당자: 최민석")
public Api<?> deleteMasterGroupMember(
@LoginMember Long memberId,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,12 @@
import org.ioteatime.meonghanyangserver.common.api.Api;
import org.ioteatime.meonghanyangserver.common.type.GroupSuccessType;
import org.ioteatime.meonghanyangserver.common.utils.LoginMember;
import org.ioteatime.meonghanyangserver.groupmember.dto.request.JoinGroupMemberRequest;
import org.ioteatime.meonghanyangserver.groupmember.service.GroupMemberService;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

Expand All @@ -25,6 +28,14 @@ public Api<?> deleteMasterGroupMember(
return Api.success(GroupSuccessType.DELETE_GROUP_MEMBER);
}

@PostMapping("/viewer")
public Api<Void> joinGroupAsMember(
@LoginMember Long memberId,
@RequestBody JoinGroupMemberRequest joinGroupMemberRequest) {
groupMemberService.joinGroupMember(memberId, joinGroupMemberRequest);
return Api.success(GroupSuccessType.JOIN_GROUP_MEMBER);
}

@DeleteMapping("/{groupId}/member")
public Api<?> deleteGroupMember(@LoginMember Long memberId, @PathVariable Long groupId) {
groupMemberService.deleteGroupMember(memberId, groupId);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package org.ioteatime.meonghanyangserver.groupmember.dto.request;

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

@Schema(description = "Viewer가 그룹에 참여하기 위한 요청")
public record JoinGroupMemberRequest(
@NotNull @Schema(description = "그룹 ID", example = "1") Long groupId,
@NotBlank @Schema(description = "Thing ID", example = "cf27414r3b22c023") String thingId) {}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ public interface GroupMemberRepository {

void deleteById(Long id);

Optional<GroupMemberEntity> findByMemberIdAndGroupId(Long memberId, Long groupId);

boolean isMasterMember(Long memberId);

Optional<GroupMemberEntity> findByGroupIdAndMemberIdAndRole(Long memberId, Long groupId);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import lombok.RequiredArgsConstructor;
import org.ioteatime.meonghanyangserver.group.domain.GroupEntity;
import org.ioteatime.meonghanyangserver.groupmember.doamin.GroupMemberEntity;
import org.ioteatime.meonghanyangserver.groupmember.doamin.QGroupMemberEntity;
import org.ioteatime.meonghanyangserver.groupmember.doamin.enums.GroupMemberRole;
import org.springframework.stereotype.Repository;

Expand Down Expand Up @@ -107,4 +108,22 @@ public Optional<GroupMemberEntity> findByGroupIdAndMemberId(Long groupId, Long m
public void deleteByGroupId(Long groupId) {
jpaGroupMemberRepository.deleteByGroupId(groupId);
}

@Override
public Optional<GroupMemberEntity> findByMemberIdAndGroupId(Long memberId, Long groupId) {
QGroupMemberEntity groupMember = QGroupMemberEntity.groupMemberEntity;

GroupMemberEntity result =
jpaQueryFactory
.selectFrom(groupMember)
.where(
groupMember
.member
.id
.eq(memberId)
.and(groupMember.group.id.eq(groupId)))
.fetchOne();

return Optional.ofNullable(result);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,25 +6,29 @@
import org.ioteatime.meonghanyangserver.cctv.repository.CctvRepository;
import org.ioteatime.meonghanyangserver.common.exception.BadRequestException;
import org.ioteatime.meonghanyangserver.common.exception.NotFoundException;
import org.ioteatime.meonghanyangserver.common.type.AuthErrorType;
import org.ioteatime.meonghanyangserver.common.type.GroupErrorType;
import org.ioteatime.meonghanyangserver.common.utils.KvsChannelNameGenerator;
import org.ioteatime.meonghanyangserver.group.domain.GroupEntity;
import org.ioteatime.meonghanyangserver.group.dto.response.GroupInfoResponse;
import org.ioteatime.meonghanyangserver.group.repository.GroupRepository;
import org.ioteatime.meonghanyangserver.groupmember.doamin.GroupMemberEntity;
import org.ioteatime.meonghanyangserver.groupmember.doamin.enums.GroupMemberRole;
import org.ioteatime.meonghanyangserver.groupmember.dto.request.JoinGroupMemberRequest;
import org.ioteatime.meonghanyangserver.groupmember.mapper.GroupMemberEntityMapper;
import org.ioteatime.meonghanyangserver.groupmember.repository.GroupMemberRepository;
import org.ioteatime.meonghanyangserver.member.domain.MemberEntity;
import org.ioteatime.meonghanyangserver.member.repository.MemberRepository;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@RequiredArgsConstructor
public class GroupMemberService {
private final GroupMemberRepository groupMemberRepository;
private final KvsChannelNameGenerator kvsChannelNameGenerator;
private final MemberRepository memberRepository;
private final GroupRepository groupRepository;
private final KvsChannelNameGenerator kvsChannelNameGenerator;
private final CctvRepository cctvRepository;

// input user
Expand All @@ -38,6 +42,26 @@ public void createGroupMember(
groupMemberRepository.save(groupMember);
}

@Transactional
public void joinGroupMember(Long memberId, JoinGroupMemberRequest joinGroupMemberRequest) {
Long groupId = joinGroupMemberRequest.groupId();
String thingId = joinGroupMemberRequest.thingId();

if (groupMemberRepository.findByMemberIdAndGroupId(memberId, groupId).isPresent()) {
throw new BadRequestException(GroupErrorType.GROUP_MEMBER_ALREADY_EXISTS);
}
GroupEntity groupEntity =
groupRepository
.findById(groupId)
.orElseThrow(() -> new BadRequestException(GroupErrorType.NOT_FOUND));
MemberEntity memberEntity =
memberRepository
.findById(memberId)
.orElseThrow(() -> new BadRequestException(AuthErrorType.NOT_FOUND));

createGroupMember(groupEntity, memberEntity, GroupMemberRole.ROLE_PARTICIPANT, thingId);
}

public boolean existsGroupMember(Long memberId) {
return groupMemberRepository.existsGroupMember(memberId);
}
Expand Down
Loading