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

record #219

Closed
wants to merge 16 commits into from
Closed
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
82 changes: 65 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,29 +31,58 @@
</details>

---
**홈** 화면 입니다.
### 홈 페이지
대표적인 기능 및 미로그인 상태의 사용자가 사용할 수 있는 메뉴가 있고, 사용자의 후기를 보여줍니다.
<img width="1673" alt="스크린샷 2024-08-29 12 47 08" src="https://github.com/user-attachments/assets/32e48752-7300-42e7-b9f0-ff18cc4607ea">
![landingPage](https://github.com/user-attachments/assets/d71683e1-78a0-4d10-8500-3faa167a370e)


### 로그인 페이지
별도의 회원가입 없이 **소셜 로그인**(최초 회원등록) 후 바로 이용 가능합니다.
![login](https://github.com/user-attachments/assets/0bfb65da-a517-4636-a700-eda461d0d6d0)

**로그인 화면**입니다. 별도의 회원가입 없이 **소셜 로그인**(최초 회원등록) 후 바로 이용 가능합니다.
![스크린샷 2024-08-14 09 59 30](https://github.com/user-attachments/assets/df2e6056-704e-4e01-a7e7-74b1832d2a26)

로그인 시 좌측 상단의 메뉴에 마이페이지, 로그아웃, 스피치 분석 버튼이 생깁니다.<br>
이 메뉴는 로그인시 항상 활성화 되는 버튼입니다.
![스크린샷 2024-08-14 09 59 45](https://github.com/user-attachments/assets/21609e17-31e4-4dff-bdac-459f08a1edcd)

현재 사용자의 **대본 리스트**입니다.<br>

### 대본 리스트 페이지
사용자의 대본들을 모두 확인할 수 있습니다.<br>
대본 작성하기 버튼을 누르면 작성 페이지로, 리스트 목록의 아이템을 선택하면 조회 및 녹음 페이지로 넘어갑니다.
![스크린샷 2024-08-14 09 59 58](https://github.com/user-attachments/assets/76af2fcd-fc0c-4c6f-8716-612de9b918dd)
![script-list](https://github.com/user-attachments/assets/65057b11-ca71-478c-a1f3-b6ee5a6c33fd)

**대본 작성** 페이지입니다.

### 대본 작성 페이지
새로운 대본을 생성할 수 있습니다.
![스크린샷 2024-08-14 10 00 09](https://github.com/user-attachments/assets/800cfac5-ea9f-4f82-97e9-1a3371658693)
![script-write](https://github.com/user-attachments/assets/e9e8462d-813d-4251-8a90-ace564c6957a)


**녹음 분석** 페이지 입니다.
### 녹음 분석 페이지
녹음하기 버튼을 눌러 녹음을 진행하고, 업로드 버튼을 클릭합니다. 클릭 시 생기는 아이템은 녹음 파일이며, 분석하기 버튼을 누르면 API를 호출하여 인식된 음성을 텍스트 파일로 보여주고 1~5점 사이로 점수가 매겨집니다.
![스크린샷 2024-08-14 10 00 18](https://github.com/user-attachments/assets/931ee61a-026a-4088-9a14-dce7ba8785db)
![MainGif](https://github.com/user-attachments/assets/3321c0dc-5e7e-43ca-9e2e-72ff5e81b810)

### 분석 페이지
분석 결과를 보여줍니다.
![report](https://github.com/user-attachments/assets/c1b15f2b-54a4-47e2-af9c-f630172e6def)

### 마이 페이지
등급과 권한, 활동 내역을 확인 할 수 있습니다.
![mypage](https://github.com/user-attachments/assets/e249ab3c-443c-41bd-a54b-0559db894df4)



### 게시판 리스트 페이지
사용자의 대본들을 모두 확인할 수 있습니다.<br>
대본 작성하기 버튼을 누르면 작성 페이지로, 리스트 목록의 아이템을 선택하면 조회 및 녹음 페이지로 넘어갑니다.
![board-list](https://github.com/user-attachments/assets/e94e6644-ce74-4fb0-bc8e-65925e065af2)


### 게시글 작성 페이지
새로운 대본을 생성할 수 있습니다.
![Board-edit](https://github.com/user-attachments/assets/a65acd64-4366-4e31-80cb-d5e999c709cb)

### 게시글 페이지
위에서 작성한 내용은 아래와 같이 보여집니다.
![board-Detail](https://github.com/user-attachments/assets/506329ac-eabb-4bab-95c4-bcae7928da0a)

---

Expand All @@ -76,14 +105,33 @@
- issue 템플릿 이용

---
>### Framework
> > <img src="https://img.shields.io/badge/Spring-6DB33F?style=for-the-badge&logo=spring&logoColor=white"> <img src="https://img.shields.io/badge/Spring_Boot-6DB33F?style=for-the-badge&logo=Spring-Boot&logoColor=white"> <img src="https://img.shields.io/badge/Spring_Security-6DB33F?style=for-the-badge&logo=Spring-Security&logoColor=white"><br>
> ### Framework
> > <img src="https://img.shields.io/badge/Spring%20Boot-6DB33F?style=for-the-badge&logo=spring&logoColor=white"> <img src="https://img.shields.io/badge/Gradle-02303A?style=for-the-badge&logo=gradle&logoColor=white">
>
> ### Language
> > <img src="https://img.shields.io/badge/Java-007396?style=for-the-badge&logo=java&logoColor=white"> <img src="https://img.shields.io/badge/HTML-E34F26?style=for-the-badge&logo=html5&logoColor=white"> <img src="https://img.shields.io/badge/Thymeleaf-6DB33F?style=for-the-badge&logo=spring&logoColor=white"> <img src="https://img.shields.io/badge/JavaScript-F7DF1E?style=for-the-badge&logo=javascript&logoColor=black">
>
> ### Test
> > <img src="https://img.shields.io/badge/JUnit-25A162?style=for-the-badge&logo=junit5&logoColor=white">
>
>### Language
> > <img src="https://img.shields.io/badge/Java-ED8B00?style=for-the-badge&logo=openjdk&logoColor=white"> <img src="https://img.shields.io/badge/HTML-239120?style=for-the-badge&logo=html5&logoColor=white"> <img src="https://img.shields.io/badge/JavaScript-323330?style=for-the-badge&logo=javascript&logoColor=F7DF1E"><br>
> ### Docs
> > <img src="https://img.shields.io/badge/Javadoc-007396?style=for-the-badge&logo=java&logoColor=white">
>
>### DB
> > <img src="https://img.shields.io/badge/MySQL-005C84?style=for-the-badge&logo=mysql&logoColor=white"><br>
> ### DB
> > <img src="https://img.shields.io/badge/MySQL-4479A1?style=for-the-badge&logo=mysql&logoColor=white">
>
> ### Auth
> > <img src="https://img.shields.io/badge/OAuth%202.0-3D9B3F?style=for-the-badge&logo=oauth&logoColor=white"> <img src="https://img.shields.io/badge/JWT-000000?style=for-the-badge&logo=json-web-tokens&logoColor=white"> <img src="https://img.shields.io/badge/Kakao-FFCD00?style=for-the-badge&logo=kakao&logoColor=white"> <img src="https://img.shields.io/badge/Google-4285F4?style=for-the-badge&logo=google&logoColor=white"> <img src="https://img.shields.io/badge/GitHub-181717?style=for-the-badge&logo=github&logoColor=white"> <img src="https://img.shields.io/badge/SSL-4D4D4D?style=for-the-badge&logo=ssl&logoColor=white">
>
> ### Infra
> > <img src="https://img.shields.io/badge/Docker-2496ED?style=for-the-badge&logo=docker&logoColor=white"> <img src="https://img.shields.io/badge/Nginx-009639?style=for-the-badge&logo=nginx&logoColor=white"> <img src="https://img.shields.io/badge/GCP-4285F4?style=for-the-badge&logo=google-cloud&logoColor=white"> <img src="https://img.shields.io/badge/.env-4C4C4C?style=for-the-badge&logo=dotenv&logoColor=white">
>
> ### CI/CD
> > <img src="https://img.shields.io/badge/GitHub%20Actions-2088FF?style=for-the-badge&logo=github-actions&logoColor=white">
>
> ### Version Control
> > <img src="https://img.shields.io/badge/GitHub-181717?style=for-the-badge&logo=github&logoColor=white"> <img src="https://img.shields.io/badge/Git-F05032?style=for-the-badge&logo=git&logoColor=white">

---

## 현재 추가 된 기능
Expand Down
3 changes: 3 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-validation'

implementation 'org.springframework.boot:spring-boot-starter-actuator'
implementation group: 'com.google.cloud', name: 'spring-cloud-gcp-starter-logging', version: '5.4.3'

implementation 'io.github.cdimascio:dotenv-java:2.3.0'
implementation 'com.google.code.gson:gson:2.8.9'
implementation 'org.json:json:20231013'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,20 @@
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;

/**
* AdminPageController는 관리자의 대시보드 페이지를 처리하는 컨트롤러입니다.
*/

@Controller
@RequestMapping("/admin")
public class AdminPageController {

/**
* 관리 페이지를 반환합니다.
*
* @return "admin-page" 뷰 이름
*/
@GetMapping("/view")
public String adminPage(){
public String adminPage() {
return "admin-page";
}
}
2 changes: 0 additions & 2 deletions src/main/java/com/speech/up/api/etri/dto/AiRequest.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
package com.speech.up.api.etri.dto;

import org.springframework.web.multipart.MultipartFile;

import com.fasterxml.jackson.databind.PropertyNamingStrategies;
import com.fasterxml.jackson.databind.annotation.JsonNaming;
import com.speech.up.api.etri.type.ApiType;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ protected SecurityFilterChain configure(HttpSecurity httpSecurity) throws Except
"/boards", "/boards/**", "/api/upload",
"/.well-known/**", "/api/open/data/*", "/banned-page",
"/report", "/scripts", "/script-write", "/scripts-list", "/replies/**",
"/admin/view", "/page/me", "/speech-record", "reports/**", "/").permitAll()
"/admin/view", "/page/me", "reports/**", "/", "/speech-record").permitAll()
.requestMatchers("/api/boards").hasAnyRole("ADMIN_USER", "GENERAL_USER")
.requestMatchers("/users/me").hasAnyRole("ADMIN_USER", "GENERAL_USER","BAN_USER")
.requestMatchers("/speech-record").hasAnyRole("ADMIN_USER", "GENERAL_USER")
Expand All @@ -76,6 +76,7 @@ protected SecurityFilterChain configure(HttpSecurity httpSecurity) throws Except
.requestMatchers("/users/banned/me").hasRole("BAN_USER")
.requestMatchers("/api/admin/user/all").hasRole("ADMIN_USER")
.requestMatchers("/api/admin/me").hasRole("ADMIN_USER")
.requestMatchers("/actuator/**").hasRole("ADMIN_USER")
.anyRequest().authenticated())
.oauth2Login(oauth2 -> oauth2
.authorizationEndpoint(endpoint -> endpoint.baseUri("/oauth2/authorization/**"))
Expand Down Expand Up @@ -107,7 +108,7 @@ class FailedAuthenticationEntryPoint implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest request, HttpServletResponse response,
AuthenticationException authException) throws
IOException, ServletException {
IOException {
response.setContentType("application/json");
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
response.getWriter().write("{\"code\" : \"NP\", \"message\" : \"No Permission\"}");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ protected void doFilterInternal(@Nullable HttpServletRequest request, @Nullable
SecurityContext securityContext = SecurityContextHolder.createEmptyContext();
AbstractAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(socialId, token, authorities);

authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
securityContext.setAuthentication(authenticationToken);
SecurityContextHolder.setContext(securityContext);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
import com.speech.up.auth.entity.CustomOAuth2User;
import com.speech.up.auth.provider.JwtProvider;

import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
Expand All @@ -19,14 +18,16 @@
@RequiredArgsConstructor
public class OAuth2SuccessHandler extends SimpleUrlAuthenticationSuccessHandler {
private final JwtProvider jwtProvider;

@Value("${google.cloud.url}")
private String allowUrl;

@Override
public void onAuthenticationSuccess(
HttpServletRequest request,
HttpServletResponse response,
Authentication authentication) throws IOException {
CustomOAuth2User oAuth2User = (CustomOAuth2User) authentication.getPrincipal();
CustomOAuth2User oAuth2User = (CustomOAuth2User)authentication.getPrincipal();
String socialId = oAuth2User.getName();
String token = jwtProvider.createToken(socialId);
response.sendRedirect(allowUrl + "?token=Bearer " + token);
Expand Down
10 changes: 8 additions & 2 deletions src/main/java/com/speech/up/demo/BannedController.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,16 @@

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

/**
* BannedController는 접근이 제한된 사용자에게 표시되는 페이지를 처리하는 컨트롤러입니다.
*/
@Controller
public class BannedController {

/**
* 접근이 제한된 페이지를 반환합니다.
*
* @return "banned" 뷰 이름
*/
@GetMapping("/banned-page")
public String bannedPage() {
return "banned";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,15 +67,15 @@ public ResponseEntity<RecordAddDto.Response> addRecord(
/**
* 특정 녹음을 삭제합니다. 실제로 데이터를 삭제하지 않고, 삭제 상태로 표시합니다.
*
* @param recordIsUseRequestDto 삭제할 녹음의 ID와 관련 정보를 포함하는 요청 객체
* @param recordId 삭제할 녹음의 ID
* @return 삭제 상태로 변경된 녹음의 세부 정보
*/
@PatchMapping("")
@PatchMapping("/{recordId}")
@PreAuthorize("hasAnyRole('GENERAL_USER', 'ADMIN_USER')")
public ResponseEntity<RecordIsUseDto.Response> deleteRecord(
@RequestBody RecordIsUseDto.Request recordIsUseRequestDto
@PathVariable Long recordId
) {
return ResponseEntity.ok(recordService.deleteRecord(recordIsUseRequestDto));
return ResponseEntity.ok(recordService.deleteRecord(recordId));
}

/**
Expand Down
18 changes: 10 additions & 8 deletions src/main/java/com/speech/up/record/entity/RecordEntity.java
Original file line number Diff line number Diff line change
Expand Up @@ -56,12 +56,14 @@ private RecordEntity(byte[] audio, RecordAddDto.Request request, ScriptEntity sc
this(audio, request.getLanguageCode(), scriptEntity, true, false);
}

private RecordEntity(RecordIsUseDto.Request recordIsUseRequestDto) {
this(recordIsUseRequestDto.getRecordEntity().getAudio(),
recordIsUseRequestDto.getRecordEntity()
.getLanguageCode(),
recordIsUseRequestDto.getRecordEntity().getScript(), true, false);
this.recordId = recordIsUseRequestDto.getRecordEntity().getRecordId();
private RecordEntity(Long recordId, RecordEntity recordEntity) {
this.recordId = recordId;
this.script = recordEntity.getScript();
this.languageCode = recordEntity.getLanguageCode();
this.isUse = false;
this.isAnalyzed = recordEntity.isAnalyzed();
this.report = recordEntity.getReport();
this.audio = recordEntity.getAudio();
}

private RecordEntity(RecordEntity recordEntity) {
Expand All @@ -78,8 +80,8 @@ public static RecordEntity create(byte[] audio, RecordAddDto.Request request, Sc
return new RecordEntity(audio, request, scriptEntity);
}

public static RecordEntity delete(RecordIsUseDto.Request recordIsUseRequestDto) {
return new RecordEntity(recordIsUseRequestDto);
public static RecordEntity delete(Long recordId, RecordEntity recordEntity) {
return new RecordEntity(recordId, recordEntity);
}

public static RecordEntity analyze(RecordEntity recordEntity) {
Expand Down
6 changes: 4 additions & 2 deletions src/main/java/com/speech/up/record/service/RecordService.java
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,10 @@ public RecordAddDto.Response addRecord(MultipartFile file, String languageCode,
return RecordAddDto.toResponse(recordRepository.save(recordEntity));
}

public RecordIsUseDto.Response deleteRecord(RecordIsUseDto.Request recordIsUseRequestDto) {
RecordEntity recordEntity = RecordEntity.delete(recordIsUseRequestDto);
@Transactional
public RecordIsUseDto.Response deleteRecord(Long recordId) {
RecordEntity record = recordRepository.findById(recordId).get();
RecordEntity recordEntity = RecordEntity.delete(recordId, record);
return RecordIsUseDto.toResponse(recordRepository.save(recordEntity));
}

Expand Down
31 changes: 28 additions & 3 deletions src/main/java/com/speech/up/record/service/dto/RecordIsUseDto.java
Original file line number Diff line number Diff line change
@@ -1,10 +1,18 @@
package com.speech.up.record.service.dto;

import com.fasterxml.jackson.annotation.JsonBackReference;
import com.fasterxml.jackson.annotation.JsonManagedReference;
import com.fasterxml.jackson.databind.PropertyNamingStrategies;
import com.fasterxml.jackson.databind.annotation.JsonNaming;
import com.speech.up.record.entity.RecordEntity;
import com.speech.up.report.entity.ReportEntity;
import com.speech.up.script.entity.ScriptEntity;

import jakarta.persistence.CascadeType;
import jakarta.persistence.FetchType;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.OneToOne;
import lombok.Getter;
import lombok.ToString;

Expand All @@ -13,12 +21,29 @@
public class RecordIsUseDto {
@Getter
@ToString
@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class)
public static class Request {
private final RecordEntity recordEntity;
private final Long recordId;

private final byte[] audio;

private final String languageCode;

private final boolean isAnalyzed;

private final ScriptEntity script;

private final boolean isUse;

private final ReportEntity report;

public Request(RecordEntity recordEntity) {
this.recordEntity = recordEntity;
this.recordId = recordEntity.getRecordId();
this.audio = recordEntity.getAudio();
this.languageCode = recordEntity.getLanguageCode();
this.isAnalyzed = recordEntity.isAnalyzed();
this.script = recordEntity.getScript();
this.isUse = recordEntity.isUse();
this.report = recordEntity.getReport();
}
}

Expand Down
7 changes: 5 additions & 2 deletions src/main/resources/application.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ spring:
format_sql: true
dialect: org.hibernate.dialect.MySQL8Dialect
show-sql: true

security:
oauth2:
client:
Expand Down Expand Up @@ -47,7 +46,11 @@ spring:
token-uri: https://kauth.kakao.com/oauth/token
user-info-uri: https://kapi.kakao.com/v2/user/me
user-name-attribute: id

cloud:
gcp:
project-id: inbound-stage-433103-i9
logging:
enabled: true
api:
voice:
score : ${API_VOICE_TO_SCORE}
Expand Down
4 changes: 4 additions & 0 deletions src/main/resources/static/css/board-style.css
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,8 @@
padding: 0;
margin: 0;
list-style: none;
}

.container {
margin-bottom: 100px;
}
2 changes: 1 addition & 1 deletion src/main/resources/static/css/script-style.css
Original file line number Diff line number Diff line change
Expand Up @@ -287,7 +287,7 @@ strong {
background-color: #000000;
color: white;
border-radius: 5%;
width: 180px;
width: 200px;
height: 20px;
text-align: center;
line-height: 20px;
Expand Down
Binary file modified src/main/resources/static/images/tooltip-image2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Loading