Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
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
7 changes: 6 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -50,13 +50,18 @@ dependencies {
implementation 'org.springframework.boot:spring-boot-starter-mail'
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'

compileOnly 'org.projectlombok:lombok'
implementation 'org.projectlombok:lombok'

// QR Code Generation
implementation 'com.google.zxing:core:3.5.3'
implementation 'com.google.zxing:javase:3.5.3'

//runtimeOnly 'com.h2database:h2'
runtimeOnly 'com.mysql:mysql-connector-j'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.springframework.security:spring-security-test'
testRuntimeOnly 'com.h2database:h2'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}

Expand Down
14 changes: 14 additions & 0 deletions logs/dev-server.err.log
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':bootRun'.
> Process 'command 'C:\Program Files\Eclipse Adoptium\jdk-17.0.18.8-hotspot\bin\java.exe'' finished with non-zero exit value 1

* Try:
> Run with --stacktrace option to get the stack trace.
> Run with --info or --debug option to get more log output.
> Run with --scan to get full insights.
> Get more help at https://help.gradle.org.

BUILD FAILED in 35s
151 changes: 151 additions & 0 deletions logs/dev-server.out.log

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package com.hongik.devtalk.controller;

import com.hongik.devtalk.global.apiPayload.ApiResponse;
import com.hongik.devtalk.service.seminar.SearchStatsService;
import com.hongik.devtalk.service.seminar.SeminarViewStatsService;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;

import java.time.LocalDate;
import java.util.List;

