Skip to content

Commit

Permalink
Merge branch 'BE_develop'
Browse files Browse the repository at this point in the history
  • Loading branch information
HABINOH committed May 20, 2024
2 parents b1eef23 + 1e6d94c commit 944817b
Show file tree
Hide file tree
Showing 286 changed files with 12,984 additions and 13 deletions.
1 change: 1 addition & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ on:
- main
- AND_develop
- BE_develop
- FE_develop

jobs:
push-to-gitlab:
Expand Down
3 changes: 2 additions & 1 deletion backEnd/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,5 @@ out/
### VS Code ###
.vscode/

application-key.yml
application-key.yml
ulvan-firebase-key.json
70 changes: 70 additions & 0 deletions backEnd/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# 울반 - NFC기반 ν•™κΈ‰ μ†Œν†΅ μ•±
![](https://blog.kakaocdn.net/dn/kTWTG/btsHtPbRbMO/ApDuCfEaKyqA57ZlnGnkU0/img.png)

<div>
<h4>πŸ“± NFC, λΈ”λ£¨νˆ¬μŠ€ 기반으둜 μ†Œν†΅ν•΄μš”<h4>

<h4>🎯 μΉœκ΅¬λ“€κ³Ό μ±„νŒ…ν•˜κ³  글을 μ“°λ©° 일상을 κ³΅μœ ν•΄μš”<h4>

<h4>πŸ‘₯ μΉœκ΅¬λ“€κ³Όμ˜ μΉœλ°€λ„λ₯Ό ν™•μΈν•΄μš”<h4>

<h4>πŸ₯‡ μ„ μƒλ‹˜μ€ ν•™μƒλ“€μ˜ μ†Œν†΅ 톡계λ₯Ό ν™•μΈν•΄μš”<h4>
</div>
<br/>

## κ°œμš”

- **ν•œ 쀄 μš”μ•½** : *울반* ν”„λ‘œμ νŠΈλŠ” NFC와 BLE 톡신을 기반으둜 ν•œ ν•™κΈ‰ μ†Œν†΅ μ•±μž…λ‹ˆλ‹€.

- **κΈ°νšμ˜λ„** : μ½”λ‘œλ‚˜ 이후 μ•…ν™”λœ κ΅μš°κ΄€κ°œλ₯Ό κ°œμ„ μ„ μœ„ν•΄ μ œμž‘λ˜μ—ˆμŠ΅λ‹ˆλ‹€.

- **개발 인원 및 κΈ°κ°„**

- **개발 인원** : Android 3λͺ…, BackEnd 3λͺ…

- **ν”„λ‘œμ νŠΈ κΈ°κ°„** : 2024.04.08 ~ 2024.05.19

- **μ£Όμš” κΈ°λŠ₯**

- NFC 기반의 νƒœκΉ…μΈμ‚¬, 이어달리기

- λΈ”λ£¨νˆ¬μŠ€ 기반의 ν•¨κ»˜λ‹¬λ¦¬κΈ°

- 학급별 μ±„νŒ…, κ²Œμ‹œνŒ, μ•Œλ¦Όμž₯

- 학급별 μ†Œν†΅ 톡계

<br/><br/><br/><br/>


# μ‹œμŠ€ν…œ μ•„ν‚€ν…μ²˜
![자율울반](https://github.com/6QuizOnTheBlock/OurClass/assets/74866067/236444d6-b74f-4859-af46-993a3f4410ec)

# ERD
![울반 ERD (1)](https://github.com/6QuizOnTheBlock/OurClass/assets/74866067/488849f1-1778-4472-a77b-8ea5cd4c70d8)


### 기술

- Java21, Spring Boot 3.2
- Spring Security + JWT + Oauth2
- Spring Data JPA, Query DSL
- MySQL, Redis, MongoDB
- AWS ec2
- Jenkins, Docker
- kafka
- Swagger, Rest api

<br/><br/><br/><br/>


# λ™μž‘ ν™”λ©΄

**μ£Όμš” λ™μž‘ν™”λ©΄μ€ μΆ”ν›„ μΆ”κ°€ μ˜ˆμ •μž…λ‹ˆλ‹€.**

### [ν”Όκ·Έλ§ˆ](https://www.figma.com/design/yfm5gTmRJED2uAdm7H70YC/6-kids-on-the-block?node-id=0%3A1&t=5blyLSniokJVPpQR-1)


<br/><br/><br/>

## μ—­ν• 
93 changes: 89 additions & 4 deletions backEnd/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,15 @@ plugins {
id 'java'
id 'org.springframework.boot' version '3.2.5'
id 'io.spring.dependency-management' version '1.1.4'
id 'org.sonarqube' version '4.0.0.2929'
}

sonarqube {
properties {
property "sonar.projectKey", System.getenv('SONAR_PROJECT_KEY')
property "sonar.host.url", System.getenv('SONAR_HOST_URL')
property "sonar.login", System.getenv('SONAR_LOGIN')
}
}

group = 'com.6quiz'
Expand All @@ -11,6 +20,10 @@ java {
sourceCompatibility = '21'
}

ext {
springCloudVersion = '2023.0.1'
}

configurations {
compileOnly {
extendsFrom annotationProcessor
Expand All @@ -21,20 +34,92 @@ repositories {
mavenCentral()
}

dependencyManagement {
imports {
mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}"
}
}


dependencies {

// ⭐ Spring Basic Setting
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-data-mongodb'
implementation 'org.springframework.boot:spring-boot-starter-data-redis'
implementation 'org.springframework.boot:spring-boot-starter-security'
testImplementation 'org.springframework.security:spring-security-test'
compileOnly 'org.projectlombok:lombok'
implementation group: 'org.springframework', name: 'spring-aop', version: '6.1.6'
developmentOnly 'org.springframework.boot:spring-boot-devtools'
implementation 'com.github.ulisesbocchio:jasypt-spring-boot-starter:3.0.5'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'

// ⭐ Database
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-data-mongodb'
implementation 'org.springframework.boot:spring-boot-starter-data-redis'
runtimeOnly 'com.mysql:mysql-connector-j'

// ⭐ Firebase
implementation 'com.google.firebase:firebase-admin:9.1.1'

// ⭐ Jasypt
implementation 'com.github.ulisesbocchio:jasypt-spring-boot-starter:3.0.5'

// ⭐ SWAGGER
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.2.0'

// ⭐ AWS
implementation 'org.springframework.cloud:spring-cloud-starter-aws:2.2.6.RELEASE'

// ⭐ QueryDsl
implementation 'com.querydsl:querydsl-jpa:5.0.0:jakarta'
annotationProcessor "com.querydsl:querydsl-apt:5.0.0:jakarta"
annotationProcessor "jakarta.annotation:jakarta.annotation-api"
annotationProcessor "jakarta.persistence:jakarta.persistence-api"

// ⭐ MapStruct
implementation 'org.mapstruct:mapstruct:1.5.3.Final'
annotationProcessor 'org.mapstruct:mapstruct-processor:1.5.3.Final'

// ⭐ JWT (ν˜„: 0.11.5 -> 0.12.3 migration μ˜ˆμ •)
implementation group: 'io.jsonwebtoken', name: 'jjwt-api', version: '0.11.5'
runtimeOnly group: 'io.jsonwebtoken', name: 'jjwt-impl', version: '0.11.5'
runtimeOnly group: 'io.jsonwebtoken', name: 'jjwt-jackson', version: '0.11.5'
implementation group: 'io.jsonwebtoken', name: 'jjwt-jackson', version: '0.11.5'

// ⭐ feign Client
implementation 'org.springframework.cloud:spring-cloud-starter-openfeign'

// ⭐ Cache
implementation 'org.springframework.boot:spring-boot-starter-cache'

// ⭐ WebSocket
implementation 'org.springframework.boot:spring-boot-starter-websocket'

// ⭐ kafka
implementation 'org.springframework.kafka:spring-kafka'
implementation 'org.apache.kafka:kafka-clients'
implementation 'org.springframework.retry:spring-retry'

implementation 'org.ojalgo:ojalgo:51.4.1'

}

tasks.named('test') {
useJUnitPlatform()
systemProperty 'jasypt.encryptor.key', findProperty("jasypt.encryptor.key")
}

//Q객체 μ €μž₯μœ„μΉ˜
def querydslDir = layout.buildDirectory.dir("generated/querydsl").get().asFile

sourceSets {
main.java.srcDirs += [querydslDir]
}
tasks.withType(JavaCompile).configureEach {
options.getGeneratedSourceOutputDirectory().set(file(querydslDir))
options.compilerArgs << "-parameters"
}
clean {
delete file(querydslDir)
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.quiz.ourClass;
package com.quiz.ourclass;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package com.quiz.ourclass.domain.board.controller;

import com.quiz.ourclass.domain.board.dto.request.CommentRequest;
import com.quiz.ourclass.domain.board.dto.request.UpdateCommentRequest;
import com.quiz.ourclass.domain.board.sevice.CommentService;
import com.quiz.ourclass.global.dto.ResultResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PatchMapping;
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;

@RestController
@RequiredArgsConstructor
@RequestMapping("/comments")
public class CommentController implements CommentControllerDocs {

private final CommentService commentService;

@PostMapping
public ResponseEntity<ResultResponse<Long>> write(
@RequestBody CommentRequest request) {
Long commentId = commentService.write(request);
return ResponseEntity.ok(ResultResponse.success(commentId));
}

@PatchMapping("/{id}")
public ResponseEntity<ResultResponse<Long>> modify(
@PathVariable(value = "id") Long id,
@RequestBody UpdateCommentRequest request) {
Long commentId = commentService.modify(id, request);
return ResponseEntity.ok(ResultResponse.success(commentId));
}

@DeleteMapping("/{id}")
public ResponseEntity<ResultResponse<Boolean>> delete(
@PathVariable(value = "id") Long id) {
Boolean isDelete = commentService.delete(id);
return ResponseEntity.ok(ResultResponse.success(isDelete));
}

@PostMapping("/{id}/report")
public ResponseEntity<ResultResponse<Boolean>> report(
@PathVariable(value = "id") Long id) {
Boolean isReport = commentService.report(id);
return ResponseEntity.ok(ResultResponse.success(isReport));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
package com.quiz.ourclass.domain.board.controller;

import com.quiz.ourclass.domain.board.dto.request.CommentRequest;
import com.quiz.ourclass.domain.board.dto.request.UpdateCommentRequest;
import com.quiz.ourclass.global.dto.ResultResponse;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.enums.ParameterIn;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PatchMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;

@Tag(name = "CommentController", description = "SNS λŒ“κΈ€ API")
public interface CommentControllerDocs {

@Operation(summary = "λŒ“κΈ€ μž‘μ„±",
description = """
μž…λ ₯으둜 λ“€μ–΄μ˜€λŠ” DTO κΈ°μ€€μœΌλ‘œ λŒ“κΈ€μ„ μž‘μ„±ν•©λ‹ˆλ‹€.
μ΄λ•Œ, λΆ€λͺ¨ λŒ“κΈ€μ€ parentId 값에 0L을 λ³΄λ‚΄μ£Όμ„Έμš”!""",
responses = {
@ApiResponse(responseCode = "200", description = "(message : \"Success\")",
content = @Content(schema = @Schema(implementation = Long.class))),
@ApiResponse(responseCode = "403", description = """
(message : "멀버가 ν•΄λ‹Ή 단체 μ†Œμ†μ΄ μ•„λ‹™λ‹ˆλ‹€.")
(message : "κ²Œμ‹œκΈ€ μž‘μ„±μž 단체와 ν˜„μž¬ μ‚¬μš©μž 단체가 λ‹€λ¦…λ‹ˆλ‹€.")
""", content = @Content),
@ApiResponse(responseCode = "404", description = """
(message : "멀버가 μ‘΄μž¬ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.")
(message : "κ²Œμ‹œκΈ€μ„ 찾을 수 μ—†μŠ΅λ‹ˆλ‹€.")
(message : "두 λ©€λ²„κ°„μ˜ 관계λ₯Ό 찾을 수 μ—†μŠ΅λ‹ˆλ‹€.")
""", content = @Content),
}
)
@PostMapping
ResponseEntity<ResultResponse<Long>> write(
@Parameter(name = "request", description = "λŒ“κΈ€ μž‘μ„± DTO", required = true, in = ParameterIn.DEFAULT)
@RequestBody CommentRequest request
);

@Operation(summary = "λŒ“κΈ€ μˆ˜μ •",
description = "μž…λ ₯으둜 λ“€μ–΄μ˜€λŠ” DTO κΈ°μ€€μœΌλ‘œ λŒ“κΈ€μ„ μˆ˜μ •ν•©λ‹ˆλ‹€.",
responses = {
@ApiResponse(responseCode = "200", description = "(message : \"Success\")",
content = @Content(schema = @Schema(implementation = Long.class))),
@ApiResponse(responseCode = "403", description = "(message : \"λŒ“κΈ€ μž‘μ„±μž 단체와 ν˜„μž¬ μ‚¬μš©μž 단체가 λ‹€λ¦…λ‹ˆλ‹€.\")", content = @Content),
@ApiResponse(responseCode = "404", description = """
(message : "멀버가 μ‘΄μž¬ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.")
(message : "λŒ“κΈ€μ„ 찾을 수 μ—†μŠ΅λ‹ˆλ‹€.")
""", content = @Content),
}
)
@PatchMapping("/{id}")
ResponseEntity<ResultResponse<Long>> modify(
@Parameter(name = "id", description = "λŒ“κΈ€ PK κ°’", required = true, in = ParameterIn.PATH)
@PathVariable(value = "id") Long id,
@Parameter(name = "request", description = "λŒ“κΈ€ μˆ˜μ • DTO", required = true, in = ParameterIn.DEFAULT)
@RequestBody UpdateCommentRequest request
);

@Operation(summary = "λŒ“κΈ€ μ‚­μ œ",
description = "μž…λ ₯으둜 λ“€μ–΄μ˜€λŠ” λŒ“κΈ€ PK κΈ°μ€€μœΌλ‘œ λŒ“κΈ€μ„ μ‚­μ œν•©λ‹ˆλ‹€.",
responses = {
@ApiResponse(responseCode = "200", description = "(message : \"Success\")",
content = @Content(schema = @Schema(implementation = Boolean.class))),
@ApiResponse(responseCode = "403", description = """
(message : "λŒ“κΈ€ μž‘μ„±μžμ™€ μš”μ²­μžκ°€ λ‹€λ¦…λ‹ˆλ‹€.")
(message : "멀버가 ν•΄λ‹Ή 단체 μ†Œμ†μ΄ μ•„λ‹™λ‹ˆλ‹€.")
""", content = @Content),
@ApiResponse(responseCode = "404", description = "(message : \"λŒ“κΈ€μ„ 찾을 수 μ—†μŠ΅λ‹ˆλ‹€.\")", content = @Content)
}
)
@DeleteMapping("/{id}")
ResponseEntity<ResultResponse<Boolean>> delete(
@Parameter(name = "id", description = "λŒ“κΈ€ PK κ°’", required = true, in = ParameterIn.PATH)
@PathVariable(value = "id") Long id
);

@Operation(
summary = "λŒ“κΈ€ μ‹ κ³ ",
description = "path μž…λ ₯으둜 λ“€μ–΄μ˜¨ κ²Œμ‹œκΈ€ PK κ°’ κΈ°μ€€μœΌλ‘œ λŒ“μ„ μ‹ κ³ ν•˜μ—¬ ν•΄λ‹Ή 단체 κ΄€λ¦¬μžμ—κ²Œ FCM μ•Œλ¦Όμ„ μ „μ†‘ν•©λ‹ˆλ‹€.",
responses = {
@ApiResponse(responseCode = "200", description = "(message : \"Success\")",
content = @Content(schema = @Schema(implementation = Boolean.class))),
@ApiResponse(responseCode = "403", description = """
(message : "멀버가 ν•΄λ‹Ή 단체 μ†Œμ†μ΄ μ•„λ‹™λ‹ˆλ‹€.")
(message : "단체 κ΄€λ¦¬μžλŠ” μ‹ κ³ ν•  수 μ—†μŠ΅λ‹ˆλ‹€.")
""", content = @Content),
@ApiResponse(responseCode = "404", description = """
(message : "멀버가 μ‘΄μž¬ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.")
(message : "λŒ“κΈ€μ„ 찾을 수 μ—†μŠ΅λ‹ˆλ‹€.")
""", content = @Content),
}
)
@PostMapping("/{id}/report")
ResponseEntity<ResultResponse<Boolean>> report(
@Parameter(name = "id", description = "λŒ“κΈ€ PK κ°’", required = true, in = ParameterIn.PATH)
@PathVariable(value = "id") Long id
);
}
Loading

0 comments on commit 944817b

Please sign in to comment.