@RestController
@RequiredArgsConstructor
@RequestMapping("/admin/stats")
public class AdminStatsController {

private final SeminarViewStatsService seminarViewStatsService;
private final SearchStatsService searchStatsService;

//세미나 카드별 조회수 (일 단위 그래프)
@GetMapping("/seminars/{seminarId}/views")
public ApiResponse<List<SeminarViewStatsService.ViewPoint>> seminarViews(
@PathVariable Long seminarId,
@RequestParam String from,
@RequestParam String to
) {
var points = seminarViewStatsService.getDailyGraph(seminarId, LocalDate.parse(from), LocalDate.parse(to));
return ApiResponse.onSuccess("세미나 조회수 그래프 조회 성공", points);
}

//인기 검색어 Top5 (막대그래프용)
// target=ALL | SEMINAR | SPEAKER
@GetMapping("/search/top5")
public ApiResponse<List<SearchStatsService.TopKeyword>> top5(
@RequestParam String from,
@RequestParam String to,
@RequestParam(defaultValue = SearchStatsService.TARGET_ALL) String target
) {
var res = searchStatsService.getTop5(target, LocalDate.parse(from), LocalDate.parse(to));
return ApiResponse.onSuccess("인기 검색어 TOP5 조회 성공", res);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.hongik.devtalk.domain.live.dto.*;
import com.hongik.devtalk.global.apiPayload.ApiResponse;
import com.hongik.devtalk.service.admin.QrService;
import com.hongik.devtalk.service.live.LiveService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.Content;
Expand All @@ -27,6 +28,7 @@
@RequiredArgsConstructor
public class LiveController {
private final LiveService liveService;
private final QrService qrService;

@PostMapping("/auth")
@Operation(summary = "신청자 인증 API", description = "학번과 이름을 받아 세미나 신청자임을 인증하고, 라이브 서비스 접근을 위한 JWT 토큰을 발급합니다.")
Expand Down Expand Up @@ -161,4 +163,10 @@ public ApiResponse<AttendanceResponseDto> attendCheck(@AuthenticationPrincipal U
LocalDateTime attendTime = LocalDateTime.now(ZoneId.of("Asia/Seoul"));
return liveService.attendanceCheck(studentNum, attendTime);
}

@Operation(summary = "QR용 출석체크 API", description = "QR코드를 인식하여 사용자 인증 및 출석체크를 진행합니다.")
@PostMapping("/auth-applicants")
public ApiResponse<AuthStudentResponseDto> authAndApplicantCheck(@RequestBody AuthStudentRequestDto authStudentRequestDto) {
return liveService.authStudentandCheck(authStudentRequestDto);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@

import com.hongik.devtalk.domain.seminar.admin.dto.*;
import com.hongik.devtalk.global.apiPayload.ApiResponse;
import com.hongik.devtalk.service.admin.QrService;
import com.hongik.devtalk.service.seminar.SeminarAdminCommandService;
import com.hongik.devtalk.service.seminar.SeminarAdminQueryService;
import com.hongik.devtalk.service.seminar.SeminarReviewService;
import com.hongik.devtalk.service.seminar.SeminarStatisticsService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Parameters;
Expand All @@ -29,6 +31,8 @@ public class SeminarAdminController {
private final SeminarAdminQueryService seminarAdminQueryService;
private final SeminarAdminCommandService seminarAdminCommandService;
private final SeminarReviewService seminarReviewService;
private final SeminarStatisticsService seminarStatisticsService;
private final QrService qrService;

@Operation(summary = "세미나 카드리스트 조회", description = "세미나 카드리스트를 조회합니다.")
@ApiResponses({
Expand Down Expand Up @@ -301,6 +305,13 @@ public ApiResponse<Void> checkingAttendance(
return ApiResponse.onSuccess("출석 체크 완료하였습니다.");
}

@Operation(summary = "세미나 출석체크 QR 생성 -by 황신애", description = "세미나 출석체크용 QR코드를 생성합니다.")
@PostMapping("/{seminarId}/applicants/qr")
public ApiResponse<String> getQrcode(@PathVariable Long seminarId) throws Exception{
String qrStr = qrService.generateAndUploadQrCode(seminarId);
return ApiResponse.onSuccess("출석체크용 QR 생성 완료",qrStr);
}

@Operation(summary = "세미나 연사별 질문 조회", description = "세미나 연사별 질문 정보를 조회합니다.")
@ApiResponses({
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "200", description = "조회 성공"),
Expand Down Expand Up @@ -329,4 +340,21 @@ public ApiResponse<List<SeminarNumResponseDTO>> getSeminarNums() {
List<SeminarNumResponseDTO> result = seminarAdminQueryService.getSeminarNums();
return ApiResponse.onSuccess("세미나 회차 리스트 조회에 성공했습니다.", result);
}

@Operation(summary = "세미나 통계 정보 조회 -by 박우주", description = "세미나별 통계 정보를 조회합니다. 학과별/학년별 신청 비율, 신청인원, 실제 참석 인원, 참석률을 제공합니다.")
@ApiResponses({
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "200", description = "조회 성공"),
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "404", description = "세미나 없음",
content = @Content(schema = @Schema(implementation = ApiResponse.class))),
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "500", description = "서버 내부 오류",
content = @Content(schema = @Schema(implementation = ApiResponse.class)))
})
@GetMapping("/{seminarId}/statistics")
public ApiResponse<SeminarStatisticsResponseDTO> getSeminarStatistics(
@Parameter(description = "세미나 ID", required = true)
@PathVariable Long seminarId
) {
SeminarStatisticsResponseDTO result = seminarStatisticsService.getSeminarStatistics(seminarId);
return ApiResponse.onSuccess("세미나 통계 정보 조회에 성공했습니다.", result);
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package com.hongik.devtalk.controller.seminar;

import com.hongik.devtalk.domain.seminar.detail.dto.SeminarDetailResponseDto;
import com.hongik.devtalk.domain.seminar.detail.dto.SeminarDetailReviewResponseDto;
import com.hongik.devtalk.domain.seminar.detail.dto.SeminarDetailSessionResponseDto;
import com.hongik.devtalk.domain.seminar.detail.dto.*;
import com.hongik.devtalk.global.apiPayload.ApiResponse;
import com.hongik.devtalk.service.seminar.SeminarDetailService;
import io.swagger.v3.oas.annotations.Operation;
Expand All @@ -11,11 +9,10 @@
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import org.springframework.web.bind.annotation.*;
import com.hongik.devtalk.service.seminar.SeminarViewStatsService;
import com.hongik.devtalk.service.seminar.SearchStatsService;
import org.springframework.web.bind.annotation.RequestHeader;
import java.time.LocalDateTime;
import java.util.List;

Expand All @@ -26,6 +23,8 @@
public class SeminarDetailController {

private final SeminarDetailService seminarDetailService;
private final SeminarViewStatsService seminarViewStatsService;
private final SearchStatsService searchStatsService;

//세미나 세부정보 조회( 세미나 )

Expand Down Expand Up @@ -53,16 +52,18 @@ public class SeminarDetailController {

)
})
public ApiResponse<SeminarDetailResponseDto> getSeminars(@PathVariable Long seminarId)
{

public ApiResponse<SeminarDetailResponseDto> getSeminars(
@PathVariable Long seminarId,
@RequestHeader(value="X-Client-Id", required=false) String clientId
){
SeminarDetailResponseDto seminarDetailInfo = seminarDetailService.getSeminarDetail(seminarId);

return ApiResponse.onSuccess("세미나 세부정보 ( 세미나 ) 조회에 성공하였습니다. ",seminarDetailInfo);
//본문 조회 성공 후 +1
seminarViewStatsService.recordSeminarView(seminarId, clientId);

return ApiResponse.onSuccess("세미나 세부정보 ( 세미나 ) 조회에 성공하였습니다. ", seminarDetailInfo);
}


//세미나 세부정보 조회 ( 리뷰 )

@Operation(summary = "세미나 세부정보 (리뷰) 조회", description = "seminarId를 사용하여 해당 세미나의 리뷰 목록을 조회합니다.")
Expand Down Expand Up @@ -133,5 +134,44 @@ public ApiResponse<List<SeminarDetailSessionResponseDto>> getSeminarSessions(@Pa

}

//세미나 검색


@Operation(summary = "세미나 검색 ", description = "세미나를 키워드로 검색하여 해당 세미나의 정보를 조회합니다.")
@GetMapping("/search")
@ApiResponses(value = {
@io.swagger.v3.oas.annotations.responses.ApiResponse(

responseCode = "200",
description = "세미나 검색 성공",
content = @Content(schema = @Schema(implementation = ApiResponse.class))

),

@io.swagger.v3.oas.annotations.responses.ApiResponse(
responseCode = "500",
description = "서버 오류",
content = @Content(schema = @Schema(implementation = ApiResponse.class))
),

@io.swagger.v3.oas.annotations.responses.ApiResponse(
responseCode = "400",
description = "잘못된 요청( 키워드 누락 )",
content = @Content(schema = @Schema(implementation = ApiResponse.class))

)
})

public ApiResponse<List<SeminarSearchResponseDto>> searchSeminars(
@RequestParam(value = "keyword", required = false) String keyword,
@RequestHeader(value="X-Client-Id", required=false) String clientId
){
//검색 요청 들어온 순간 +1
searchStatsService.recordSearch(SearchStatsService.TARGET_SEMINAR, keyword, clientId);

List<SeminarSearchResponseDto> seminarList = seminarDetailService.searchSeminars(keyword);
return ApiResponse.onSuccess("세미나 검색에 성공하였습니다.", seminarList);
}


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
package com.hongik.devtalk.controller.speaker;


import com.hongik.devtalk.domain.speaker.dto.SpeakerDetailResponseDto;
import com.hongik.devtalk.domain.speaker.dto.SpeakerSearchResponseDto;
import com.hongik.devtalk.global.apiPayload.ApiResponse;
import com.hongik.devtalk.service.speaker.SpeakerService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
import com.hongik.devtalk.service.seminar.SearchStatsService;
import org.springframework.web.bind.annotation.RequestHeader;

import java.util.List;

@Tag(name="Speaker",description = "연사 정보 조회 관련 API - by 박서영")
@RestController
@RequestMapping("/user/speakers")
@RequiredArgsConstructor
public class SpeakerController {

private final SpeakerService speakerService;
private final SearchStatsService searchStatsService;

//세미나 연사 검색


@Operation(summary = "세미나 연사 검색 ", description = "연사를 검색하여 해당 연사의 정보를 조회합니다.")
@GetMapping("/search")
@ApiResponses(value = {
@io.swagger.v3.oas.annotations.responses.ApiResponse(

responseCode = "200",
description = "연사 검색 성공",
content = @Content(schema = @Schema(implementation = ApiResponse.class))

),

@io.swagger.v3.oas.annotations.responses.ApiResponse(
responseCode = "500",
description = "서버 오류",
content = @Content(schema = @Schema(implementation = ApiResponse.class))
),

@io.swagger.v3.oas.annotations.responses.ApiResponse(
responseCode = "400",
description = "잘못된 요청( 키워드 누락 )",
content = @Content(schema = @Schema(implementation = ApiResponse.class))


)
})

public ApiResponse<List<SpeakerSearchResponseDto>> searchSpeakers(
@RequestParam(value = "keyword", required = false) String keyword,
@RequestHeader(value="X-Client-Id", required=false) String clientId
){
//검색 요청 들어온 순간 +1
searchStatsService.recordSearch(SearchStatsService.TARGET_SPEAKER, keyword, clientId);

List<SpeakerSearchResponseDto> speakerList = speakerService.searchSpeakers(keyword);
return ApiResponse.onSuccess("연사 검색에 성공하였습니다.", speakerList);
}


@Operation(summary = " 연사 목록 조회 ", description = "연사의 목록을 조회합니다.")
@GetMapping
@ApiResponses(value = {
@io.swagger.v3.oas.annotations.responses.ApiResponse(

responseCode = "200",
description = "연사 목록 조회 성공",
content = @Content(schema = @Schema(implementation = ApiResponse.class))

),

@io.swagger.v3.oas.annotations.responses.ApiResponse(
responseCode = "500",
description = "서버 오류",
content = @Content(schema = @Schema(implementation = ApiResponse.class))
)
})


//연사 목록 조회

public ApiResponse<List<SpeakerSearchResponseDto>> getSpeakers()
{
//모든 연사 호출
List<SpeakerSearchResponseDto> speakers = speakerService.getAllSpeakers();

return ApiResponse.onSuccess("연사 목록 조회에 성공했습니다",speakers);
}

//연사 상세정보 조회
@Operation(summary = "연사 상세정보 조회", description = "특정 연사의 상세 정보를 조회합니다.")
@GetMapping("/{speakerId}") // 경로 변수 사용
@ApiResponses(value = {
@io.swagger.v3.oas.annotations.responses.ApiResponse(
responseCode = "200",
description = "연사 상세정보 조회 성공",
content = @Content(schema = @Schema(implementation = ApiResponse.class))
),
@io.swagger.v3.oas.annotations.responses.ApiResponse(
responseCode = "404",
description = "연사를 찾을 수 없음",
content = @Content(schema = @Schema(implementation = ApiResponse.class))
),
@io.swagger.v3.oas.annotations.responses.ApiResponse(
responseCode = "500",
description = "서버 오류",
content = @Content(schema = @Schema(implementation = ApiResponse.class))
)
})

public ApiResponse<SpeakerDetailResponseDto> getSpeakerDetails(@PathVariable Long speakerId)
{
SpeakerDetailResponseDto speakers = speakerService.getSpeakerDetails(speakerId);

return ApiResponse.onSuccess("연사 상세정보 조회에 성공했습니다.",speakers);
}



}
Loading