From 85b271356cf352be7b923639e8ca1b6a8bb44445 Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Thu, 14 Mar 2024 16:57:17 +0900 Subject: [PATCH 001/265] =?UTF-8?q?chore:=20gradle=20=EB=B0=8F=20gitignore?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 290 ++++++++++++++++++ HELP.md | 22 ++ build.gradle | 44 +++ gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 43462 bytes gradle/wrapper/gradle-wrapper.properties | 23 ++ gradlew | 249 +++++++++++++++ gradlew.bat | 92 ++++++ settings.gradle | 1 + .../facefriend/FacefriendApplication.java | 13 + src/main/resources/application.properties | 1 + .../FacefriendApplicationTests.java | 13 + 11 files changed, 748 insertions(+) create mode 100644 .gitignore create mode 100644 HELP.md create mode 100644 build.gradle create mode 100644 gradle/wrapper/gradle-wrapper.jar create mode 100644 gradle/wrapper/gradle-wrapper.properties create mode 100755 gradlew create mode 100644 gradlew.bat create mode 100644 settings.gradle create mode 100644 src/main/java/capstone/facefriend/FacefriendApplication.java create mode 100644 src/main/resources/application.properties create mode 100644 src/test/java/capstone/facefriend/FacefriendApplicationTests.java diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000..64bc76fe20 --- /dev/null +++ b/.gitignore @@ -0,0 +1,290 @@ +# Created by https://www.toptal.com/developers/gitignore/api/java,intellij,gradle,macos,windows,intellij+all +# Edit at https://www.toptal.com/developers/gitignore?templates=java,intellij,gradle,macos,windows,intellij+all + +### Intellij ### +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# AWS User-specific +.idea/**/aws.xml + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# Application.yml +application.yml + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# SonarLint plugin +.idea/sonarlint/ + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + +### Intellij Patch ### +# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 + +# *.iml +# modules.xml +# .idea/misc.xml +# *.ipr + +# Sonarlint plugin +# https://plugins.jetbrains.com/plugin/7973-sonarlint +.idea/**/sonarlint/ + +# SonarQube Plugin +# https://plugins.jetbrains.com/plugin/7238-sonarqube-community-plugin +.idea/**/sonarIssues.xml + +# Markdown Navigator plugin +# https://plugins.jetbrains.com/plugin/7896-markdown-navigator-enhanced +.idea/**/markdown-navigator.xml +.idea/**/markdown-navigator-enh.xml +.idea/**/markdown-navigator/ + +# Cache file creation bug +# See https://youtrack.jetbrains.com/issue/JBR-2257 +.idea/$CACHE_FILE$ + +# CodeStream plugin +# https://plugins.jetbrains.com/plugin/12206-codestream +.idea/codestream.xml + +# Azure Toolkit for IntelliJ plugin +# https://plugins.jetbrains.com/plugin/8053-azure-toolkit-for-intellij +.idea/**/azureSettings.xml + +### Intellij+all ### +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff + +# AWS User-specific + +# Generated files + +# Sensitive or high-churn files + +# Gradle + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake + +# Mongo Explorer plugin + +# File-based project format + +# IntelliJ + +# mpeltonen/sbt-idea plugin + +# JIRA plugin + +# Cursive Clojure plugin + +# SonarLint plugin + +# Crashlytics plugin (for Android Studio and IntelliJ) + +# Editor-based Rest Client + +# Android studio 3.1+ serialized cache file + +### Intellij+all Patch ### +# Ignore everything but code style settings and run configurations +# that are supposed to be shared within teams. + +.idea/* + +!.idea/codeStyles +!.idea/runConfigurations + +### Java ### +# Compiled class file +*.class + +# Log file +*.log + +# BlueJ files +*.ctxt + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.jar +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* +replay_pid* + +### macOS ### +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### macOS Patch ### +# iCloud generated files +*.icloud + +### Windows ### +# Windows thumbnail cache files +Thumbs.db +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +### Gradle ### +.gradle +**/build/ +!src/**/build/ + +# Ignore Gradle GUI config +gradle-app.setting + +# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) +!gradle-wrapper.jar + +# Avoid ignore Gradle wrappper properties +!gradle-wrapper.properties + +# Cache of project +.gradletasknamecache + +# Eclipse Gradle plugin generated files +# Eclipse Core +.project +# JDT-specific (Eclipse Java Development Tools) +.classpath + +### Gradle Patch ### +# Java heap dump +*.hprof + +# End of https://www.toptal.com/developers/gitignore/api/java,intellij,gradle,macos,windows,intellij+all \ No newline at end of file diff --git a/HELP.md b/HELP.md new file mode 100644 index 0000000000..6c4cbe9828 --- /dev/null +++ b/HELP.md @@ -0,0 +1,22 @@ +# Getting Started + +### Reference Documentation +For further reference, please consider the following sections: + +* [Official Gradle documentation](https://docs.gradle.org) +* [Spring Boot Gradle Plugin Reference Guide](https://docs.spring.io/spring-boot/docs/3.2.3/gradle-plugin/reference/html/) +* [Create an OCI image](https://docs.spring.io/spring-boot/docs/3.2.3/gradle-plugin/reference/html/#build-image) +* [Spring Web](https://docs.spring.io/spring-boot/docs/3.2.3/reference/htmlsingle/index.html#web) + +### Guides +The following guides illustrate how to use some features concretely: + +* [Building a RESTful Web Service](https://spring.io/guides/gs/rest-service/) +* [Serving Web Content with Spring MVC](https://spring.io/guides/gs/serving-web-content/) +* [Building REST services with Spring](https://spring.io/guides/tutorials/rest/) + +### Additional Links +These additional references should also help you: + +* [Gradle Build Scans – insights for your project's build](https://scans.gradle.com#gradle) + diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000000..f3b7f18f26 --- /dev/null +++ b/build.gradle @@ -0,0 +1,44 @@ +plugins { + id 'java' + id 'org.springframework.boot' version '3.2.3' + id 'io.spring.dependency-management' version '1.1.4' +} + +group = 'capstone' +version = '0.0.1-SNAPSHOT' + +java { + sourceCompatibility = '17' +} + +repositories { + mavenCentral() +} + +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-web' + + annotationProcessor "org.springframework.boot:spring-boot-configuration-processor" + testImplementation 'org.springframework.boot:spring-boot-starter-test' + + // postgresql + implementation 'org.postgresql:postgresql' + + // JWT + implementation 'io.jsonwebtoken:jjwt-api:0.10.5' + runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.10.5' + runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.10.5' + + // lombok + compileOnly 'org.projectlombok:lombok' + annotationProcessor 'org.projectlombok:lombok' + + // swagger + implementation 'io.springfox:springfox-boot-starter:3.0.0' +} + +tasks.named('test') { + useJUnitPlatform() +} diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..d64cd4917707c1f8861d8cb53dd15194d4248596 GIT binary patch literal 43462 zcma&NWl&^owk(X(xVyW%ySuwf;qI=D6|RlDJ2cR^yEKh!@I- zp9QeisK*rlxC>+~7Dk4IxIRsKBHqdR9b3+fyL=ynHmIDe&|>O*VlvO+%z5;9Z$|DJ zb4dO}-R=MKr^6EKJiOrJdLnCJn>np?~vU-1sSFgPu;pthGwf}bG z(1db%xwr#x)r+`4AGu$j7~u2MpVs3VpLp|mx&;>`0p0vH6kF+D2CY0fVdQOZ@h;A` z{infNyvmFUiu*XG}RNMNwXrbec_*a3N=2zJ|Wh5z* z5rAX$JJR{#zP>KY**>xHTuw?|-Rg|o24V)74HcfVT;WtQHXlE+_4iPE8QE#DUm%x0 zEKr75ur~W%w#-My3Tj`hH6EuEW+8K-^5P62$7Sc5OK+22qj&Pd1;)1#4tKihi=~8C zHiQSst0cpri6%OeaR`PY>HH_;CPaRNty%WTm4{wDK8V6gCZlG@U3$~JQZ;HPvDJcT1V{ z?>H@13MJcCNe#5z+MecYNi@VT5|&UiN1D4ATT+%M+h4c$t;C#UAs3O_q=GxK0}8%8 z8J(_M9bayxN}69ex4dzM_P3oh@ZGREjVvn%%r7=xjkqxJP4kj}5tlf;QosR=%4L5y zWhgejO=vao5oX%mOHbhJ8V+SG&K5dABn6!WiKl{|oPkq(9z8l&Mm%(=qGcFzI=eLu zWc_oCLyf;hVlB@dnwY98?75B20=n$>u3b|NB28H0u-6Rpl((%KWEBOfElVWJx+5yg z#SGqwza7f}$z;n~g%4HDU{;V{gXIhft*q2=4zSezGK~nBgu9-Q*rZ#2f=Q}i2|qOp z!!y4p)4o=LVUNhlkp#JL{tfkhXNbB=Ox>M=n6soptJw-IDI|_$is2w}(XY>a=H52d z3zE$tjPUhWWS+5h=KVH&uqQS=$v3nRs&p$%11b%5qtF}S2#Pc`IiyBIF4%A!;AVoI zXU8-Rpv!DQNcF~(qQnyyMy=-AN~U>#&X1j5BLDP{?K!%h!;hfJI>$mdLSvktEr*89 zdJHvby^$xEX0^l9g$xW-d?J;L0#(`UT~zpL&*cEh$L|HPAu=P8`OQZV!-}l`noSp_ zQ-1$q$R-gDL)?6YaM!=8H=QGW$NT2SeZlb8PKJdc=F-cT@j7Xags+Pr*jPtlHFnf- zh?q<6;)27IdPc^Wdy-mX%2s84C1xZq9Xms+==F4);O`VUASmu3(RlgE#0+#giLh-& zcxm3_e}n4{%|X zJp{G_j+%`j_q5}k{eW&TlP}J2wtZ2^<^E(O)4OQX8FDp6RJq!F{(6eHWSD3=f~(h} zJXCf7=r<16X{pHkm%yzYI_=VDP&9bmI1*)YXZeB}F? z(%QsB5fo*FUZxK$oX~X^69;x~j7ms8xlzpt-T15e9}$4T-pC z6PFg@;B-j|Ywajpe4~bk#S6(fO^|mm1hKOPfA%8-_iGCfICE|=P_~e;Wz6my&)h_~ zkv&_xSAw7AZ%ThYF(4jADW4vg=oEdJGVOs>FqamoL3Np8>?!W#!R-0%2Bg4h?kz5I zKV-rKN2n(vUL%D<4oj@|`eJ>0i#TmYBtYmfla;c!ATW%;xGQ0*TW@PTlGG><@dxUI zg>+3SiGdZ%?5N=8uoLA|$4isK$aJ%i{hECP$bK{J#0W2gQ3YEa zZQ50Stn6hqdfxJ*9#NuSLwKFCUGk@c=(igyVL;;2^wi4o30YXSIb2g_ud$ zgpCr@H0qWtk2hK8Q|&wx)}4+hTYlf;$a4#oUM=V@Cw#!$(nOFFpZ;0lc!qd=c$S}Z zGGI-0jg~S~cgVT=4Vo)b)|4phjStD49*EqC)IPwyeKBLcN;Wu@Aeph;emROAwJ-0< z_#>wVm$)ygH|qyxZaet&(Vf%pVdnvKWJn9`%DAxj3ot;v>S$I}jJ$FLBF*~iZ!ZXE zkvui&p}fI0Y=IDX)mm0@tAd|fEHl~J&K}ZX(Mm3cm1UAuwJ42+AO5@HwYfDH7ipIc zmI;1J;J@+aCNG1M`Btf>YT>~c&3j~Qi@Py5JT6;zjx$cvOQW@3oQ>|}GH?TW-E z1R;q^QFjm5W~7f}c3Ww|awg1BAJ^slEV~Pk`Kd`PS$7;SqJZNj->it4DW2l15}xP6 zoCl$kyEF%yJni0(L!Z&14m!1urXh6Btj_5JYt1{#+H8w?5QI%% zo-$KYWNMJVH?Hh@1n7OSu~QhSswL8x0=$<8QG_zepi_`y_79=nK=_ZP_`Em2UI*tyQoB+r{1QYZCpb?2OrgUw#oRH$?^Tj!Req>XiE#~B|~ z+%HB;=ic+R@px4Ld8mwpY;W^A%8%l8$@B@1m5n`TlKI6bz2mp*^^^1mK$COW$HOfp zUGTz-cN9?BGEp}5A!mDFjaiWa2_J2Iq8qj0mXzk; z66JBKRP{p%wN7XobR0YjhAuW9T1Gw3FDvR5dWJ8ElNYF94eF3ebu+QwKjtvVu4L zI9ip#mQ@4uqVdkl-TUQMb^XBJVLW(-$s;Nq;@5gr4`UfLgF$adIhd?rHOa%D);whv z=;krPp~@I+-Z|r#s3yCH+c1US?dnm+C*)r{m+86sTJusLdNu^sqLrfWed^ndHXH`m zd3#cOe3>w-ga(Dus_^ppG9AC>Iq{y%%CK+Cro_sqLCs{VLuK=dev>OL1dis4(PQ5R zcz)>DjEkfV+MO;~>VUlYF00SgfUo~@(&9$Iy2|G0T9BSP?&T22>K46D zL*~j#yJ?)^*%J3!16f)@Y2Z^kS*BzwfAQ7K96rFRIh>#$*$_Io;z>ux@}G98!fWR@ zGTFxv4r~v)Gsd|pF91*-eaZ3Qw1MH$K^7JhWIdX%o$2kCbvGDXy)a?@8T&1dY4`;L z4Kn+f%SSFWE_rpEpL9bnlmYq`D!6F%di<&Hh=+!VI~j)2mfil03T#jJ_s?}VV0_hp z7T9bWxc>Jm2Z0WMU?`Z$xE74Gu~%s{mW!d4uvKCx@WD+gPUQ zV0vQS(Ig++z=EHN)BR44*EDSWIyT~R4$FcF*VEY*8@l=218Q05D2$|fXKFhRgBIEE zdDFB}1dKkoO^7}{5crKX!p?dZWNz$m>1icsXG2N+((x0OIST9Zo^DW_tytvlwXGpn zs8?pJXjEG;T@qrZi%#h93?FP$!&P4JA(&H61tqQi=opRzNpm zkrG}$^t9&XduK*Qa1?355wd8G2CI6QEh@Ua>AsD;7oRUNLPb76m4HG3K?)wF~IyS3`fXuNM>${?wmB zpVz;?6_(Fiadfd{vUCBM*_kt$+F3J+IojI;9L(gc9n3{sEZyzR9o!_mOwFC#tQ{Q~ zP3-`#uK#tP3Q7~Q;4H|wjZHO8h7e4IuBxl&vz2w~D8)w=Wtg31zpZhz%+kzSzL*dV zwp@{WU4i;hJ7c2f1O;7Mz6qRKeASoIv0_bV=i@NMG*l<#+;INk-^`5w@}Dj~;k=|}qM1vq_P z|GpBGe_IKq|LNy9SJhKOQ$c=5L{Dv|Q_lZl=-ky*BFBJLW9&y_C|!vyM~rQx=!vun z?rZJQB5t}Dctmui5i31C_;_}CEn}_W%>oSXtt>@kE1=JW*4*v4tPp;O6 zmAk{)m!)}34pTWg8{i>($%NQ(Tl;QC@J@FfBoc%Gr&m560^kgSfodAFrIjF}aIw)X zoXZ`@IsMkc8_=w%-7`D6Y4e*CG8k%Ud=GXhsTR50jUnm+R*0A(O3UKFg0`K;qp1bl z7``HN=?39ic_kR|^R^~w-*pa?Vj#7|e9F1iRx{GN2?wK!xR1GW!qa=~pjJb-#u1K8 zeR?Y2i-pt}yJq;SCiVHODIvQJX|ZJaT8nO+(?HXbLefulKKgM^B(UIO1r+S=7;kLJ zcH}1J=Px2jsh3Tec&v8Jcbng8;V-`#*UHt?hB(pmOipKwf3Lz8rG$heEB30Sg*2rx zV<|KN86$soN(I!BwO`1n^^uF2*x&vJ$2d$>+`(romzHP|)K_KkO6Hc>_dwMW-M(#S zK(~SiXT1@fvc#U+?|?PniDRm01)f^#55;nhM|wi?oG>yBsa?~?^xTU|fX-R(sTA+5 zaq}-8Tx7zrOy#3*JLIIVsBmHYLdD}!0NP!+ITW+Thn0)8SS!$@)HXwB3tY!fMxc#1 zMp3H?q3eD?u&Njx4;KQ5G>32+GRp1Ee5qMO0lZjaRRu&{W<&~DoJNGkcYF<5(Ab+J zgO>VhBl{okDPn78<%&e2mR{jwVCz5Og;*Z;;3%VvoGo_;HaGLWYF7q#jDX=Z#Ml`H z858YVV$%J|e<1n`%6Vsvq7GmnAV0wW4$5qQ3uR@1i>tW{xrl|ExywIc?fNgYlA?C5 zh$ezAFb5{rQu6i7BSS5*J-|9DQ{6^BVQ{b*lq`xS@RyrsJN?-t=MTMPY;WYeKBCNg z^2|pN!Q^WPJuuO4!|P@jzt&tY1Y8d%FNK5xK(!@`jO2aEA*4 zkO6b|UVBipci?){-Ke=+1;mGlND8)6+P;8sq}UXw2hn;fc7nM>g}GSMWu&v&fqh

iViYT=fZ(|3Ox^$aWPp4a8h24tD<|8-!aK0lHgL$N7Efw}J zVIB!7=T$U`ao1?upi5V4Et*-lTG0XvExbf!ya{cua==$WJyVG(CmA6Of*8E@DSE%L z`V^$qz&RU$7G5mg;8;=#`@rRG`-uS18$0WPN@!v2d{H2sOqP|!(cQ@ zUHo!d>>yFArLPf1q`uBvY32miqShLT1B@gDL4XoVTK&@owOoD)OIHXrYK-a1d$B{v zF^}8D3Y^g%^cnvScOSJR5QNH+BI%d|;J;wWM3~l>${fb8DNPg)wrf|GBP8p%LNGN# z3EaIiItgwtGgT&iYCFy9-LG}bMI|4LdmmJt@V@% zb6B)1kc=T)(|L@0;wr<>=?r04N;E&ef+7C^`wPWtyQe(*pD1pI_&XHy|0gIGHMekd zF_*M4yi6J&Z4LQj65)S zXwdM{SwUo%3SbPwFsHgqF@V|6afT|R6?&S;lw=8% z3}@9B=#JI3@B*#4s!O))~z zc>2_4Q_#&+5V`GFd?88^;c1i7;Vv_I*qt!_Yx*n=;rj!82rrR2rQ8u5(Ejlo{15P% zs~!{%XJ>FmJ})H^I9bn^Re&38H{xA!0l3^89k(oU;bZWXM@kn$#aoS&Y4l^-WEn-fH39Jb9lA%s*WsKJQl?n9B7_~P z-XM&WL7Z!PcoF6_D>V@$CvUIEy=+Z&0kt{szMk=f1|M+r*a43^$$B^MidrT0J;RI` z(?f!O<8UZkm$_Ny$Hth1J#^4ni+im8M9mr&k|3cIgwvjAgjH z8`N&h25xV#v*d$qBX5jkI|xOhQn!>IYZK7l5#^P4M&twe9&Ey@@GxYMxBZq2e7?`q z$~Szs0!g{2fGcp9PZEt|rdQ6bhAgpcLHPz?f-vB?$dc*!9OL?Q8mn7->bFD2Si60* z!O%y)fCdMSV|lkF9w%x~J*A&srMyYY3{=&$}H zGQ4VG_?$2X(0|vT0{=;W$~icCI{b6W{B!Q8xdGhF|D{25G_5_+%s(46lhvNLkik~R z>nr(&C#5wwOzJZQo9m|U<;&Wk!_#q|V>fsmj1g<6%hB{jGoNUPjgJslld>xmODzGjYc?7JSuA?A_QzjDw5AsRgi@Y|Z0{F{!1=!NES-#*f^s4l0Hu zz468))2IY5dmD9pa*(yT5{EyP^G>@ZWumealS-*WeRcZ}B%gxq{MiJ|RyX-^C1V=0 z@iKdrGi1jTe8Ya^x7yyH$kBNvM4R~`fbPq$BzHum-3Zo8C6=KW@||>zsA8-Y9uV5V z#oq-f5L5}V<&wF4@X@<3^C%ptp6+Ce)~hGl`kwj)bsAjmo_GU^r940Z-|`<)oGnh7 zFF0Tde3>ui?8Yj{sF-Z@)yQd~CGZ*w-6p2U<8}JO-sRsVI5dBji`01W8A&3$?}lxBaC&vn0E$c5tW* zX>5(zzZ=qn&!J~KdsPl;P@bmA-Pr8T*)eh_+Dv5=Ma|XSle6t(k8qcgNyar{*ReQ8 zTXwi=8vr>!3Ywr+BhggHDw8ke==NTQVMCK`$69fhzEFB*4+H9LIvdt-#IbhZvpS}} zO3lz;P?zr0*0$%-Rq_y^k(?I{Mk}h@w}cZpMUp|ucs55bcloL2)($u%mXQw({Wzc~ z;6nu5MkjP)0C(@%6Q_I_vsWrfhl7Zpoxw#WoE~r&GOSCz;_ro6i(^hM>I$8y>`!wW z*U^@?B!MMmb89I}2(hcE4zN2G^kwyWCZp5JG>$Ez7zP~D=J^LMjSM)27_0B_X^C(M z`fFT+%DcKlu?^)FCK>QzSnV%IsXVcUFhFdBP!6~se&xxrIxsvySAWu++IrH;FbcY$ z2DWTvSBRfLwdhr0nMx+URA$j3i7_*6BWv#DXfym?ZRDcX9C?cY9sD3q)uBDR3uWg= z(lUIzB)G$Hr!){>E{s4Dew+tb9kvToZp-1&c?y2wn@Z~(VBhqz`cB;{E4(P3N2*nJ z_>~g@;UF2iG{Kt(<1PyePTKahF8<)pozZ*xH~U-kfoAayCwJViIrnqwqO}7{0pHw$ zs2Kx?s#vQr7XZ264>5RNKSL8|Ty^=PsIx^}QqOOcfpGUU4tRkUc|kc7-!Ae6!+B{o~7nFpm3|G5^=0#Bnm6`V}oSQlrX(u%OWnC zoLPy&Q;1Jui&7ST0~#+}I^&?vcE*t47~Xq#YwvA^6^} z`WkC)$AkNub|t@S!$8CBlwbV~?yp&@9h{D|3z-vJXgzRC5^nYm+PyPcgRzAnEi6Q^gslXYRv4nycsy-SJu?lMps-? zV`U*#WnFsdPLL)Q$AmD|0`UaC4ND07+&UmOu!eHruzV|OUox<+Jl|Mr@6~C`T@P%s zW7sgXLF2SSe9Fl^O(I*{9wsFSYb2l%-;&Pi^dpv!{)C3d0AlNY6!4fgmSgj_wQ*7Am7&$z;Jg&wgR-Ih;lUvWS|KTSg!&s_E9_bXBkZvGiC6bFKDWZxsD$*NZ#_8bl zG1P-#@?OQzED7@jlMJTH@V!6k;W>auvft)}g zhoV{7$q=*;=l{O>Q4a@ ziMjf_u*o^PsO)#BjC%0^h>Xp@;5$p{JSYDt)zbb}s{Kbt!T*I@Pk@X0zds6wsefuU zW$XY%yyRGC94=6mf?x+bbA5CDQ2AgW1T-jVAJbm7K(gp+;v6E0WI#kuACgV$r}6L? zd|Tj?^%^*N&b>Dd{Wr$FS2qI#Ucs1yd4N+RBUQiSZGujH`#I)mG&VKoDh=KKFl4=G z&MagXl6*<)$6P}*Tiebpz5L=oMaPrN+caUXRJ`D?=K9!e0f{@D&cZLKN?iNP@X0aF zE(^pl+;*T5qt?1jRC=5PMgV!XNITRLS_=9{CJExaQj;lt!&pdzpK?8p>%Mb+D z?yO*uSung=-`QQ@yX@Hyd4@CI^r{2oiu`%^bNkz+Nkk!IunjwNC|WcqvX~k=><-I3 zDQdbdb|!v+Iz01$w@aMl!R)koD77Xp;eZwzSl-AT zr@Vu{=xvgfq9akRrrM)}=!=xcs+U1JO}{t(avgz`6RqiiX<|hGG1pmop8k6Q+G_mv zJv|RfDheUp2L3=^C=4aCBMBn0aRCU(DQwX-W(RkRwmLeuJYF<0urcaf(=7)JPg<3P zQs!~G)9CT18o!J4{zX{_e}4eS)U-E)0FAt}wEI(c0%HkxgggW;(1E=>J17_hsH^sP z%lT0LGgbUXHx-K*CI-MCrP66UP0PvGqM$MkeLyqHdbgP|_Cm!7te~b8p+e6sQ_3k| zVcwTh6d83ltdnR>D^)BYQpDKlLk3g0Hdcgz2}%qUs9~~Rie)A-BV1mS&naYai#xcZ z(d{8=-LVpTp}2*y)|gR~;qc7fp26}lPcLZ#=JpYcn3AT9(UIdOyg+d(P5T7D&*P}# zQCYplZO5|7+r19%9e`v^vfSS1sbX1c%=w1;oyruXB%Kl$ACgKQ6=qNWLsc=28xJjg zwvsI5-%SGU|3p>&zXVl^vVtQT3o-#$UT9LI@Npz~6=4!>mc431VRNN8od&Ul^+G_kHC`G=6WVWM z%9eWNyy(FTO|A+@x}Ou3CH)oi;t#7rAxdIXfNFwOj_@Y&TGz6P_sqiB`Q6Lxy|Q{`|fgmRG(k+!#b*M+Z9zFce)f-7;?Km5O=LHV9f9_87; zF7%R2B+$?@sH&&-$@tzaPYkw0;=i|;vWdI|Wl3q_Zu>l;XdIw2FjV=;Mq5t1Q0|f< zs08j54Bp`3RzqE=2enlkZxmX6OF+@|2<)A^RNQpBd6o@OXl+i)zO%D4iGiQNuXd+zIR{_lb96{lc~bxsBveIw6umhShTX+3@ZJ=YHh@ zWY3(d0azg;7oHn>H<>?4@*RQbi>SmM=JrHvIG(~BrvI)#W(EAeO6fS+}mxxcc+X~W6&YVl86W9WFSS}Vz-f9vS?XUDBk)3TcF z8V?$4Q)`uKFq>xT=)Y9mMFVTUk*NIA!0$?RP6Ig0TBmUFrq*Q-Agq~DzxjStQyJ({ zBeZ;o5qUUKg=4Hypm|}>>L=XKsZ!F$yNTDO)jt4H0gdQ5$f|d&bnVCMMXhNh)~mN z@_UV6D7MVlsWz+zM+inZZp&P4fj=tm6fX)SG5H>OsQf_I8c~uGCig$GzuwViK54bcgL;VN|FnyQl>Ed7(@>=8$a_UKIz|V6CeVSd2(P z0Uu>A8A+muM%HLFJQ9UZ5c)BSAv_zH#1f02x?h9C}@pN@6{>UiAp>({Fn(T9Q8B z^`zB;kJ5b`>%dLm+Ol}ty!3;8f1XDSVX0AUe5P#@I+FQ-`$(a;zNgz)4x5hz$Hfbg z!Q(z26wHLXko(1`;(BAOg_wShpX0ixfWq3ponndY+u%1gyX)_h=v1zR#V}#q{au6; z!3K=7fQwnRfg6FXtNQmP>`<;!N137paFS%y?;lb1@BEdbvQHYC{976l`cLqn;b8lp zIDY>~m{gDj(wfnK!lpW6pli)HyLEiUrNc%eXTil|F2s(AY+LW5hkKb>TQ3|Q4S9rr zpDs4uK_co6XPsn_z$LeS{K4jFF`2>U`tbgKdyDne`xmR<@6AA+_hPNKCOR-Zqv;xk zu5!HsBUb^!4uJ7v0RuH-7?l?}b=w5lzzXJ~gZcxRKOovSk@|#V+MuX%Y+=;14i*%{)_gSW9(#4%)AV#3__kac1|qUy!uyP{>?U#5wYNq}y$S9pCc zFc~4mgSC*G~j0u#qqp9 z${>3HV~@->GqEhr_Xwoxq?Hjn#=s2;i~g^&Hn|aDKpA>Oc%HlW(KA1?BXqpxB;Ydx)w;2z^MpjJ(Qi(X!$5RC z*P{~%JGDQqojV>2JbEeCE*OEu!$XJ>bWA9Oa_Hd;y)F%MhBRi*LPcdqR8X`NQ&1L# z5#9L*@qxrx8n}LfeB^J{%-?SU{FCwiWyHp682F+|pa+CQa3ZLzBqN1{)h4d6+vBbV zC#NEbQLC;}me3eeYnOG*nXOJZEU$xLZ1<1Y=7r0(-U0P6-AqwMAM`a(Ed#7vJkn6plb4eI4?2y3yOTGmmDQ!z9`wzbf z_OY#0@5=bnep;MV0X_;;SJJWEf^E6Bd^tVJ9znWx&Ks8t*B>AM@?;D4oWUGc z!H*`6d7Cxo6VuyS4Eye&L1ZRhrRmN6Lr`{NL(wDbif|y&z)JN>Fl5#Wi&mMIr5i;x zBx}3YfF>>8EC(fYnmpu~)CYHuHCyr5*`ECap%t@y=jD>!_%3iiE|LN$mK9>- zHdtpy8fGZtkZF?%TW~29JIAfi2jZT8>OA7=h;8T{{k?c2`nCEx9$r zS+*&vt~2o^^J+}RDG@+9&M^K*z4p{5#IEVbz`1%`m5c2};aGt=V?~vIM}ZdPECDI)47|CWBCfDWUbxBCnmYivQ*0Nu_xb*C>~C9(VjHM zxe<*D<#dQ8TlpMX2c@M<9$w!RP$hpG4cs%AI){jp*Sj|*`m)5(Bw*A0$*i-(CA5#%>a)$+jI2C9r6|(>J8InryENI z$NohnxDUB;wAYDwrb*!N3noBTKPpPN}~09SEL18tkG zxgz(RYU_;DPT{l?Q$+eaZaxnsWCA^ds^0PVRkIM%bOd|G2IEBBiz{&^JtNsODs;5z zICt_Zj8wo^KT$7Bg4H+y!Df#3mbl%%?|EXe!&(Vmac1DJ*y~3+kRKAD=Ovde4^^%~ zw<9av18HLyrf*_>Slp;^i`Uy~`mvBjZ|?Ad63yQa#YK`4+c6;pW4?XIY9G1(Xh9WO8{F-Aju+nS9Vmv=$Ac0ienZ+p9*O%NG zMZKy5?%Z6TAJTE?o5vEr0r>f>hb#2w2U3DL64*au_@P!J!TL`oH2r*{>ffu6|A7tv zL4juf$DZ1MW5ZPsG!5)`k8d8c$J$o;%EIL0va9&GzWvkS%ZsGb#S(?{!UFOZ9<$a| zY|a+5kmD5N&{vRqkgY>aHsBT&`rg|&kezoD)gP0fsNYHsO#TRc_$n6Lf1Z{?+DLziXlHrq4sf(!>O{?Tj;Eh@%)+nRE_2VxbN&&%%caU#JDU%vL3}Cb zsb4AazPI{>8H&d=jUaZDS$-0^AxE@utGs;-Ez_F(qC9T=UZX=>ok2k2 ziTn{K?y~a5reD2A)P${NoI^>JXn>`IeArow(41c-Wm~)wiryEP(OS{YXWi7;%dG9v zI?mwu1MxD{yp_rrk!j^cKM)dc4@p4Ezyo%lRN|XyD}}>v=Xoib0gOcdXrQ^*61HNj z=NP|pd>@yfvr-=m{8$3A8TQGMTE7g=z!%yt`8`Bk-0MMwW~h^++;qyUP!J~ykh1GO z(FZ59xuFR$(WE;F@UUyE@Sp>`aVNjyj=Ty>_Vo}xf`e7`F;j-IgL5`1~-#70$9_=uBMq!2&1l zomRgpD58@)YYfvLtPW}{C5B35R;ZVvB<<#)x%srmc_S=A7F@DW8>QOEGwD6suhwCg z>Pa+YyULhmw%BA*4yjDp|2{!T98~<6Yfd(wo1mQ!KWwq0eg+6)o1>W~f~kL<-S+P@$wx*zeI|1t7z#Sxr5 zt6w+;YblPQNplq4Z#T$GLX#j6yldXAqj>4gAnnWtBICUnA&-dtnlh=t0Ho_vEKwV` z)DlJi#!@nkYV#$!)@>udAU*hF?V`2$Hf=V&6PP_|r#Iv*J$9)pF@X3`k;5})9^o4y z&)~?EjX5yX12O(BsFy-l6}nYeuKkiq`u9145&3Ssg^y{5G3Pse z9w(YVa0)N-fLaBq1`P!_#>SS(8fh_5!f{UrgZ~uEdeMJIz7DzI5!NHHqQtm~#CPij z?=N|J>nPR6_sL7!f4hD_|KH`vf8(Wpnj-(gPWH+ZvID}%?~68SwhPTC3u1_cB`otq z)U?6qo!ZLi5b>*KnYHWW=3F!p%h1;h{L&(Q&{qY6)_qxNfbP6E3yYpW!EO+IW3?@J z);4>g4gnl^8klu7uA>eGF6rIGSynacogr)KUwE_R4E5Xzi*Qir@b-jy55-JPC8c~( zo!W8y9OGZ&`xmc8;=4-U9=h{vCqfCNzYirONmGbRQlR`WWlgnY+1wCXbMz&NT~9*| z6@FrzP!LX&{no2!Ln_3|I==_4`@}V?4a;YZKTdw;vT<+K+z=uWbW(&bXEaWJ^W8Td z-3&1bY^Z*oM<=M}LVt>_j+p=2Iu7pZmbXrhQ_k)ysE9yXKygFNw$5hwDn(M>H+e1&9BM5!|81vd%r%vEm zqxY3?F@fb6O#5UunwgAHR9jp_W2zZ}NGp2%mTW@(hz7$^+a`A?mb8|_G*GNMJ) zjqegXQio=i@AINre&%ofexAr95aop5C+0MZ0m-l=MeO8m3epm7U%vZB8+I+C*iNFM z#T3l`gknX;D$-`2XT^Cg*vrv=RH+P;_dfF++cP?B_msQI4j+lt&rX2)3GaJx%W*Nn zkML%D{z5tpHH=dksQ*gzc|}gzW;lwAbxoR07VNgS*-c3d&8J|;@3t^ zVUz*J*&r7DFRuFVDCJDK8V9NN5hvpgGjwx+5n)qa;YCKe8TKtdnh{I7NU9BCN!0dq zczrBk8pE{{@vJa9ywR@mq*J=v+PG;?fwqlJVhijG!3VmIKs>9T6r7MJpC)m!Tc#>g zMtVsU>wbwFJEfwZ{vB|ZlttNe83)$iz`~#8UJ^r)lJ@HA&G#}W&ZH*;k{=TavpjWE z7hdyLZPf*X%Gm}i`Y{OGeeu^~nB8=`{r#TUrM-`;1cBvEd#d!kPqIgYySYhN-*1;L z^byj%Yi}Gx)Wnkosi337BKs}+5H5dth1JA{Ir-JKN$7zC)*}hqeoD(WfaUDPT>0`- z(6sa0AoIqASwF`>hP}^|)a_j2s^PQn*qVC{Q}htR z5-)duBFXT_V56-+UohKXlq~^6uf!6sA#ttk1o~*QEy_Y-S$gAvq47J9Vtk$5oA$Ct zYhYJ@8{hsC^98${!#Ho?4y5MCa7iGnfz}b9jE~h%EAAv~Qxu)_rAV;^cygV~5r_~?l=B`zObj7S=H=~$W zPtI_m%g$`kL_fVUk9J@>EiBH zOO&jtn~&`hIFMS5S`g8w94R4H40mdNUH4W@@XQk1sr17b{@y|JB*G9z1|CrQjd+GX z6+KyURG3;!*BQrentw{B2R&@2&`2}n(z-2&X7#r!{yg@Soy}cRD~j zj9@UBW+N|4HW4AWapy4wfUI- zZ`gSL6DUlgj*f1hSOGXG0IVH8HxK?o2|3HZ;KW{K+yPAlxtb)NV_2AwJm|E)FRs&& z=c^e7bvUsztY|+f^k7NXs$o1EUq>cR7C0$UKi6IooHWlK_#?IWDkvywnzg&ThWo^? z2O_N{5X39#?eV9l)xI(>@!vSB{DLt*oY!K1R8}_?%+0^C{d9a%N4 zoxHVT1&Lm|uDX%$QrBun5e-F`HJ^T$ zmzv)p@4ZHd_w9!%Hf9UYNvGCw2TTTbrj9pl+T9%-_-}L(tES>Or-}Z4F*{##n3~L~TuxjirGuIY#H7{%$E${?p{Q01 zi6T`n;rbK1yIB9jmQNycD~yZq&mbIsFWHo|ZAChSFPQa<(%d8mGw*V3fh|yFoxOOiWJd(qvVb!Z$b88cg->N=qO*4k~6;R==|9ihg&riu#P~s4Oap9O7f%crSr^rljeIfXDEg>wi)&v*a%7zpz<9w z*r!3q9J|390x`Zk;g$&OeN&ctp)VKRpDSV@kU2Q>jtok($Y-*x8_$2piTxun81@vt z!Vj?COa0fg2RPXMSIo26T=~0d`{oGP*eV+$!0I<(4azk&Vj3SiG=Q!6mX0p$z7I}; z9BJUFgT-K9MQQ-0@Z=^7R<{bn2Fm48endsSs`V7_@%8?Bxkqv>BDoVcj?K#dV#uUP zL1ND~?D-|VGKe3Rw_7-Idpht>H6XRLh*U7epS6byiGvJpr%d}XwfusjH9g;Z98H`x zyde%%5mhGOiL4wljCaWCk-&uE4_OOccb9c!ZaWt4B(wYl!?vyzl%7n~QepN&eFUrw zFIOl9c({``6~QD+43*_tzP{f2x41h(?b43^y6=iwyB)2os5hBE!@YUS5?N_tXd=h( z)WE286Fbd>R4M^P{!G)f;h<3Q>Fipuy+d2q-)!RyTgt;wr$(?9ox3;q+{E*ZQHhOn;lM`cjnu9 zXa48ks-v(~b*;MAI<>YZH(^NV8vjb34beE<_cwKlJoR;k6lJNSP6v}uiyRD?|0w+X@o1ONrH8a$fCxXpf? z?$DL0)7|X}Oc%h^zrMKWc-NS9I0Utu@>*j}b@tJ=ixQSJ={4@854wzW@E>VSL+Y{i z#0b=WpbCZS>kUCO_iQz)LoE>P5LIG-hv9E+oG}DtlIDF>$tJ1aw9^LuhLEHt?BCj& z(O4I8v1s#HUi5A>nIS-JK{v!7dJx)^Yg%XjNmlkWAq2*cv#tHgz`Y(bETc6CuO1VkN^L-L3j_x<4NqYb5rzrLC-7uOv z!5e`GZt%B782C5-fGnn*GhDF$%(qP<74Z}3xx+{$4cYKy2ikxI7B2N+2r07DN;|-T->nU&!=Cm#rZt%O_5c&1Z%nlWq3TKAW0w zQqemZw_ue--2uKQsx+niCUou?HjD`xhEjjQd3%rrBi82crq*~#uA4+>vR<_S{~5ce z-2EIl?~s z1=GVL{NxP1N3%=AOaC}j_Fv=ur&THz zyO!d9kHq|c73kpq`$+t+8Bw7MgeR5~`d7ChYyGCBWSteTB>8WAU(NPYt2Dk`@#+}= zI4SvLlyk#pBgVigEe`?NG*vl7V6m+<}%FwPV=~PvvA)=#ths==DRTDEYh4V5}Cf$z@#;< zyWfLY_5sP$gc3LLl2x+Ii)#b2nhNXJ{R~vk`s5U7Nyu^3yFg&D%Txwj6QezMX`V(x z=C`{76*mNb!qHHs)#GgGZ_7|vkt9izl_&PBrsu@}L`X{95-2jf99K)0=*N)VxBX2q z((vkpP2RneSIiIUEnGb?VqbMb=Zia+rF~+iqslydE34cSLJ&BJW^3knX@M;t*b=EA zNvGzv41Ld_T+WT#XjDB840vovUU^FtN_)G}7v)1lPetgpEK9YS^OWFkPoE{ovj^=@ zO9N$S=G$1ecndT_=5ehth2Lmd1II-PuT~C9`XVePw$y8J#dpZ?Tss<6wtVglm(Ok7 z3?^oi@pPio6l&!z8JY(pJvG=*pI?GIOu}e^EB6QYk$#FJQ%^AIK$I4epJ+9t?KjqA+bkj&PQ*|vLttme+`9G=L% ziadyMw_7-M)hS(3E$QGNCu|o23|%O+VN7;Qggp?PB3K-iSeBa2b}V4_wY`G1Jsfz4 z9|SdB^;|I8E8gWqHKx!vj_@SMY^hLEIbSMCuE?WKq=c2mJK z8LoG-pnY!uhqFv&L?yEuxo{dpMTsmCn)95xanqBrNPTgXP((H$9N${Ow~Is-FBg%h z53;|Y5$MUN)9W2HBe2TD`ct^LHI<(xWrw}$qSoei?}s)&w$;&!14w6B6>Yr6Y8b)S z0r71`WmAvJJ`1h&poLftLUS6Ir zC$bG9!Im_4Zjse)#K=oJM9mHW1{%l8sz$1o?ltdKlLTxWWPB>Vk22czVt|1%^wnN@*!l)}?EgtvhC>vlHm^t+ogpgHI1_$1ox9e;>0!+b(tBrmXRB`PY1vp-R**8N7 zGP|QqI$m(Rdu#=(?!(N}G9QhQ%o!aXE=aN{&wtGP8|_qh+7a_j_sU5|J^)vxq;# zjvzLn%_QPHZZIWu1&mRAj;Sa_97p_lLq_{~j!M9N^1yp3U_SxRqK&JnR%6VI#^E12 z>CdOVI^_9aPK2eZ4h&^{pQs}xsijXgFYRIxJ~N7&BB9jUR1fm!(xl)mvy|3e6-B3j zJn#ajL;bFTYJ2+Q)tDjx=3IklO@Q+FFM}6UJr6km7hj7th9n_&JR7fnqC!hTZoM~T zBeaVFp%)0cbPhejX<8pf5HyRUj2>aXnXBqDJe73~J%P(2C?-RT{c3NjE`)om! zl$uewSgWkE66$Kb34+QZZvRn`fob~Cl9=cRk@Es}KQm=?E~CE%spXaMO6YmrMl%9Q zlA3Q$3|L1QJ4?->UjT&CBd!~ru{Ih^in&JXO=|<6J!&qp zRe*OZ*cj5bHYlz!!~iEKcuE|;U4vN1rk$xq6>bUWD*u(V@8sG^7>kVuo(QL@Ki;yL zWC!FT(q{E8#on>%1iAS0HMZDJg{Z{^!De(vSIq&;1$+b)oRMwA3nc3mdTSG#3uYO_ z>+x;7p4I;uHz?ZB>dA-BKl+t-3IB!jBRgdvAbW!aJ(Q{aT>+iz?91`C-xbe)IBoND z9_Xth{6?(y3rddwY$GD65IT#f3<(0o#`di{sh2gm{dw*#-Vnc3r=4==&PU^hCv$qd zjw;>i&?L*Wq#TxG$mFIUf>eK+170KG;~+o&1;Tom9}}mKo23KwdEM6UonXgc z!6N(@k8q@HPw{O8O!lAyi{rZv|DpgfU{py+j(X_cwpKqcalcqKIr0kM^%Br3SdeD> zHSKV94Yxw;pjzDHo!Q?8^0bb%L|wC;4U^9I#pd5O&eexX+Im{ z?jKnCcsE|H?{uGMqVie_C~w7GX)kYGWAg%-?8|N_1#W-|4F)3YTDC+QSq1s!DnOML3@d`mG%o2YbYd#jww|jD$gotpa)kntakp#K;+yo-_ZF9qrNZw<%#C zuPE@#3RocLgPyiBZ+R_-FJ_$xP!RzWm|aN)S+{$LY9vvN+IW~Kf3TsEIvP+B9Mtm! zpfNNxObWQpLoaO&cJh5>%slZnHl_Q~(-Tfh!DMz(dTWld@LG1VRF`9`DYKhyNv z2pU|UZ$#_yUx_B_|MxUq^glT}O5Xt(Vm4Mr02><%C)@v;vPb@pT$*yzJ4aPc_FZ3z z3}PLoMBIM>q_9U2rl^sGhk1VUJ89=*?7|v`{!Z{6bqFMq(mYiA?%KbsI~JwuqVA9$H5vDE+VocjX+G^%bieqx->s;XWlKcuv(s%y%D5Xbc9+ zc(_2nYS1&^yL*ey664&4`IoOeDIig}y-E~_GS?m;D!xv5-xwz+G`5l6V+}CpeJDi^ z%4ed$qowm88=iYG+(`ld5Uh&>Dgs4uPHSJ^TngXP_V6fPyl~>2bhi20QB%lSd#yYn zO05?KT1z@?^-bqO8Cg`;ft>ilejsw@2%RR7;`$Vs;FmO(Yr3Fp`pHGr@P2hC%QcA|X&N2Dn zYf`MqXdHi%cGR@%y7Rg7?d3?an){s$zA{!H;Ie5exE#c~@NhQUFG8V=SQh%UxUeiV zd7#UcYqD=lk-}sEwlpu&H^T_V0{#G?lZMxL7ih_&{(g)MWBnCZxtXg znr#}>U^6!jA%e}@Gj49LWG@*&t0V>Cxc3?oO7LSG%~)Y5}f7vqUUnQ;STjdDU}P9IF9d9<$;=QaXc zL1^X7>fa^jHBu_}9}J~#-oz3Oq^JmGR#?GO7b9a(=R@fw@}Q{{@`Wy1vIQ#Bw?>@X z-_RGG@wt|%u`XUc%W{J z>iSeiz8C3H7@St3mOr_mU+&bL#Uif;+Xw-aZdNYUpdf>Rvu0i0t6k*}vwU`XNO2he z%miH|1tQ8~ZK!zmL&wa3E;l?!!XzgV#%PMVU!0xrDsNNZUWKlbiOjzH-1Uoxm8E#r`#2Sz;-o&qcqB zC-O_R{QGuynW14@)7&@yw1U}uP(1cov)twxeLus0s|7ayrtT8c#`&2~Fiu2=R;1_4bCaD=*E@cYI>7YSnt)nQc zohw5CsK%m?8Ack)qNx`W0_v$5S}nO|(V|RZKBD+btO?JXe|~^Qqur%@eO~<8-L^9d z=GA3-V14ng9L29~XJ>a5k~xT2152zLhM*@zlp2P5Eu}bywkcqR;ISbas&#T#;HZSf z2m69qTV(V@EkY(1Dk3`}j)JMo%ZVJ*5eB zYOjIisi+igK0#yW*gBGj?@I{~mUOvRFQR^pJbEbzFxTubnrw(Muk%}jI+vXmJ;{Q6 zrSobKD>T%}jV4Ub?L1+MGOD~0Ir%-`iTnWZN^~YPrcP5y3VMAzQ+&en^VzKEb$K!Q z<7Dbg&DNXuow*eD5yMr+#08nF!;%4vGrJI++5HdCFcGLfMW!KS*Oi@=7hFwDG!h2< zPunUEAF+HncQkbfFj&pbzp|MU*~60Z(|Ik%Tn{BXMN!hZOosNIseT?R;A`W?=d?5X zK(FB=9mZusYahp|K-wyb={rOpdn=@;4YI2W0EcbMKyo~-#^?h`BA9~o285%oY zfifCh5Lk$SY@|2A@a!T2V+{^!psQkx4?x0HSV`(w9{l75QxMk!)U52Lbhn{8ol?S) zCKo*7R(z!uk<6*qO=wh!Pul{(qq6g6xW;X68GI_CXp`XwO zxuSgPRAtM8K7}5E#-GM!*ydOOG_{A{)hkCII<|2=ma*71ci_-}VPARm3crFQjLYV! z9zbz82$|l01mv`$WahE2$=fAGWkd^X2kY(J7iz}WGS z@%MyBEO=A?HB9=^?nX`@nh;7;laAjs+fbo!|K^mE!tOB>$2a_O0y-*uaIn8k^6Y zSbuv;5~##*4Y~+y7Z5O*3w4qgI5V^17u*ZeupVGH^nM&$qmAk|anf*>r zWc5CV;-JY-Z@Uq1Irpb^O`L_7AGiqd*YpGUShb==os$uN3yYvb`wm6d=?T*it&pDk zo`vhw)RZX|91^^Wa_ti2zBFyWy4cJu#g)_S6~jT}CC{DJ_kKpT`$oAL%b^!2M;JgT zM3ZNbUB?}kP(*YYvXDIH8^7LUxz5oE%kMhF!rnPqv!GiY0o}NR$OD=ITDo9r%4E>E0Y^R(rS^~XjWyVI6 zMOR5rPXhTp*G*M&X#NTL`Hu*R+u*QNoiOKg4CtNPrjgH>c?Hi4MUG#I917fx**+pJfOo!zFM&*da&G_x)L(`k&TPI*t3e^{crd zX<4I$5nBQ8Ax_lmNRa~E*zS-R0sxkz`|>7q_?*e%7bxqNm3_eRG#1ae3gtV9!fQpY z+!^a38o4ZGy9!J5sylDxZTx$JmG!wg7;>&5H1)>f4dXj;B+@6tMlL=)cLl={jLMxY zbbf1ax3S4>bwB9-$;SN2?+GULu;UA-35;VY*^9Blx)Jwyb$=U!D>HhB&=jSsd^6yw zL)?a|>GxU!W}ocTC(?-%z3!IUhw^uzc`Vz_g>-tv)(XA#JK^)ZnC|l1`@CdX1@|!| z_9gQ)7uOf?cR@KDp97*>6X|;t@Y`k_N@)aH7gY27)COv^P3ya9I{4z~vUjLR9~z1Z z5=G{mVtKH*&$*t0@}-i_v|3B$AHHYale7>E+jP`ClqG%L{u;*ff_h@)al?RuL7tOO z->;I}>%WI{;vbLP3VIQ^iA$4wl6@0sDj|~112Y4OFjMs`13!$JGkp%b&E8QzJw_L5 zOnw9joc0^;O%OpF$Qp)W1HI!$4BaXX84`%@#^dk^hFp^pQ@rx4g(8Xjy#!X%+X5Jd@fs3amGT`}mhq#L97R>OwT5-m|h#yT_-v@(k$q7P*9X~T*3)LTdzP!*B} z+SldbVWrrwQo9wX*%FyK+sRXTa@O?WM^FGWOE?S`R(0P{<6p#f?0NJvnBia?k^fX2 zNQs7K-?EijgHJY}&zsr;qJ<*PCZUd*x|dD=IQPUK_nn)@X4KWtqoJNHkT?ZWL_hF? zS8lp2(q>;RXR|F;1O}EE#}gCrY~#n^O`_I&?&z5~7N;zL0)3Tup`%)oHMK-^r$NT% zbFg|o?b9w(q@)6w5V%si<$!U<#}s#x@0aX-hP>zwS#9*75VXA4K*%gUc>+yzupTDBOKH8WR4V0pM(HrfbQ&eJ79>HdCvE=F z|J>s;;iDLB^3(9}?biKbxf1$lI!*Z%*0&8UUq}wMyPs_hclyQQi4;NUY+x2qy|0J; zhn8;5)4ED1oHwg+VZF|80<4MrL97tGGXc5Sw$wAI#|2*cvQ=jB5+{AjMiDHmhUC*a zlmiZ`LAuAn_}hftXh;`Kq0zblDk8?O-`tnilIh|;3lZp@F_osJUV9`*R29M?7H{Fy z`nfVEIDIWXmU&YW;NjU8)EJpXhxe5t+scf|VXM!^bBlwNh)~7|3?fWwo_~ZFk(22% zTMesYw+LNx3J-_|DM~`v93yXe=jPD{q;li;5PD?Dyk+b? zo21|XpT@)$BM$%F=P9J19Vi&1#{jM3!^Y&fr&_`toi`XB1!n>sbL%U9I5<7!@?t)~ z;&H%z>bAaQ4f$wIzkjH70;<8tpUoxzKrPhn#IQfS%9l5=Iu))^XC<58D!-O z{B+o5R^Z21H0T9JQ5gNJnqh#qH^na|z92=hONIM~@_iuOi|F>jBh-?aA20}Qx~EpDGElELNn~|7WRXRFnw+Wdo`|# zBpU=Cz3z%cUJ0mx_1($X<40XEIYz(`noWeO+x#yb_pwj6)R(__%@_Cf>txOQ74wSJ z0#F3(zWWaR-jMEY$7C*3HJrohc79>MCUu26mfYN)f4M~4gD`}EX4e}A!U}QV8!S47 z6y-U-%+h`1n`*pQuKE%Av0@)+wBZr9mH}@vH@i{v(m-6QK7Ncf17x_D=)32`FOjjo zg|^VPf5c6-!FxN{25dvVh#fog=NNpXz zfB$o+0jbRkHH{!TKhE709f+jI^$3#v1Nmf80w`@7-5$1Iv_`)W^px8P-({xwb;D0y z7LKDAHgX<84?l!I*Dvi2#D@oAE^J|g$3!)x1Ua;_;<@#l1fD}lqU2_tS^6Ht$1Wl} zBESo7o^)9-Tjuz$8YQSGhfs{BQV6zW7dA?0b(Dbt=UnQs&4zHfe_sj{RJ4uS-vQpC zX;Bbsuju4%!o8?&m4UZU@~ZZjeFF6ex2ss5_60_JS_|iNc+R0GIjH1@Z z=rLT9%B|WWgOrR7IiIwr2=T;Ne?30M!@{%Qf8o`!>=s<2CBpCK_TWc(DX51>e^xh8 z&@$^b6CgOd7KXQV&Y4%}_#uN*mbanXq(2=Nj`L7H7*k(6F8s6{FOw@(DzU`4-*77{ zF+dxpv}%mFpYK?>N_2*#Y?oB*qEKB}VoQ@bzm>ptmVS_EC(#}Lxxx730trt0G)#$b zE=wVvtqOct1%*9}U{q<)2?{+0TzZzP0jgf9*)arV)*e!f`|jgT{7_9iS@e)recI#z zbzolURQ+TOzE!ymqvBY7+5NnAbWxvMLsLTwEbFqW=CPyCsmJ}P1^V30|D5E|p3BC5 z)3|qgw@ra7aXb-wsa|l^in~1_fm{7bS9jhVRkYVO#U{qMp z)Wce+|DJ}4<2gp8r0_xfZpMo#{Hl2MfjLcZdRB9(B(A(f;+4s*FxV{1F|4d`*sRNd zp4#@sEY|?^FIJ;tmH{@keZ$P(sLh5IdOk@k^0uB^BWr@pk6mHy$qf&~rI>P*a;h0C{%oA*i!VjWn&D~O#MxN&f@1Po# zKN+ zrGrkSjcr?^R#nGl<#Q722^wbYcgW@{+6CBS<1@%dPA8HC!~a`jTz<`g_l5N1M@9wn9GOAZ>nqNgq!yOCbZ@1z`U_N`Z>}+1HIZxk*5RDc&rd5{3qjRh8QmT$VyS;jK z;AF+r6XnnCp=wQYoG|rT2@8&IvKq*IB_WvS%nt%e{MCFm`&W*#LXc|HrD?nVBo=(8*=Aq?u$sDA_sC_RPDUiQ+wnIJET8vx$&fxkW~kP9qXKt zozR)@xGC!P)CTkjeWvXW5&@2?)qt)jiYWWBU?AUtzAN}{JE1I)dfz~7$;}~BmQF`k zpn11qmObXwRB8&rnEG*#4Xax3XBkKlw(;tb?Np^i+H8m(Wyz9k{~ogba@laiEk;2! zV*QV^6g6(QG%vX5Um#^sT&_e`B1pBW5yVth~xUs#0}nv?~C#l?W+9Lsb_5)!71rirGvY zTIJ$OPOY516Y|_014sNv+Z8cc5t_V=i>lWV=vNu#!58y9Zl&GsMEW#pPYPYGHQ|;vFvd*9eM==$_=vc7xnyz0~ zY}r??$<`wAO?JQk@?RGvkWVJlq2dk9vB(yV^vm{=NVI8dhsX<)O(#nr9YD?I?(VmQ z^r7VfUBn<~p3()8yOBjm$#KWx!5hRW)5Jl7wY@ky9lNM^jaT##8QGVsYeaVywmpv>X|Xj7gWE1Ezai&wVLt3p)k4w~yrskT-!PR!kiyQlaxl(( zXhF%Q9x}1TMt3~u@|#wWm-Vq?ZerK={8@~&@9r5JW}r#45#rWii};t`{5#&3$W)|@ zbAf2yDNe0q}NEUvq_Quq3cTjcw z@H_;$hu&xllCI9CFDLuScEMg|x{S7GdV8<&Mq=ezDnRZAyX-8gv97YTm0bg=d)(>N z+B2FcqvI9>jGtnK%eO%y zoBPkJTk%y`8TLf4)IXPBn`U|9>O~WL2C~C$z~9|0m*YH<-vg2CD^SX#&)B4ngOSG$ zV^wmy_iQk>dfN@Pv(ckfy&#ak@MLC7&Q6Ro#!ezM*VEh`+b3Jt%m(^T&p&WJ2Oqvj zs-4nq0TW6cv~(YI$n0UkfwN}kg3_fp?(ijSV#tR9L0}l2qjc7W?i*q01=St0eZ=4h zyGQbEw`9OEH>NMuIe)hVwYHsGERWOD;JxEiO7cQv%pFCeR+IyhwQ|y@&^24k+|8fD zLiOWFNJ2&vu2&`Jv96_z-Cd5RLgmeY3*4rDOQo?Jm`;I_(+ejsPM03!ly!*Cu}Cco zrQSrEDHNyzT(D5s1rZq!8#?f6@v6dB7a-aWs(Qk>N?UGAo{gytlh$%_IhyL7h?DLXDGx zgxGEBQoCAWo-$LRvM=F5MTle`M})t3vVv;2j0HZY&G z22^iGhV@uaJh(XyyY%} zd4iH_UfdV#T=3n}(Lj^|n;O4|$;xhu*8T3hR1mc_A}fK}jfZ7LX~*n5+`8N2q#rI$ z@<_2VANlYF$vIH$ zl<)+*tIWW78IIINA7Rr7i{<;#^yzxoLNkXL)eSs=%|P>$YQIh+ea_3k z_s7r4%j7%&*NHSl?R4k%1>Z=M9o#zxY!n8sL5>BO-ZP;T3Gut>iLS@U%IBrX6BA3k z)&@q}V8a{X<5B}K5s(c(LQ=%v1ocr`t$EqqY0EqVjr65usa=0bkf|O#ky{j3)WBR(((L^wmyHRzoWuL2~WTC=`yZ zn%VX`L=|Ok0v7?s>IHg?yArBcync5rG#^+u)>a%qjES%dRZoIyA8gQ;StH z1Ao7{<&}6U=5}4v<)1T7t!J_CL%U}CKNs-0xWoTTeqj{5{?Be$L0_tk>M9o8 zo371}S#30rKZFM{`H_(L`EM9DGp+Mifk&IP|C2Zu_)Ghr4Qtpmkm1osCf@%Z$%t+7 zYH$Cr)Ro@3-QDeQJ8m+x6%;?YYT;k6Z0E-?kr>x33`H%*ueBD7Zx~3&HtWn0?2Wt} zTG}*|v?{$ajzt}xPzV%lL1t-URi8*Zn)YljXNGDb>;!905Td|mpa@mHjIH%VIiGx- zd@MqhpYFu4_?y5N4xiHn3vX&|e6r~Xt> zZG`aGq|yTNjv;9E+Txuoa@A(9V7g?1_T5FzRI;!=NP1Kqou1z5?%X~Wwb{trRfd>i z8&y^H)8YnKyA_Fyx>}RNmQIczT?w2J4SNvI{5J&}Wto|8FR(W;Qw#b1G<1%#tmYzQ zQ2mZA-PAdi%RQOhkHy9Ea#TPSw?WxwL@H@cbkZwIq0B!@ns}niALidmn&W?!Vd4Gj zO7FiuV4*6Mr^2xlFSvM;Cp_#r8UaqIzHJQg_z^rEJw&OMm_8NGAY2)rKvki|o1bH~ z$2IbfVeY2L(^*rMRU1lM5Y_sgrDS`Z??nR2lX;zyR=c%UyGb*%TC-Dil?SihkjrQy~TMv6;BMs7P8il`H7DmpVm@rJ;b)hW)BL)GjS154b*xq-NXq2cwE z^;VP7ua2pxvCmxrnqUYQMH%a%nHmwmI33nJM(>4LznvY*k&C0{8f*%?zggpDgkuz&JBx{9mfb@wegEl2v!=}Sq2Gaty0<)UrOT0{MZtZ~j5y&w zXlYa_jY)I_+VA-^#mEox#+G>UgvM!Ac8zI<%JRXM_73Q!#i3O|)lOP*qBeJG#BST0 zqohi)O!|$|2SeJQo(w6w7%*92S})XfnhrH_Z8qe!G5>CglP=nI7JAOW?(Z29;pXJ9 zR9`KzQ=WEhy*)WH>$;7Cdz|>*i>=##0bB)oU0OR>>N<21e4rMCHDemNi2LD>Nc$;& zQRFthpWniC1J6@Zh~iJCoLOxN`oCKD5Q4r%ynwgUKPlIEd#?QViIqovY|czyK8>6B zSP%{2-<;%;1`#0mG^B(8KbtXF;Nf>K#Di72UWE4gQ%(_26Koiad)q$xRL~?pN71ZZ zujaaCx~jXjygw;rI!WB=xrOJO6HJ!!w}7eiivtCg5K|F6$EXa)=xUC za^JXSX98W`7g-tm@uo|BKj39Dl;sg5ta;4qjo^pCh~{-HdLl6qI9Ix6f$+qiZ$}s= zNguKrU;u+T@ko(Vr1>)Q%h$?UKXCY>3se%&;h2osl2D zE4A9bd7_|^njDd)6cI*FupHpE3){4NQ*$k*cOWZ_?CZ>Z4_fl@n(mMnYK62Q1d@+I zr&O))G4hMihgBqRIAJkLdk(p(D~X{-oBUA+If@B}j& zsHbeJ3RzTq96lB7d($h$xTeZ^gP0c{t!Y0c)aQE;$FY2!mACg!GDEMKXFOPI^)nHZ z`aSPJpvV0|bbrzhWWkuPURlDeN%VT8tndV8?d)eN*i4I@u zVKl^6{?}A?P)Fsy?3oi#clf}L18t;TjNI2>eI&(ezDK7RyqFxcv%>?oxUlonv(px) z$vnPzRH`y5A(x!yOIfL0bmgeMQB$H5wenx~!ujQK*nUBW;@Em&6Xv2%s(~H5WcU2R z;%Nw<$tI)a`Ve!>x+qegJnQsN2N7HaKzrFqM>`6R*gvh%O*-%THt zrB$Nk;lE;z{s{r^PPm5qz(&lM{sO*g+W{sK+m3M_z=4=&CC>T`{X}1Vg2PEfSj2x_ zmT*(x;ov%3F?qoEeeM>dUn$a*?SIGyO8m806J1W1o+4HRhc2`9$s6hM#qAm zChQ87b~GEw{ADfs+5}FJ8+|bIlIv(jT$Ap#hSHoXdd9#w<#cA<1Rkq^*EEkknUd4& zoIWIY)sAswy6fSERVm&!SO~#iN$OgOX*{9@_BWFyJTvC%S++ilSfCrO(?u=Dc?CXZ zzCG&0yVR{Z`|ZF0eEApWEo#s9osV>F{uK{QA@BES#&;#KsScf>y zvs?vIbI>VrT<*!;XmQS=bhq%46-aambZ(8KU-wOO2=en~D}MCToB_u;Yz{)1ySrPZ z@=$}EvjTdzTWU7c0ZI6L8=yP+YRD_eMMos}b5vY^S*~VZysrkq<`cK3>>v%uy7jgq z0ilW9KjVDHLv0b<1K_`1IkbTOINs0=m-22c%M~l=^S}%hbli-3?BnNq?b`hx^HX2J zIe6ECljRL0uBWb`%{EA=%!i^4sMcj+U_TaTZRb+~GOk z^ZW!nky0n*Wb*r+Q|9H@ml@Z5gU&W`(z4-j!OzC1wOke`TRAYGZVl$PmQ16{3196( zO*?`--I}Qf(2HIwb2&1FB^!faPA2=sLg(@6P4mN)>Dc3i(B0;@O-y2;lM4akD>@^v z=u>*|!s&9zem70g7zfw9FXl1bpJW(C#5w#uy5!V?Q(U35A~$dR%LDVnq@}kQm13{} zd53q3N(s$Eu{R}k2esbftfjfOITCL;jWa$}(mmm}d(&7JZ6d3%IABCapFFYjdEjdK z&4Edqf$G^MNAtL=uCDRs&Fu@FXRgX{*0<(@c3|PNHa>L%zvxWS={L8%qw`STm+=Rd zA}FLspESSIpE_^41~#5yI2bJ=9`oc;GIL!JuW&7YetZ?0H}$$%8rW@*J37L-~Rsx!)8($nI4 zZhcZ2^=Y+p4YPl%j!nFJA|*M^gc(0o$i3nlphe+~-_m}jVkRN{spFs(o0ajW@f3K{ zDV!#BwL322CET$}Y}^0ixYj2w>&Xh12|R8&yEw|wLDvF!lZ#dOTHM9pK6@Nm-@9Lnng4ZHBgBSrr7KI8YCC9DX5Kg|`HsiwJHg2(7#nS;A{b3tVO?Z% za{m5b3rFV6EpX;=;n#wltDv1LE*|g5pQ+OY&*6qCJZc5oDS6Z6JD#6F)bWxZSF@q% z+1WV;m!lRB!n^PC>RgQCI#D1br_o^#iPk>;K2hB~0^<~)?p}LG%kigm@moD#q3PE+ zA^Qca)(xnqw6x>XFhV6ku9r$E>bWNrVH9fum0?4s?Rn2LG{Vm_+QJHse6xa%nzQ?k zKug4PW~#Gtb;#5+9!QBgyB@q=sk9=$S{4T>wjFICStOM?__fr+Kei1 z3j~xPqW;W@YkiUM;HngG!;>@AITg}vAE`M2Pj9Irl4w1fo4w<|Bu!%rh%a(Ai^Zhi zs92>v5;@Y(Zi#RI*ua*h`d_7;byQSa*v9E{2x$<-_=5Z<7{%)}4XExANcz@rK69T0x3%H<@frW>RA8^swA+^a(FxK| zFl3LD*ImHN=XDUkrRhp6RY5$rQ{bRgSO*(vEHYV)3Mo6Jy3puiLmU&g82p{qr0F?ohmbz)f2r{X2|T2 z$4fdQ=>0BeKbiVM!e-lIIs8wVTuC_m7}y4A_%ikI;Wm5$9j(^Y z(cD%U%k)X>_>9~t8;pGzL6L-fmQO@K; zo&vQzMlgY95;1BSkngY)e{`n0!NfVgf}2mB3t}D9@*N;FQ{HZ3Pb%BK6;5#-O|WI( zb6h@qTLU~AbVW#_6?c!?Dj65Now7*pU{h!1+eCV^KCuPAGs28~3k@ueL5+u|Z-7}t z9|lskE`4B7W8wMs@xJa{#bsCGDFoRSNSnmNYB&U7 zVGKWe%+kFB6kb)e;TyHfqtU6~fRg)f|>=5(N36)0+C z`hv65J<$B}WUc!wFAb^QtY31yNleq4dzmG`1wHTj=c*=hay9iD071Hc?oYoUk|M*_ zU1GihAMBsM@5rUJ(qS?9ZYJ6@{bNqJ`2Mr+5#hKf?doa?F|+^IR!8lq9)wS3tF_9n zW_?hm)G(M+MYb?V9YoX^_mu5h-LP^TL^!Q9Z7|@sO(rg_4+@=PdI)WL(B7`!K^ND- z-uIuVDCVEdH_C@c71YGYT^_Scf_dhB8Z2Xy6vGtBSlYud9vggOqv^L~F{BraSE_t} zIkP+Hp2&nH^-MNEs}^`oMLy11`PQW$T|K(`Bu*(f@)mv1-qY(_YG&J2M2<7k;;RK~ zL{Fqj9yCz8(S{}@c)S!65aF<=&eLI{hAMErCx&>i7OeDN>okvegO87OaG{Jmi<|}D zaT@b|0X{d@OIJ7zvT>r+eTzgLq~|Dpu)Z&db-P4z*`M$UL51lf>FLlq6rfG)%doyp z)3kk_YIM!03eQ8Vu_2fg{+osaEJPtJ-s36R+5_AEG12`NG)IQ#TF9c@$99%0iye+ zUzZ57=m2)$D(5Nx!n)=5Au&O0BBgwxIBaeI(mro$#&UGCr<;C{UjJVAbVi%|+WP(a zL$U@TYCxJ=1{Z~}rnW;7UVb7+ZnzgmrogDxhjLGo>c~MiJAWs&&;AGg@%U?Y^0JhL ze(x6Z74JG6FlOFK(T}SXQfhr}RIFl@QXKnIcXYF)5|V~e-}suHILKT-k|<*~Ij|VF zC;t@=uj=hot~*!C68G8hTA%8SzOfETOXQ|3FSaIEjvBJp(A)7SWUi5!Eu#yWgY+;n zlm<$+UDou*V+246_o#V4kMdto8hF%%Lki#zPh}KYXmMf?hrN0;>Mv%`@{0Qn`Ujp) z=lZe+13>^Q!9zT);H<(#bIeRWz%#*}sgUX9P|9($kexOyKIOc`dLux}c$7It4u|Rl z6SSkY*V~g_B-hMPo_ak>>z@AVQ(_N)VY2kB3IZ0G(iDUYw+2d7W^~(Jq}KY=JnWS( z#rzEa&0uNhJ>QE8iiyz;n2H|SV#Og+wEZv=f2%1ELX!SX-(d3tEj$5$1}70Mp<&eI zCkfbByL7af=qQE@5vDVxx1}FSGt_a1DoE3SDI+G)mBAna)KBG4p8Epxl9QZ4BfdAN zFnF|Y(umr;gRgG6NLQ$?ZWgllEeeq~z^ZS7L?<(~O&$5|y)Al^iMKy}&W+eMm1W z7EMU)u^ke(A1#XCV>CZ71}P}0x)4wtHO8#JRG3MA-6g=`ZM!FcICCZ{IEw8Dm2&LQ z1|r)BUG^0GzI6f946RrBlfB1Vs)~8toZf~7)+G;pv&XiUO(%5bm)pl=p>nV^o*;&T z;}@oZSibzto$arQgfkp|z4Z($P>dTXE{4O=vY0!)kDO* zGF8a4wq#VaFpLfK!iELy@?-SeRrdz%F*}hjKcA*y@mj~VD3!it9lhRhX}5YOaR9$} z3mS%$2Be7{l(+MVx3 z(4?h;P!jnRmX9J9sYN#7i=iyj_5q7n#X(!cdqI2lnr8T$IfOW<_v`eB!d9xY1P=2q&WtOXY=D9QYteP)De?S4}FK6#6Ma z=E*V+#s8>L;8aVroK^6iKo=MH{4yEZ_>N-N z`(|;aOATba1^asjxlILk<4}f~`39dBFlxj>Dw(hMYKPO3EEt1@S`1lxFNM+J@uB7T zZ8WKjz7HF1-5&2=l=fqF-*@>n5J}jIxdDwpT?oKM3s8Nr`x8JnN-kCE?~aM1H!hAE z%%w(3kHfGwMnMmNj(SU(w42OrC-euI>Dsjk&jz3ts}WHqmMpzQ3vZrsXrZ|}+MHA7 z068obeXZTsO*6RS@o3x80E4ok``rV^Y3hr&C1;|ZZ0|*EKO`$lECUYG2gVFtUTw)R z4Um<0ZzlON`zTdvVdL#KFoMFQX*a5wM0Czp%wTtfK4Sjs)P**RW&?lP$(<}q%r68Z zS53Y!d@&~ne9O)A^tNrXHhXBkj~$8j%pT1%%mypa9AW5E&s9)rjF4@O3ytH{0z6riz|@< zB~UPh*wRFg2^7EbQrHf0y?E~dHlkOxof_a?M{LqQ^C!i2dawHTPYUE=X@2(3<=OOxs8qn_(y>pU>u^}3y&df{JarR0@VJn0f+U%UiF=$Wyq zQvnVHESil@d|8&R<%}uidGh7@u^(%?$#|&J$pvFC-n8&A>utA=n3#)yMkz+qnG3wd zP7xCnF|$9Dif@N~L)Vde3hW8W!UY0BgT2v(wzp;tlLmyk2%N|0jfG$%<;A&IVrOI< z!L)o>j>;dFaqA3pL}b-Je(bB@VJ4%!JeX@3x!i{yIeIso^=n?fDX`3bU=eG7sTc%g%ye8$v8P@yKE^XD=NYxTb zbf!Mk=h|otpqjFaA-vs5YOF-*GwWPc7VbaOW&stlANnCN8iftFMMrUdYNJ_Bnn5Vt zxfz@Ah|+4&P;reZxp;MmEI7C|FOv8NKUm8njF7Wb6Gi7DeODLl&G~}G4be&*Hi0Qw z5}77vL0P+7-B%UL@3n1&JPxW^d@vVwp?u#gVcJqY9#@-3X{ok#UfW3<1fb%FT`|)V~ggq z(3AUoUS-;7)^hCjdT0Kf{i}h)mBg4qhtHHBti=~h^n^OTH5U*XMgDLIR@sre`AaB$ zg)IGBET_4??m@cx&c~bA80O7B8CHR7(LX7%HThkeC*@vi{-pL%e)yXp!B2InafbDF zjPXf1mko3h59{lT6EEbxKO1Z5GF71)WwowO6kY|6tjSVSWdQ}NsK2x{>i|MKZK8%Q zfu&_0D;CO-Jg0#YmyfctyJ!mRJp)e#@O0mYdp|8x;G1%OZQ3Q847YWTyy|%^cpA;m zze0(5p{tMu^lDkpe?HynyO?a1$_LJl2L&mpeKu%8YvgRNr=%2z${%WThHG=vrWY@4 zsA`OP#O&)TetZ>s%h!=+CE15lOOls&nvC~$Qz0Ph7tHiP;O$i|eDwpT{cp>+)0-|; zY$|bB+Gbel>5aRN3>c0x)4U=|X+z+{ zn*_p*EQoquRL+=+p;=lm`d71&1NqBz&_ph)MXu(Nv6&XE7(RsS)^MGj5Q?Fwude-(sq zjJ>aOq!7!EN>@(fK7EE#;i_BGvli`5U;r!YA{JRodLBc6-`n8K+Fjgwb%sX;j=qHQ z7&Tr!)!{HXoO<2BQrV9Sw?JRaLXV8HrsNevvnf>Y-6|{T!pYLl7jp$-nEE z#X!4G4L#K0qG_4Z;Cj6=;b|Be$hi4JvMH!-voxqx^@8cXp`B??eFBz2lLD8RRaRGh zn7kUfy!YV~p(R|p7iC1Rdgt$_24i0cd-S8HpG|`@my70g^y`gu%#Tf_L21-k?sRRZHK&at(*ED0P8iw{7?R$9~OF$Ko;Iu5)ur5<->x!m93Eb zFYpIx60s=Wxxw=`$aS-O&dCO_9?b1yKiPCQmSQb>T)963`*U+Ydj5kI(B(B?HNP8r z*bfSBpSu)w(Z3j7HQoRjUG(+d=IaE~tv}y14zHHs|0UcN52fT8V_<@2ep_ee{QgZG zmgp8iv4V{k;~8@I%M3<#B;2R>Ef(Gg_cQM7%}0s*^)SK6!Ym+~P^58*wnwV1BW@eG z4sZLqsUvBbFsr#8u7S1r4teQ;t)Y@jnn_m5jS$CsW1um!p&PqAcc8!zyiXHVta9QC zY~wCwCF0U%xiQPD_INKtTb;A|Zf29(mu9NI;E zc-e>*1%(LSXB`g}kd`#}O;veb<(sk~RWL|f3ljxCnEZDdNSTDV6#Td({6l&y4IjKF z^}lIUq*ZUqgTPumD)RrCN{M^jhY>E~1pn|KOZ5((%F)G|*ZQ|r4zIbrEiV%42hJV8 z3xS)=!X1+=olbdGJ=yZil?oXLct8FM{(6ikLL3E%=q#O6(H$p~gQu6T8N!plf!96| z&Q3=`L~>U0zZh;z(pGR2^S^{#PrPxTRHD1RQOON&f)Siaf`GLj#UOk&(|@0?zm;Sx ztsGt8=29-MZs5CSf1l1jNFtNt5rFNZxJPvkNu~2}7*9468TWm>nN9TP&^!;J{-h)_ z7WsHH9|F%I`Pb!>KAS3jQWKfGivTVkMJLO-HUGM_a4UQ_%RgL6WZvrW+Z4ujZn;y@ zz9$=oO!7qVTaQAA^BhX&ZxS*|5dj803M=k&2%QrXda`-Q#IoZL6E(g+tN!6CA!CP* zCpWtCujIea)ENl0liwVfj)Nc<9mV%+e@=d`haoZ*`B7+PNjEbXBkv=B+Pi^~L#EO$D$ZqTiD8f<5$eyb54-(=3 zh)6i8i|jp(@OnRrY5B8t|LFXFQVQ895n*P16cEKTrT*~yLH6Z4e*bZ5otpRDri&+A zfNbK1D5@O=sm`fN=WzWyse!za5n%^+6dHPGX#8DyIK>?9qyX}2XvBWVqbP%%D)7$= z=#$WulZlZR<{m#gU7lwqK4WS1Ne$#_P{b17qe$~UOXCl>5b|6WVh;5vVnR<%d+Lnp z$uEmML38}U4vaW8>shm6CzB(Wei3s#NAWE3)a2)z@i{4jTn;;aQS)O@l{rUM`J@K& l00vQ5JBs~;vo!vr%%-k{2_Fq1Mn4QF81S)AQ99zk{{c4yR+0b! literal 0 HcmV?d00001 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000000..97f9ad6d9c --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,23 @@ +# +# Copyright 2012-2024 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip +networkTimeout=10000 +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew new file mode 100755 index 0000000000..1aa94a4269 --- /dev/null +++ b/gradlew @@ -0,0 +1,249 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000000..6689b85bee --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,92 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000000..d9b68b2647 --- /dev/null +++ b/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'facefriend' diff --git a/src/main/java/capstone/facefriend/FacefriendApplication.java b/src/main/java/capstone/facefriend/FacefriendApplication.java new file mode 100644 index 0000000000..d368606ae6 --- /dev/null +++ b/src/main/java/capstone/facefriend/FacefriendApplication.java @@ -0,0 +1,13 @@ +package capstone.facefriend; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class FacefriendApplication { + + public static void main(String[] args) { + SpringApplication.run(FacefriendApplication.class, args); + } + +} diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties new file mode 100644 index 0000000000..1fb93e9102 --- /dev/null +++ b/src/main/resources/application.properties @@ -0,0 +1 @@ +spring.application.name=facefriend diff --git a/src/test/java/capstone/facefriend/FacefriendApplicationTests.java b/src/test/java/capstone/facefriend/FacefriendApplicationTests.java new file mode 100644 index 0000000000..5233b8ef1a --- /dev/null +++ b/src/test/java/capstone/facefriend/FacefriendApplicationTests.java @@ -0,0 +1,13 @@ +package capstone.facefriend; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class FacefriendApplicationTests { + + @Test + void contextLoads() { + } + +} From 59b336b93e5b6177401e76e7a7d76467bb2ff0da Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Thu, 14 Mar 2024 17:03:20 +0900 Subject: [PATCH 002/265] =?UTF-8?q?feat:=20BaseEntity,=20BaseException=20?= =?UTF-8?q?=ED=81=B4=EB=9E=98=EC=8A=A4=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../facefriend/common/domain/BaseEntity.java | 30 +++++++++++++++++ .../common/exception/BaseException.java | 15 +++++++++ .../common/exception/ExceptionResponse.java | 4 +++ .../common/exception/ExceptionStatus.java | 33 +++++++++++++++++++ .../common/exception/ExceptionType.java | 10 ++++++ .../facefriend/common/exception/Status.java | 10 ++++++ 6 files changed, 102 insertions(+) create mode 100644 src/main/java/capstone/facefriend/common/domain/BaseEntity.java create mode 100644 src/main/java/capstone/facefriend/common/exception/BaseException.java create mode 100644 src/main/java/capstone/facefriend/common/exception/ExceptionResponse.java create mode 100644 src/main/java/capstone/facefriend/common/exception/ExceptionStatus.java create mode 100644 src/main/java/capstone/facefriend/common/exception/ExceptionType.java create mode 100644 src/main/java/capstone/facefriend/common/exception/Status.java diff --git a/src/main/java/capstone/facefriend/common/domain/BaseEntity.java b/src/main/java/capstone/facefriend/common/domain/BaseEntity.java new file mode 100644 index 0000000000..2a19a8fee1 --- /dev/null +++ b/src/main/java/capstone/facefriend/common/domain/BaseEntity.java @@ -0,0 +1,30 @@ +package capstone.facefriend.common.domain; + +import jakarta.persistence.Column; +import jakarta.persistence.EntityListeners; +import jakarta.persistence.MappedSuperclass; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import org.springframework.data.annotation.CreatedDate; +import org.springframework.data.annotation.LastModifiedDate; +import org.springframework.data.jpa.domain.support.AuditingEntityListener; + +import java.time.LocalDateTime; + +@Getter +@MappedSuperclass +@NoArgsConstructor +@AllArgsConstructor +@EntityListeners(AuditingEntityListener.class) +public abstract class BaseEntity { + + @Column(updatable = false, nullable = false) + @CreatedDate + private LocalDateTime createdAt; + + @Column(nullable = false) + @LastModifiedDate + private LocalDateTime updatedAt; +} + diff --git a/src/main/java/capstone/facefriend/common/exception/BaseException.java b/src/main/java/capstone/facefriend/common/exception/BaseException.java new file mode 100644 index 0000000000..90d03e6b84 --- /dev/null +++ b/src/main/java/capstone/facefriend/common/exception/BaseException.java @@ -0,0 +1,15 @@ +package capstone.facefriend.common.exception; + +public class BaseException extends RuntimeException { + + private final ExceptionType exceptionType; + + public BaseException(ExceptionType exceptionType) { + super(exceptionType.message()); + this.exceptionType = exceptionType; + } + + public ExceptionType getExceptionType() { + return exceptionType; + } +} diff --git a/src/main/java/capstone/facefriend/common/exception/ExceptionResponse.java b/src/main/java/capstone/facefriend/common/exception/ExceptionResponse.java new file mode 100644 index 0000000000..3d7c4327b3 --- /dev/null +++ b/src/main/java/capstone/facefriend/common/exception/ExceptionResponse.java @@ -0,0 +1,4 @@ +package capstone.facefriend.common.exception; + +public record ExceptionResponse(int exceptionCode, String message) { +} diff --git a/src/main/java/capstone/facefriend/common/exception/ExceptionStatus.java b/src/main/java/capstone/facefriend/common/exception/ExceptionStatus.java new file mode 100644 index 0000000000..d2e02d93cd --- /dev/null +++ b/src/main/java/capstone/facefriend/common/exception/ExceptionStatus.java @@ -0,0 +1,33 @@ +package capstone.facefriend.common.exception; + +import org.springframework.http.HttpStatus; + +import java.util.Arrays; + +public enum ExceptionStatus { + + SERVER_ERROR(Status.SERVER_ERROR, HttpStatus.INTERNAL_SERVER_ERROR), + NOT_FOUND(Status.FORBIDDEN, HttpStatus.NOT_FOUND), + UNAUTHORIZED(Status.UNAUTHORIZED, HttpStatus.UNAUTHORIZED), + FORBIDDEN(Status.FORBIDDEN, HttpStatus.FORBIDDEN), + BAD_REQUEST(Status.BAD_REQUEST, HttpStatus.BAD_REQUEST); + + private final Status status; + private final HttpStatus httpStatus; + + ExceptionStatus(Status status, HttpStatus httpStatus) { + this.status = status; + this.httpStatus = httpStatus; + } + + public static ExceptionStatus from(Status input) { + return Arrays.stream(ExceptionStatus.values()) + .filter(it -> it.status == input) + .findAny() + .orElse(SERVER_ERROR); + } + + public HttpStatus getHttpStatus() { + return httpStatus; + } +} diff --git a/src/main/java/capstone/facefriend/common/exception/ExceptionType.java b/src/main/java/capstone/facefriend/common/exception/ExceptionType.java new file mode 100644 index 0000000000..a4e18f3259 --- /dev/null +++ b/src/main/java/capstone/facefriend/common/exception/ExceptionType.java @@ -0,0 +1,10 @@ +package capstone.facefriend.common.exception; + +public interface ExceptionType { + + Status status(); + + int exceptionCode(); + + String message(); +} diff --git a/src/main/java/capstone/facefriend/common/exception/Status.java b/src/main/java/capstone/facefriend/common/exception/Status.java new file mode 100644 index 0000000000..e68a6cffb1 --- /dev/null +++ b/src/main/java/capstone/facefriend/common/exception/Status.java @@ -0,0 +1,10 @@ +package capstone.facefriend.common.exception; + +public enum Status { + + SERVER_ERROR, + NOT_FOUND, + FORBIDDEN, + UNAUTHORIZED, + BAD_REQUEST +} From b949bf9a56ab428921acb6c4881dad2308558dd7 Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Thu, 14 Mar 2024 17:10:15 +0900 Subject: [PATCH 003/265] =?UTF-8?q?feat:=20Member=20=EB=8F=84=EB=A9=94?= =?UTF-8?q?=EC=9D=B8=20=ED=81=B4=EB=9E=98=EC=8A=A4=EC=99=80=20=EC=98=88?= =?UTF-8?q?=EC=99=B8=20=ED=81=B4=EB=9E=98=EC=8A=A4=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../facefriend/member/domain/BasicInfo.java | 20 +++++++ .../facefriend/member/domain/FaceInfo.java | 22 ++++++++ .../facefriend/member/domain/Member.java | 53 +++++++++++++++++++ .../member/domain/MemberRepository.java | 20 +++++++ .../facefriend/member/domain/Role.java | 26 +++++++++ .../member/exception/MemberException.java | 11 ++++ .../member/exception/MemberExceptionType.java | 37 +++++++++++++ 7 files changed, 189 insertions(+) create mode 100644 src/main/java/capstone/facefriend/member/domain/BasicInfo.java create mode 100644 src/main/java/capstone/facefriend/member/domain/FaceInfo.java create mode 100644 src/main/java/capstone/facefriend/member/domain/Member.java create mode 100644 src/main/java/capstone/facefriend/member/domain/MemberRepository.java create mode 100644 src/main/java/capstone/facefriend/member/domain/Role.java create mode 100644 src/main/java/capstone/facefriend/member/exception/MemberException.java create mode 100644 src/main/java/capstone/facefriend/member/exception/MemberExceptionType.java diff --git a/src/main/java/capstone/facefriend/member/domain/BasicInfo.java b/src/main/java/capstone/facefriend/member/domain/BasicInfo.java new file mode 100644 index 0000000000..07793ca864 --- /dev/null +++ b/src/main/java/capstone/facefriend/member/domain/BasicInfo.java @@ -0,0 +1,20 @@ +package capstone.facefriend.member.domain; + +import lombok.Getter; + +@Getter +public class BasicInfo { + + private Long id; + + private String nickname; + + private String gender; + + private String age; + + private String height; + + private String region; + +} diff --git a/src/main/java/capstone/facefriend/member/domain/FaceInfo.java b/src/main/java/capstone/facefriend/member/domain/FaceInfo.java new file mode 100644 index 0000000000..e135c4a573 --- /dev/null +++ b/src/main/java/capstone/facefriend/member/domain/FaceInfo.java @@ -0,0 +1,22 @@ +package capstone.facefriend.member.domain; + +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; + +@Entity +public class FaceInfo { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + private String token; + + private String imageURL; + + private String generatedImageURL; + + private String description; +} diff --git a/src/main/java/capstone/facefriend/member/domain/Member.java b/src/main/java/capstone/facefriend/member/domain/Member.java new file mode 100644 index 0000000000..c0931d6ecf --- /dev/null +++ b/src/main/java/capstone/facefriend/member/domain/Member.java @@ -0,0 +1,53 @@ +package capstone.facefriend.member.domain; + +import capstone.back.common.domain.BaseEntity; +import jakarta.persistence.*; +import lombok.*; + +@Getter +@Builder +@EqualsAndHashCode(of = "id", callSuper = false) +@AllArgsConstructor(access = AccessLevel.PRIVATE) +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@Entity +public class Member extends BaseEntity { + + private static final int EMAIL_MASKING_LENGTH = 2; + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(nullable = false) + private String name; + + @Column(nullable = false, unique = true) + private String email; + + private String imageUrl; + + @Enumerated(EnumType.STRING) + @Column(nullable = false) + private Role role; + + public Member(String email) { + this.email = email; + this.role = Role.USER; + } + + public boolean isAdmin() { + return role == Role.ADMIN; + } + + public boolean isSame(Long id) { + return this.id.equals(id); + } + + public String maskEmail() { + return this.email.charAt(0) + "*".repeat(EMAIL_MASKING_LENGTH) + this.email.substring(EMAIL_MASKING_LENGTH + 1); + } + + public void updateRole(Role role) { + this.role = role; + } +} \ No newline at end of file diff --git a/src/main/java/capstone/facefriend/member/domain/MemberRepository.java b/src/main/java/capstone/facefriend/member/domain/MemberRepository.java new file mode 100644 index 0000000000..159b5974ac --- /dev/null +++ b/src/main/java/capstone/facefriend/member/domain/MemberRepository.java @@ -0,0 +1,20 @@ +package capstone.facefriend.member.domain; + +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.repository.Repository; + +import java.util.Optional; + +public interface MemberRepository extends Repository { + + Optional findByEmail(String email); + + Member save(Member member); + + boolean existsById(Long id); + + Optional findById(Long id); + + Page findAll(Pageable pageable); +} diff --git a/src/main/java/capstone/facefriend/member/domain/Role.java b/src/main/java/capstone/facefriend/member/domain/Role.java new file mode 100644 index 0000000000..946731bde4 --- /dev/null +++ b/src/main/java/capstone/facefriend/member/domain/Role.java @@ -0,0 +1,26 @@ +package capstone.facefriend.member.domain; + +import capstone.back.member.exception.MemberException; +import capstone.back.member.exception.MemberExceptionType; +import lombok.Getter; + +import java.util.Arrays; + +@Getter +public enum Role { + USER("user"), + ADMIN("admin"); + + private final String value; + + Role(String value) { + this.value = value; + } + + public static Role from(String role) { + return Arrays.stream(Role.values()) + .filter(it -> it.value.equalsIgnoreCase(role)) + .findFirst() + .orElseThrow(() -> new MemberException(MemberExceptionType.NOT_FOUND_ROLE)); + } +} diff --git a/src/main/java/capstone/facefriend/member/exception/MemberException.java b/src/main/java/capstone/facefriend/member/exception/MemberException.java new file mode 100644 index 0000000000..1c9dd3a855 --- /dev/null +++ b/src/main/java/capstone/facefriend/member/exception/MemberException.java @@ -0,0 +1,11 @@ +package capstone.facefriend.member.exception; + +import capstone.back.common.exception.BaseException; +import capstone.back.common.exception.ExceptionType; + +public class MemberException extends BaseException { + + public MemberException(ExceptionType exceptionType) { + super(exceptionType); + } +} diff --git a/src/main/java/capstone/facefriend/member/exception/MemberExceptionType.java b/src/main/java/capstone/facefriend/member/exception/MemberExceptionType.java new file mode 100644 index 0000000000..3b1219eb69 --- /dev/null +++ b/src/main/java/capstone/facefriend/member/exception/MemberExceptionType.java @@ -0,0 +1,37 @@ +package capstone.facefriend.member.exception; + +import capstone.back.common.exception.ExceptionType; +import capstone.back.common.exception.Status; + +public enum MemberExceptionType implements ExceptionType { + + OT_FOUND(Status.NOT_FOUND, 3001, "회원이 없습니다"), + NOT_FOUND_ROLE(Status.NOT_FOUND, 3002, "일치하는 권한이 없습니다"), + INVALID_ACCESS(Status.FORBIDDEN, 3003, "본인의 계정이 아닙니다"), + UNAUTHORIZED(Status.UNAUTHORIZED, 3005, "접근 정보가 잘못되었습니다"); + + private final Status status; + private final int exceptionCode; + private final String message; + + MemberExceptionType(Status status, int exceptionCode, String message) { + this.status = status; + this.exceptionCode = exceptionCode; + this.message = message; + } + + @Override + public Status status() { + return status; + } + + @Override + public int exceptionCode() { + return exceptionCode; + } + + @Override + public String message() { + return message; + } +} From 62df65c7f6f5190585d3fe0fc5a9055f4f768f41 Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Thu, 14 Mar 2024 17:14:21 +0900 Subject: [PATCH 004/265] =?UTF-8?q?feat:=20GlobalExceptionHandler=20?= =?UTF-8?q?=ED=81=B4=EB=9E=98=EC=8A=A4=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../exception/GlobalExceptionHandler.java | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 src/main/java/capstone/facefriend/exception/GlobalExceptionHandler.java diff --git a/src/main/java/capstone/facefriend/exception/GlobalExceptionHandler.java b/src/main/java/capstone/facefriend/exception/GlobalExceptionHandler.java new file mode 100644 index 0000000000..5a2090ce60 --- /dev/null +++ b/src/main/java/capstone/facefriend/exception/GlobalExceptionHandler.java @@ -0,0 +1,38 @@ +package capstone.facefriend.exception; + + +import capstone.facefriend.common.exception.BaseException; +import capstone.facefriend.common.exception.ExceptionResponse; +import capstone.facefriend.common.exception.ExceptionStatus; +import capstone.facefriend.common.exception.ExceptionType; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +@Slf4j +@RestControllerAdvice +public class GlobalExceptionHandler { + + @ExceptionHandler + public ResponseEntity handleException(BaseException e) { + log.warn(e.getMessage(), e); + + ExceptionType exceptionType = e.getExceptionType(); + ExceptionStatus exceptionStatus = ExceptionStatus.from(exceptionType.status()); + + return ResponseEntity + .status(exceptionStatus.getHttpStatus()) + .body(new ExceptionResponse(exceptionType.exceptionCode(), exceptionType.message())); + } + + @ExceptionHandler + public ResponseEntity handleException(Exception e) { + log.error(e.getMessage(), e); + + return ResponseEntity + .status(HttpStatus.INTERNAL_SERVER_ERROR) + .body(new ExceptionResponse(0000, e.getMessage())); + } +} From a6042442c08d5cdecd76d1af6235ff44ebe1ddf7 Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Thu, 14 Mar 2024 17:15:37 +0900 Subject: [PATCH 005/265] =?UTF-8?q?feat:=20CorsFilter=20=ED=81=B4=EB=9E=98?= =?UTF-8?q?=EC=8A=A4=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../capstone/facefriend/web/CorsFilter.java | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 src/main/java/capstone/facefriend/web/CorsFilter.java diff --git a/src/main/java/capstone/facefriend/web/CorsFilter.java b/src/main/java/capstone/facefriend/web/CorsFilter.java new file mode 100644 index 0000000000..20ed0975c1 --- /dev/null +++ b/src/main/java/capstone/facefriend/web/CorsFilter.java @@ -0,0 +1,45 @@ +package capstone.facefriend.web; + +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.http.HttpMethod; +import org.springframework.web.filter.OncePerRequestFilter; + +import java.io.IOException; +import java.util.Objects; + +public class CorsFilter extends OncePerRequestFilter { + + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { + response.setHeader("Access-Control-Allow-Origin", "*"); + response.setHeader("Access-Control-Allow-Credentials", "true"); + response.setHeader("Access-Control-Allow-Methods", "*"); + response.setHeader("Access-Control-Max-Age", "3600"); + response.setHeader("Access-Control-Allow-Headers", "*"); + + filterChain.doFilter(request, response); + } + + private boolean isPreflightRequest(HttpServletRequest request) { + return isOptions(request) && hasHeaders(request) && hasMethod(request) && hasOrigin(request); + } + + private boolean isOptions(HttpServletRequest request) { + return request.getMethod().equalsIgnoreCase(HttpMethod.OPTIONS.toString()); + } + + private boolean hasHeaders(HttpServletRequest request) { + return Objects.nonNull(request.getHeader("Access-Control-Request-Headers")); + } + + private boolean hasMethod(HttpServletRequest request) { + return Objects.nonNull(request.getHeader("Access-Control-Request-Method")); + } + + private boolean hasOrigin(HttpServletRequest request) { + return Objects.nonNull(request.getHeader("Origin")); + } +} From bf6650cdffafb8678c883c07a615c94907895959 Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Thu, 14 Mar 2024 17:27:09 +0900 Subject: [PATCH 006/265] =?UTF-8?q?fix:=20import=20=EC=97=90=EB=9F=AC=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20&=20Application=20=ED=81=B4=EB=9E=98?= =?UTF-8?q?=EC=8A=A4=EC=9D=B4=EB=A6=84=20=EC=B9=B4=EB=A9=9C=20=EC=BC=80?= =?UTF-8?q?=EC=9D=B4=EC=8A=A4=EB=A1=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/capstone/facefriend/FacefriendApplication.java | 4 ++-- src/main/java/capstone/facefriend/member/domain/Member.java | 2 +- src/main/java/capstone/facefriend/member/domain/Role.java | 4 ++-- .../capstone/facefriend/member/exception/MemberException.java | 4 ++-- .../facefriend/member/exception/MemberExceptionType.java | 4 ++-- src/main/resources/application.properties | 1 - 6 files changed, 9 insertions(+), 10 deletions(-) delete mode 100644 src/main/resources/application.properties diff --git a/src/main/java/capstone/facefriend/FacefriendApplication.java b/src/main/java/capstone/facefriend/FacefriendApplication.java index d368606ae6..93449b70d6 100644 --- a/src/main/java/capstone/facefriend/FacefriendApplication.java +++ b/src/main/java/capstone/facefriend/FacefriendApplication.java @@ -4,10 +4,10 @@ import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication -public class FacefriendApplication { +public class FaceFriendApplication { public static void main(String[] args) { - SpringApplication.run(FacefriendApplication.class, args); + SpringApplication.run(FaceFriendApplication.class, args); } } diff --git a/src/main/java/capstone/facefriend/member/domain/Member.java b/src/main/java/capstone/facefriend/member/domain/Member.java index c0931d6ecf..509b1396dd 100644 --- a/src/main/java/capstone/facefriend/member/domain/Member.java +++ b/src/main/java/capstone/facefriend/member/domain/Member.java @@ -1,6 +1,6 @@ package capstone.facefriend.member.domain; -import capstone.back.common.domain.BaseEntity; +import capstone.facefriend.common.domain.BaseEntity; import jakarta.persistence.*; import lombok.*; diff --git a/src/main/java/capstone/facefriend/member/domain/Role.java b/src/main/java/capstone/facefriend/member/domain/Role.java index 946731bde4..5b1b2a06e5 100644 --- a/src/main/java/capstone/facefriend/member/domain/Role.java +++ b/src/main/java/capstone/facefriend/member/domain/Role.java @@ -1,7 +1,7 @@ package capstone.facefriend.member.domain; -import capstone.back.member.exception.MemberException; -import capstone.back.member.exception.MemberExceptionType; +import capstone.facefriend.member.exception.MemberException; +import capstone.facefriend.member.exception.MemberExceptionType; import lombok.Getter; import java.util.Arrays; diff --git a/src/main/java/capstone/facefriend/member/exception/MemberException.java b/src/main/java/capstone/facefriend/member/exception/MemberException.java index 1c9dd3a855..e099585bd3 100644 --- a/src/main/java/capstone/facefriend/member/exception/MemberException.java +++ b/src/main/java/capstone/facefriend/member/exception/MemberException.java @@ -1,7 +1,7 @@ package capstone.facefriend.member.exception; -import capstone.back.common.exception.BaseException; -import capstone.back.common.exception.ExceptionType; +import capstone.facefriend.common.exception.BaseException; +import capstone.facefriend.common.exception.ExceptionType; public class MemberException extends BaseException { diff --git a/src/main/java/capstone/facefriend/member/exception/MemberExceptionType.java b/src/main/java/capstone/facefriend/member/exception/MemberExceptionType.java index 3b1219eb69..2ff17c3672 100644 --- a/src/main/java/capstone/facefriend/member/exception/MemberExceptionType.java +++ b/src/main/java/capstone/facefriend/member/exception/MemberExceptionType.java @@ -1,7 +1,7 @@ package capstone.facefriend.member.exception; -import capstone.back.common.exception.ExceptionType; -import capstone.back.common.exception.Status; +import capstone.facefriend.common.exception.ExceptionType; +import capstone.facefriend.common.exception.Status; public enum MemberExceptionType implements ExceptionType { diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties deleted file mode 100644 index 1fb93e9102..0000000000 --- a/src/main/resources/application.properties +++ /dev/null @@ -1 +0,0 @@ -spring.application.name=facefriend From 32fa3c17c8ea1cf2099b798df1fc91e7b5c9a3a5 Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Thu, 14 Mar 2024 17:30:13 +0900 Subject: [PATCH 007/265] =?UTF-8?q?feat:=20RestTemplateConfig,=20WebConfig?= =?UTF-8?q?=20=ED=81=B4=EB=9E=98=EC=8A=A4=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../facefriend/config/RestTemplateConfig.java | 20 ++++++++++++++++++ .../capstone/facefriend/config/WebConfig.java | 21 +++++++++++++++++++ 2 files changed, 41 insertions(+) create mode 100644 src/main/java/capstone/facefriend/config/RestTemplateConfig.java create mode 100644 src/main/java/capstone/facefriend/config/WebConfig.java diff --git a/src/main/java/capstone/facefriend/config/RestTemplateConfig.java b/src/main/java/capstone/facefriend/config/RestTemplateConfig.java new file mode 100644 index 0000000000..fb8816077f --- /dev/null +++ b/src/main/java/capstone/facefriend/config/RestTemplateConfig.java @@ -0,0 +1,20 @@ +package capstone.facefriend.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.client.RestTemplate; +import org.springframework.web.util.DefaultUriBuilderFactory; + +@Configuration +public class RestTemplateConfig { + + @Bean + public RestTemplate restTemplate() { + DefaultUriBuilderFactory defaultUriBuilderFactory = new DefaultUriBuilderFactory(); + defaultUriBuilderFactory.setEncodingMode(DefaultUriBuilderFactory.EncodingMode.NONE); + RestTemplate restTemplate = new RestTemplate(); + restTemplate.setUriTemplateHandler(defaultUriBuilderFactory); + return restTemplate; + } +} + diff --git a/src/main/java/capstone/facefriend/config/WebConfig.java b/src/main/java/capstone/facefriend/config/WebConfig.java new file mode 100644 index 0000000000..6802b35fb6 --- /dev/null +++ b/src/main/java/capstone/facefriend/config/WebConfig.java @@ -0,0 +1,21 @@ +package capstone.facefriend.config; + +import capstone.facefriend.web.CorsFilter; +import jakarta.servlet.Filter; +import org.springframework.boot.web.servlet.FilterRegistrationBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +@Configuration +public class WebConfig implements WebMvcConfigurer { + + @Bean + public FilterRegistrationBean corsFilter() { + FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean<>(); + filterRegistrationBean.setFilter(new CorsFilter()); + filterRegistrationBean.setOrder(1); + filterRegistrationBean.addUrlPatterns("/*"); + return filterRegistrationBean; + } +} From bebceef2a9f91fde90799032f683820973eed890 Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Thu, 14 Mar 2024 17:35:34 +0900 Subject: [PATCH 008/265] =?UTF-8?q?feat:=20oauth2=20exception=20=ED=81=B4?= =?UTF-8?q?=EB=9E=98=EC=8A=A4=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../auth/exception/AuthException.java | 11 +++++ .../auth/exception/AuthExceptionType.java | 42 +++++++++++++++++++ 2 files changed, 53 insertions(+) create mode 100644 src/main/java/capstone/facefriend/auth/exception/AuthException.java create mode 100644 src/main/java/capstone/facefriend/auth/exception/AuthExceptionType.java diff --git a/src/main/java/capstone/facefriend/auth/exception/AuthException.java b/src/main/java/capstone/facefriend/auth/exception/AuthException.java new file mode 100644 index 0000000000..3c6a53712c --- /dev/null +++ b/src/main/java/capstone/facefriend/auth/exception/AuthException.java @@ -0,0 +1,11 @@ +package capstone.facefriend.auth.exception; + +import capstone.facefriend.common.exception.BaseException; +import capstone.facefriend.common.exception.ExceptionType; + +public class AuthException extends BaseException { + + public AuthException(ExceptionType exceptionType) { + super(exceptionType); + } +} diff --git a/src/main/java/capstone/facefriend/auth/exception/AuthExceptionType.java b/src/main/java/capstone/facefriend/auth/exception/AuthExceptionType.java new file mode 100644 index 0000000000..f2c0a60411 --- /dev/null +++ b/src/main/java/capstone/facefriend/auth/exception/AuthExceptionType.java @@ -0,0 +1,42 @@ +package capstone.facefriend.auth.exception; + +import capstone.facefriend.common.exception.ExceptionType; +import capstone.facefriend.common.exception.Status; + +public enum AuthExceptionType implements ExceptionType { + + INVALID_AUTH_PROVIDER(Status.UNAUTHORIZED, 2001, "지원하지 않는 로그인 플랫폼입니다."), + SIGNATURE_NOT_FOUND(Status.BAD_REQUEST, 2002, "JWT 서명을 확인하지 못했습니다."), + MALFORMED_TOKEN(Status.BAD_REQUEST, 2003, "토큰의 길이 및 형식이 올바르지 않습니다."), + EXPIRED_TOKEN(Status.UNAUTHORIZED, 2004, "이미 만료된 토큰입니다."), + UNSUPPORTED_TOKEN(Status.BAD_REQUEST, 2005, "지원되지 않는 토큰입니다."), + INVALID_TOKEN(Status.BAD_REQUEST, 2006, "토큰이 유효하지 않습니다"), + BAD_REQUEST_TO_PROVIDER(Status.BAD_REQUEST, 2007, "토큰이 유효하지 않습니다"), + UNAUTHORIZED(Status.UNAUTHORIZED, 2008, "로그인한 정보가 없습니다"), + ; + + private final Status status; + private final int exceptionCode; + private final String message; + + AuthExceptionType(Status status, int exceptionCode, String message) { + this.status = status; + this.exceptionCode = exceptionCode; + this.message = message; + } + + @Override + public Status status() { + return null; + } + + @Override + public int exceptionCode() { + return 0; + } + + @Override + public String message() { + return null; + } +} From 69a9f95b88a6f5c2f570a59941780579ee54a96e Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Thu, 14 Mar 2024 17:36:28 +0900 Subject: [PATCH 009/265] =?UTF-8?q?feat:=20oauth2=20domain=20=ED=81=B4?= =?UTF-8?q?=EB=9E=98=EC=8A=A4=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../facefriend/auth/domain/GoogleMember.java | 38 +++++++++++++++++++ .../facefriend/auth/domain/OAuthMember.java | 12 ++++++ .../facefriend/auth/domain/Provider.java | 33 ++++++++++++++++ .../facefriend/auth/domain/TokenProvider.java | 8 ++++ 4 files changed, 91 insertions(+) create mode 100644 src/main/java/capstone/facefriend/auth/domain/GoogleMember.java create mode 100644 src/main/java/capstone/facefriend/auth/domain/OAuthMember.java create mode 100644 src/main/java/capstone/facefriend/auth/domain/Provider.java create mode 100644 src/main/java/capstone/facefriend/auth/domain/TokenProvider.java diff --git a/src/main/java/capstone/facefriend/auth/domain/GoogleMember.java b/src/main/java/capstone/facefriend/auth/domain/GoogleMember.java new file mode 100644 index 0000000000..3e7f9ff64a --- /dev/null +++ b/src/main/java/capstone/facefriend/auth/domain/GoogleMember.java @@ -0,0 +1,38 @@ +package capstone.facefriend.auth.domain; + +import java.util.Map; + +public class GoogleMember implements OAuthMember { + + private final String id; + private final String name; + private final String email; + private final String imageURL; + + public GoogleMember(Map attribute) { + this.id = (String) attribute.get("id"); + this.name = (String) attribute.get("name"); + this.email = (String) attribute.get("email"); + this.imageURL = (String) attribute.get("picture"); + } + + @Override + public String id() { + return id; + } + + @Override + public String nickname() { + return name; + } + + @Override + public String email() { + return email; + } + + @Override + public String imageUrl() { + return imageURL; + } +} diff --git a/src/main/java/capstone/facefriend/auth/domain/OAuthMember.java b/src/main/java/capstone/facefriend/auth/domain/OAuthMember.java new file mode 100644 index 0000000000..d3338c69a4 --- /dev/null +++ b/src/main/java/capstone/facefriend/auth/domain/OAuthMember.java @@ -0,0 +1,12 @@ +package capstone.facefriend.auth.domain; + +public interface OAuthMember { + + String id(); + + String nickname(); + + String email(); + + String imageUrl(); +} diff --git a/src/main/java/capstone/facefriend/auth/domain/Provider.java b/src/main/java/capstone/facefriend/auth/domain/Provider.java new file mode 100644 index 0000000000..b9a3263147 --- /dev/null +++ b/src/main/java/capstone/facefriend/auth/domain/Provider.java @@ -0,0 +1,33 @@ +package capstone.facefriend.auth.domain; + +import capstone.facefriend.auth.exception.AuthException; +import capstone.facefriend.auth.exception.AuthExceptionType; + +import java.util.Arrays; +import java.util.Map; +import java.util.function.Function; + +public enum Provider { + + GOOGLE("google", GoogleMember::new), + ; + + private final String providerName; + private final Function, OAuthMember> function; + + Provider(String providerName, Function, OAuthMember> function) { + this.providerName = providerName; + this.function = function; + } + + public static Provider from(String name) { + return Arrays.stream(values()) + .filter(it -> it.providerName.equals(name)) + .findFirst() + .orElseThrow(() -> new AuthException(AuthExceptionType.INVALID_AUTH_PROVIDER)); + } + + public OAuthMember getOAuthProvider(Map body) { + return function.apply(body); + } +} diff --git a/src/main/java/capstone/facefriend/auth/domain/TokenProvider.java b/src/main/java/capstone/facefriend/auth/domain/TokenProvider.java new file mode 100644 index 0000000000..9780b0b2b0 --- /dev/null +++ b/src/main/java/capstone/facefriend/auth/domain/TokenProvider.java @@ -0,0 +1,8 @@ +package capstone.facefriend.auth.domain; + +public interface TokenProvider { + + String create(Long id); + + Long extractId(String token); +} From c7fdbec3ddc82eb5c6f2e3aafd99b9cd5bd75799 Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Thu, 14 Mar 2024 17:43:58 +0900 Subject: [PATCH 010/265] =?UTF-8?q?feat:=20oauth2=20DTO=20=ED=81=B4?= =?UTF-8?q?=EB=9E=98=EC=8A=A4=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../auth/infrastructure/dto/OAuthTokenResponse.java | 10 ++++++++++ .../facefriend/auth/service/dto/OAuthLoginRequest.java | 7 +++++++ .../facefriend/auth/service/dto/OAuthUriRequest.java | 6 ++++++ 3 files changed, 23 insertions(+) create mode 100644 src/main/java/capstone/facefriend/auth/infrastructure/dto/OAuthTokenResponse.java create mode 100644 src/main/java/capstone/facefriend/auth/service/dto/OAuthLoginRequest.java create mode 100644 src/main/java/capstone/facefriend/auth/service/dto/OAuthUriRequest.java diff --git a/src/main/java/capstone/facefriend/auth/infrastructure/dto/OAuthTokenResponse.java b/src/main/java/capstone/facefriend/auth/infrastructure/dto/OAuthTokenResponse.java new file mode 100644 index 0000000000..1f4444aa4b --- /dev/null +++ b/src/main/java/capstone/facefriend/auth/infrastructure/dto/OAuthTokenResponse.java @@ -0,0 +1,10 @@ +package capstone.facefriend.auth.infrastructure.dto; + +import com.fasterxml.jackson.annotation.JsonProperty; + +public record OAuthTokenResponse( + @JsonProperty("access_token") String accessToken, + String scope, + @JsonProperty("token_type") String tokenType +) { +} diff --git a/src/main/java/capstone/facefriend/auth/service/dto/OAuthLoginRequest.java b/src/main/java/capstone/facefriend/auth/service/dto/OAuthLoginRequest.java new file mode 100644 index 0000000000..0245b077c7 --- /dev/null +++ b/src/main/java/capstone/facefriend/auth/service/dto/OAuthLoginRequest.java @@ -0,0 +1,7 @@ +package capstone.facefriend.auth.service.dto; + +public record OAuthLoginRequest( + String redirectUri, + String code +) { +} diff --git a/src/main/java/capstone/facefriend/auth/service/dto/OAuthUriRequest.java b/src/main/java/capstone/facefriend/auth/service/dto/OAuthUriRequest.java new file mode 100644 index 0000000000..482ef09868 --- /dev/null +++ b/src/main/java/capstone/facefriend/auth/service/dto/OAuthUriRequest.java @@ -0,0 +1,6 @@ +package capstone.facefriend.auth.service.dto; + +public record OAuthUriRequest( + String redirectUri +) { +} From 10a749ee0d5c0cac04398245cfe8d618f938d346 Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Thu, 14 Mar 2024 17:45:46 +0900 Subject: [PATCH 011/265] =?UTF-8?q?feat:=20oauth2=20service=20=ED=81=B4?= =?UTF-8?q?=EB=9E=98=EC=8A=A4=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../facefriend/auth/service/AuthService.java | 39 +++++++++++++++++++ .../auth/service/MemberService.java | 4 ++ .../auth/service/OAuthProviderProperties.java | 33 ++++++++++++++++ .../auth/service/OAuthRequester.java | 12 ++++++ 4 files changed, 88 insertions(+) create mode 100644 src/main/java/capstone/facefriend/auth/service/AuthService.java create mode 100644 src/main/java/capstone/facefriend/auth/service/MemberService.java create mode 100644 src/main/java/capstone/facefriend/auth/service/OAuthProviderProperties.java create mode 100644 src/main/java/capstone/facefriend/auth/service/OAuthRequester.java diff --git a/src/main/java/capstone/facefriend/auth/service/AuthService.java b/src/main/java/capstone/facefriend/auth/service/AuthService.java new file mode 100644 index 0000000000..02cbd9301b --- /dev/null +++ b/src/main/java/capstone/facefriend/auth/service/AuthService.java @@ -0,0 +1,39 @@ +package capstone.facefriend.auth.service; + + +import capstone.facefriend.auth.domain.OAuthMember; +import capstone.facefriend.auth.domain.Provider; +import capstone.facefriend.auth.domain.TokenProvider; +import capstone.facefriend.member.domain.Member; +import capstone.facefriend.member.domain.MemberRepository; +import capstone.facefriend.member.domain.Role; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +// After OAuth2 Authentication succeeds, JWT is created by user's email +@RequiredArgsConstructor +@Service +public class AuthService { + + private final TokenProvider tokenProvider; + private final OAuthRequester oAuthRequester; + private final MemberRepository memberRepository; + + public String loginUri(String redirectUri, String provider) { + return oAuthRequester.loginUri(Provider.from(provider), redirectUri); + } + + @Transactional + public String generateToken(OAuthMember oAuthMember) { + Member newMember = Member.builder() + .email(oAuthMember.email()) + .name(oAuthMember.nickname()) + .imageUrl(oAuthMember.imageUrl()) + .role(Role.USER) + .build(); + Member member = memberRepository.findByEmail(oAuthMember.email()) + .orElseGet(() -> memberRepository.save(newMember)); + return tokenProvider.create(member.getId()); + } +} diff --git a/src/main/java/capstone/facefriend/auth/service/MemberService.java b/src/main/java/capstone/facefriend/auth/service/MemberService.java new file mode 100644 index 0000000000..6b93130996 --- /dev/null +++ b/src/main/java/capstone/facefriend/auth/service/MemberService.java @@ -0,0 +1,4 @@ +package capstone.facefriend.auth.service; + +public class MemberService { +} diff --git a/src/main/java/capstone/facefriend/auth/service/OAuthProviderProperties.java b/src/main/java/capstone/facefriend/auth/service/OAuthProviderProperties.java new file mode 100644 index 0000000000..0a36e7f2fe --- /dev/null +++ b/src/main/java/capstone/facefriend/auth/service/OAuthProviderProperties.java @@ -0,0 +1,33 @@ +package capstone.facefriend.auth.service; + +import capstone.facefriend.auth.domain.Provider; +import lombok.Getter; +import lombok.Setter; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +import java.util.EnumMap; +import java.util.Map; + +@Getter +@Component +@ConfigurationProperties(prefix = "oauth2") +public class OAuthProviderProperties { + + private final Map provider = new EnumMap<>(Provider.class); + + public OAuthProviderProperty getProviderProperties(Provider provider) { + return this.provider.get(provider); + } + + @Getter + @Setter + public static class OAuthProviderProperty { + private String id; + private String secret; + private String tokenUrl; + private String infoUrl; + private String loginUrl; + private String scope; + } +} diff --git a/src/main/java/capstone/facefriend/auth/service/OAuthRequester.java b/src/main/java/capstone/facefriend/auth/service/OAuthRequester.java new file mode 100644 index 0000000000..47bcfefad5 --- /dev/null +++ b/src/main/java/capstone/facefriend/auth/service/OAuthRequester.java @@ -0,0 +1,12 @@ +package capstone.facefriend.auth.service; + +import capstone.facefriend.auth.domain.OAuthMember; +import capstone.facefriend.auth.domain.Provider; +import capstone.facefriend.auth.service.dto.OAuthLoginRequest; + +public interface OAuthRequester { + + OAuthMember login(OAuthLoginRequest request, String provider); + + String loginUri(Provider provider, String redirectUri); +} From a875c9007719a3735276ed37b1fc6a3f25311f0a Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Thu, 14 Mar 2024 17:46:57 +0900 Subject: [PATCH 012/265] =?UTF-8?q?feat:=20JwtProvider,=20RestTemplateOAut?= =?UTF-8?q?hRequester=20=ED=81=B4=EB=9E=98=EC=8A=A4=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../auth/infrastructure/JwtProvider.java | 86 +++++++++++++ .../RestTemplateOAuthRequester.java | 116 ++++++++++++++++++ 2 files changed, 202 insertions(+) create mode 100644 src/main/java/capstone/facefriend/auth/infrastructure/JwtProvider.java create mode 100644 src/main/java/capstone/facefriend/auth/infrastructure/RestTemplateOAuthRequester.java diff --git a/src/main/java/capstone/facefriend/auth/infrastructure/JwtProvider.java b/src/main/java/capstone/facefriend/auth/infrastructure/JwtProvider.java new file mode 100644 index 0000000000..5881dcbabe --- /dev/null +++ b/src/main/java/capstone/facefriend/auth/infrastructure/JwtProvider.java @@ -0,0 +1,86 @@ +package capstone.facefriend.auth.infrastructure; + + +import capstone.facefriend.auth.domain.TokenProvider; +import capstone.facefriend.auth.exception.AuthException; +import io.jsonwebtoken.*; +import io.jsonwebtoken.security.Keys; +import jakarta.annotation.PostConstruct; +import lombok.Getter; +import lombok.NoArgsConstructor; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import java.security.Key; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.util.Date; + +import static capstone.facefriend.auth.exception.AuthExceptionType.*; + +@Getter +@Component +@NoArgsConstructor +public class JwtProvider implements TokenProvider { + + @Value("${jwt.secret}") + private String secret; + @Value("${jwt.expiration-period}") + private int expirationPeriod; + private Key key; + + private static final long ACCESS_TOKEN_EXPIRATION_TIME = 1000 * 60 * 30L; // 30 minutes + private static final long REFRESH_TOKEN_EXPIRATION_TIME = 1000 * 60 * 60 * 24 * 7L; // 7 days + + @PostConstruct + private void init() { + key = Keys.hmacShaKeyFor(secret.getBytes()); + } + + @Override + public String create(Long id) { + Claims claims = Jwts.claims(); + claims.put("id", id); + return createToken(claims); + } + + private String createToken(Claims claims) { + return Jwts.builder() + .setClaims(claims) + .setIssuedAt(issuedAt()) + .setExpiration(expiredAt()) + .signWith(key, SignatureAlgorithm.HS256) + .compact(); + } + + private Date issuedAt() { + LocalDateTime now = LocalDateTime.now(); + return Date.from(now.atZone(ZoneId.systemDefault()).toInstant()); + } + + private Date expiredAt() { + LocalDateTime now = LocalDateTime.now(); + return Date.from(now.plusHours(expirationPeriod).atZone(ZoneId.systemDefault()).toInstant()); + } + + @Override + public Long extractId(String token) { + try { + return Jwts.parser() + .setSigningKey(secret.getBytes()) + .parseClaimsJws(token) + .getBody() + .get("id", Long.class); + } catch (SecurityException e) { + throw new AuthException(SIGNATURE_NOT_FOUND); + } catch (MalformedJwtException e) { + throw new AuthException(MALFORMED_TOKEN); + } catch (ExpiredJwtException e) { + throw new AuthException(EXPIRED_TOKEN); + } catch (UnsupportedJwtException e) { + throw new AuthException(UNSUPPORTED_TOKEN); + } catch (IllegalArgumentException e) { + throw new AuthException(INVALID_TOKEN); + } + } +} diff --git a/src/main/java/capstone/facefriend/auth/infrastructure/RestTemplateOAuthRequester.java b/src/main/java/capstone/facefriend/auth/infrastructure/RestTemplateOAuthRequester.java new file mode 100644 index 0000000000..c368e11222 --- /dev/null +++ b/src/main/java/capstone/facefriend/auth/infrastructure/RestTemplateOAuthRequester.java @@ -0,0 +1,116 @@ +package capstone.facefriend.auth.infrastructure; + +import capstone.facefriend.auth.domain.OAuthMember; +import capstone.facefriend.auth.domain.Provider; +import capstone.facefriend.auth.exception.AuthException; +import capstone.facefriend.auth.exception.AuthExceptionType; +import capstone.facefriend.auth.infrastructure.dto.OAuthTokenResponse; +import capstone.facefriend.auth.service.OAuthProviderProperties; +import capstone.facefriend.auth.service.OAuthProviderProperties.OAuthProviderProperty; +import capstone.facefriend.auth.service.OAuthRequester; +import capstone.facefriend.auth.service.dto.OAuthLoginRequest; +import lombok.RequiredArgsConstructor; +import org.springframework.core.ParameterizedTypeReference; +import org.springframework.http.*; +import org.springframework.stereotype.Component; +import org.springframework.util.MultiValueMap; +import org.springframework.web.client.RestTemplate; +import org.springframework.web.util.UriComponentsBuilder; + +import java.net.URI; +import java.net.URLDecoder; +import java.nio.charset.StandardCharsets; +import java.util.Map; + + +@RequiredArgsConstructor +@Component +public class RestTemplateOAuthRequester implements OAuthRequester { + + private static final String CODE = "code"; + private static final String GRANT_TYPE = "grant_type"; + private static final String AUTHORIZATION_CODE = "authorization_code"; + private static final String REDIRECT_URI = "redirect_uri"; + private static final String CLIENT_ID = "client_id"; + private static final String RESPONSE_TYPE = "response_type"; + private static final String SCOPE = "scope"; + + private final RestTemplate restTemplate; + private final OAuthProviderProperties oAuthProviderProperties; + + @Override + public String loginUri(Provider provider, String redirectUri) { + OAuthProviderProperty providerProperties = oAuthProviderProperties.getProviderProperties(provider); + return UriComponentsBuilder.fromUriString(providerProperties.getLoginUrl()) + .queryParam(CLIENT_ID, providerProperties.getId()) + .queryParam(REDIRECT_URI, redirectUri) // need to fill query parameter + .queryParam(RESPONSE_TYPE, CODE) + .queryParam(SCOPE, providerProperties.getScope()) + .build() + .toString(); + } + + @Override + public OAuthMember login(OAuthLoginRequest request, String requestProvider) { + Provider provider = Provider.from(requestProvider); + OAuthProviderProperty property = oAuthProviderProperties.getProviderProperties(provider); + OAuthTokenResponse token = getAccessToken(property, request); + return provider.getOAuthProvider(getUserAttributes(property, token)); + } + + private OAuthTokenResponse getAccessToken(OAuthProviderProperty property, OAuthLoginRequest loginRequest) { + HttpHeaders headers = headerWithProviderSecret(property); + HttpEntity> request = new HttpEntity<>(headers); + URI tokenUri = getTokenUri(property, loginRequest); + return requestAccessToken(tokenUri, request); + } + + private HttpHeaders headerWithProviderSecret(OAuthProviderProperty property) { + HttpHeaders headers = new HttpHeaders(); + headers.setBasicAuth(property.getId(), property.getSecret()); + headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); + return headers; + } + + private URI getTokenUri(OAuthProviderProperty property, OAuthLoginRequest request) { + return UriComponentsBuilder.fromUriString(property.getTokenUrl()) + .queryParam(CODE, URLDecoder.decode(request.code(), StandardCharsets.UTF_8)) + .queryParam(GRANT_TYPE, AUTHORIZATION_CODE) + .queryParam(REDIRECT_URI, request.redirectUri()) + .build() + .toUri(); + } + + private OAuthTokenResponse requestAccessToken(URI tokenUri, HttpEntity> request) { + try { + return restTemplate.postForEntity(tokenUri, request, OAuthTokenResponse.class).getBody(); + } catch (Exception e) { + throw new AuthException(AuthExceptionType.BAD_REQUEST_TO_PROVIDER); + } + } + + + private Map getUserAttributes(OAuthProviderProperty property, OAuthTokenResponse tokenResponse) { + HttpHeaders headers = headerWithToken(tokenResponse); + URI uri = URI.create(property.getInfoUrl()); + RequestEntity requestEntity = new RequestEntity<>(headers, HttpMethod.GET, uri); + return requestUserAttributes(requestEntity); + } + + private HttpHeaders headerWithToken(OAuthTokenResponse tokenResponse) { + HttpHeaders headers = new HttpHeaders(); + headers.setBearerAuth(tokenResponse.accessToken()); + headers.setContentType(MediaType.APPLICATION_JSON); + return headers; + } + + private Map requestUserAttributes(RequestEntity requestEntity) { + try { + ResponseEntity> responseEntity = restTemplate.exchange(requestEntity, new ParameterizedTypeReference<>() { + }); + return responseEntity.getBody(); + } catch (Exception e) { + throw new AuthException(AuthExceptionType.BAD_REQUEST_TO_PROVIDER); + } + } +} From a4ea8a74838b002583d512e8be2d5b73bd1cb95d Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Thu, 14 Mar 2024 17:49:36 +0900 Subject: [PATCH 013/265] =?UTF-8?q?feat:=20oauth2=20controller=20DTO=20?= =?UTF-8?q?=ED=81=B4=EB=9E=98=EC=8A=A4=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../facefriend/auth/controller/dto/LoginUriResponse.java | 6 ++++++ .../facefriend/auth/controller/dto/TokenResponse.java | 6 ++++++ 2 files changed, 12 insertions(+) create mode 100644 src/main/java/capstone/facefriend/auth/controller/dto/LoginUriResponse.java create mode 100644 src/main/java/capstone/facefriend/auth/controller/dto/TokenResponse.java diff --git a/src/main/java/capstone/facefriend/auth/controller/dto/LoginUriResponse.java b/src/main/java/capstone/facefriend/auth/controller/dto/LoginUriResponse.java new file mode 100644 index 0000000000..819c3dbd88 --- /dev/null +++ b/src/main/java/capstone/facefriend/auth/controller/dto/LoginUriResponse.java @@ -0,0 +1,6 @@ +package capstone.facefriend.auth.controller.dto; + +public record LoginUriResponse( + String loginUri +) { +} diff --git a/src/main/java/capstone/facefriend/auth/controller/dto/TokenResponse.java b/src/main/java/capstone/facefriend/auth/controller/dto/TokenResponse.java new file mode 100644 index 0000000000..f0f7e3c40d --- /dev/null +++ b/src/main/java/capstone/facefriend/auth/controller/dto/TokenResponse.java @@ -0,0 +1,6 @@ +package capstone.facefriend.auth.controller.dto; + +public record TokenResponse( + String token +) { +} From afa1a1d788e62ff1ae026b0f1405581e9dc7d798 Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Thu, 14 Mar 2024 17:52:31 +0900 Subject: [PATCH 014/265] =?UTF-8?q?feat:=20oauth2=20support=20=ED=81=B4?= =?UTF-8?q?=EB=9E=98=EC=8A=A4=EB=93=A4=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../auth/controller/AuthArgumentResolver.java | 33 +++++++++++++++++++ .../auth/controller/support/AuthMember.java | 11 +++++++ .../support/AuthenticationContext.java | 32 ++++++++++++++++++ .../support/AuthenticationExtractor.java | 27 +++++++++++++++ 4 files changed, 103 insertions(+) create mode 100644 src/main/java/capstone/facefriend/auth/controller/AuthArgumentResolver.java create mode 100644 src/main/java/capstone/facefriend/auth/controller/support/AuthMember.java create mode 100644 src/main/java/capstone/facefriend/auth/controller/support/AuthenticationContext.java create mode 100644 src/main/java/capstone/facefriend/auth/controller/support/AuthenticationExtractor.java diff --git a/src/main/java/capstone/facefriend/auth/controller/AuthArgumentResolver.java b/src/main/java/capstone/facefriend/auth/controller/AuthArgumentResolver.java new file mode 100644 index 0000000000..53c043190c --- /dev/null +++ b/src/main/java/capstone/facefriend/auth/controller/AuthArgumentResolver.java @@ -0,0 +1,33 @@ +package capstone.facefriend.auth.controller; + +import capstone.facefriend.auth.controller.support.AuthMember; +import capstone.facefriend.auth.controller.support.AuthenticationContext; +import lombok.RequiredArgsConstructor; +import org.springframework.core.MethodParameter; +import org.springframework.stereotype.Component; +import org.springframework.web.bind.support.WebDataBinderFactory; +import org.springframework.web.context.request.NativeWebRequest; +import org.springframework.web.method.support.HandlerMethodArgumentResolver; +import org.springframework.web.method.support.ModelAndViewContainer; + +@RequiredArgsConstructor +@Component +public class AuthArgumentResolver implements HandlerMethodArgumentResolver { + + private final AuthenticationContext authenticationContext; + + @Override + public boolean supportsParameter(MethodParameter parameter) { + return parameter.hasParameterAnnotation(AuthMember.class) && + parameter.getParameterType().equals(Long.class); + } + + @Override + public Object resolveArgument( + MethodParameter parameter, + ModelAndViewContainer mavContainer, + NativeWebRequest webRequest, + WebDataBinderFactory binderFactory) { + return authenticationContext.getPrincipal(); + } +} diff --git a/src/main/java/capstone/facefriend/auth/controller/support/AuthMember.java b/src/main/java/capstone/facefriend/auth/controller/support/AuthMember.java new file mode 100644 index 0000000000..9d28827865 --- /dev/null +++ b/src/main/java/capstone/facefriend/auth/controller/support/AuthMember.java @@ -0,0 +1,11 @@ +package capstone.facefriend.auth.controller.support; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.PARAMETER) +@Retention(RetentionPolicy.RUNTIME) +public @interface AuthMember { +} diff --git a/src/main/java/capstone/facefriend/auth/controller/support/AuthenticationContext.java b/src/main/java/capstone/facefriend/auth/controller/support/AuthenticationContext.java new file mode 100644 index 0000000000..2a6034e5fb --- /dev/null +++ b/src/main/java/capstone/facefriend/auth/controller/support/AuthenticationContext.java @@ -0,0 +1,32 @@ +package capstone.facefriend.auth.controller.support; + + +import capstone.facefriend.auth.exception.AuthException; +import capstone.facefriend.auth.exception.AuthExceptionType; +import org.springframework.stereotype.Component; +import org.springframework.web.context.annotation.RequestScope; + +import java.util.Objects; + +@RequestScope +@Component +public class AuthenticationContext { + + private static final long ANONYMOUS_MEMBER = -1L; + private Long memberId; + + public void setAuthentication(Long memerId) { + this.memberId = memerId; + } + + public Long getPrincipal() { + if (Objects.isNull(this.memberId)) { + throw new AuthException(AuthExceptionType.UNAUTHORIZED); + } + return memberId; + } + + public void setAnonymous() { + this.memberId = ANONYMOUS_MEMBER; + } +} diff --git a/src/main/java/capstone/facefriend/auth/controller/support/AuthenticationExtractor.java b/src/main/java/capstone/facefriend/auth/controller/support/AuthenticationExtractor.java new file mode 100644 index 0000000000..f3cf86ea72 --- /dev/null +++ b/src/main/java/capstone/facefriend/auth/controller/support/AuthenticationExtractor.java @@ -0,0 +1,27 @@ +package capstone.facefriend.auth.controller.support; + +import jakarta.servlet.http.HttpServletRequest; +import org.springframework.util.StringUtils; + +import java.util.Optional; + +public class AuthenticationExtractor { + + private static final String AUTHORIZATION_HEADER = "Authorization"; + private static final String BEARER = "Bearer"; + + public static Optional extractAuth(HttpServletRequest request) { + String header = request.getHeader(AUTHORIZATION_HEADER); + if (!StringUtils.hasText(header)) { + return Optional.empty(); + } + return extractToken(header.split(" ")); + } + + private static Optional extractToken(String[] parts) { + if (parts.length == 2 && parts[0].equals(BEARER)) { + return Optional.ofNullable(parts[1]); + } + return Optional.empty(); + } +} From 89b2a151dbf78d2db7a46a82b817650147c03ed4 Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Thu, 14 Mar 2024 17:53:11 +0900 Subject: [PATCH 015/265] =?UTF-8?q?feat:=20oauth2=20interceptor=20?= =?UTF-8?q?=ED=81=B4=EB=9E=98=EC=8A=A4=EB=93=A4=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/interceptor/HttpMethod.java | 21 +++++++++ .../interceptor/LoginCheckInterceptor.java | 27 ++++++++++++ .../interceptor/LoginInterceptor.java | 29 +++++++++++++ .../controller/interceptor/PathContainer.java | 43 +++++++++++++++++++ .../interceptor/PathMatchInterceptor.java | 36 ++++++++++++++++ .../controller/interceptor/PathRequest.java | 18 ++++++++ 6 files changed, 174 insertions(+) create mode 100644 src/main/java/capstone/facefriend/auth/controller/interceptor/HttpMethod.java create mode 100644 src/main/java/capstone/facefriend/auth/controller/interceptor/LoginCheckInterceptor.java create mode 100644 src/main/java/capstone/facefriend/auth/controller/interceptor/LoginInterceptor.java create mode 100644 src/main/java/capstone/facefriend/auth/controller/interceptor/PathContainer.java create mode 100644 src/main/java/capstone/facefriend/auth/controller/interceptor/PathMatchInterceptor.java create mode 100644 src/main/java/capstone/facefriend/auth/controller/interceptor/PathRequest.java diff --git a/src/main/java/capstone/facefriend/auth/controller/interceptor/HttpMethod.java b/src/main/java/capstone/facefriend/auth/controller/interceptor/HttpMethod.java new file mode 100644 index 0000000000..eda7106fa0 --- /dev/null +++ b/src/main/java/capstone/facefriend/auth/controller/interceptor/HttpMethod.java @@ -0,0 +1,21 @@ +package capstone.facefriend.auth.controller.interceptor; + +public enum HttpMethod { + + GET, + POST, + PUT, + PATCH, + DELETE, + OPTIONS, + HEAD, + TRACE, + CONNECT, + ANY, + ; + + public boolean matches(String pathMethod) { + return this == ANY || this.name().equalsIgnoreCase(pathMethod); + } +} + diff --git a/src/main/java/capstone/facefriend/auth/controller/interceptor/LoginCheckInterceptor.java b/src/main/java/capstone/facefriend/auth/controller/interceptor/LoginCheckInterceptor.java new file mode 100644 index 0000000000..3e527f5bb3 --- /dev/null +++ b/src/main/java/capstone/facefriend/auth/controller/interceptor/LoginCheckInterceptor.java @@ -0,0 +1,27 @@ +package capstone.facefriend.auth.controller.interceptor; + + +import capstone.facefriend.auth.controller.support.AuthenticationContext; +import capstone.facefriend.auth.controller.support.AuthenticationExtractor; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; +import org.springframework.web.servlet.HandlerInterceptor; + +@RequiredArgsConstructor +@Component +public class LoginCheckInterceptor implements HandlerInterceptor { + + private final LoginInterceptor loginInterceptor; + private final AuthenticationContext authenticationContext; + + @Override + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { + if (AuthenticationExtractor.extractAuth(request).isEmpty()) { + authenticationContext.setAnonymous(); + return true; + } + return loginInterceptor.preHandle(request, response, handler); + } +} diff --git a/src/main/java/capstone/facefriend/auth/controller/interceptor/LoginInterceptor.java b/src/main/java/capstone/facefriend/auth/controller/interceptor/LoginInterceptor.java new file mode 100644 index 0000000000..400155d9ca --- /dev/null +++ b/src/main/java/capstone/facefriend/auth/controller/interceptor/LoginInterceptor.java @@ -0,0 +1,29 @@ +package capstone.facefriend.auth.controller.interceptor; + +import capstone.facefriend.auth.controller.support.AuthenticationContext; +import capstone.facefriend.auth.controller.support.AuthenticationExtractor; +import capstone.facefriend.auth.domain.TokenProvider; +import capstone.facefriend.auth.exception.AuthException; +import capstone.facefriend.auth.exception.AuthExceptionType; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; +import org.springframework.web.servlet.HandlerInterceptor; + +@RequiredArgsConstructor +@Component +public class LoginInterceptor implements HandlerInterceptor { + + private final TokenProvider tokenProvider; + private final AuthenticationContext authenticationContext; + + @Override + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { + String token = AuthenticationExtractor.extractAuth(request) + .orElseThrow(() -> new AuthException(AuthExceptionType.UNAUTHORIZED)); + Long memberId = tokenProvider.extractId(token); + authenticationContext.setAuthentication(memberId); + return true; + } +} diff --git a/src/main/java/capstone/facefriend/auth/controller/interceptor/PathContainer.java b/src/main/java/capstone/facefriend/auth/controller/interceptor/PathContainer.java new file mode 100644 index 0000000000..fbcc578883 --- /dev/null +++ b/src/main/java/capstone/facefriend/auth/controller/interceptor/PathContainer.java @@ -0,0 +1,43 @@ +package capstone.facefriend.auth.controller.interceptor; + +import org.springframework.util.AntPathMatcher; +import org.springframework.util.PathMatcher; + +import java.util.ArrayList; +import java.util.List; + +public class PathContainer { + + private final PathMatcher pathMatcher; + private final List includePatterns; + private final List excludePatterns; + + public PathContainer() { + this.pathMatcher = new AntPathMatcher(); + this.includePatterns = new ArrayList<>(); + this.excludePatterns = new ArrayList<>(); + } + + public boolean isNotIncludePathPattern(String targetPath, String pathMethod) { + boolean isExcludePattern = excludePatterns.stream() + .anyMatch(pathPattern -> pathPattern.matches(pathMatcher, targetPath, pathMethod)); + + boolean isNotIncludePattern = includePatterns.stream() + .noneMatch(pathPattern -> pathPattern.matches(pathMatcher, targetPath, pathMethod)); + + return isExcludePattern || isNotIncludePattern; + } + + public void addIncludePattern(String path, HttpMethod... method) { + for (HttpMethod httpMethod : method) { + includePatterns.add(new PathRequest(path, httpMethod)); + } + } + + public void addExcludePattern(String path, HttpMethod... method) { + for (HttpMethod httpMethod : method) { + excludePatterns.add(new PathRequest(path, httpMethod)); + } + } + +} diff --git a/src/main/java/capstone/facefriend/auth/controller/interceptor/PathMatchInterceptor.java b/src/main/java/capstone/facefriend/auth/controller/interceptor/PathMatchInterceptor.java new file mode 100644 index 0000000000..6d3838a307 --- /dev/null +++ b/src/main/java/capstone/facefriend/auth/controller/interceptor/PathMatchInterceptor.java @@ -0,0 +1,36 @@ +package capstone.facefriend.auth.controller.interceptor; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.web.servlet.HandlerInterceptor; + +// PathMatchInterceptor 가 LoginCheckInterceptor, LoginInterceptor 를 호출하는 방식 +// AuthConfig 참고 +public class PathMatchInterceptor implements HandlerInterceptor { + + private final HandlerInterceptor handlerInterceptor; + private final PathContainer pathContainer; + + public PathMatchInterceptor(HandlerInterceptor handlerInterceptor) { + this.handlerInterceptor = handlerInterceptor; + this.pathContainer = new PathContainer(); + } + + @Override + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { + if (pathContainer.isNotIncludePathPattern(request.getServletPath(), request.getMethod())) { + return true; + } + return handlerInterceptor.preHandle(request, response, handler); + } + + public PathMatchInterceptor addIncludePathPattern(String pathPattern, HttpMethod... httpMethod) { + pathContainer.addIncludePattern(pathPattern, httpMethod); + return this; + } + + public PathMatchInterceptor addExcludePathPattern(String pathPattern, HttpMethod... httpMethod) { + pathContainer.addExcludePattern(pathPattern, httpMethod); + return this; + } +} diff --git a/src/main/java/capstone/facefriend/auth/controller/interceptor/PathRequest.java b/src/main/java/capstone/facefriend/auth/controller/interceptor/PathRequest.java new file mode 100644 index 0000000000..cbade3d4f7 --- /dev/null +++ b/src/main/java/capstone/facefriend/auth/controller/interceptor/PathRequest.java @@ -0,0 +1,18 @@ +package capstone.facefriend.auth.controller.interceptor; + +import org.springframework.util.PathMatcher; + +public class PathRequest { + + private final String path; + private final HttpMethod method; + + public PathRequest(String path, HttpMethod method) { + this.path = path; + this.method = method; + } + + public boolean matches(PathMatcher pathMatcher, String targetPath, String pathMethod) { + return pathMatcher.match(path, targetPath) && method.matches(pathMethod); + } +} From 47fb78fe3f0872b33c13e6d476f79d5eedf5a18e Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Thu, 14 Mar 2024 17:54:42 +0900 Subject: [PATCH 016/265] =?UTF-8?q?feat:=20oauth2=20controller=20=ED=81=B4?= =?UTF-8?q?=EB=9E=98=EC=8A=A4=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../auth/controller/AuthController.java | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 src/main/java/capstone/facefriend/auth/controller/AuthController.java diff --git a/src/main/java/capstone/facefriend/auth/controller/AuthController.java b/src/main/java/capstone/facefriend/auth/controller/AuthController.java new file mode 100644 index 0000000000..ef43d372b1 --- /dev/null +++ b/src/main/java/capstone/facefriend/auth/controller/AuthController.java @@ -0,0 +1,39 @@ +package capstone.facefriend.auth.controller; + + +import capstone.facefriend.auth.controller.dto.LoginUriResponse; +import capstone.facefriend.auth.controller.dto.TokenResponse; +import capstone.facefriend.auth.domain.OAuthMember; +import capstone.facefriend.auth.service.AuthService; +import capstone.facefriend.auth.service.OAuthRequester; +import capstone.facefriend.auth.service.dto.OAuthLoginRequest; +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequiredArgsConstructor +public class AuthController { + + private final AuthService authService; + private final OAuthRequester oAuthRequester; + + @GetMapping("/oauth/{provider}/login-uri") + public ResponseEntity getRedirect( + @PathVariable String provider, + @RequestParam("redirect-uri") String redirectUri + ) { + String loginUri = authService.loginUri(redirectUri, provider); + return ResponseEntity.ok(new LoginUriResponse(loginUri)); + } + + @PostMapping("/oauth/{provider}/login") + public ResponseEntity login( + @RequestBody OAuthLoginRequest request, + @PathVariable String provider + ) { + OAuthMember oAuthMember = oAuthRequester.login(request, provider); + String token = authService.generateToken(oAuthMember); + return ResponseEntity.ok(new TokenResponse(token)); + } +} From c407526588ec343ea91ed7010627b4f14caa7648 Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Thu, 14 Mar 2024 17:55:37 +0900 Subject: [PATCH 017/265] =?UTF-8?q?feat:=20oauth2=20config=20=ED=81=B4?= =?UTF-8?q?=EB=9E=98=EC=8A=A4=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../facefriend/auth/config/AuthConfig.java | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 src/main/java/capstone/facefriend/auth/config/AuthConfig.java diff --git a/src/main/java/capstone/facefriend/auth/config/AuthConfig.java b/src/main/java/capstone/facefriend/auth/config/AuthConfig.java new file mode 100644 index 0000000000..d27ea6b616 --- /dev/null +++ b/src/main/java/capstone/facefriend/auth/config/AuthConfig.java @@ -0,0 +1,49 @@ +package capstone.facefriend.auth.config; + +import capstone.facefriend.auth.controller.AuthArgumentResolver; +import capstone.facefriend.auth.controller.interceptor.LoginCheckInterceptor; +import capstone.facefriend.auth.controller.interceptor.LoginInterceptor; +import capstone.facefriend.auth.controller.interceptor.PathMatchInterceptor; +import lombok.RequiredArgsConstructor; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.method.support.HandlerMethodArgumentResolver; +import org.springframework.web.servlet.HandlerInterceptor; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +import java.util.List; + +import static capstone.facefriend.auth.controller.interceptor.HttpMethod.ANY; +import static capstone.facefriend.auth.controller.interceptor.HttpMethod.OPTIONS; + +@RequiredArgsConstructor +@Configuration +public class AuthConfig implements WebMvcConfigurer { + + private final AuthArgumentResolver authArgumentResolver; + private final LoginInterceptor loginInterceptor; + private final LoginCheckInterceptor loginCheckInterceptor; + + @Override + public void addInterceptors(InterceptorRegistry registry) { + registry.addInterceptor(loginCheckInterceptor()); + registry.addInterceptor(loginInterceptor()); + } + + private HandlerInterceptor loginCheckInterceptor() { + return new PathMatchInterceptor(loginCheckInterceptor) + .addExcludePathPattern("/**", OPTIONS); + } + + private HandlerInterceptor loginInterceptor() { + return new PathMatchInterceptor(loginInterceptor) + .addExcludePathPattern("/**", OPTIONS) + .addIncludePathPattern("/admin/**", ANY) + .addIncludePathPattern("/members/**", ANY); + } + + @Override + public void addArgumentResolvers(List resolvers) { + resolvers.add(authArgumentResolver); + } +} From a2400f5f006de347d6a2dc26dbfbf14b41efe7b5 Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Fri, 15 Mar 2024 13:22:38 +0900 Subject: [PATCH 018/265] =?UTF-8?q?feat:=20@EnableJpaAuditing=20=EC=95=A0?= =?UTF-8?q?=EB=85=B8=ED=85=8C=EC=9D=B4=EC=85=98=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/capstone/facefriend/FacefriendApplication.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/java/capstone/facefriend/FacefriendApplication.java b/src/main/java/capstone/facefriend/FacefriendApplication.java index 93449b70d6..78f26d506d 100644 --- a/src/main/java/capstone/facefriend/FacefriendApplication.java +++ b/src/main/java/capstone/facefriend/FacefriendApplication.java @@ -2,11 +2,16 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.data.jpa.repository.config.EnableJpaAuditing; +import java.util.TimeZone; + +@EnableJpaAuditing @SpringBootApplication public class FaceFriendApplication { public static void main(String[] args) { + TimeZone.setDefault(TimeZone.getTimeZone("Asia/Seoul")); SpringApplication.run(FaceFriendApplication.class, args); } From 065c1d6df043d54afbf1ccb1a759723171f79ae1 Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Fri, 15 Mar 2024 13:41:13 +0900 Subject: [PATCH 019/265] =?UTF-8?q?feat:=20oauth2=20loginUri(),=20accessTo?= =?UTF-8?q?ken(),=20login()=20=EA=B4=80=EB=A0=A8=20=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../auth/controller/AuthController.java | 11 ++++++- .../auth/exception/AuthExceptionType.java | 6 ++-- .../auth/infrastructure/JwtProvider.java | 1 - .../RestTemplateOAuthRequester.java | 10 +++++-- .../dto/OAuthTokenResponse.java | 4 ++- .../infrastructure/redis/CacheConfig.java | 30 +++++++++++++++++++ .../auth/infrastructure/redis/CacheNames.java | 7 +++++ .../auth/infrastructure/redis/RedisDao.java | 4 +++ .../facefriend/auth/service/AuthService.java | 2 +- .../auth/service/OAuthRequester.java | 7 +++-- .../member/exception/MemberExceptionType.java | 8 ++--- src/main/resources/static/index.html | 1 + 12 files changed, 76 insertions(+), 15 deletions(-) create mode 100644 src/main/java/capstone/facefriend/auth/infrastructure/redis/CacheConfig.java create mode 100644 src/main/java/capstone/facefriend/auth/infrastructure/redis/CacheNames.java create mode 100644 src/main/java/capstone/facefriend/auth/infrastructure/redis/RedisDao.java create mode 100644 src/main/resources/static/index.html diff --git a/src/main/java/capstone/facefriend/auth/controller/AuthController.java b/src/main/java/capstone/facefriend/auth/controller/AuthController.java index ef43d372b1..1381787f34 100644 --- a/src/main/java/capstone/facefriend/auth/controller/AuthController.java +++ b/src/main/java/capstone/facefriend/auth/controller/AuthController.java @@ -4,6 +4,7 @@ import capstone.facefriend.auth.controller.dto.LoginUriResponse; import capstone.facefriend.auth.controller.dto.TokenResponse; import capstone.facefriend.auth.domain.OAuthMember; +import capstone.facefriend.auth.infrastructure.dto.OAuthTokenResponse; import capstone.facefriend.auth.service.AuthService; import capstone.facefriend.auth.service.OAuthRequester; import capstone.facefriend.auth.service.dto.OAuthLoginRequest; @@ -19,7 +20,7 @@ public class AuthController { private final OAuthRequester oAuthRequester; @GetMapping("/oauth/{provider}/login-uri") - public ResponseEntity getRedirect( + public ResponseEntity loginUri( @PathVariable String provider, @RequestParam("redirect-uri") String redirectUri ) { @@ -27,6 +28,14 @@ public ResponseEntity getRedirect( return ResponseEntity.ok(new LoginUriResponse(loginUri)); } + @PostMapping("/oauth/{provider}/accessToken") + public ResponseEntity accessToken( + @RequestBody OAuthLoginRequest request, + @PathVariable String provider + ) { + return ResponseEntity.ok(oAuthRequester.accessToken(request, provider)); + } + @PostMapping("/oauth/{provider}/login") public ResponseEntity login( @RequestBody OAuthLoginRequest request, diff --git a/src/main/java/capstone/facefriend/auth/exception/AuthExceptionType.java b/src/main/java/capstone/facefriend/auth/exception/AuthExceptionType.java index f2c0a60411..858b10df21 100644 --- a/src/main/java/capstone/facefriend/auth/exception/AuthExceptionType.java +++ b/src/main/java/capstone/facefriend/auth/exception/AuthExceptionType.java @@ -10,9 +10,9 @@ public enum AuthExceptionType implements ExceptionType { MALFORMED_TOKEN(Status.BAD_REQUEST, 2003, "토큰의 길이 및 형식이 올바르지 않습니다."), EXPIRED_TOKEN(Status.UNAUTHORIZED, 2004, "이미 만료된 토큰입니다."), UNSUPPORTED_TOKEN(Status.BAD_REQUEST, 2005, "지원되지 않는 토큰입니다."), - INVALID_TOKEN(Status.BAD_REQUEST, 2006, "토큰이 유효하지 않습니다"), - BAD_REQUEST_TO_PROVIDER(Status.BAD_REQUEST, 2007, "토큰이 유효하지 않습니다"), - UNAUTHORIZED(Status.UNAUTHORIZED, 2008, "로그인한 정보가 없습니다"), + INVALID_TOKEN(Status.BAD_REQUEST, 2006, "토큰이 유효하지 않습니다."), + BAD_REQUEST_TO_PROVIDER(Status.BAD_REQUEST, 2007, "토큰이 유효하지 않습니다."), + UNAUTHORIZED(Status.UNAUTHORIZED, 2008, "로그인한 정보가 없습니다."), ; private final Status status; diff --git a/src/main/java/capstone/facefriend/auth/infrastructure/JwtProvider.java b/src/main/java/capstone/facefriend/auth/infrastructure/JwtProvider.java index 5881dcbabe..3a0c7a6076 100644 --- a/src/main/java/capstone/facefriend/auth/infrastructure/JwtProvider.java +++ b/src/main/java/capstone/facefriend/auth/infrastructure/JwtProvider.java @@ -29,7 +29,6 @@ public class JwtProvider implements TokenProvider { private int expirationPeriod; private Key key; - private static final long ACCESS_TOKEN_EXPIRATION_TIME = 1000 * 60 * 30L; // 30 minutes private static final long REFRESH_TOKEN_EXPIRATION_TIME = 1000 * 60 * 60 * 24 * 7L; // 7 days @PostConstruct diff --git a/src/main/java/capstone/facefriend/auth/infrastructure/RestTemplateOAuthRequester.java b/src/main/java/capstone/facefriend/auth/infrastructure/RestTemplateOAuthRequester.java index c368e11222..91fc2e0fe0 100644 --- a/src/main/java/capstone/facefriend/auth/infrastructure/RestTemplateOAuthRequester.java +++ b/src/main/java/capstone/facefriend/auth/infrastructure/RestTemplateOAuthRequester.java @@ -43,13 +43,20 @@ public String loginUri(Provider provider, String redirectUri) { OAuthProviderProperty providerProperties = oAuthProviderProperties.getProviderProperties(provider); return UriComponentsBuilder.fromUriString(providerProperties.getLoginUrl()) .queryParam(CLIENT_ID, providerProperties.getId()) - .queryParam(REDIRECT_URI, redirectUri) // need to fill query parameter + .queryParam(REDIRECT_URI, redirectUri) // should register redirectUri in google cloud .queryParam(RESPONSE_TYPE, CODE) .queryParam(SCOPE, providerProperties.getScope()) .build() .toString(); } + @Override + public OAuthTokenResponse accessToken(OAuthLoginRequest request, String requestProvider) { + Provider provider = Provider.from(requestProvider); + OAuthProviderProperty property = oAuthProviderProperties.getProviderProperties(provider); + return getAccessToken(property, request); + } + @Override public OAuthMember login(OAuthLoginRequest request, String requestProvider) { Provider provider = Provider.from(requestProvider); @@ -89,7 +96,6 @@ private OAuthTokenResponse requestAccessToken(URI tokenUri, HttpEntity getUserAttributes(OAuthProviderProperty property, OAuthTokenResponse tokenResponse) { HttpHeaders headers = headerWithToken(tokenResponse); URI uri = URI.create(property.getInfoUrl()); diff --git a/src/main/java/capstone/facefriend/auth/infrastructure/dto/OAuthTokenResponse.java b/src/main/java/capstone/facefriend/auth/infrastructure/dto/OAuthTokenResponse.java index 1f4444aa4b..9e464dce79 100644 --- a/src/main/java/capstone/facefriend/auth/infrastructure/dto/OAuthTokenResponse.java +++ b/src/main/java/capstone/facefriend/auth/infrastructure/dto/OAuthTokenResponse.java @@ -4,7 +4,9 @@ public record OAuthTokenResponse( @JsonProperty("access_token") String accessToken, + @JsonProperty("expires_in") Long expiresIn, String scope, - @JsonProperty("token_type") String tokenType + @JsonProperty("token_type") String tokenType, + @JsonProperty("id_token") String idToken ) { } diff --git a/src/main/java/capstone/facefriend/auth/infrastructure/redis/CacheConfig.java b/src/main/java/capstone/facefriend/auth/infrastructure/redis/CacheConfig.java new file mode 100644 index 0000000000..139c7a60b9 --- /dev/null +++ b/src/main/java/capstone/facefriend/auth/infrastructure/redis/CacheConfig.java @@ -0,0 +1,30 @@ +package capstone.facefriend.auth.infrastructure.redis; + +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.cache.RedisCacheConfiguration; +import org.springframework.data.redis.cache.RedisCacheManager; +import org.springframework.data.redis.connection.RedisConnectionFactory; + +import org.springframework.core.io.ResourceLoader; +import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer; +import org.springframework.data.redis.serializer.RedisSerializationContext; + +import java.util.HashMap; +import java.util.Map; + +@Configuration +public class CacheConfig { + +// public RedisCacheManager cacheManager(RedisConnectionFactory connectionFactory, ResourceLoader resourceLoader) { +// RedisCacheConfiguration defaultConfig = RedisCacheConfiguration +// .defaultCacheConfig() +// .disableCachingNullValues() +// .serializeValuesWith(RedisSerializationContext.SerializationPair +// .fromSerializer(new GenericJackson2JsonRedisSerializer())); +// +// Map redisCacheConfigMap = new HashMap<>(); +// +// +// +// } +} diff --git a/src/main/java/capstone/facefriend/auth/infrastructure/redis/CacheNames.java b/src/main/java/capstone/facefriend/auth/infrastructure/redis/CacheNames.java new file mode 100644 index 0000000000..255996385b --- /dev/null +++ b/src/main/java/capstone/facefriend/auth/infrastructure/redis/CacheNames.java @@ -0,0 +1,7 @@ +package capstone.facefriend.auth.infrastructure.redis; + +public class CacheNames { + public static final String ALL_USERS = "CACHE_ALL_USERS"; + public static final String LOGIN_USER = "CACHE_LOGIN_USER"; + public static final String USER_BY_ACCOUNT = "CACHE_USER_BY_EMAIL"; +} diff --git a/src/main/java/capstone/facefriend/auth/infrastructure/redis/RedisDao.java b/src/main/java/capstone/facefriend/auth/infrastructure/redis/RedisDao.java new file mode 100644 index 0000000000..7bec69eb61 --- /dev/null +++ b/src/main/java/capstone/facefriend/auth/infrastructure/redis/RedisDao.java @@ -0,0 +1,4 @@ +package capstone.facefriend.auth.infrastructure.redis; + +public class RedisDao { +} diff --git a/src/main/java/capstone/facefriend/auth/service/AuthService.java b/src/main/java/capstone/facefriend/auth/service/AuthService.java index 02cbd9301b..9790d3e3b8 100644 --- a/src/main/java/capstone/facefriend/auth/service/AuthService.java +++ b/src/main/java/capstone/facefriend/auth/service/AuthService.java @@ -11,7 +11,7 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -// After OAuth2 Authentication succeeds, JWT is created by user's email + @RequiredArgsConstructor @Service public class AuthService { diff --git a/src/main/java/capstone/facefriend/auth/service/OAuthRequester.java b/src/main/java/capstone/facefriend/auth/service/OAuthRequester.java index 47bcfefad5..a2e1c1d7b1 100644 --- a/src/main/java/capstone/facefriend/auth/service/OAuthRequester.java +++ b/src/main/java/capstone/facefriend/auth/service/OAuthRequester.java @@ -2,11 +2,14 @@ import capstone.facefriend.auth.domain.OAuthMember; import capstone.facefriend.auth.domain.Provider; +import capstone.facefriend.auth.infrastructure.dto.OAuthTokenResponse; import capstone.facefriend.auth.service.dto.OAuthLoginRequest; public interface OAuthRequester { - OAuthMember login(OAuthLoginRequest request, String provider); - String loginUri(Provider provider, String redirectUri); + + OAuthTokenResponse accessToken(OAuthLoginRequest request, String provider); + + OAuthMember login(OAuthLoginRequest request, String provider); } diff --git a/src/main/java/capstone/facefriend/member/exception/MemberExceptionType.java b/src/main/java/capstone/facefriend/member/exception/MemberExceptionType.java index 2ff17c3672..a5e19fbe4a 100644 --- a/src/main/java/capstone/facefriend/member/exception/MemberExceptionType.java +++ b/src/main/java/capstone/facefriend/member/exception/MemberExceptionType.java @@ -5,10 +5,10 @@ public enum MemberExceptionType implements ExceptionType { - OT_FOUND(Status.NOT_FOUND, 3001, "회원이 없습니다"), - NOT_FOUND_ROLE(Status.NOT_FOUND, 3002, "일치하는 권한이 없습니다"), - INVALID_ACCESS(Status.FORBIDDEN, 3003, "본인의 계정이 아닙니다"), - UNAUTHORIZED(Status.UNAUTHORIZED, 3005, "접근 정보가 잘못되었습니다"); + NOT_FOUND(Status.NOT_FOUND, 3001, "회원이 없습니다."), + NOT_FOUND_ROLE(Status.NOT_FOUND, 3002, "일치하는 권한이 없습니다."), + INVALID_ACCESS(Status.FORBIDDEN, 3003, "본인의 계정이 아닙니다."), + UNAUTHORIZED(Status.UNAUTHORIZED, 3005, "접근 정보가 잘못되었습니다."); private final Status status; private final int exceptionCode; diff --git a/src/main/resources/static/index.html b/src/main/resources/static/index.html new file mode 100644 index 0000000000..0ee7a2abbd --- /dev/null +++ b/src/main/resources/static/index.html @@ -0,0 +1 @@ +

하이?

\ No newline at end of file From 867628b1b7f9ce7137de4c2d95bbebe10a2f0951 Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Fri, 15 Mar 2024 13:42:50 +0900 Subject: [PATCH 020/265] =?UTF-8?q?chore:=20build.gradle=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/build.gradle b/build.gradle index f3b7f18f26..403a63265c 100644 --- a/build.gradle +++ b/build.gradle @@ -26,6 +26,10 @@ dependencies { // postgresql implementation 'org.postgresql:postgresql' + // redis + implementation "org.springframework.boot:spring-boot-starter-data-redis" + implementation 'org.springframework.boot:spring-boot-starter-cache' + // JWT implementation 'io.jsonwebtoken:jjwt-api:0.10.5' runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.10.5' From 4b2562af7c98e16df0bf8a27af6d350f4f56d2c3 Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Tue, 19 Mar 2024 00:30:20 +0900 Subject: [PATCH 021/265] =?UTF-8?q?chore:=20build.gradle=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/build.gradle b/build.gradle index 403a63265c..9f999dc001 100644 --- a/build.gradle +++ b/build.gradle @@ -31,9 +31,9 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-cache' // JWT - implementation 'io.jsonwebtoken:jjwt-api:0.10.5' - runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.10.5' - runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.10.5' + implementation("io.jsonwebtoken:jjwt-api:0.11.5") + runtimeOnly("io.jsonwebtoken:jjwt-impl:0.11.5") + implementation("io.jsonwebtoken:jjwt-jackson:0.11.5") // lombok compileOnly 'org.projectlombok:lombok' From 60f8812e8d44b0c7b4d07110e11340ffe62d4e32 Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Tue, 19 Mar 2024 00:31:49 +0900 Subject: [PATCH 022/265] =?UTF-8?q?feat:=20=EC=9D=B8=ED=84=B0=EC=85=89?= =?UTF-8?q?=ED=84=B0=20=ED=81=B4=EB=9E=98=EC=8A=A4=20=EC=88=98=EC=A0=95=20?= =?UTF-8?q?=EB=B0=8F=20=EC=B6=94=EA=B0=80=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../interceptor/LoginCheckInterceptor.java | 5 ++- .../interceptor/LoginInterceptor.java | 18 ++++++++--- .../interceptor/PathMatchInterceptor.java | 2 -- .../interceptor/TokenInterceptor.java | 32 +++++++++++++++++++ 4 files changed, 50 insertions(+), 7 deletions(-) create mode 100644 src/main/java/capstone/facefriend/auth/controller/interceptor/TokenInterceptor.java diff --git a/src/main/java/capstone/facefriend/auth/controller/interceptor/LoginCheckInterceptor.java b/src/main/java/capstone/facefriend/auth/controller/interceptor/LoginCheckInterceptor.java index 3e527f5bb3..ecf28afdf8 100644 --- a/src/main/java/capstone/facefriend/auth/controller/interceptor/LoginCheckInterceptor.java +++ b/src/main/java/capstone/facefriend/auth/controller/interceptor/LoginCheckInterceptor.java @@ -6,11 +6,13 @@ import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; import org.springframework.web.servlet.HandlerInterceptor; @RequiredArgsConstructor @Component +@Slf4j public class LoginCheckInterceptor implements HandlerInterceptor { private final LoginInterceptor loginInterceptor; @@ -18,7 +20,8 @@ public class LoginCheckInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { - if (AuthenticationExtractor.extractAuth(request).isEmpty()) { + // 액세스 토큰이 없을 경우를 위한 인터셉터 + if (AuthenticationExtractor.extractAccessToken(request).isEmpty()) { authenticationContext.setAnonymous(); return true; } diff --git a/src/main/java/capstone/facefriend/auth/controller/interceptor/LoginInterceptor.java b/src/main/java/capstone/facefriend/auth/controller/interceptor/LoginInterceptor.java index 400155d9ca..1b3c2fba4d 100644 --- a/src/main/java/capstone/facefriend/auth/controller/interceptor/LoginInterceptor.java +++ b/src/main/java/capstone/facefriend/auth/controller/interceptor/LoginInterceptor.java @@ -5,25 +5,35 @@ import capstone.facefriend.auth.domain.TokenProvider; import capstone.facefriend.auth.exception.AuthException; import capstone.facefriend.auth.exception.AuthExceptionType; +import capstone.facefriend.member.exception.MemberExceptionType; +import capstone.facefriend.redis.RedisDao; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; import org.springframework.web.servlet.HandlerInterceptor; +import static capstone.facefriend.auth.exception.AuthExceptionType.*; + @RequiredArgsConstructor @Component +@Slf4j public class LoginInterceptor implements HandlerInterceptor { private final TokenProvider tokenProvider; private final AuthenticationContext authenticationContext; + private final TokenInterceptor tokenInterceptor; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { - String token = AuthenticationExtractor.extractAuth(request) - .orElseThrow(() -> new AuthException(AuthExceptionType.UNAUTHORIZED)); - Long memberId = tokenProvider.extractId(token); + // 액세스 토큰이 존재할 경우를 위한 인터셉터 + String accessToken = AuthenticationExtractor.extractAccessToken(request) + .orElseThrow(() -> new AuthException(UNAUTHORIZED)); + + Long memberId = tokenProvider.extractId(accessToken); authenticationContext.setAuthentication(memberId); - return true; + + return tokenInterceptor.preHandle(request, response, handler); } } diff --git a/src/main/java/capstone/facefriend/auth/controller/interceptor/PathMatchInterceptor.java b/src/main/java/capstone/facefriend/auth/controller/interceptor/PathMatchInterceptor.java index 6d3838a307..ec046ee290 100644 --- a/src/main/java/capstone/facefriend/auth/controller/interceptor/PathMatchInterceptor.java +++ b/src/main/java/capstone/facefriend/auth/controller/interceptor/PathMatchInterceptor.java @@ -4,8 +4,6 @@ import jakarta.servlet.http.HttpServletResponse; import org.springframework.web.servlet.HandlerInterceptor; -// PathMatchInterceptor 가 LoginCheckInterceptor, LoginInterceptor 를 호출하는 방식 -// AuthConfig 참고 public class PathMatchInterceptor implements HandlerInterceptor { private final HandlerInterceptor handlerInterceptor; diff --git a/src/main/java/capstone/facefriend/auth/controller/interceptor/TokenInterceptor.java b/src/main/java/capstone/facefriend/auth/controller/interceptor/TokenInterceptor.java new file mode 100644 index 0000000000..4aeb8538a3 --- /dev/null +++ b/src/main/java/capstone/facefriend/auth/controller/interceptor/TokenInterceptor.java @@ -0,0 +1,32 @@ +package capstone.facefriend.auth.controller.interceptor; + +import capstone.facefriend.auth.controller.support.AuthenticationExtractor; +import capstone.facefriend.auth.domain.TokenProvider; +import capstone.facefriend.auth.exception.AuthException; +import capstone.facefriend.auth.exception.AuthExceptionType; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; +import org.springframework.web.servlet.HandlerInterceptor; + +import static capstone.facefriend.auth.exception.AuthExceptionType.*; + + +@RequiredArgsConstructor +@Component +@Slf4j +public class TokenInterceptor implements HandlerInterceptor { + + private final TokenProvider tokenProvider; + + @Override + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { + // 액세스 토큰이 존재하지만 만료되거나 변조되었을 경우를 위한 인터셉터 + String accessToken = AuthenticationExtractor.extractAccessToken(request) + .orElseThrow(() -> new AuthException(EXPIRED_TOKEN)); + + return tokenProvider.validateExpiration(accessToken) && tokenProvider.validateIntegrity(accessToken); + } +} From 47d753a2082f663667d1fd1dfdc8a4ab4f424481 Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Tue, 19 Mar 2024 00:33:27 +0900 Subject: [PATCH 023/265] =?UTF-8?q?chore:=20warning=20=EC=A7=80=EC=9A=B0?= =?UTF-8?q?=EA=B8=B0=20=EC=9C=84=ED=95=B4=20favicon.ico=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/static/favicon.ico | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 src/main/resources/static/favicon.ico diff --git a/src/main/resources/static/favicon.ico b/src/main/resources/static/favicon.ico new file mode 100644 index 0000000000..e69de29bb2 From c09ece3b036b47728b762bcbb9ff651db383eda3 Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Tue, 19 Mar 2024 00:38:52 +0900 Subject: [PATCH 024/265] =?UTF-8?q?feat:=20Google=20=EC=95=A1=EC=84=B8?= =?UTF-8?q?=EC=8A=A4=ED=86=A0=ED=81=B0=20=EB=8C=80=EC=8B=A0=20=EC=9E=90?= =?UTF-8?q?=EC=B2=B4=20=ED=86=A0=ED=81=B0=EC=9D=84=20=EC=82=AC=EC=9A=A9?= =?UTF-8?q?=ED=95=98=EB=8A=94=20=EB=A1=9C=EC=A7=81=EC=9C=BC=EB=A1=9C=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../facefriend/auth/config/AuthConfig.java | 9 ++- .../auth/controller/AuthController.java | 20 ++--- .../auth/controller/dto/TokenResponse.java | 3 +- .../support/AuthenticationExtractor.java | 6 +- .../facefriend/auth/domain/Provider.java | 4 +- .../facefriend/auth/domain/TokenProvider.java | 8 +- .../auth/infrastructure/JwtProvider.java | 81 ++++++++++++++++--- .../facefriend/auth/service/AuthService.java | 16 +++- .../auth/service/OAuthRequester.java | 3 - .../exception/GlobalExceptionHandler.java | 1 - 10 files changed, 116 insertions(+), 35 deletions(-) diff --git a/src/main/java/capstone/facefriend/auth/config/AuthConfig.java b/src/main/java/capstone/facefriend/auth/config/AuthConfig.java index d27ea6b616..f289baf164 100644 --- a/src/main/java/capstone/facefriend/auth/config/AuthConfig.java +++ b/src/main/java/capstone/facefriend/auth/config/AuthConfig.java @@ -5,6 +5,7 @@ import capstone.facefriend.auth.controller.interceptor.LoginInterceptor; import capstone.facefriend.auth.controller.interceptor.PathMatchInterceptor; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.springframework.context.annotation.Configuration; import org.springframework.web.method.support.HandlerMethodArgumentResolver; import org.springframework.web.servlet.HandlerInterceptor; @@ -18,11 +19,12 @@ @RequiredArgsConstructor @Configuration +@Slf4j public class AuthConfig implements WebMvcConfigurer { private final AuthArgumentResolver authArgumentResolver; - private final LoginInterceptor loginInterceptor; private final LoginCheckInterceptor loginCheckInterceptor; + private final LoginInterceptor loginInterceptor; @Override public void addInterceptors(InterceptorRegistry registry) { @@ -32,12 +34,15 @@ public void addInterceptors(InterceptorRegistry registry) { private HandlerInterceptor loginCheckInterceptor() { return new PathMatchInterceptor(loginCheckInterceptor) - .addExcludePathPattern("/**", OPTIONS); + .addExcludePathPattern("/**", OPTIONS) + .addIncludePathPattern("/test", ANY) // test 용도 + .addExcludePathPattern("/favicon.ico", ANY); } private HandlerInterceptor loginInterceptor() { return new PathMatchInterceptor(loginInterceptor) .addExcludePathPattern("/**", OPTIONS) + .addIncludePathPattern("/test", ANY) // test 용도 .addIncludePathPattern("/admin/**", ANY) .addIncludePathPattern("/members/**", ANY); } diff --git a/src/main/java/capstone/facefriend/auth/controller/AuthController.java b/src/main/java/capstone/facefriend/auth/controller/AuthController.java index 1381787f34..f258b4c086 100644 --- a/src/main/java/capstone/facefriend/auth/controller/AuthController.java +++ b/src/main/java/capstone/facefriend/auth/controller/AuthController.java @@ -4,7 +4,6 @@ import capstone.facefriend.auth.controller.dto.LoginUriResponse; import capstone.facefriend.auth.controller.dto.TokenResponse; import capstone.facefriend.auth.domain.OAuthMember; -import capstone.facefriend.auth.infrastructure.dto.OAuthTokenResponse; import capstone.facefriend.auth.service.AuthService; import capstone.facefriend.auth.service.OAuthRequester; import capstone.facefriend.auth.service.dto.OAuthLoginRequest; @@ -28,21 +27,18 @@ public ResponseEntity loginUri( return ResponseEntity.ok(new LoginUriResponse(loginUri)); } - @PostMapping("/oauth/{provider}/accessToken") - public ResponseEntity accessToken( - @RequestBody OAuthLoginRequest request, - @PathVariable String provider - ) { - return ResponseEntity.ok(oAuthRequester.accessToken(request, provider)); - } - @PostMapping("/oauth/{provider}/login") - public ResponseEntity login( + public ResponseEntity login( @RequestBody OAuthLoginRequest request, @PathVariable String provider ) { OAuthMember oAuthMember = oAuthRequester.login(request, provider); - String token = authService.generateToken(oAuthMember); - return ResponseEntity.ok(new TokenResponse(token)); + TokenResponse tokens = authService.generateTokens(oAuthMember); + return ResponseEntity.ok(tokens); + } + + @GetMapping("/test") + public ResponseEntity test() { + return ResponseEntity.ok("과연?"); } } diff --git a/src/main/java/capstone/facefriend/auth/controller/dto/TokenResponse.java b/src/main/java/capstone/facefriend/auth/controller/dto/TokenResponse.java index f0f7e3c40d..ff282370fc 100644 --- a/src/main/java/capstone/facefriend/auth/controller/dto/TokenResponse.java +++ b/src/main/java/capstone/facefriend/auth/controller/dto/TokenResponse.java @@ -1,6 +1,7 @@ package capstone.facefriend.auth.controller.dto; public record TokenResponse( - String token + String accessToken, + String refreshToken ) { } diff --git a/src/main/java/capstone/facefriend/auth/controller/support/AuthenticationExtractor.java b/src/main/java/capstone/facefriend/auth/controller/support/AuthenticationExtractor.java index f3cf86ea72..86fbecd1fb 100644 --- a/src/main/java/capstone/facefriend/auth/controller/support/AuthenticationExtractor.java +++ b/src/main/java/capstone/facefriend/auth/controller/support/AuthenticationExtractor.java @@ -10,15 +10,15 @@ public class AuthenticationExtractor { private static final String AUTHORIZATION_HEADER = "Authorization"; private static final String BEARER = "Bearer"; - public static Optional extractAuth(HttpServletRequest request) { + public static Optional extractAccessToken(HttpServletRequest request) { String header = request.getHeader(AUTHORIZATION_HEADER); if (!StringUtils.hasText(header)) { return Optional.empty(); } - return extractToken(header.split(" ")); + return splitAuthorizationHeader(header.split(" ")); } - private static Optional extractToken(String[] parts) { + private static Optional splitAuthorizationHeader(String[] parts) { if (parts.length == 2 && parts[0].equals(BEARER)) { return Optional.ofNullable(parts[1]); } diff --git a/src/main/java/capstone/facefriend/auth/domain/Provider.java b/src/main/java/capstone/facefriend/auth/domain/Provider.java index b9a3263147..b55e1563ea 100644 --- a/src/main/java/capstone/facefriend/auth/domain/Provider.java +++ b/src/main/java/capstone/facefriend/auth/domain/Provider.java @@ -27,7 +27,9 @@ public static Provider from(String name) { .orElseThrow(() -> new AuthException(AuthExceptionType.INVALID_AUTH_PROVIDER)); } - public OAuthMember getOAuthProvider(Map body) { + public OAuthMember getOAuthMember(Map body) { return function.apply(body); + // function 은 GoogleMember 의 생성자를 의미 + // GoogleMember 생성자의 파라미터에 request body (=Map) 넣어서 OAuthMember 객체 반환 } } diff --git a/src/main/java/capstone/facefriend/auth/domain/TokenProvider.java b/src/main/java/capstone/facefriend/auth/domain/TokenProvider.java index 9780b0b2b0..3424496619 100644 --- a/src/main/java/capstone/facefriend/auth/domain/TokenProvider.java +++ b/src/main/java/capstone/facefriend/auth/domain/TokenProvider.java @@ -2,7 +2,13 @@ public interface TokenProvider { - String create(Long id); + String createAccessToken(Long id); + + String createRefreshToken(Long id); Long extractId(String token); + + Boolean validateExpiration(String token); + + Boolean validateIntegrity(String token); } diff --git a/src/main/java/capstone/facefriend/auth/infrastructure/JwtProvider.java b/src/main/java/capstone/facefriend/auth/infrastructure/JwtProvider.java index 3a0c7a6076..3b3b5a5477 100644 --- a/src/main/java/capstone/facefriend/auth/infrastructure/JwtProvider.java +++ b/src/main/java/capstone/facefriend/auth/infrastructure/JwtProvider.java @@ -5,9 +5,11 @@ import capstone.facefriend.auth.exception.AuthException; import io.jsonwebtoken.*; import io.jsonwebtoken.security.Keys; +import io.jsonwebtoken.security.SecurityException; import jakarta.annotation.PostConstruct; import lombok.Getter; import lombok.NoArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; @@ -21,15 +23,15 @@ @Getter @Component @NoArgsConstructor +@Slf4j public class JwtProvider implements TokenProvider { @Value("${jwt.secret}") private String secret; - @Value("${jwt.expiration-period}") - private int expirationPeriod; private Key key; - private static final long REFRESH_TOKEN_EXPIRATION_TIME = 1000 * 60 * 60 * 24 * 7L; // 7 days + private static final long ACCESS_TOKEN_EXPIRATION_TIME = 60 * 2L; // 2분 // 1000 * 60 * 60 * 3L; // 3시간 + private static final long REFRESH_TOKEN_EXPIRATION_TIME = 60 * 60 * 24 * 7L; // 7일 @PostConstruct private void init() { @@ -37,17 +39,33 @@ private void init() { } @Override - public String create(Long id) { + public String createAccessToken(Long id) { Claims claims = Jwts.claims(); claims.put("id", id); - return createToken(claims); + return accessToken(claims); } - private String createToken(Claims claims) { + @Override + public String createRefreshToken(Long id) { + Claims claims = Jwts.claims(); + claims.put("id", id); + return refreshToken(claims); + } + + private String accessToken(Claims claims) { + return Jwts.builder() + .setClaims(claims) + .setIssuedAt(issuedAt()) + .setExpiration(accessTokenExpiredAt()) + .signWith(key, SignatureAlgorithm.HS256) + .compact(); + } + + private String refreshToken(Claims claims) { return Jwts.builder() .setClaims(claims) .setIssuedAt(issuedAt()) - .setExpiration(expiredAt()) + .setExpiration(refreshTokenExpiredAt()) .signWith(key, SignatureAlgorithm.HS256) .compact(); } @@ -57,9 +75,14 @@ private Date issuedAt() { return Date.from(now.atZone(ZoneId.systemDefault()).toInstant()); } - private Date expiredAt() { + private Date accessTokenExpiredAt() { LocalDateTime now = LocalDateTime.now(); - return Date.from(now.plusHours(expirationPeriod).atZone(ZoneId.systemDefault()).toInstant()); + return Date.from(now.plusSeconds(ACCESS_TOKEN_EXPIRATION_TIME).atZone(ZoneId.systemDefault()).toInstant()); + } + + private Date refreshTokenExpiredAt() { + LocalDateTime now = LocalDateTime.now(); + return Date.from(now.plusHours(REFRESH_TOKEN_EXPIRATION_TIME).atZone(ZoneId.systemDefault()).toInstant()); } @Override @@ -82,4 +105,44 @@ public Long extractId(String token) { throw new AuthException(INVALID_TOKEN); } } + + @Override + public Boolean validateExpiration(String token) { + try { + Date expiration = Jwts.parser() + .setSigningKey(secret.getBytes()) + .parseClaimsJws(token) + .getBody() + .getExpiration(); + return expiration.after(new Date()); + } catch (SecurityException e) { + throw new AuthException(SIGNATURE_NOT_FOUND); + } catch (MalformedJwtException e) { + throw new AuthException(MALFORMED_TOKEN); + } catch (ExpiredJwtException e) { + throw new AuthException(EXPIRED_TOKEN); + } catch (UnsupportedJwtException e) { + throw new AuthException(UNSUPPORTED_TOKEN); + } catch (IllegalArgumentException e) { + throw new AuthException(INVALID_TOKEN); + } + } + + @Override + public Boolean validateIntegrity(String token) { + try { + Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(token); + return true; + } catch (SecurityException e) { + throw new AuthException(SIGNATURE_NOT_FOUND); + } catch (MalformedJwtException e) { + throw new AuthException(MALFORMED_TOKEN); + } catch (ExpiredJwtException e) { + throw new AuthException(EXPIRED_TOKEN); + } catch (UnsupportedJwtException e) { + throw new AuthException(UNSUPPORTED_TOKEN); + } catch (IllegalArgumentException e) { + throw new AuthException(INVALID_TOKEN); + } + } } diff --git a/src/main/java/capstone/facefriend/auth/service/AuthService.java b/src/main/java/capstone/facefriend/auth/service/AuthService.java index 9790d3e3b8..1186c26cd6 100644 --- a/src/main/java/capstone/facefriend/auth/service/AuthService.java +++ b/src/main/java/capstone/facefriend/auth/service/AuthService.java @@ -1,6 +1,7 @@ package capstone.facefriend.auth.service; +import capstone.facefriend.auth.controller.dto.TokenResponse; import capstone.facefriend.auth.domain.OAuthMember; import capstone.facefriend.auth.domain.Provider; import capstone.facefriend.auth.domain.TokenProvider; @@ -25,7 +26,7 @@ public String loginUri(String redirectUri, String provider) { } @Transactional - public String generateToken(OAuthMember oAuthMember) { + public TokenResponse generateTokens(OAuthMember oAuthMember) { Member newMember = Member.builder() .email(oAuthMember.email()) .name(oAuthMember.nickname()) @@ -34,6 +35,17 @@ public String generateToken(OAuthMember oAuthMember) { .build(); Member member = memberRepository.findByEmail(oAuthMember.email()) .orElseGet(() -> memberRepository.save(newMember)); - return tokenProvider.create(member.getId()); + + return new TokenResponse(getAccessToken(member), getRefreshToken(member)); + } + + private String getAccessToken(Member member) { + String accessToken = tokenProvider.createAccessToken(member.getId()); + return accessToken; + } + + private String getRefreshToken(Member member) { + String refreshToken = tokenProvider.createRefreshToken(member.getId()); + return refreshToken; } } diff --git a/src/main/java/capstone/facefriend/auth/service/OAuthRequester.java b/src/main/java/capstone/facefriend/auth/service/OAuthRequester.java index a2e1c1d7b1..1c2c009d11 100644 --- a/src/main/java/capstone/facefriend/auth/service/OAuthRequester.java +++ b/src/main/java/capstone/facefriend/auth/service/OAuthRequester.java @@ -2,14 +2,11 @@ import capstone.facefriend.auth.domain.OAuthMember; import capstone.facefriend.auth.domain.Provider; -import capstone.facefriend.auth.infrastructure.dto.OAuthTokenResponse; import capstone.facefriend.auth.service.dto.OAuthLoginRequest; public interface OAuthRequester { String loginUri(Provider provider, String redirectUri); - OAuthTokenResponse accessToken(OAuthLoginRequest request, String provider); - OAuthMember login(OAuthLoginRequest request, String provider); } diff --git a/src/main/java/capstone/facefriend/exception/GlobalExceptionHandler.java b/src/main/java/capstone/facefriend/exception/GlobalExceptionHandler.java index 5a2090ce60..a7db52354e 100644 --- a/src/main/java/capstone/facefriend/exception/GlobalExceptionHandler.java +++ b/src/main/java/capstone/facefriend/exception/GlobalExceptionHandler.java @@ -18,7 +18,6 @@ public class GlobalExceptionHandler { @ExceptionHandler public ResponseEntity handleException(BaseException e) { log.warn(e.getMessage(), e); - ExceptionType exceptionType = e.getExceptionType(); ExceptionStatus exceptionStatus = ExceptionStatus.from(exceptionType.status()); From 53939426f7cb6672a0909759248e050db512f343 Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Tue, 19 Mar 2024 00:39:59 +0900 Subject: [PATCH 025/265] =?UTF-8?q?fix:=20exception.status()=20null?= =?UTF-8?q?=EA=B0=92=20=EB=82=98=EC=98=A4=EB=8A=94=20=EC=97=90=EB=9F=AC=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../facefriend/auth/exception/AuthExceptionType.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/capstone/facefriend/auth/exception/AuthExceptionType.java b/src/main/java/capstone/facefriend/auth/exception/AuthExceptionType.java index 858b10df21..3f04817e68 100644 --- a/src/main/java/capstone/facefriend/auth/exception/AuthExceptionType.java +++ b/src/main/java/capstone/facefriend/auth/exception/AuthExceptionType.java @@ -8,7 +8,7 @@ public enum AuthExceptionType implements ExceptionType { INVALID_AUTH_PROVIDER(Status.UNAUTHORIZED, 2001, "지원하지 않는 로그인 플랫폼입니다."), SIGNATURE_NOT_FOUND(Status.BAD_REQUEST, 2002, "JWT 서명을 확인하지 못했습니다."), MALFORMED_TOKEN(Status.BAD_REQUEST, 2003, "토큰의 길이 및 형식이 올바르지 않습니다."), - EXPIRED_TOKEN(Status.UNAUTHORIZED, 2004, "이미 만료된 토큰입니다."), + EXPIRED_TOKEN(Status.UNAUTHORIZED, 2004, "이미 만료된 토큰입니다. 토큰을 재발급하시기 바랍니다."), UNSUPPORTED_TOKEN(Status.BAD_REQUEST, 2005, "지원되지 않는 토큰입니다."), INVALID_TOKEN(Status.BAD_REQUEST, 2006, "토큰이 유효하지 않습니다."), BAD_REQUEST_TO_PROVIDER(Status.BAD_REQUEST, 2007, "토큰이 유효하지 않습니다."), @@ -27,16 +27,16 @@ public enum AuthExceptionType implements ExceptionType { @Override public Status status() { - return null; + return status; } @Override public int exceptionCode() { - return 0; + return exceptionCode; } @Override public String message() { - return null; + return message; } } From 99b7c083fe8aff68e2a80f57a0a2c7d7ce8d8e74 Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Tue, 19 Mar 2024 00:45:07 +0900 Subject: [PATCH 026/265] =?UTF-8?q?style:=20=EB=A9=94=EC=84=9C=EB=93=9C?= =?UTF-8?q?=EB=AA=85=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../infrastructure/RestTemplateOAuthRequester.java | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/src/main/java/capstone/facefriend/auth/infrastructure/RestTemplateOAuthRequester.java b/src/main/java/capstone/facefriend/auth/infrastructure/RestTemplateOAuthRequester.java index 91fc2e0fe0..b6519ee737 100644 --- a/src/main/java/capstone/facefriend/auth/infrastructure/RestTemplateOAuthRequester.java +++ b/src/main/java/capstone/facefriend/auth/infrastructure/RestTemplateOAuthRequester.java @@ -50,22 +50,15 @@ public String loginUri(Provider provider, String redirectUri) { .toString(); } - @Override - public OAuthTokenResponse accessToken(OAuthLoginRequest request, String requestProvider) { - Provider provider = Provider.from(requestProvider); - OAuthProviderProperty property = oAuthProviderProperties.getProviderProperties(provider); - return getAccessToken(property, request); - } - @Override public OAuthMember login(OAuthLoginRequest request, String requestProvider) { Provider provider = Provider.from(requestProvider); OAuthProviderProperty property = oAuthProviderProperties.getProviderProperties(provider); - OAuthTokenResponse token = getAccessToken(property, request); - return provider.getOAuthProvider(getUserAttributes(property, token)); + OAuthTokenResponse token = getOAuthToken(property, request); + return provider.getOAuthMember(getUserAttributes(property, token)); } - private OAuthTokenResponse getAccessToken(OAuthProviderProperty property, OAuthLoginRequest loginRequest) { + private OAuthTokenResponse getOAuthToken(OAuthProviderProperty property, OAuthLoginRequest loginRequest) { HttpHeaders headers = headerWithProviderSecret(property); HttpEntity> request = new HttpEntity<>(headers); URI tokenUri = getTokenUri(property, loginRequest); From dc05c9c60af90df2b1308c1a02bbbe801ab47df6 Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Tue, 19 Mar 2024 00:46:28 +0900 Subject: [PATCH 027/265] =?UTF-8?q?style:=20=EB=A9=94=EC=84=9C=EB=93=9C?= =?UTF-8?q?=EB=AA=85=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../auth/infrastructure/RestTemplateOAuthRequester.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/capstone/facefriend/auth/infrastructure/RestTemplateOAuthRequester.java b/src/main/java/capstone/facefriend/auth/infrastructure/RestTemplateOAuthRequester.java index b6519ee737..c6f23fc60f 100644 --- a/src/main/java/capstone/facefriend/auth/infrastructure/RestTemplateOAuthRequester.java +++ b/src/main/java/capstone/facefriend/auth/infrastructure/RestTemplateOAuthRequester.java @@ -62,7 +62,7 @@ private OAuthTokenResponse getOAuthToken(OAuthProviderProperty property, OAuthLo HttpHeaders headers = headerWithProviderSecret(property); HttpEntity> request = new HttpEntity<>(headers); URI tokenUri = getTokenUri(property, loginRequest); - return requestAccessToken(tokenUri, request); + return requestOAuthToken(tokenUri, request); } private HttpHeaders headerWithProviderSecret(OAuthProviderProperty property) { @@ -81,7 +81,7 @@ private URI getTokenUri(OAuthProviderProperty property, OAuthLoginRequest reques .toUri(); } - private OAuthTokenResponse requestAccessToken(URI tokenUri, HttpEntity> request) { + private OAuthTokenResponse requestOAuthToken(URI tokenUri, HttpEntity> request) { try { return restTemplate.postForEntity(tokenUri, request, OAuthTokenResponse.class).getBody(); } catch (Exception e) { From 10d4d229fdff109d4141c1deb2ed6dde59efa16a Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Tue, 19 Mar 2024 00:47:34 +0900 Subject: [PATCH 028/265] =?UTF-8?q?style:=20=EC=BD=94=EB=93=9C=20=ED=8F=AC?= =?UTF-8?q?=EB=A7=B7=ED=8C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../facefriend/common/exception/ExceptionResponse.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/java/capstone/facefriend/common/exception/ExceptionResponse.java b/src/main/java/capstone/facefriend/common/exception/ExceptionResponse.java index 3d7c4327b3..d19e8666fe 100644 --- a/src/main/java/capstone/facefriend/common/exception/ExceptionResponse.java +++ b/src/main/java/capstone/facefriend/common/exception/ExceptionResponse.java @@ -1,4 +1,7 @@ package capstone.facefriend.common.exception; -public record ExceptionResponse(int exceptionCode, String message) { +public record ExceptionResponse( + int exceptionCode, + String message +) { } From 5543a99dc90d6bebee4389cbf23a8a592009d4b0 Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Tue, 19 Mar 2024 15:45:33 +0900 Subject: [PATCH 029/265] =?UTF-8?q?chore:=20passwordEncoder=20=EC=82=AC?= =?UTF-8?q?=EC=9A=A9=EC=9D=84=20=EC=9C=84=ED=95=9C=20=EC=9D=98=EC=A1=B4?= =?UTF-8?q?=EC=84=B1=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 3 +++ .../facefriend/{auth => member}/service/MemberService.java | 0 2 files changed, 3 insertions(+) rename src/main/java/capstone/facefriend/{auth => member}/service/MemberService.java (100%) diff --git a/build.gradle b/build.gradle index 9f999dc001..a41805c257 100644 --- a/build.gradle +++ b/build.gradle @@ -35,6 +35,9 @@ dependencies { runtimeOnly("io.jsonwebtoken:jjwt-impl:0.11.5") implementation("io.jsonwebtoken:jjwt-jackson:0.11.5") + // Spring Security + implementation 'org.springframework.security:spring-security-crypto' + // lombok compileOnly 'org.projectlombok:lombok' annotationProcessor 'org.projectlombok:lombok' diff --git a/src/main/java/capstone/facefriend/auth/service/MemberService.java b/src/main/java/capstone/facefriend/member/service/MemberService.java similarity index 100% rename from src/main/java/capstone/facefriend/auth/service/MemberService.java rename to src/main/java/capstone/facefriend/member/service/MemberService.java From 3a2b210438440697305e68845382555f57d37a84 Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Tue, 19 Mar 2024 15:48:20 +0900 Subject: [PATCH 030/265] =?UTF-8?q?feat:=20RedisDao=20=ED=81=B4=EB=9E=98?= =?UTF-8?q?=EC=8A=A4=20=EC=83=9D=EC=84=B1=20=EB=B0=8F=20=ED=86=A0=ED=81=B0?= =?UTF-8?q?=20=EC=A0=80=EC=9E=A5,=20=EC=82=AD=EC=84=B8=20=EB=A9=94?= =?UTF-8?q?=EC=84=9C=EB=93=9C=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../capstone/facefriend/redis/RedisDao.java | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 src/main/java/capstone/facefriend/redis/RedisDao.java diff --git a/src/main/java/capstone/facefriend/redis/RedisDao.java b/src/main/java/capstone/facefriend/redis/RedisDao.java new file mode 100644 index 0000000000..ab0ed2df44 --- /dev/null +++ b/src/main/java/capstone/facefriend/redis/RedisDao.java @@ -0,0 +1,55 @@ +package capstone.facefriend.redis; + + +import capstone.facefriend.member.exception.MemberException; +import capstone.facefriend.member.exception.MemberExceptionType; +import lombok.RequiredArgsConstructor; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; +import org.springframework.stereotype.Component; + +import java.util.concurrent.TimeUnit; + +import static capstone.facefriend.member.exception.MemberExceptionType.*; + +@Component +@RequiredArgsConstructor +public class RedisDao { + + private final RedisTemplate redisTemplate; + private final String SIGN_OUT_VALUE = "signOut"; + + public void setRefreshToken(String memberId, String refreshToken, long refreshTokenTime) { + redisTemplate.setValueSerializer(new Jackson2JsonRedisSerializer<>(refreshToken.getClass())); + redisTemplate.opsForValue().set(memberId, refreshToken, refreshTokenTime, TimeUnit.MINUTES); + } + + public String getRefreshToken(String memberId) { + return redisTemplate.opsForValue().get(memberId); + } + + public void deleteRefreshToken(String memberId) { + redisTemplate.delete(memberId); + } + + public boolean hasValueOfRefreshToken(String memberId) { + return Boolean.TRUE.equals(redisTemplate.hasKey(memberId)); + } + + public void setAccessTokenSignOut(String accessToken, Long minute) { + redisTemplate.setValueSerializer(new Jackson2JsonRedisSerializer<>(SIGN_OUT_VALUE.getClass())); + redisTemplate.opsForValue().set(accessToken, SIGN_OUT_VALUE, minute, TimeUnit.MINUTES); + } + + public boolean isKeyOfAccessTokenInBlackList(String accessToken) { + String signOutValue = redisTemplate.opsForValue().get(accessToken); + if (signOutValue != null && signOutValue.equals(SIGN_OUT_VALUE)) { + throw new MemberException(ALREADY_SIGN_OUT_ACCESS_TOKEN); + } + return false; + } + + public void flushAll() { + redisTemplate.getConnectionFactory().getConnection().serverCommands().flushAll(); + } +} From 08f13288e17b7879ec0ab719326c045250f2b604 Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Tue, 19 Mar 2024 15:50:16 +0900 Subject: [PATCH 031/265] =?UTF-8?q?fix:=20cacheName=20=EC=82=AC=EC=9A=A9?= =?UTF-8?q?=ED=95=98=EC=A7=80=20=EC=95=8A=EC=9C=BC=EB=AF=80=EB=A1=9C=20?= =?UTF-8?q?=ED=81=B4=EB=9E=98=EC=8A=A4=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../infrastructure/redis/CacheConfig.java | 30 ------------------- .../auth/infrastructure/redis/CacheNames.java | 7 ----- .../auth/infrastructure/redis/RedisDao.java | 4 --- 3 files changed, 41 deletions(-) delete mode 100644 src/main/java/capstone/facefriend/auth/infrastructure/redis/CacheConfig.java delete mode 100644 src/main/java/capstone/facefriend/auth/infrastructure/redis/CacheNames.java delete mode 100644 src/main/java/capstone/facefriend/auth/infrastructure/redis/RedisDao.java diff --git a/src/main/java/capstone/facefriend/auth/infrastructure/redis/CacheConfig.java b/src/main/java/capstone/facefriend/auth/infrastructure/redis/CacheConfig.java deleted file mode 100644 index 139c7a60b9..0000000000 --- a/src/main/java/capstone/facefriend/auth/infrastructure/redis/CacheConfig.java +++ /dev/null @@ -1,30 +0,0 @@ -package capstone.facefriend.auth.infrastructure.redis; - -import org.springframework.context.annotation.Configuration; -import org.springframework.data.redis.cache.RedisCacheConfiguration; -import org.springframework.data.redis.cache.RedisCacheManager; -import org.springframework.data.redis.connection.RedisConnectionFactory; - -import org.springframework.core.io.ResourceLoader; -import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer; -import org.springframework.data.redis.serializer.RedisSerializationContext; - -import java.util.HashMap; -import java.util.Map; - -@Configuration -public class CacheConfig { - -// public RedisCacheManager cacheManager(RedisConnectionFactory connectionFactory, ResourceLoader resourceLoader) { -// RedisCacheConfiguration defaultConfig = RedisCacheConfiguration -// .defaultCacheConfig() -// .disableCachingNullValues() -// .serializeValuesWith(RedisSerializationContext.SerializationPair -// .fromSerializer(new GenericJackson2JsonRedisSerializer())); -// -// Map redisCacheConfigMap = new HashMap<>(); -// -// -// -// } -} diff --git a/src/main/java/capstone/facefriend/auth/infrastructure/redis/CacheNames.java b/src/main/java/capstone/facefriend/auth/infrastructure/redis/CacheNames.java deleted file mode 100644 index 255996385b..0000000000 --- a/src/main/java/capstone/facefriend/auth/infrastructure/redis/CacheNames.java +++ /dev/null @@ -1,7 +0,0 @@ -package capstone.facefriend.auth.infrastructure.redis; - -public class CacheNames { - public static final String ALL_USERS = "CACHE_ALL_USERS"; - public static final String LOGIN_USER = "CACHE_LOGIN_USER"; - public static final String USER_BY_ACCOUNT = "CACHE_USER_BY_EMAIL"; -} diff --git a/src/main/java/capstone/facefriend/auth/infrastructure/redis/RedisDao.java b/src/main/java/capstone/facefriend/auth/infrastructure/redis/RedisDao.java deleted file mode 100644 index 7bec69eb61..0000000000 --- a/src/main/java/capstone/facefriend/auth/infrastructure/redis/RedisDao.java +++ /dev/null @@ -1,4 +0,0 @@ -package capstone.facefriend.auth.infrastructure.redis; - -public class RedisDao { -} From 4a9138fd3e66970b9cac100325a36b3bf65985b0 Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Tue, 19 Mar 2024 15:54:53 +0900 Subject: [PATCH 032/265] =?UTF-8?q?feat:=20refreshToken=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1=EC=8B=9C=20redisDao=20=EC=97=90=20=EC=A0=80=EC=9E=A5?= =?UTF-8?q?=20&=20TokenProvider=20=EC=B6=94=EC=83=81=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../facefriend/auth/domain/TokenProvider.java | 6 ++---- .../auth/infrastructure/JwtProvider.java | 18 +++++++++++++----- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/src/main/java/capstone/facefriend/auth/domain/TokenProvider.java b/src/main/java/capstone/facefriend/auth/domain/TokenProvider.java index 3424496619..3121e8516f 100644 --- a/src/main/java/capstone/facefriend/auth/domain/TokenProvider.java +++ b/src/main/java/capstone/facefriend/auth/domain/TokenProvider.java @@ -3,12 +3,10 @@ public interface TokenProvider { String createAccessToken(Long id); - String createRefreshToken(Long id); Long extractId(String token); - Boolean validateExpiration(String token); - - Boolean validateIntegrity(String token); + boolean validateExpiration(String token); + boolean validateIntegrity(String token); } diff --git a/src/main/java/capstone/facefriend/auth/infrastructure/JwtProvider.java b/src/main/java/capstone/facefriend/auth/infrastructure/JwtProvider.java index 3b3b5a5477..67163cb7e9 100644 --- a/src/main/java/capstone/facefriend/auth/infrastructure/JwtProvider.java +++ b/src/main/java/capstone/facefriend/auth/infrastructure/JwtProvider.java @@ -3,12 +3,14 @@ import capstone.facefriend.auth.domain.TokenProvider; import capstone.facefriend.auth.exception.AuthException; +import capstone.facefriend.redis.RedisDao; import io.jsonwebtoken.*; import io.jsonwebtoken.security.Keys; import io.jsonwebtoken.security.SecurityException; import jakarta.annotation.PostConstruct; +import jakarta.servlet.http.HttpServletRequest; import lombok.Getter; -import lombok.NoArgsConstructor; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; @@ -22,7 +24,7 @@ @Getter @Component -@NoArgsConstructor +@RequiredArgsConstructor @Slf4j public class JwtProvider implements TokenProvider { @@ -30,6 +32,8 @@ public class JwtProvider implements TokenProvider { private String secret; private Key key; + private final RedisDao redisDao; + private static final long ACCESS_TOKEN_EXPIRATION_TIME = 60 * 2L; // 2분 // 1000 * 60 * 60 * 3L; // 3시간 private static final long REFRESH_TOKEN_EXPIRATION_TIME = 60 * 60 * 24 * 7L; // 7일 @@ -49,7 +53,11 @@ public String createAccessToken(Long id) { public String createRefreshToken(Long id) { Claims claims = Jwts.claims(); claims.put("id", id); - return refreshToken(claims); + + String refreshToken = refreshToken(claims); + redisDao.setRefreshToken(String.valueOf(id), refreshToken, REFRESH_TOKEN_EXPIRATION_TIME); + + return refreshToken; } private String accessToken(Claims claims) { @@ -107,7 +115,7 @@ public Long extractId(String token) { } @Override - public Boolean validateExpiration(String token) { + public boolean validateExpiration(String token) { try { Date expiration = Jwts.parser() .setSigningKey(secret.getBytes()) @@ -129,7 +137,7 @@ public Boolean validateExpiration(String token) { } @Override - public Boolean validateIntegrity(String token) { + public boolean validateIntegrity(String token) { try { Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(token); return true; From ac8549b22e8ed82ac743fbfe1ad7496141df959d Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Tue, 19 Mar 2024 15:56:14 +0900 Subject: [PATCH 033/265] =?UTF-8?q?feat:=20Member=20=ED=81=B4=EB=9E=98?= =?UTF-8?q?=EC=8A=A4=EC=97=90=20phone=20=ED=95=84=EB=93=9C=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/capstone/facefriend/member/domain/Member.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/main/java/capstone/facefriend/member/domain/Member.java b/src/main/java/capstone/facefriend/member/domain/Member.java index 509b1396dd..5fc333a27d 100644 --- a/src/main/java/capstone/facefriend/member/domain/Member.java +++ b/src/main/java/capstone/facefriend/member/domain/Member.java @@ -18,14 +18,20 @@ public class Member extends BaseEntity { @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; + @Column(nullable = false, unique = true) + private String email; + @Column(nullable = false) private String name; - @Column(nullable = false, unique = true) - private String email; + @Column(nullable = false) + private String password; private String imageUrl; + @Column(unique = true) + private String phone; + @Enumerated(EnumType.STRING) @Column(nullable = false) private Role role; From bff42b86e444ad6a020363d6c90189321e8f7aa7 Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Tue, 19 Mar 2024 15:57:01 +0900 Subject: [PATCH 034/265] =?UTF-8?q?feat:=20MemberExceptionType=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../facefriend/member/exception/MemberExceptionType.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/main/java/capstone/facefriend/member/exception/MemberExceptionType.java b/src/main/java/capstone/facefriend/member/exception/MemberExceptionType.java index a5e19fbe4a..ee8bc6372c 100644 --- a/src/main/java/capstone/facefriend/member/exception/MemberExceptionType.java +++ b/src/main/java/capstone/facefriend/member/exception/MemberExceptionType.java @@ -8,7 +8,13 @@ public enum MemberExceptionType implements ExceptionType { NOT_FOUND(Status.NOT_FOUND, 3001, "회원이 없습니다."), NOT_FOUND_ROLE(Status.NOT_FOUND, 3002, "일치하는 권한이 없습니다."), INVALID_ACCESS(Status.FORBIDDEN, 3003, "본인의 계정이 아닙니다."), - UNAUTHORIZED(Status.UNAUTHORIZED, 3005, "접근 정보가 잘못되었습니다."); + UNAUTHORIZED(Status.UNAUTHORIZED, 3005, "접근 정보가 잘못되었습니다."), + DUPLICATED_EMAIL(Status.BAD_REQUEST, 3006, "이미 사용중인 이메일입니다."), + WRONG_PASSWORD(Status.BAD_REQUEST, 3007, "잘못된 비밀번호입니다."), + EXPIRED_ACCESS_TOKEN(Status.BAD_REQUEST, 3008, "만료된 액세스 토큰이므로 재발급해야 합니다."), + INVALID_ACCESS_TOKEN(Status.BAD_REQUEST, 3009, "유효하지 않은 액세스 토큰입니다."), + INVALID_REFRESH_TOKEN(Status.BAD_REQUEST, 3010, "유효하지 않은 리프레시 토큰입니다."), + ALREADY_SIGN_OUT_ACCESS_TOKEN(Status.BAD_REQUEST, 3011, "액세스 토큰이 이미 로그아웃 처리되었습니다. 재로그인하시기 바랍니다."); private final Status status; private final int exceptionCode; From 20f334df660fbc0bd3a063f8e47bacbad539551a Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Tue, 19 Mar 2024 15:58:37 +0900 Subject: [PATCH 035/265] =?UTF-8?q?fix:=20Interceptor=20=ED=81=B4=EB=9E=98?= =?UTF-8?q?=EC=8A=A4=EB=93=A4=20validation=20=EB=A1=9C=EC=A7=81=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../interceptor/LoginCheckInterceptor.java | 1 + .../interceptor/LoginInterceptor.java | 1 + .../interceptor/TokenInterceptor.java | 20 +++++++++++++++++-- 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/src/main/java/capstone/facefriend/auth/controller/interceptor/LoginCheckInterceptor.java b/src/main/java/capstone/facefriend/auth/controller/interceptor/LoginCheckInterceptor.java index ecf28afdf8..2165c6af5f 100644 --- a/src/main/java/capstone/facefriend/auth/controller/interceptor/LoginCheckInterceptor.java +++ b/src/main/java/capstone/facefriend/auth/controller/interceptor/LoginCheckInterceptor.java @@ -21,6 +21,7 @@ public class LoginCheckInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { // 액세스 토큰이 없을 경우를 위한 인터셉터 + log.info("[ LoginCheckInterceptor ]"); if (AuthenticationExtractor.extractAccessToken(request).isEmpty()) { authenticationContext.setAnonymous(); return true; diff --git a/src/main/java/capstone/facefriend/auth/controller/interceptor/LoginInterceptor.java b/src/main/java/capstone/facefriend/auth/controller/interceptor/LoginInterceptor.java index 1b3c2fba4d..9b41ea29ef 100644 --- a/src/main/java/capstone/facefriend/auth/controller/interceptor/LoginInterceptor.java +++ b/src/main/java/capstone/facefriend/auth/controller/interceptor/LoginInterceptor.java @@ -28,6 +28,7 @@ public class LoginInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { // 액세스 토큰이 존재할 경우를 위한 인터셉터 + log.info("[ LoginInterceptor ]"); String accessToken = AuthenticationExtractor.extractAccessToken(request) .orElseThrow(() -> new AuthException(UNAUTHORIZED)); diff --git a/src/main/java/capstone/facefriend/auth/controller/interceptor/TokenInterceptor.java b/src/main/java/capstone/facefriend/auth/controller/interceptor/TokenInterceptor.java index 4aeb8538a3..7f53c98fda 100644 --- a/src/main/java/capstone/facefriend/auth/controller/interceptor/TokenInterceptor.java +++ b/src/main/java/capstone/facefriend/auth/controller/interceptor/TokenInterceptor.java @@ -4,6 +4,7 @@ import capstone.facefriend.auth.domain.TokenProvider; import capstone.facefriend.auth.exception.AuthException; import capstone.facefriend.auth.exception.AuthExceptionType; +import capstone.facefriend.redis.RedisDao; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import lombok.RequiredArgsConstructor; @@ -20,13 +21,28 @@ public class TokenInterceptor implements HandlerInterceptor { private final TokenProvider tokenProvider; + private final RedisDao redisDao; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { - // 액세스 토큰이 존재하지만 만료되거나 변조되었을 경우를 위한 인터셉터 String accessToken = AuthenticationExtractor.extractAccessToken(request) .orElseThrow(() -> new AuthException(EXPIRED_TOKEN)); - return tokenProvider.validateExpiration(accessToken) && tokenProvider.validateIntegrity(accessToken); + Long memberId = tokenProvider.extractId(accessToken); + + boolean hasValueOfRefreshToken = redisDao.hasValueOfRefreshToken(String.valueOf(memberId)); + log.info("[ TokenInterceptor ] hasValueOfRefreshToken = {}", hasValueOfRefreshToken); + boolean isKeyOfAccessTokenInBlackList = redisDao.isKeyOfAccessTokenInBlackList(accessToken); + log.info("[ TokenInterceptor ] isKeyOfAccessTokenInBlackList = {}", isKeyOfAccessTokenInBlackList); + + boolean isAccessTokenAlive = tokenProvider.validateExpiration(accessToken); + log.info("[ TokenInterceptor ] isAccessTokenAlive = {}", isAccessTokenAlive); + boolean isAccessTokenIntact = tokenProvider.validateIntegrity(accessToken); + log.info("[ TokenInterceptor ] isAccessTokenIntact = {}", isAccessTokenIntact); + + return hasValueOfRefreshToken + && isAccessTokenAlive + && isAccessTokenIntact + && !isKeyOfAccessTokenInBlackList; } } From 3ca7884fd93b30bec14a0630389dc6a29c23b30a Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Tue, 19 Mar 2024 16:01:18 +0900 Subject: [PATCH 036/265] =?UTF-8?q?feat:=20MemberService=20=ED=81=B4?= =?UTF-8?q?=EB=9E=98=EC=8A=A4=20=EC=83=9D=EC=84=B1=20=EB=B0=8F=20=ED=9A=8C?= =?UTF-8?q?=EC=9B=90=EA=B0=80=EC=9E=85,=20=EB=A1=9C=EA=B7=B8=EC=9D=B8,=20?= =?UTF-8?q?=EB=A1=9C=EA=B7=B8=EC=95=84=EC=9B=83=20=EB=A9=94=EC=84=9C?= =?UTF-8?q?=EB=93=9C=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../facefriend/auth/service/AuthService.java | 18 ++-- .../member/service/MemberService.java | 95 ++++++++++++++++++- 2 files changed, 106 insertions(+), 7 deletions(-) diff --git a/src/main/java/capstone/facefriend/auth/service/AuthService.java b/src/main/java/capstone/facefriend/auth/service/AuthService.java index 1186c26cd6..5d5b7766e9 100644 --- a/src/main/java/capstone/facefriend/auth/service/AuthService.java +++ b/src/main/java/capstone/facefriend/auth/service/AuthService.java @@ -12,6 +12,8 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import static capstone.facefriend.member.domain.Role.*; + @RequiredArgsConstructor @Service @@ -21,6 +23,8 @@ public class AuthService { private final OAuthRequester oAuthRequester; private final MemberRepository memberRepository; + private static final String TEMPORARY_GOOGLE_PASSWORD = "google"; + public String loginUri(String redirectUri, String provider) { return oAuthRequester.loginUri(Provider.from(provider), redirectUri); } @@ -30,22 +34,24 @@ public TokenResponse generateTokens(OAuthMember oAuthMember) { Member newMember = Member.builder() .email(oAuthMember.email()) .name(oAuthMember.nickname()) + .password(TEMPORARY_GOOGLE_PASSWORD) .imageUrl(oAuthMember.imageUrl()) - .role(Role.USER) + .role(USER) .build(); Member member = memberRepository.findByEmail(oAuthMember.email()) .orElseGet(() -> memberRepository.save(newMember)); - return new TokenResponse(getAccessToken(member), getRefreshToken(member)); + Long memberId = member.getId(); + return new TokenResponse(getAccessToken(memberId), getRefreshToken(memberId)); } - private String getAccessToken(Member member) { - String accessToken = tokenProvider.createAccessToken(member.getId()); + private String getAccessToken(Long memberId) { + String accessToken = tokenProvider.createAccessToken(memberId); return accessToken; } - private String getRefreshToken(Member member) { - String refreshToken = tokenProvider.createRefreshToken(member.getId()); + private String getRefreshToken(Long memberId) { + String refreshToken = tokenProvider.createRefreshToken(memberId); return refreshToken; } } diff --git a/src/main/java/capstone/facefriend/member/service/MemberService.java b/src/main/java/capstone/facefriend/member/service/MemberService.java index 6b93130996..acb3bd8d21 100644 --- a/src/main/java/capstone/facefriend/member/service/MemberService.java +++ b/src/main/java/capstone/facefriend/member/service/MemberService.java @@ -1,4 +1,97 @@ -package capstone.facefriend.auth.service; +package capstone.facefriend.member.service; +import capstone.facefriend.auth.controller.dto.TokenResponse; +import capstone.facefriend.auth.controller.support.AuthMember; +import capstone.facefriend.auth.domain.TokenProvider; +import capstone.facefriend.member.domain.Member; +import capstone.facefriend.member.domain.MemberRepository; +import capstone.facefriend.member.exception.MemberException; +import capstone.facefriend.member.service.dto.SignInRequest; +import capstone.facefriend.member.service.dto.SignUpRequest; +import capstone.facefriend.redis.RedisDao; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Optional; + +import static capstone.facefriend.member.domain.Role.USER; +import static capstone.facefriend.member.exception.MemberExceptionType.*; + +@Service +@Slf4j +@RequiredArgsConstructor public class MemberService { + + private final TokenProvider tokenProvider; + private final MemberRepository memberRepository; + private final PasswordEncoder passwordEncoder; + + private final RedisDao redisDao; + + private static final String SIGN_UP_SUCCESS_MESSAGE = "회원가입 성공"; + private static final String SIGN_OUT_SUCCESS_MESSAGE = "로그아웃 성공"; + private static final Long SIGN_OUT_MINUTE = 1000 * 60 * 60 * 12L; // 12 시간 + + @Transactional + public String signUp(SignUpRequest request) { + String email = request.email(); + + Optional isPresent = memberRepository.findByEmail(email); + if (isPresent.isPresent()) { + throw new MemberException(DUPLICATED_EMAIL); + } + + String encodedPassword = passwordEncoder.encode(request.password()); + + Member member = Member.builder() + .email(email) + .password(encodedPassword) + .name(request.name()) + .role(USER) + .build(); + memberRepository.save(member); + + return SIGN_UP_SUCCESS_MESSAGE; + } + + @Transactional + public TokenResponse signIn(SignInRequest request) { + + String email = request.email(); + String password = request.password(); + + Member member = memberRepository.findByEmail(email) + .orElseThrow(() -> new MemberException(NOT_FOUND)); + + if (!passwordEncoder.matches(password, member.getPassword())) { + throw new MemberException(WRONG_PASSWORD); + } + + return generateTokens(member.getId()); + } + + @Transactional + public TokenResponse generateTokens(Long memberId) { + return new TokenResponse(getAccessToken(memberId), getRefreshToken(memberId)); + } + + private String getAccessToken(Long memberId) { + String accessToken = tokenProvider.createAccessToken(memberId); + return accessToken; + } + + private String getRefreshToken(Long memberId) { + String refreshToken = tokenProvider.createRefreshToken(memberId); + return refreshToken; + } + + @Transactional + public String signOut(@AuthMember Long memberId, String accessToken) { + redisDao.deleteRefreshToken(String.valueOf(memberId)); + redisDao.setAccessTokenSignOut(accessToken, SIGN_OUT_MINUTE); + return SIGN_OUT_SUCCESS_MESSAGE; + } } From cb50e7679c784a67b9f239d951733c1cfa4b77d8 Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Tue, 19 Mar 2024 16:03:57 +0900 Subject: [PATCH 037/265] =?UTF-8?q?style:=20=EA=B5=AC=EA=B8=80=20=EB=A1=9C?= =?UTF-8?q?=EA=B7=B8=EC=9D=B8=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EB=A9=94?= =?UTF-8?q?=EC=84=9C=EB=93=9C=20=ED=98=B8=EC=B6=9C=20url=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../capstone/facefriend/auth/controller/AuthController.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/capstone/facefriend/auth/controller/AuthController.java b/src/main/java/capstone/facefriend/auth/controller/AuthController.java index f258b4c086..77521518cb 100644 --- a/src/main/java/capstone/facefriend/auth/controller/AuthController.java +++ b/src/main/java/capstone/facefriend/auth/controller/AuthController.java @@ -37,8 +37,8 @@ public ResponseEntity login( return ResponseEntity.ok(tokens); } - @GetMapping("/test") + @GetMapping("/oauth/google/test") public ResponseEntity test() { - return ResponseEntity.ok("과연?"); + return ResponseEntity.ok("[ OAuthController ] 과연?"); } } From 773680e680a4bcd050ce74ccf47ede7fc93fca80 Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Tue, 19 Mar 2024 16:04:40 +0900 Subject: [PATCH 038/265] =?UTF-8?q?feat:=20AuthConfig=20=EC=97=90=20TokenI?= =?UTF-8?q?nterceptor=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../facefriend/auth/config/AuthConfig.java | 34 +++++++++++++++---- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/src/main/java/capstone/facefriend/auth/config/AuthConfig.java b/src/main/java/capstone/facefriend/auth/config/AuthConfig.java index f289baf164..311f8ea547 100644 --- a/src/main/java/capstone/facefriend/auth/config/AuthConfig.java +++ b/src/main/java/capstone/facefriend/auth/config/AuthConfig.java @@ -4,9 +4,13 @@ import capstone.facefriend.auth.controller.interceptor.LoginCheckInterceptor; import capstone.facefriend.auth.controller.interceptor.LoginInterceptor; import capstone.facefriend.auth.controller.interceptor.PathMatchInterceptor; +import capstone.facefriend.auth.controller.interceptor.TokenInterceptor; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.web.method.support.HandlerMethodArgumentResolver; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; @@ -14,8 +18,7 @@ import java.util.List; -import static capstone.facefriend.auth.controller.interceptor.HttpMethod.ANY; -import static capstone.facefriend.auth.controller.interceptor.HttpMethod.OPTIONS; +import static capstone.facefriend.auth.controller.interceptor.HttpMethod.*; @RequiredArgsConstructor @Configuration @@ -23,28 +26,45 @@ public class AuthConfig implements WebMvcConfigurer { private final AuthArgumentResolver authArgumentResolver; + private final LoginCheckInterceptor loginCheckInterceptor; private final LoginInterceptor loginInterceptor; + private final TokenInterceptor tokenInterceptor; + + @Bean + public PasswordEncoder passwordEncoder() { + return new BCryptPasswordEncoder(); + } @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(loginCheckInterceptor()); registry.addInterceptor(loginInterceptor()); + registry.addInterceptor(tokenInterceptor()); } private HandlerInterceptor loginCheckInterceptor() { return new PathMatchInterceptor(loginCheckInterceptor) .addExcludePathPattern("/**", OPTIONS) - .addIncludePathPattern("/test", ANY) // test 용도 - .addExcludePathPattern("/favicon.ico", ANY); + .addIncludePathPattern("/oauth/google/test", ANY) + .addIncludePathPattern("/members/test", ANY) + .addIncludePathPattern("/members/signout", DELETE); } private HandlerInterceptor loginInterceptor() { return new PathMatchInterceptor(loginInterceptor) .addExcludePathPattern("/**", OPTIONS) - .addIncludePathPattern("/test", ANY) // test 용도 - .addIncludePathPattern("/admin/**", ANY) - .addIncludePathPattern("/members/**", ANY); + .addIncludePathPattern("/oauth/google/test", ANY) + .addIncludePathPattern("/members/test", ANY) + .addIncludePathPattern("/members/signout", DELETE); + } + + private HandlerInterceptor tokenInterceptor() { + return new PathMatchInterceptor(tokenInterceptor) + .addExcludePathPattern("/**", OPTIONS) + .addIncludePathPattern("/oauth/google/test", ANY) + .addIncludePathPattern("/members/test", ANY) + .addIncludePathPattern("/members/signout", DELETE); } @Override From b843296dec770e4c506ece8204dde3295bd76da7 Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Tue, 19 Mar 2024 16:07:59 +0900 Subject: [PATCH 039/265] =?UTF-8?q?feat:=20MemberController=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84=20=EB=B0=8F=20=ED=9A=8C=EC=9B=90=EA=B0=80=EC=9E=85,?= =?UTF-8?q?=20=EB=A1=9C=EA=B7=B8=EC=9D=B8,=20=EB=A1=9C=EA=B7=B8=EC=95=84?= =?UTF-8?q?=EC=9B=83=20=EB=A9=94=EC=84=9C=EB=93=9C=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../member/controller/MemberController.java | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 src/main/java/capstone/facefriend/member/controller/MemberController.java diff --git a/src/main/java/capstone/facefriend/member/controller/MemberController.java b/src/main/java/capstone/facefriend/member/controller/MemberController.java new file mode 100644 index 0000000000..6de1a27c34 --- /dev/null +++ b/src/main/java/capstone/facefriend/member/controller/MemberController.java @@ -0,0 +1,50 @@ +package capstone.facefriend.member.controller; + + +import capstone.facefriend.auth.controller.dto.TokenResponse; +import capstone.facefriend.auth.controller.support.AuthMember; +import capstone.facefriend.auth.controller.support.AuthenticationExtractor; +import capstone.facefriend.member.exception.MemberException; +import capstone.facefriend.member.service.MemberService; +import capstone.facefriend.member.service.dto.SignInRequest; +import capstone.facefriend.member.service.dto.SignUpRequest; +import jakarta.servlet.http.HttpServletRequest; +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import static capstone.facefriend.member.exception.MemberExceptionType.UNAUTHORIZED; + +@RestController +@RequiredArgsConstructor +public class MemberController { + + private final MemberService memberService; + + @PostMapping("/members/signup") + public ResponseEntity signUp(@RequestBody SignUpRequest request) { + return ResponseEntity.ok(memberService.signUp(request)); + } + + @PostMapping("/members/signin") + public ResponseEntity signIn(@RequestBody SignInRequest request) { + return ResponseEntity.ok(memberService.signIn(request)); + } + + @GetMapping("/members/reissue") + public ResponseEntity reissueTokens(@AuthMember Long memberId) { + return ResponseEntity.ok(memberService.generateTokens(memberId)); + } + + @DeleteMapping("/members/signout") + public ResponseEntity signOut(HttpServletRequest request, @AuthMember Long memberId) { + String accessToken = AuthenticationExtractor.extractAccessToken(request) + .orElseThrow(() -> new MemberException(UNAUTHORIZED)); + return ResponseEntity.ok(memberService.signOut(memberId, accessToken)); + } + + @GetMapping("/members/test") + public ResponseEntity test() { + return ResponseEntity.ok("[ MemberController ] 과연?"); + } +} From 345f8567b446b8429bd8c8ac148e9cddfe4af00c Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Tue, 19 Mar 2024 16:09:19 +0900 Subject: [PATCH 040/265] =?UTF-8?q?feat:=20=EA=B8=B0=EB=B3=B8=20=ED=9A=8C?= =?UTF-8?q?=EC=9B=90=EA=B0=80=EC=9E=85,=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=20DT?= =?UTF-8?q?O=20=EB=A0=88=EC=BD=94=EB=93=9C=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../facefriend/member/service/dto/SignInRequest.java | 7 +++++++ .../facefriend/member/service/dto/SignUpRequest.java | 9 +++++++++ 2 files changed, 16 insertions(+) create mode 100644 src/main/java/capstone/facefriend/member/service/dto/SignInRequest.java create mode 100644 src/main/java/capstone/facefriend/member/service/dto/SignUpRequest.java diff --git a/src/main/java/capstone/facefriend/member/service/dto/SignInRequest.java b/src/main/java/capstone/facefriend/member/service/dto/SignInRequest.java new file mode 100644 index 0000000000..a1f9b59326 --- /dev/null +++ b/src/main/java/capstone/facefriend/member/service/dto/SignInRequest.java @@ -0,0 +1,7 @@ +package capstone.facefriend.member.service.dto; + +public record SignInRequest( + String email, + String password +) { +} diff --git a/src/main/java/capstone/facefriend/member/service/dto/SignUpRequest.java b/src/main/java/capstone/facefriend/member/service/dto/SignUpRequest.java new file mode 100644 index 0000000000..ccce672f8c --- /dev/null +++ b/src/main/java/capstone/facefriend/member/service/dto/SignUpRequest.java @@ -0,0 +1,9 @@ +package capstone.facefriend.member.service.dto; + +public record SignUpRequest( + String email, + String password, + String password2, + String name +) { +} From f3c2bf06b94c09c64b25536b1e4d7c39368dd66f Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Tue, 19 Mar 2024 17:25:25 +0900 Subject: [PATCH 041/265] =?UTF-8?q?chore:=20swagger=20=EB=8C=80=EC=8B=A0?= =?UTF-8?q?=20openAPI=20=EC=9D=98=EC=A1=B4=EC=84=B1=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index a41805c257..ac3fe53ecd 100644 --- a/build.gradle +++ b/build.gradle @@ -43,7 +43,7 @@ dependencies { annotationProcessor 'org.projectlombok:lombok' // swagger - implementation 'io.springfox:springfox-boot-starter:3.0.0' + implementation("org.springdoc:springdoc-openapi-starter-webmvc-ui:2.2.0") } tasks.named('test') { From 46a18e30fa55fc0816b89f6d4ec4ec7c5a973036 Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Tue, 19 Mar 2024 17:26:14 +0900 Subject: [PATCH 042/265] =?UTF-8?q?fix:=20swagger=20=EC=95=A0=EB=85=B8?= =?UTF-8?q?=ED=85=8C=EC=9D=B4=EC=85=98=20=EC=82=AD=EC=A0=9C=20&=20?= =?UTF-8?q?=EC=95=A1=EC=84=B8=EC=8A=A4=ED=86=A0=ED=81=B0=20=EB=A7=8C?= =?UTF-8?q?=EB=A3=8C=EA=B8=B0=ED=95=9C=205=EB=B6=84=EC=9C=BC=EB=A1=9C=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../capstone/facefriend/auth/controller/AuthController.java | 1 + .../capstone/facefriend/auth/infrastructure/JwtProvider.java | 3 +-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/capstone/facefriend/auth/controller/AuthController.java b/src/main/java/capstone/facefriend/auth/controller/AuthController.java index 77521518cb..6a3bada6cd 100644 --- a/src/main/java/capstone/facefriend/auth/controller/AuthController.java +++ b/src/main/java/capstone/facefriend/auth/controller/AuthController.java @@ -11,6 +11,7 @@ import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; + @RestController @RequiredArgsConstructor public class AuthController { diff --git a/src/main/java/capstone/facefriend/auth/infrastructure/JwtProvider.java b/src/main/java/capstone/facefriend/auth/infrastructure/JwtProvider.java index 67163cb7e9..6ea7950952 100644 --- a/src/main/java/capstone/facefriend/auth/infrastructure/JwtProvider.java +++ b/src/main/java/capstone/facefriend/auth/infrastructure/JwtProvider.java @@ -8,7 +8,6 @@ import io.jsonwebtoken.security.Keys; import io.jsonwebtoken.security.SecurityException; import jakarta.annotation.PostConstruct; -import jakarta.servlet.http.HttpServletRequest; import lombok.Getter; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -34,7 +33,7 @@ public class JwtProvider implements TokenProvider { private final RedisDao redisDao; - private static final long ACCESS_TOKEN_EXPIRATION_TIME = 60 * 2L; // 2분 // 1000 * 60 * 60 * 3L; // 3시간 + private static final long ACCESS_TOKEN_EXPIRATION_TIME = 60 * 5L; // 5분 // 1000 * 60 * 60 * 3L; // 3시간 private static final long REFRESH_TOKEN_EXPIRATION_TIME = 60 * 60 * 24 * 7L; // 7일 @PostConstruct From 9eef3d9aa1530b2cc6cd9a4a395dfef0e59bcb7c Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Wed, 20 Mar 2024 00:28:34 +0900 Subject: [PATCH 043/265] =?UTF-8?q?feat:=20=ED=86=A0=ED=81=B0=20=EC=9E=AC?= =?UTF-8?q?=EB=B0=9C=EA=B8=89=20=EB=A9=94=EC=84=9C=EB=93=9C=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../facefriend/auth/domain/TokenProvider.java | 3 ++ .../auth/infrastructure/JwtProvider.java | 8 ++++- .../member/controller/MemberController.java | 6 ++-- .../member/service/MemberService.java | 30 +++++++------------ .../member/service/dto/ReissueRequest.java | 7 +++++ .../capstone/facefriend/redis/RedisDao.java | 7 +---- 6 files changed, 32 insertions(+), 29 deletions(-) create mode 100644 src/main/java/capstone/facefriend/member/service/dto/ReissueRequest.java diff --git a/src/main/java/capstone/facefriend/auth/domain/TokenProvider.java b/src/main/java/capstone/facefriend/auth/domain/TokenProvider.java index 3121e8516f..7cb9ec80f0 100644 --- a/src/main/java/capstone/facefriend/auth/domain/TokenProvider.java +++ b/src/main/java/capstone/facefriend/auth/domain/TokenProvider.java @@ -1,7 +1,10 @@ package capstone.facefriend.auth.domain; +import capstone.facefriend.auth.controller.dto.TokenResponse; + public interface TokenProvider { + TokenResponse createTokens(Long id); String createAccessToken(Long id); String createRefreshToken(Long id); diff --git a/src/main/java/capstone/facefriend/auth/infrastructure/JwtProvider.java b/src/main/java/capstone/facefriend/auth/infrastructure/JwtProvider.java index 6ea7950952..6e5c82c8ed 100644 --- a/src/main/java/capstone/facefriend/auth/infrastructure/JwtProvider.java +++ b/src/main/java/capstone/facefriend/auth/infrastructure/JwtProvider.java @@ -1,6 +1,7 @@ package capstone.facefriend.auth.infrastructure; +import capstone.facefriend.auth.controller.dto.TokenResponse; import capstone.facefriend.auth.domain.TokenProvider; import capstone.facefriend.auth.exception.AuthException; import capstone.facefriend.redis.RedisDao; @@ -41,6 +42,12 @@ private void init() { key = Keys.hmacShaKeyFor(secret.getBytes()); } + public TokenResponse createTokens(Long memberId) { + String accessToken = createAccessToken(memberId); + String refreshToken = createRefreshToken(memberId); + return new TokenResponse(accessToken, refreshToken); + } + @Override public String createAccessToken(Long id) { Claims claims = Jwts.claims(); @@ -55,7 +62,6 @@ public String createRefreshToken(Long id) { String refreshToken = refreshToken(claims); redisDao.setRefreshToken(String.valueOf(id), refreshToken, REFRESH_TOKEN_EXPIRATION_TIME); - return refreshToken; } diff --git a/src/main/java/capstone/facefriend/member/controller/MemberController.java b/src/main/java/capstone/facefriend/member/controller/MemberController.java index 6de1a27c34..8e8af78d0e 100644 --- a/src/main/java/capstone/facefriend/member/controller/MemberController.java +++ b/src/main/java/capstone/facefriend/member/controller/MemberController.java @@ -6,6 +6,7 @@ import capstone.facefriend.auth.controller.support.AuthenticationExtractor; import capstone.facefriend.member.exception.MemberException; import capstone.facefriend.member.service.MemberService; +import capstone.facefriend.member.service.dto.ReissueRequest; import capstone.facefriend.member.service.dto.SignInRequest; import capstone.facefriend.member.service.dto.SignUpRequest; import jakarta.servlet.http.HttpServletRequest; @@ -32,8 +33,9 @@ public ResponseEntity signIn(@RequestBody SignInRequest request) } @GetMapping("/members/reissue") - public ResponseEntity reissueTokens(@AuthMember Long memberId) { - return ResponseEntity.ok(memberService.generateTokens(memberId)); + public ResponseEntity reissueTokens(@RequestBody ReissueRequest request, @AuthMember Long memberId) { + String refreshToken = request.refreshToken(); + return ResponseEntity.ok(memberService.reissueTokens(memberId, refreshToken)); } @DeleteMapping("/members/signout") diff --git a/src/main/java/capstone/facefriend/member/service/MemberService.java b/src/main/java/capstone/facefriend/member/service/MemberService.java index acb3bd8d21..8131d90e3b 100644 --- a/src/main/java/capstone/facefriend/member/service/MemberService.java +++ b/src/main/java/capstone/facefriend/member/service/MemberService.java @@ -1,7 +1,6 @@ package capstone.facefriend.member.service; import capstone.facefriend.auth.controller.dto.TokenResponse; -import capstone.facefriend.auth.controller.support.AuthMember; import capstone.facefriend.auth.domain.TokenProvider; import capstone.facefriend.member.domain.Member; import capstone.facefriend.member.domain.MemberRepository; @@ -59,7 +58,6 @@ public String signUp(SignUpRequest request) { @Transactional public TokenResponse signIn(SignInRequest request) { - String email = request.email(); String password = request.password(); @@ -69,29 +67,21 @@ public TokenResponse signIn(SignInRequest request) { if (!passwordEncoder.matches(password, member.getPassword())) { throw new MemberException(WRONG_PASSWORD); } - - return generateTokens(member.getId()); - } - - @Transactional - public TokenResponse generateTokens(Long memberId) { - return new TokenResponse(getAccessToken(memberId), getRefreshToken(memberId)); - } - - private String getAccessToken(Long memberId) { - String accessToken = tokenProvider.createAccessToken(memberId); - return accessToken; - } - - private String getRefreshToken(Long memberId) { - String refreshToken = tokenProvider.createRefreshToken(memberId); - return refreshToken; + return tokenProvider.createTokens(member.getId()); } @Transactional - public String signOut(@AuthMember Long memberId, String accessToken) { + public String signOut(Long memberId, String accessToken) { redisDao.deleteRefreshToken(String.valueOf(memberId)); redisDao.setAccessTokenSignOut(accessToken, SIGN_OUT_MINUTE); return SIGN_OUT_SUCCESS_MESSAGE; } + + public TokenResponse reissueTokens(Long memberId, String refreshTokenInput) { + String refreshToken = redisDao.getRefreshToken(String.valueOf(memberId)); + if (!refreshToken.equals(refreshTokenInput)) { + throw new MemberException(INVALID_REFRESH_TOKEN); + } + return tokenProvider.createTokens(memberId); + } } diff --git a/src/main/java/capstone/facefriend/member/service/dto/ReissueRequest.java b/src/main/java/capstone/facefriend/member/service/dto/ReissueRequest.java new file mode 100644 index 0000000000..f36fd9b747 --- /dev/null +++ b/src/main/java/capstone/facefriend/member/service/dto/ReissueRequest.java @@ -0,0 +1,7 @@ +package capstone.facefriend.member.service.dto; + +public record ReissueRequest( + String accessToken, + String refreshToken +) { +} diff --git a/src/main/java/capstone/facefriend/redis/RedisDao.java b/src/main/java/capstone/facefriend/redis/RedisDao.java index ab0ed2df44..866fb0a6bd 100644 --- a/src/main/java/capstone/facefriend/redis/RedisDao.java +++ b/src/main/java/capstone/facefriend/redis/RedisDao.java @@ -2,7 +2,6 @@ import capstone.facefriend.member.exception.MemberException; -import capstone.facefriend.member.exception.MemberExceptionType; import lombok.RequiredArgsConstructor; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; @@ -10,7 +9,7 @@ import java.util.concurrent.TimeUnit; -import static capstone.facefriend.member.exception.MemberExceptionType.*; +import static capstone.facefriend.member.exception.MemberExceptionType.ALREADY_SIGN_OUT_ACCESS_TOKEN; @Component @RequiredArgsConstructor @@ -32,10 +31,6 @@ public void deleteRefreshToken(String memberId) { redisTemplate.delete(memberId); } - public boolean hasValueOfRefreshToken(String memberId) { - return Boolean.TRUE.equals(redisTemplate.hasKey(memberId)); - } - public void setAccessTokenSignOut(String accessToken, Long minute) { redisTemplate.setValueSerializer(new Jackson2JsonRedisSerializer<>(SIGN_OUT_VALUE.getClass())); redisTemplate.opsForValue().set(accessToken, SIGN_OUT_VALUE, minute, TimeUnit.MINUTES); From 1d87b8e0e277d010fb320c7cff00d1b43be7968e Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Wed, 20 Mar 2024 00:29:35 +0900 Subject: [PATCH 044/265] =?UTF-8?q?style:=20=EC=A3=BC=EC=84=9D=20=EB=B0=8F?= =?UTF-8?q?=20=EB=A1=9C=EA=B7=B8=EC=B6=9C=EB=A0=A5=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../interceptor/LoginCheckInterceptor.java | 2 -- .../controller/interceptor/LoginInterceptor.java | 2 -- .../controller/interceptor/TokenInterceptor.java | 13 +------------ 3 files changed, 1 insertion(+), 16 deletions(-) diff --git a/src/main/java/capstone/facefriend/auth/controller/interceptor/LoginCheckInterceptor.java b/src/main/java/capstone/facefriend/auth/controller/interceptor/LoginCheckInterceptor.java index 2165c6af5f..a5c2521fcf 100644 --- a/src/main/java/capstone/facefriend/auth/controller/interceptor/LoginCheckInterceptor.java +++ b/src/main/java/capstone/facefriend/auth/controller/interceptor/LoginCheckInterceptor.java @@ -20,8 +20,6 @@ public class LoginCheckInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { - // 액세스 토큰이 없을 경우를 위한 인터셉터 - log.info("[ LoginCheckInterceptor ]"); if (AuthenticationExtractor.extractAccessToken(request).isEmpty()) { authenticationContext.setAnonymous(); return true; diff --git a/src/main/java/capstone/facefriend/auth/controller/interceptor/LoginInterceptor.java b/src/main/java/capstone/facefriend/auth/controller/interceptor/LoginInterceptor.java index 9b41ea29ef..2496df7dc5 100644 --- a/src/main/java/capstone/facefriend/auth/controller/interceptor/LoginInterceptor.java +++ b/src/main/java/capstone/facefriend/auth/controller/interceptor/LoginInterceptor.java @@ -27,8 +27,6 @@ public class LoginInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { - // 액세스 토큰이 존재할 경우를 위한 인터셉터 - log.info("[ LoginInterceptor ]"); String accessToken = AuthenticationExtractor.extractAccessToken(request) .orElseThrow(() -> new AuthException(UNAUTHORIZED)); diff --git a/src/main/java/capstone/facefriend/auth/controller/interceptor/TokenInterceptor.java b/src/main/java/capstone/facefriend/auth/controller/interceptor/TokenInterceptor.java index 7f53c98fda..244f609e60 100644 --- a/src/main/java/capstone/facefriend/auth/controller/interceptor/TokenInterceptor.java +++ b/src/main/java/capstone/facefriend/auth/controller/interceptor/TokenInterceptor.java @@ -28,21 +28,10 @@ public boolean preHandle(HttpServletRequest request, HttpServletResponse respons String accessToken = AuthenticationExtractor.extractAccessToken(request) .orElseThrow(() -> new AuthException(EXPIRED_TOKEN)); - Long memberId = tokenProvider.extractId(accessToken); - - boolean hasValueOfRefreshToken = redisDao.hasValueOfRefreshToken(String.valueOf(memberId)); - log.info("[ TokenInterceptor ] hasValueOfRefreshToken = {}", hasValueOfRefreshToken); boolean isKeyOfAccessTokenInBlackList = redisDao.isKeyOfAccessTokenInBlackList(accessToken); - log.info("[ TokenInterceptor ] isKeyOfAccessTokenInBlackList = {}", isKeyOfAccessTokenInBlackList); - boolean isAccessTokenAlive = tokenProvider.validateExpiration(accessToken); - log.info("[ TokenInterceptor ] isAccessTokenAlive = {}", isAccessTokenAlive); boolean isAccessTokenIntact = tokenProvider.validateIntegrity(accessToken); - log.info("[ TokenInterceptor ] isAccessTokenIntact = {}", isAccessTokenIntact); - return hasValueOfRefreshToken - && isAccessTokenAlive - && isAccessTokenIntact - && !isKeyOfAccessTokenInBlackList; + return isAccessTokenAlive && isAccessTokenIntact && !isKeyOfAccessTokenInBlackList; } } From 4a5b7873edadc1a14754b68da6b883f44052225b Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Wed, 20 Mar 2024 00:30:16 +0900 Subject: [PATCH 045/265] =?UTF-8?q?style:=20MemberExceptionType=20?= =?UTF-8?q?=EB=A9=94=EC=8B=9C=EC=A7=80=20=EB=B6=80=EC=97=B0=EC=84=A4?= =?UTF-8?q?=EB=AA=85=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../facefriend/member/exception/MemberExceptionType.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/capstone/facefriend/member/exception/MemberExceptionType.java b/src/main/java/capstone/facefriend/member/exception/MemberExceptionType.java index ee8bc6372c..77d70ac088 100644 --- a/src/main/java/capstone/facefriend/member/exception/MemberExceptionType.java +++ b/src/main/java/capstone/facefriend/member/exception/MemberExceptionType.java @@ -9,11 +9,11 @@ public enum MemberExceptionType implements ExceptionType { NOT_FOUND_ROLE(Status.NOT_FOUND, 3002, "일치하는 권한이 없습니다."), INVALID_ACCESS(Status.FORBIDDEN, 3003, "본인의 계정이 아닙니다."), UNAUTHORIZED(Status.UNAUTHORIZED, 3005, "접근 정보가 잘못되었습니다."), - DUPLICATED_EMAIL(Status.BAD_REQUEST, 3006, "이미 사용중인 이메일입니다."), + DUPLICATED_EMAIL(Status.BAD_REQUEST, 3006, "이미 사용 중인 이메일입니다."), WRONG_PASSWORD(Status.BAD_REQUEST, 3007, "잘못된 비밀번호입니다."), EXPIRED_ACCESS_TOKEN(Status.BAD_REQUEST, 3008, "만료된 액세스 토큰이므로 재발급해야 합니다."), - INVALID_ACCESS_TOKEN(Status.BAD_REQUEST, 3009, "유효하지 않은 액세스 토큰입니다."), - INVALID_REFRESH_TOKEN(Status.BAD_REQUEST, 3010, "유효하지 않은 리프레시 토큰입니다."), + INVALID_ACCESS_TOKEN(Status.BAD_REQUEST, 3009, "유효하지 않은 액세스 토큰이므로 재발급해야 합니다."), + INVALID_REFRESH_TOKEN(Status.BAD_REQUEST, 3010, "유효하지 않은 리프레시 토큰입니다. 토큰 재발급이 불가능합니다."), ALREADY_SIGN_OUT_ACCESS_TOKEN(Status.BAD_REQUEST, 3011, "액세스 토큰이 이미 로그아웃 처리되었습니다. 재로그인하시기 바랍니다."); private final Status status; From 15c79fab047a6f8f8517fad3651069988bfc6848 Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Wed, 20 Mar 2024 16:59:04 +0900 Subject: [PATCH 046/265] =?UTF-8?q?feat:=20=ED=86=A0=ED=81=B0=EC=9E=AC?= =?UTF-8?q?=EB=B0=9C=EA=B8=89=20=EC=9D=B8=ED=84=B0=EC=85=89=ED=84=B0?= =?UTF-8?q?=EC=99=80=20=ED=86=A0=ED=81=B0=EB=B8=94=EB=9E=99=EB=A6=AC?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=ED=99=95=EC=9D=B8=20=EC=9D=B8=ED=84=B0?= =?UTF-8?q?=EC=85=89=ED=84=B0=20=EC=88=98=EC=A0=95=20=EB=B0=8F=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../facefriend/auth/config/AuthConfig.java | 37 +++++++++------- .../interceptor/LoginInterceptor.java | 6 +-- .../TokenBlackListInterceptor.java | 25 +++++++++++ .../interceptor/TokenInterceptor.java | 37 ---------------- .../interceptor/TokenReissueInterceptor.java | 31 +++++++++++++ .../facefriend/auth/domain/TokenProvider.java | 4 +- .../auth/infrastructure/JwtProvider.java | 44 ++++++------------- .../member/controller/MemberController.java | 5 ++- .../member/service/dto/ReissueRequest.java | 1 - .../capstone/facefriend/redis/RedisDao.java | 4 +- 10 files changed, 101 insertions(+), 93 deletions(-) create mode 100644 src/main/java/capstone/facefriend/auth/controller/interceptor/TokenBlackListInterceptor.java delete mode 100644 src/main/java/capstone/facefriend/auth/controller/interceptor/TokenInterceptor.java create mode 100644 src/main/java/capstone/facefriend/auth/controller/interceptor/TokenReissueInterceptor.java diff --git a/src/main/java/capstone/facefriend/auth/config/AuthConfig.java b/src/main/java/capstone/facefriend/auth/config/AuthConfig.java index 311f8ea547..dd06e6d207 100644 --- a/src/main/java/capstone/facefriend/auth/config/AuthConfig.java +++ b/src/main/java/capstone/facefriend/auth/config/AuthConfig.java @@ -1,10 +1,7 @@ package capstone.facefriend.auth.config; import capstone.facefriend.auth.controller.AuthArgumentResolver; -import capstone.facefriend.auth.controller.interceptor.LoginCheckInterceptor; -import capstone.facefriend.auth.controller.interceptor.LoginInterceptor; -import capstone.facefriend.auth.controller.interceptor.PathMatchInterceptor; -import capstone.facefriend.auth.controller.interceptor.TokenInterceptor; +import capstone.facefriend.auth.controller.interceptor.*; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.context.annotation.Bean; @@ -29,7 +26,8 @@ public class AuthConfig implements WebMvcConfigurer { private final LoginCheckInterceptor loginCheckInterceptor; private final LoginInterceptor loginInterceptor; - private final TokenInterceptor tokenInterceptor; + private final TokenReissueInterceptor tokenReissueInterceptor; + private final TokenBlackListInterceptor tokenBlackListInterceptor; @Bean public PasswordEncoder passwordEncoder() { @@ -40,31 +38,40 @@ public PasswordEncoder passwordEncoder() { public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(loginCheckInterceptor()); registry.addInterceptor(loginInterceptor()); - registry.addInterceptor(tokenInterceptor()); + registry.addInterceptor(tokenReissueInterceptor()); + registry.addInterceptor(tokenBlackListInterceptor()); } private HandlerInterceptor loginCheckInterceptor() { return new PathMatchInterceptor(loginCheckInterceptor) .addExcludePathPattern("/**", OPTIONS) - .addIncludePathPattern("/oauth/google/test", ANY) - .addIncludePathPattern("/members/test", ANY) + .addExcludePathPattern("/members/reissue", POST) + .addIncludePathPattern("/oauth/google/test", GET) + .addIncludePathPattern("/members/test", GET) .addIncludePathPattern("/members/signout", DELETE); } + // ExpiredJwtException 를 catch 하고 토큰 재발급을 할 수 있도록 예외를 터트리는 인터셉터 private HandlerInterceptor loginInterceptor() { return new PathMatchInterceptor(loginInterceptor) .addExcludePathPattern("/**", OPTIONS) - .addIncludePathPattern("/oauth/google/test", ANY) - .addIncludePathPattern("/members/test", ANY) + .addExcludePathPattern("/members/reissue", POST) // 토큰 만료 시에는 해당 요청을 가로채지 않아야 합니다. + .addIncludePathPattern("/oauth/google/test", GET) + .addIncludePathPattern("/members/test", GET) .addIncludePathPattern("/members/signout", DELETE); } - private HandlerInterceptor tokenInterceptor() { - return new PathMatchInterceptor(tokenInterceptor) + // ExpiredJwtException 를 catch 하고 memberId 를 추출하기 위한 인터셉터 + private HandlerInterceptor tokenReissueInterceptor() { + return new PathMatchInterceptor(tokenReissueInterceptor) .addExcludePathPattern("/**", OPTIONS) - .addIncludePathPattern("/oauth/google/test", ANY) - .addIncludePathPattern("/members/test", ANY) - .addIncludePathPattern("/members/signout", DELETE); + .addIncludePathPattern("/members/reissue", POST); // 토큰 만료 시에는 해당 요청을 가로채야 합니다. + } + + private HandlerInterceptor tokenBlackListInterceptor() { + return new PathMatchInterceptor(tokenBlackListInterceptor) + .addExcludePathPattern("/**", OPTIONS) + .addIncludePathPattern("/members/test", GET); } @Override diff --git a/src/main/java/capstone/facefriend/auth/controller/interceptor/LoginInterceptor.java b/src/main/java/capstone/facefriend/auth/controller/interceptor/LoginInterceptor.java index 2496df7dc5..58ab4ca72d 100644 --- a/src/main/java/capstone/facefriend/auth/controller/interceptor/LoginInterceptor.java +++ b/src/main/java/capstone/facefriend/auth/controller/interceptor/LoginInterceptor.java @@ -4,8 +4,6 @@ import capstone.facefriend.auth.controller.support.AuthenticationExtractor; import capstone.facefriend.auth.domain.TokenProvider; import capstone.facefriend.auth.exception.AuthException; -import capstone.facefriend.auth.exception.AuthExceptionType; -import capstone.facefriend.member.exception.MemberExceptionType; import capstone.facefriend.redis.RedisDao; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; @@ -23,7 +21,7 @@ public class LoginInterceptor implements HandlerInterceptor { private final TokenProvider tokenProvider; private final AuthenticationContext authenticationContext; - private final TokenInterceptor tokenInterceptor; + private final TokenReissueInterceptor tokenReissueInterceptor; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { @@ -33,6 +31,6 @@ public boolean preHandle(HttpServletRequest request, HttpServletResponse respons Long memberId = tokenProvider.extractId(accessToken); authenticationContext.setAuthentication(memberId); - return tokenInterceptor.preHandle(request, response, handler); + return tokenReissueInterceptor.preHandle(request, response, handler); } } diff --git a/src/main/java/capstone/facefriend/auth/controller/interceptor/TokenBlackListInterceptor.java b/src/main/java/capstone/facefriend/auth/controller/interceptor/TokenBlackListInterceptor.java new file mode 100644 index 0000000000..eced5d9ed6 --- /dev/null +++ b/src/main/java/capstone/facefriend/auth/controller/interceptor/TokenBlackListInterceptor.java @@ -0,0 +1,25 @@ +package capstone.facefriend.auth.controller.interceptor; + +import capstone.facefriend.auth.controller.support.AuthenticationExtractor; +import capstone.facefriend.redis.RedisDao; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; +import org.springframework.web.servlet.HandlerInterceptor; + + +@RequiredArgsConstructor +@Component +@Slf4j +public class TokenBlackListInterceptor implements HandlerInterceptor { + + private final RedisDao redisDao; + + @Override + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { + String accessToken = AuthenticationExtractor.extractAccessToken(request).get(); + return !redisDao.isKeyOfAccessTokenInBlackList(accessToken); // 액세스 토큰이 블랙리스트에 등록되었다면 false 반환해야 합니다. + } +} \ No newline at end of file diff --git a/src/main/java/capstone/facefriend/auth/controller/interceptor/TokenInterceptor.java b/src/main/java/capstone/facefriend/auth/controller/interceptor/TokenInterceptor.java deleted file mode 100644 index 244f609e60..0000000000 --- a/src/main/java/capstone/facefriend/auth/controller/interceptor/TokenInterceptor.java +++ /dev/null @@ -1,37 +0,0 @@ -package capstone.facefriend.auth.controller.interceptor; - -import capstone.facefriend.auth.controller.support.AuthenticationExtractor; -import capstone.facefriend.auth.domain.TokenProvider; -import capstone.facefriend.auth.exception.AuthException; -import capstone.facefriend.auth.exception.AuthExceptionType; -import capstone.facefriend.redis.RedisDao; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Component; -import org.springframework.web.servlet.HandlerInterceptor; - -import static capstone.facefriend.auth.exception.AuthExceptionType.*; - - -@RequiredArgsConstructor -@Component -@Slf4j -public class TokenInterceptor implements HandlerInterceptor { - - private final TokenProvider tokenProvider; - private final RedisDao redisDao; - - @Override - public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { - String accessToken = AuthenticationExtractor.extractAccessToken(request) - .orElseThrow(() -> new AuthException(EXPIRED_TOKEN)); - - boolean isKeyOfAccessTokenInBlackList = redisDao.isKeyOfAccessTokenInBlackList(accessToken); - boolean isAccessTokenAlive = tokenProvider.validateExpiration(accessToken); - boolean isAccessTokenIntact = tokenProvider.validateIntegrity(accessToken); - - return isAccessTokenAlive && isAccessTokenIntact && !isKeyOfAccessTokenInBlackList; - } -} diff --git a/src/main/java/capstone/facefriend/auth/controller/interceptor/TokenReissueInterceptor.java b/src/main/java/capstone/facefriend/auth/controller/interceptor/TokenReissueInterceptor.java new file mode 100644 index 0000000000..d274caa37b --- /dev/null +++ b/src/main/java/capstone/facefriend/auth/controller/interceptor/TokenReissueInterceptor.java @@ -0,0 +1,31 @@ +package capstone.facefriend.auth.controller.interceptor; + +import capstone.facefriend.auth.controller.support.AuthenticationContext; +import capstone.facefriend.auth.controller.support.AuthenticationExtractor; +import capstone.facefriend.auth.domain.TokenProvider; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; +import org.springframework.web.servlet.HandlerInterceptor; + + +@RequiredArgsConstructor +@Component +@Slf4j +public class TokenReissueInterceptor implements HandlerInterceptor { + + private final TokenProvider tokenProvider; + private final AuthenticationContext authenticationContext; + + @Override + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { + String accessToken = AuthenticationExtractor.extractAccessToken(request).get(); + + Long memberId = tokenProvider.extractIdIgnoringExpiration(accessToken); + authenticationContext.setAuthentication(memberId); + + return true; + } +} diff --git a/src/main/java/capstone/facefriend/auth/domain/TokenProvider.java b/src/main/java/capstone/facefriend/auth/domain/TokenProvider.java index 7cb9ec80f0..6827b08cf6 100644 --- a/src/main/java/capstone/facefriend/auth/domain/TokenProvider.java +++ b/src/main/java/capstone/facefriend/auth/domain/TokenProvider.java @@ -9,7 +9,5 @@ public interface TokenProvider { String createRefreshToken(Long id); Long extractId(String token); - - boolean validateExpiration(String token); - boolean validateIntegrity(String token); + Long extractIdIgnoringExpiration(String token); } diff --git a/src/main/java/capstone/facefriend/auth/infrastructure/JwtProvider.java b/src/main/java/capstone/facefriend/auth/infrastructure/JwtProvider.java index 6e5c82c8ed..45be2e957a 100644 --- a/src/main/java/capstone/facefriend/auth/infrastructure/JwtProvider.java +++ b/src/main/java/capstone/facefriend/auth/infrastructure/JwtProvider.java @@ -34,7 +34,7 @@ public class JwtProvider implements TokenProvider { private final RedisDao redisDao; - private static final long ACCESS_TOKEN_EXPIRATION_TIME = 60 * 5L; // 5분 // 1000 * 60 * 60 * 3L; // 3시간 + private static final long ACCESS_TOKEN_EXPIRATION_TIME = 60 * 2L; // 5분 // 1000 * 60 * 60 * 3L; // 3시간 private static final long REFRESH_TOKEN_EXPIRATION_TIME = 60 * 60 * 24 * 7L; // 7일 @PostConstruct @@ -95,23 +95,23 @@ private Date accessTokenExpiredAt() { private Date refreshTokenExpiredAt() { LocalDateTime now = LocalDateTime.now(); - return Date.from(now.plusHours(REFRESH_TOKEN_EXPIRATION_TIME).atZone(ZoneId.systemDefault()).toInstant()); + return Date.from(now.plusSeconds(REFRESH_TOKEN_EXPIRATION_TIME).atZone(ZoneId.systemDefault()).toInstant()); } @Override public Long extractId(String token) { try { - return Jwts.parser() + Claims claims = Jwts.parser() .setSigningKey(secret.getBytes()) .parseClaimsJws(token) - .getBody() - .get("id", Long.class); + .getBody(); + return claims.get("id", Long.class); + } catch (ExpiredJwtException e) { + throw new AuthException(EXPIRED_TOKEN); } catch (SecurityException e) { throw new AuthException(SIGNATURE_NOT_FOUND); } catch (MalformedJwtException e) { throw new AuthException(MALFORMED_TOKEN); - } catch (ExpiredJwtException e) { - throw new AuthException(EXPIRED_TOKEN); } catch (UnsupportedJwtException e) { throw new AuthException(UNSUPPORTED_TOKEN); } catch (IllegalArgumentException e) { @@ -120,38 +120,20 @@ public Long extractId(String token) { } @Override - public boolean validateExpiration(String token) { + public Long extractIdIgnoringExpiration(String token) { try { - Date expiration = Jwts.parser() + Claims claims = Jwts.parser() .setSigningKey(secret.getBytes()) .parseClaimsJws(token) - .getBody() - .getExpiration(); - return expiration.after(new Date()); - } catch (SecurityException e) { - throw new AuthException(SIGNATURE_NOT_FOUND); - } catch (MalformedJwtException e) { - throw new AuthException(MALFORMED_TOKEN); + .getBody(); + return claims.get("id", Long.class); } catch (ExpiredJwtException e) { - throw new AuthException(EXPIRED_TOKEN); - } catch (UnsupportedJwtException e) { - throw new AuthException(UNSUPPORTED_TOKEN); - } catch (IllegalArgumentException e) { - throw new AuthException(INVALID_TOKEN); - } - } - - @Override - public boolean validateIntegrity(String token) { - try { - Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(token); - return true; + Claims expiredClaims = e.getClaims(); // 만료된 토큰일 경우 catch 후 id 를 반환해야만 합니다. + return expiredClaims.get("id", Long.class); } catch (SecurityException e) { throw new AuthException(SIGNATURE_NOT_FOUND); } catch (MalformedJwtException e) { throw new AuthException(MALFORMED_TOKEN); - } catch (ExpiredJwtException e) { - throw new AuthException(EXPIRED_TOKEN); } catch (UnsupportedJwtException e) { throw new AuthException(UNSUPPORTED_TOKEN); } catch (IllegalArgumentException e) { diff --git a/src/main/java/capstone/facefriend/member/controller/MemberController.java b/src/main/java/capstone/facefriend/member/controller/MemberController.java index 8e8af78d0e..8b25bd4619 100644 --- a/src/main/java/capstone/facefriend/member/controller/MemberController.java +++ b/src/main/java/capstone/facefriend/member/controller/MemberController.java @@ -11,11 +11,13 @@ import capstone.facefriend.member.service.dto.SignUpRequest; import jakarta.servlet.http.HttpServletRequest; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import static capstone.facefriend.member.exception.MemberExceptionType.UNAUTHORIZED; +@Slf4j @RestController @RequiredArgsConstructor public class MemberController { @@ -32,9 +34,10 @@ public ResponseEntity signIn(@RequestBody SignInRequest request) return ResponseEntity.ok(memberService.signIn(request)); } - @GetMapping("/members/reissue") + @PostMapping("/members/reissue") public ResponseEntity reissueTokens(@RequestBody ReissueRequest request, @AuthMember Long memberId) { String refreshToken = request.refreshToken(); + return ResponseEntity.ok(memberService.reissueTokens(memberId, refreshToken)); } diff --git a/src/main/java/capstone/facefriend/member/service/dto/ReissueRequest.java b/src/main/java/capstone/facefriend/member/service/dto/ReissueRequest.java index f36fd9b747..cbb5c1c34a 100644 --- a/src/main/java/capstone/facefriend/member/service/dto/ReissueRequest.java +++ b/src/main/java/capstone/facefriend/member/service/dto/ReissueRequest.java @@ -1,7 +1,6 @@ package capstone.facefriend.member.service.dto; public record ReissueRequest( - String accessToken, String refreshToken ) { } diff --git a/src/main/java/capstone/facefriend/redis/RedisDao.java b/src/main/java/capstone/facefriend/redis/RedisDao.java index 866fb0a6bd..1370f4a3af 100644 --- a/src/main/java/capstone/facefriend/redis/RedisDao.java +++ b/src/main/java/capstone/facefriend/redis/RedisDao.java @@ -3,6 +3,7 @@ import capstone.facefriend.member.exception.MemberException; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; import org.springframework.stereotype.Component; @@ -12,6 +13,7 @@ import static capstone.facefriend.member.exception.MemberExceptionType.ALREADY_SIGN_OUT_ACCESS_TOKEN; @Component +@Slf4j @RequiredArgsConstructor public class RedisDao { @@ -38,7 +40,7 @@ public void setAccessTokenSignOut(String accessToken, Long minute) { public boolean isKeyOfAccessTokenInBlackList(String accessToken) { String signOutValue = redisTemplate.opsForValue().get(accessToken); - if (signOutValue != null && signOutValue.equals(SIGN_OUT_VALUE)) { + if (signOutValue.equals(SIGN_OUT_VALUE)) { throw new MemberException(ALREADY_SIGN_OUT_ACCESS_TOKEN); } return false; From 424a0491f9cd0b76ceb790be85638ee2ef81bfe3 Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Thu, 21 Mar 2024 16:48:31 +0900 Subject: [PATCH 047/265] =?UTF-8?q?feat:=20=EB=B3=B8=EC=9D=B8=EC=9D=B8?= =?UTF-8?q?=EC=A6=9D=20=EC=9D=B8=ED=84=B0=EC=85=89=ED=84=B0=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84=EA=B3=BC=20=EC=9D=B8=ED=84=B0=EC=85=89=ED=84=B0=20?= =?UTF-8?q?=ED=98=B8=EC=B6=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 4 ++ .../facefriend/auth/config/AuthConfig.java | 64 +++++++++++++++---- .../interceptor/LoginCheckInterceptor.java | 2 +- .../interceptor/LoginInterceptor.java | 3 +- .../interceptor/TokenReissueInterceptor.java | 4 +- .../interceptor/VerificationInterceptor.java | 52 +++++++++++++++ 6 files changed, 113 insertions(+), 16 deletions(-) create mode 100644 src/main/java/capstone/facefriend/auth/mail/controller/interceptor/VerificationInterceptor.java diff --git a/build.gradle b/build.gradle index ac3fe53ecd..46e45615d4 100644 --- a/build.gradle +++ b/build.gradle @@ -44,6 +44,10 @@ dependencies { // swagger implementation("org.springdoc:springdoc-openapi-starter-webmvc-ui:2.2.0") + + // 메일 인증 + implementation group: 'org.springframework.boot', name: 'spring-boot-starter-mail', version: '3.0.5' + } tasks.named('test') { diff --git a/src/main/java/capstone/facefriend/auth/config/AuthConfig.java b/src/main/java/capstone/facefriend/auth/config/AuthConfig.java index dd06e6d207..aa682e2b35 100644 --- a/src/main/java/capstone/facefriend/auth/config/AuthConfig.java +++ b/src/main/java/capstone/facefriend/auth/config/AuthConfig.java @@ -2,6 +2,7 @@ import capstone.facefriend.auth.controller.AuthArgumentResolver; import capstone.facefriend.auth.controller.interceptor.*; +import capstone.facefriend.auth.mail.controller.interceptor.VerificationInterceptor; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.context.annotation.Bean; @@ -28,6 +29,7 @@ public class AuthConfig implements WebMvcConfigurer { private final LoginInterceptor loginInterceptor; private final TokenReissueInterceptor tokenReissueInterceptor; private final TokenBlackListInterceptor tokenBlackListInterceptor; + private final VerificationInterceptor verificationInterceptor; @Bean public PasswordEncoder passwordEncoder() { @@ -40,38 +42,76 @@ public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(loginInterceptor()); registry.addInterceptor(tokenReissueInterceptor()); registry.addInterceptor(tokenBlackListInterceptor()); + registry.addInterceptor(verificationInterceptor()); } + // 회원가입하지 않은 사용자 id 를 -1 로 저장하기 위한 인터셉터입니다. + // * 해당 인터셉터는 로그인 시점을 포함한 그 이후의 메서드들을 호출할 때 사용됩니다. private HandlerInterceptor loginCheckInterceptor() { return new PathMatchInterceptor(loginCheckInterceptor) .addExcludePathPattern("/**", OPTIONS) - .addExcludePathPattern("/members/reissue", POST) - .addIncludePathPattern("/oauth/google/test", GET) - .addIncludePathPattern("/members/test", GET) - .addIncludePathPattern("/members/signout", DELETE); + .addExcludePathPattern("/reissue", POST) // 토큰 만료 시에는 해당 요청을 가로채지 않아야 합니다. + + // 구글 유저 + .addIncludePathPattern("/oauth/google/login", POST) + // 일반 유저 + .addIncludePathPattern("/members/signin", POST) + // 구글 유저, 일반 유저 공통 로직 + .addIncludePathPattern("/signout", DELETE) + .addIncludePathPattern("/test", GET); } - // ExpiredJwtException 를 catch 하고 토큰 재발급을 할 수 있도록 예외를 터트리는 인터셉터 + // 회원가입한 사용자의 id 를 추출하기 위한 인터셉터입니다. + // (1) 토큰이 만료된 경우 (2) 토큰이 위조된 경우 + // (3) 토큰이 블랙리스트에 등록된 경우 (4) 로그인하는 경우 + // 예외를 터트려 재로그인 또는 토큰 재발급을 강제하기 위한 인터셉터입니다. + // * 해당 인터셉터는 로그인 시점을 포함한 그 이후의 메서드들을 호출할 때 사용됩니다. private HandlerInterceptor loginInterceptor() { return new PathMatchInterceptor(loginInterceptor) .addExcludePathPattern("/**", OPTIONS) - .addExcludePathPattern("/members/reissue", POST) // 토큰 만료 시에는 해당 요청을 가로채지 않아야 합니다. - .addIncludePathPattern("/oauth/google/test", GET) - .addIncludePathPattern("/members/test", GET) - .addIncludePathPattern("/members/signout", DELETE); + .addExcludePathPattern("/reissue", POST) // 토큰 만료 시에는 해당 요청을 가로채지 않아야 합니다. + + // 구글 유저, 일반 유저 공통 로직 + .addIncludePathPattern("/signout", DELETE) + .addIncludePathPattern("/test", GET); } - // ExpiredJwtException 를 catch 하고 memberId 를 추출하기 위한 인터셉터 + // 회원가입한 사용자의 id 를 추출하기 위한 인터셉터입니다. + // (1) 토큰이 만료된 경우 (2) 토큰이 위조된 경우 + // (3) 토큰이 블랙리스트에 등록된 경우 (4) 로그인하는 경우 + // 예외를 터트리지 않고 실제로 토큰 재발급을 해주기 위해 사용됩니다. + // * 해당 인터셉터는 토큰 재발급 시에만 사용되어야만 합니다. + // * 요청을 보낼 때 실려있는 토큰은 유효하지 않은 토큰(위 3가지 경우)인 것이 정상입니다. private HandlerInterceptor tokenReissueInterceptor() { return new PathMatchInterceptor(tokenReissueInterceptor) .addExcludePathPattern("/**", OPTIONS) - .addIncludePathPattern("/members/reissue", POST); // 토큰 만료 시에는 해당 요청을 가로채야 합니다. + + // 구글 유저, 일반 유저 공통 로직 + .addIncludePathPattern("/reissue", POST); // 토큰 만료 시에는 해당 요청을 가로채야 합니다. } + // 재로그인 또는 토큰 재발급 이후, 토큰을 Authorization header 에 제대로 갈아끼웠는지를 확인하기 위한 인터셉터입니다. + // * 해당 인터셉터는 로그인 이후의 메서드들을 호출할 때 사용됩니다. + // * 최초 로그인 시에는 토큰이 없는게 정상입니다. 로그인 이후에 토큰을 발급받기 때문입니다. private HandlerInterceptor tokenBlackListInterceptor() { return new PathMatchInterceptor(tokenBlackListInterceptor) .addExcludePathPattern("/**", OPTIONS) - .addIncludePathPattern("/members/test", GET); + + // 구글 유저, 일반 유저 공통 로직 + .addIncludePathPattern("/test", GET) + ; + } + + // 서비스를 이용하기 위해서 본인인증 여부를 판단하고 본인인증하지 않았다면 이를 강제하기 위한 인터셉터입니다. + // * 해당 인터셉터는 로그인 이후의 메서드들 호출할 때 사용됩니다, + private HandlerInterceptor verificationInterceptor() { + return new PathMatchInterceptor(verificationInterceptor) + .addExcludePathPattern("/**", OPTIONS) + + // 구글 유저, 일반 유저 공통 로직 + .addExcludePathPattern("/mail/*", POST) + .addExcludePathPattern("/mail/*", GET) + .addIncludePathPattern("/test", GET); } @Override diff --git a/src/main/java/capstone/facefriend/auth/controller/interceptor/LoginCheckInterceptor.java b/src/main/java/capstone/facefriend/auth/controller/interceptor/LoginCheckInterceptor.java index a5c2521fcf..49352ad12a 100644 --- a/src/main/java/capstone/facefriend/auth/controller/interceptor/LoginCheckInterceptor.java +++ b/src/main/java/capstone/facefriend/auth/controller/interceptor/LoginCheckInterceptor.java @@ -21,7 +21,7 @@ public class LoginCheckInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { if (AuthenticationExtractor.extractAccessToken(request).isEmpty()) { - authenticationContext.setAnonymous(); + authenticationContext.setNotAuthenticated(); return true; } return loginInterceptor.preHandle(request, response, handler); diff --git a/src/main/java/capstone/facefriend/auth/controller/interceptor/LoginInterceptor.java b/src/main/java/capstone/facefriend/auth/controller/interceptor/LoginInterceptor.java index 58ab4ca72d..a9ba204f58 100644 --- a/src/main/java/capstone/facefriend/auth/controller/interceptor/LoginInterceptor.java +++ b/src/main/java/capstone/facefriend/auth/controller/interceptor/LoginInterceptor.java @@ -4,7 +4,6 @@ import capstone.facefriend.auth.controller.support.AuthenticationExtractor; import capstone.facefriend.auth.domain.TokenProvider; import capstone.facefriend.auth.exception.AuthException; -import capstone.facefriend.redis.RedisDao; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import lombok.RequiredArgsConstructor; @@ -12,7 +11,7 @@ import org.springframework.stereotype.Component; import org.springframework.web.servlet.HandlerInterceptor; -import static capstone.facefriend.auth.exception.AuthExceptionType.*; +import static capstone.facefriend.auth.exception.AuthExceptionType.UNAUTHORIZED; @RequiredArgsConstructor @Component diff --git a/src/main/java/capstone/facefriend/auth/controller/interceptor/TokenReissueInterceptor.java b/src/main/java/capstone/facefriend/auth/controller/interceptor/TokenReissueInterceptor.java index d274caa37b..f3905518c5 100644 --- a/src/main/java/capstone/facefriend/auth/controller/interceptor/TokenReissueInterceptor.java +++ b/src/main/java/capstone/facefriend/auth/controller/interceptor/TokenReissueInterceptor.java @@ -3,6 +3,7 @@ import capstone.facefriend.auth.controller.support.AuthenticationContext; import capstone.facefriend.auth.controller.support.AuthenticationExtractor; import capstone.facefriend.auth.domain.TokenProvider; +import capstone.facefriend.auth.mail.controller.interceptor.VerificationInterceptor; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import lombok.RequiredArgsConstructor; @@ -18,6 +19,7 @@ public class TokenReissueInterceptor implements HandlerInterceptor { private final TokenProvider tokenProvider; private final AuthenticationContext authenticationContext; + private final VerificationInterceptor verificationInterceptor; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { @@ -26,6 +28,6 @@ public boolean preHandle(HttpServletRequest request, HttpServletResponse respons Long memberId = tokenProvider.extractIdIgnoringExpiration(accessToken); authenticationContext.setAuthentication(memberId); - return true; + return verificationInterceptor.preHandle(request, response, handler); } } diff --git a/src/main/java/capstone/facefriend/auth/mail/controller/interceptor/VerificationInterceptor.java b/src/main/java/capstone/facefriend/auth/mail/controller/interceptor/VerificationInterceptor.java new file mode 100644 index 0000000000..2c09f0d0a2 --- /dev/null +++ b/src/main/java/capstone/facefriend/auth/mail/controller/interceptor/VerificationInterceptor.java @@ -0,0 +1,52 @@ +package capstone.facefriend.auth.mail.controller.interceptor; + +import capstone.facefriend.auth.controller.support.AuthenticationExtractor; +import capstone.facefriend.auth.domain.TokenProvider; +import capstone.facefriend.auth.exception.AuthException; +import capstone.facefriend.auth.mail.exception.VerificationException; +import capstone.facefriend.auth.mail.exception.VerificationExceptionType; +import capstone.facefriend.auth.mail.support.VerificationContext; +import capstone.facefriend.member.domain.Member; +import capstone.facefriend.member.domain.MemberRepository; +import capstone.facefriend.member.exception.MemberException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; +import org.springframework.web.servlet.HandlerInterceptor; + +import static capstone.facefriend.auth.exception.AuthExceptionType.UNAUTHORIZED; +import static capstone.facefriend.auth.mail.exception.VerificationExceptionType.*; +import static capstone.facefriend.member.exception.MemberExceptionType.NOT_FOUND; + + +@RequiredArgsConstructor +@Component +@Slf4j +public class VerificationInterceptor implements HandlerInterceptor { + + private final TokenProvider tokenProvider; + private final VerificationContext verificationContext; + private final MemberRepository memberRepository; + + @Override + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { + String accessToken = AuthenticationExtractor.extractAccessToken(request) + .orElseThrow(() -> new AuthException(UNAUTHORIZED)); + + Long memberId = tokenProvider.extractId(accessToken); + + Member member = memberRepository.findById(memberId) + .orElseThrow(() -> new MemberException(NOT_FOUND)); + + Boolean isVerified = member.getIsVerified(); + verificationContext.setIsVerified(isVerified); + + if (isVerified == null) { + throw new VerificationException(NOT_VERIFIED); + } + + return true; + } +} From bac0833e75839913e412427c0c578e1a09ce4e0d Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Thu, 21 Mar 2024 16:49:59 +0900 Subject: [PATCH 048/265] =?UTF-8?q?feat:=20=EC=BD=94=EB=93=9C=EB=B0=9C?= =?UTF-8?q?=EC=86=A1,=20=EB=B3=B8=EC=9D=B8=EC=9D=B8=EC=A6=9D=20=EC=98=88?= =?UTF-8?q?=EC=99=B8=20=ED=81=B4=EB=9E=98=EC=8A=A4=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../auth/mail/exception/MailException.java | 11 ++++++ .../mail/exception/MailExceptionType.java | 35 +++++++++++++++++++ .../mail/exception/VerificationException.java | 11 ++++++ .../exception/VerificationExceptionType.java | 35 +++++++++++++++++++ 4 files changed, 92 insertions(+) create mode 100644 src/main/java/capstone/facefriend/auth/mail/exception/MailException.java create mode 100644 src/main/java/capstone/facefriend/auth/mail/exception/MailExceptionType.java create mode 100644 src/main/java/capstone/facefriend/auth/mail/exception/VerificationException.java create mode 100644 src/main/java/capstone/facefriend/auth/mail/exception/VerificationExceptionType.java diff --git a/src/main/java/capstone/facefriend/auth/mail/exception/MailException.java b/src/main/java/capstone/facefriend/auth/mail/exception/MailException.java new file mode 100644 index 0000000000..d534a98ee3 --- /dev/null +++ b/src/main/java/capstone/facefriend/auth/mail/exception/MailException.java @@ -0,0 +1,11 @@ +package capstone.facefriend.auth.mail.exception; + +import capstone.facefriend.common.exception.BaseException; +import capstone.facefriend.common.exception.ExceptionType; + +public class MailException extends BaseException { + + public MailException(ExceptionType exceptionType) { + super(exceptionType); + } +} diff --git a/src/main/java/capstone/facefriend/auth/mail/exception/MailExceptionType.java b/src/main/java/capstone/facefriend/auth/mail/exception/MailExceptionType.java new file mode 100644 index 0000000000..84917d691f --- /dev/null +++ b/src/main/java/capstone/facefriend/auth/mail/exception/MailExceptionType.java @@ -0,0 +1,35 @@ +package capstone.facefriend.auth.mail.exception; + +import capstone.facefriend.common.exception.ExceptionType; +import capstone.facefriend.common.exception.Status; + +public enum MailExceptionType implements ExceptionType { + + UNABLE_TO_SEND_MAIL(Status.BAD_REQUEST, 1001, "인증 메일을 보낼 수 없습니다."), + NO_SUCH_ALGORITHM(Status.SERVER_ERROR,1002,"인증 코드 생성에 실패했습니다."); + + private final Status status; + private final int exceptionCode; + private final String message; + + MailExceptionType(Status status, int exceptionCode, String message) { + this.status = status; + this.exceptionCode = exceptionCode; + this.message = message; + } + + @Override + public Status status() { + return status; + } + + @Override + public int exceptionCode() { + return exceptionCode; + } + + @Override + public String message() { + return message; + } +} diff --git a/src/main/java/capstone/facefriend/auth/mail/exception/VerificationException.java b/src/main/java/capstone/facefriend/auth/mail/exception/VerificationException.java new file mode 100644 index 0000000000..0cb4605cf0 --- /dev/null +++ b/src/main/java/capstone/facefriend/auth/mail/exception/VerificationException.java @@ -0,0 +1,11 @@ +package capstone.facefriend.auth.mail.exception; + +import capstone.facefriend.common.exception.BaseException; +import capstone.facefriend.common.exception.ExceptionType; + +public class VerificationException extends BaseException { + + public VerificationException(ExceptionType exceptionType) { + super(exceptionType); + } +} diff --git a/src/main/java/capstone/facefriend/auth/mail/exception/VerificationExceptionType.java b/src/main/java/capstone/facefriend/auth/mail/exception/VerificationExceptionType.java new file mode 100644 index 0000000000..c695b33415 --- /dev/null +++ b/src/main/java/capstone/facefriend/auth/mail/exception/VerificationExceptionType.java @@ -0,0 +1,35 @@ +package capstone.facefriend.auth.mail.exception; + +import capstone.facefriend.common.exception.ExceptionType; +import capstone.facefriend.common.exception.Status; + +public enum VerificationExceptionType implements ExceptionType { + + NOT_VERIFIED(Status.UNAUTHORIZED, 4001, "서비스를 사용하기 위해선 본인인증이 필요합니다.") + ; + + private final Status status; + private final int exceptionCode; + private final String message; + + VerificationExceptionType(Status status, int exceptionCode, String message) { + this.status = status; + this.exceptionCode = exceptionCode; + this.message = message; + } + + @Override + public Status status() { + return status; + } + + @Override + public int exceptionCode() { + return exceptionCode; + } + + @Override + public String message() { + return message; + } +} From cfbeb074d7adef989153b6da554fa5e4ec13235c Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Thu, 21 Mar 2024 16:51:25 +0900 Subject: [PATCH 049/265] =?UTF-8?q?feat:=20=EB=B3=B8=EC=9D=B8=EC=9D=B8?= =?UTF-8?q?=EC=A6=9D=20annotation,=20argumentResolver=20=ED=81=B4=EB=9E=98?= =?UTF-8?q?=EC=8A=A4=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mail/support/VerificationContext.java | 34 +++++++++++++++++++ .../auth/mail/support/VerifiedMember.java | 11 ++++++ 2 files changed, 45 insertions(+) create mode 100644 src/main/java/capstone/facefriend/auth/mail/support/VerificationContext.java create mode 100644 src/main/java/capstone/facefriend/auth/mail/support/VerifiedMember.java diff --git a/src/main/java/capstone/facefriend/auth/mail/support/VerificationContext.java b/src/main/java/capstone/facefriend/auth/mail/support/VerificationContext.java new file mode 100644 index 0000000000..b5d6efa8a8 --- /dev/null +++ b/src/main/java/capstone/facefriend/auth/mail/support/VerificationContext.java @@ -0,0 +1,34 @@ +package capstone.facefriend.auth.mail.support; + + +import capstone.facefriend.auth.mail.exception.VerificationException; +import capstone.facefriend.auth.mail.exception.VerificationExceptionType; +import org.springframework.stereotype.Component; +import org.springframework.web.context.annotation.RequestScope; + +import java.util.Objects; + +import static capstone.facefriend.auth.mail.exception.VerificationExceptionType.*; + +@RequestScope +@Component +public class VerificationContext { + + private static final boolean IS_NOT_VERIFIED = false; + private Boolean isVerified; + + public void setIsVerified(Boolean isVerified) { + this.isVerified = isVerified; + } + + public Boolean getIsVerified() { + if (Objects.isNull(this.isVerified)) { + throw new VerificationException(NOT_VERIFIED); + } + return isVerified; + } + + public void setNotVerified() { + this.isVerified = IS_NOT_VERIFIED; + } +} diff --git a/src/main/java/capstone/facefriend/auth/mail/support/VerifiedMember.java b/src/main/java/capstone/facefriend/auth/mail/support/VerifiedMember.java new file mode 100644 index 0000000000..94c89b759b --- /dev/null +++ b/src/main/java/capstone/facefriend/auth/mail/support/VerifiedMember.java @@ -0,0 +1,11 @@ +package capstone.facefriend.auth.mail.support; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.PARAMETER) +@Retention(RetentionPolicy.RUNTIME) +public @interface VerifiedMember { +} From 2767351f37653e8bca908ad393b308a17e7c976d Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Thu, 21 Mar 2024 16:53:40 +0900 Subject: [PATCH 050/265] =?UTF-8?q?feat:=20=EC=BD=94=EB=93=9C=EB=B0=9C?= =?UTF-8?q?=EC=86=A1=20config,=20controller,=20dto,=20service=20=ED=81=B4?= =?UTF-8?q?=EB=9E=98=EC=8A=A4=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mail/VerificationArgumentResolver.java | 33 ++++++++ .../auth/mail/config/MailConfig.java | 70 +++++++++++++++++ .../auth/mail/controller/MailController.java | 60 +++++++++++++++ .../dto/MailVerificationResponse.java | 6 ++ .../auth/mail/service/MailService.java | 77 +++++++++++++++++++ 5 files changed, 246 insertions(+) create mode 100644 src/main/java/capstone/facefriend/auth/mail/VerificationArgumentResolver.java create mode 100644 src/main/java/capstone/facefriend/auth/mail/config/MailConfig.java create mode 100644 src/main/java/capstone/facefriend/auth/mail/controller/MailController.java create mode 100644 src/main/java/capstone/facefriend/auth/mail/controller/dto/MailVerificationResponse.java create mode 100644 src/main/java/capstone/facefriend/auth/mail/service/MailService.java diff --git a/src/main/java/capstone/facefriend/auth/mail/VerificationArgumentResolver.java b/src/main/java/capstone/facefriend/auth/mail/VerificationArgumentResolver.java new file mode 100644 index 0000000000..ec1e32a087 --- /dev/null +++ b/src/main/java/capstone/facefriend/auth/mail/VerificationArgumentResolver.java @@ -0,0 +1,33 @@ +package capstone.facefriend.auth.mail; + +import capstone.facefriend.auth.mail.support.VerifiedMember; +import capstone.facefriend.auth.mail.support.VerificationContext; +import lombok.RequiredArgsConstructor; +import org.springframework.core.MethodParameter; +import org.springframework.stereotype.Component; +import org.springframework.web.bind.support.WebDataBinderFactory; +import org.springframework.web.context.request.NativeWebRequest; +import org.springframework.web.method.support.HandlerMethodArgumentResolver; +import org.springframework.web.method.support.ModelAndViewContainer; + +@RequiredArgsConstructor +@Component +public class VerificationArgumentResolver implements HandlerMethodArgumentResolver { + + private final VerificationContext verificationContext; + + @Override + public boolean supportsParameter(MethodParameter parameter) { + return parameter.hasParameterAnnotation(VerifiedMember.class) && + parameter.getParameterType().equals(Boolean.class); + } + + @Override + public Object resolveArgument( + MethodParameter parameter, + ModelAndViewContainer mavContainer, + NativeWebRequest webRequest, + WebDataBinderFactory binderFactory) { + return verificationContext.getIsVerified(); + } +} diff --git a/src/main/java/capstone/facefriend/auth/mail/config/MailConfig.java b/src/main/java/capstone/facefriend/auth/mail/config/MailConfig.java new file mode 100644 index 0000000000..033d05114b --- /dev/null +++ b/src/main/java/capstone/facefriend/auth/mail/config/MailConfig.java @@ -0,0 +1,70 @@ +package capstone.facefriend.auth.mail.config; + + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.mail.javamail.JavaMailSender; +import org.springframework.mail.javamail.JavaMailSenderImpl; + +import java.util.Properties; + +@Configuration +public class MailConfig { + + @Value("${spring.mail.host}") + private String host; + + @Value("${spring.mail.port}") + private int port; + + @Value("${spring.mail.username}") + private String username; + + @Value("${spring.mail.password}") + private String password; + + @Value("${spring.mail.properties.mail.smtp.auth}") + private boolean auth; + + @Value("${spring.mail.properties.mail.smtp.starttls.enable}") + private boolean starttlsEnable; + + @Value("${spring.mail.properties.mail.smtp.starttls.required}") + private boolean starttlsRequired; + + @Value("${spring.mail.properties.mail.smtp.connectiontimeout}") + private int connectionTimeout; + + @Value("${spring.mail.properties.mail.smtp.timeout}") + private int timeout; + + @Value("${spring.mail.properties.mail.smtp.writetimeout}") + private int writeTimeout; + + @Bean + public JavaMailSender javaMailSender() { + JavaMailSenderImpl mailSender = new JavaMailSenderImpl(); + mailSender.setHost(host); + mailSender.setPort(port); + mailSender.setUsername(username); + mailSender.setPassword(password); + mailSender.setDefaultEncoding("UTF-8"); + mailSender.setJavaMailProperties(getMailProperties()); + + return mailSender; + } + + private Properties getMailProperties() { + Properties properties = new Properties(); + properties.put("mail.smtp.auth", auth); + properties.put("mail.smtp.starttls.enable", starttlsEnable); + properties.put("mail.smtp.starttls.required", starttlsRequired); + properties.put("mail.smtp.connectiontimeout", connectionTimeout); + properties.put("mail.smtp.timeout", timeout); + properties.put("mail.smtp.writetimeout", writeTimeout); + + return properties; + } + +} diff --git a/src/main/java/capstone/facefriend/auth/mail/controller/MailController.java b/src/main/java/capstone/facefriend/auth/mail/controller/MailController.java new file mode 100644 index 0000000000..17c042f431 --- /dev/null +++ b/src/main/java/capstone/facefriend/auth/mail/controller/MailController.java @@ -0,0 +1,60 @@ +package capstone.facefriend.auth.mail.controller; + +import capstone.facefriend.auth.controller.support.AuthMember; +import capstone.facefriend.auth.controller.support.AuthenticationExtractor; +import capstone.facefriend.auth.domain.TokenProvider; +import capstone.facefriend.auth.exception.AuthException; +import capstone.facefriend.auth.exception.AuthExceptionType; +import capstone.facefriend.auth.mail.controller.dto.MailVerificationResponse; +import capstone.facefriend.auth.mail.service.MailService; +import capstone.facefriend.member.domain.Member; +import capstone.facefriend.member.domain.MemberRepository; +import capstone.facefriend.member.exception.MemberException; +import capstone.facefriend.member.exception.MemberExceptionType; +import jakarta.servlet.http.HttpServletRequest; +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import static capstone.facefriend.auth.exception.AuthExceptionType.*; +import static capstone.facefriend.auth.exception.AuthExceptionType.UNAUTHORIZED; +import static capstone.facefriend.member.exception.MemberExceptionType.*; + +@RestController +@RequiredArgsConstructor +public class MailController { + + private final MailService mailService; + private final MemberRepository memberRepository; + private final TokenProvider tokenProvider; + + @PostMapping("/mail/send-code") + public ResponseEntity sendCode( + @RequestParam("mail") String mail + ) { + return ResponseEntity.ok(mailService.sendCode(mail)); + } + + @GetMapping("/mail/verify-code") + public ResponseEntity submitCode( + @RequestParam("mail") String mail, + @RequestParam("code") String code, + HttpServletRequest request + ) { + Boolean isVerified = mailService.verifyCode(mail, code); + String accessToken = AuthenticationExtractor.extractAccessToken(request) + .orElseThrow(() -> new AuthException(UNAUTHORIZED)); + + Long memberId = tokenProvider.extractId(accessToken); + + Member member = memberRepository.findById(memberId) + .orElseThrow(() -> new MemberException(NOT_FOUND)); + + member.setIsVerified(isVerified); + + return ResponseEntity.ok(new MailVerificationResponse(isVerified)); + } +} diff --git a/src/main/java/capstone/facefriend/auth/mail/controller/dto/MailVerificationResponse.java b/src/main/java/capstone/facefriend/auth/mail/controller/dto/MailVerificationResponse.java new file mode 100644 index 0000000000..e8c81ee6ae --- /dev/null +++ b/src/main/java/capstone/facefriend/auth/mail/controller/dto/MailVerificationResponse.java @@ -0,0 +1,6 @@ +package capstone.facefriend.auth.mail.controller.dto; + +public record MailVerificationResponse( + boolean isVerified +) { +} diff --git a/src/main/java/capstone/facefriend/auth/mail/service/MailService.java b/src/main/java/capstone/facefriend/auth/mail/service/MailService.java new file mode 100644 index 0000000000..e0e37f5ef0 --- /dev/null +++ b/src/main/java/capstone/facefriend/auth/mail/service/MailService.java @@ -0,0 +1,77 @@ +package capstone.facefriend.auth.mail.service; + +import capstone.facefriend.auth.mail.exception.MailException; +import capstone.facefriend.member.domain.MemberRepository; +import capstone.facefriend.redis.RedisDao; +import jakarta.transaction.Transactional; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.mail.SimpleMailMessage; +import org.springframework.mail.javamail.JavaMailSender; +import org.springframework.stereotype.Service; + +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.util.Random; + +import static capstone.facefriend.auth.mail.exception.MailExceptionType.NO_SUCH_ALGORITHM; +import static capstone.facefriend.auth.mail.exception.MailExceptionType.UNABLE_TO_SEND_MAIL; + +@Slf4j +@Service +@Transactional +@RequiredArgsConstructor +public class MailService { + + @Value("${spring.mail.auth-code-expiration-millis}") + private long authCodeExpirationMillis; + private static final String MAIL_TITLE = "[ FACE FRIEND ] 본인 인증을 위한 코드가 도착했어요! \uD83D\uDE0E"; + private static final String MAIL_SUCCESS = "이메일로 코드를 전송했습니다."; + + private final RedisDao redisDao; + private final JavaMailSender mailSender; + + public String sendCode(String mail) { + String code = createCode(); // 코드 생성 + sendMail(mail, MAIL_TITLE, code); // 메일로 코드 보내기 + redisDao.setCode(mail, code, authCodeExpirationMillis); // 코드 레디스 저장 + return MAIL_SUCCESS; + } + + private String createCode() { + int length = 6; + try { + Random random = SecureRandom.getInstanceStrong(); + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < length; i++) { + sb.append(random.nextInt(10)); + } + return sb.toString(); + } catch (NoSuchAlgorithmException e) { + throw new MailException(NO_SUCH_ALGORITHM); + } + } + + private void sendMail(String mail, String title, String text) { + SimpleMailMessage emailForm = createMailForm(mail, title, text); + try { + mailSender.send(emailForm); + } catch (RuntimeException e) { + throw new MailException(UNABLE_TO_SEND_MAIL); + } + } + + private SimpleMailMessage createMailForm(String mail, String title, String text) { + SimpleMailMessage message = new SimpleMailMessage(); + message.setTo(mail); + message.setSubject(title); + message.setText(text); + return message; + } + + public Boolean verifyCode(String mail, String codeInput) { + String code = redisDao.getCode(mail); + return code.equals(codeInput); + } +} From 32d80e5de484a3724ef8f6c0564d8dcb67f1d913 Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Thu, 21 Mar 2024 16:54:28 +0900 Subject: [PATCH 051/265] =?UTF-8?q?feat:=20=EB=B3=B8=EC=9D=B8=EC=9D=B8?= =?UTF-8?q?=EC=A6=9D=20=EC=BD=94=EB=93=9C=20=EB=A0=88=EB=94=94=EC=8A=A4=20?= =?UTF-8?q?=EC=A0=80=EC=9E=A5=20=EB=A1=9C=EC=A7=81=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/capstone/facefriend/redis/RedisDao.java | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/main/java/capstone/facefriend/redis/RedisDao.java b/src/main/java/capstone/facefriend/redis/RedisDao.java index 1370f4a3af..cfa17400f6 100644 --- a/src/main/java/capstone/facefriend/redis/RedisDao.java +++ b/src/main/java/capstone/facefriend/redis/RedisDao.java @@ -7,7 +7,6 @@ import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; import org.springframework.stereotype.Component; - import java.util.concurrent.TimeUnit; import static capstone.facefriend.member.exception.MemberExceptionType.ALREADY_SIGN_OUT_ACCESS_TOKEN; @@ -21,7 +20,7 @@ public class RedisDao { private final String SIGN_OUT_VALUE = "signOut"; public void setRefreshToken(String memberId, String refreshToken, long refreshTokenTime) { - redisTemplate.setValueSerializer(new Jackson2JsonRedisSerializer<>(refreshToken.getClass())); + redisTemplate.setValueSerializer(new Jackson2JsonRedisSerializer<>(String.class)); redisTemplate.opsForValue().set(memberId, refreshToken, refreshTokenTime, TimeUnit.MINUTES); } @@ -34,7 +33,7 @@ public void deleteRefreshToken(String memberId) { } public void setAccessTokenSignOut(String accessToken, Long minute) { - redisTemplate.setValueSerializer(new Jackson2JsonRedisSerializer<>(SIGN_OUT_VALUE.getClass())); + redisTemplate.setValueSerializer(new Jackson2JsonRedisSerializer<>(String.class)); redisTemplate.opsForValue().set(accessToken, SIGN_OUT_VALUE, minute, TimeUnit.MINUTES); } @@ -46,6 +45,15 @@ public boolean isKeyOfAccessTokenInBlackList(String accessToken) { return false; } + public void setCode(String mail, String code, long codeTime) { + redisTemplate.setValueSerializer(new Jackson2JsonRedisSerializer<>(String.class)); + redisTemplate.opsForValue().set(mail, code, codeTime, TimeUnit.MINUTES); + } + + public String getCode(String mail) { + return redisTemplate.opsForValue().get(mail); + } + public void flushAll() { redisTemplate.getConnectionFactory().getConnection().serverCommands().flushAll(); } From cd11ac1123a4dab619246e171e6253cac9a043fb Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Thu, 21 Mar 2024 16:57:27 +0900 Subject: [PATCH 052/265] =?UTF-8?q?style:=20=EB=A9=94=EC=84=9C=EB=93=9C?= =?UTF-8?q?=EB=AA=85=20=EC=88=98=EC=A0=95=20=EB=B0=8F=20=EC=A3=BC=EC=84=9D?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../facefriend/auth/controller/AuthArgumentResolver.java | 2 +- .../facefriend/auth/controller/AuthController.java | 5 ----- .../auth/controller/support/AuthenticationContext.java | 9 +++++---- .../facefriend/auth/infrastructure/JwtProvider.java | 6 ++++-- 4 files changed, 10 insertions(+), 12 deletions(-) diff --git a/src/main/java/capstone/facefriend/auth/controller/AuthArgumentResolver.java b/src/main/java/capstone/facefriend/auth/controller/AuthArgumentResolver.java index 53c043190c..bb7d90d55a 100644 --- a/src/main/java/capstone/facefriend/auth/controller/AuthArgumentResolver.java +++ b/src/main/java/capstone/facefriend/auth/controller/AuthArgumentResolver.java @@ -28,6 +28,6 @@ public Object resolveArgument( ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) { - return authenticationContext.getPrincipal(); + return authenticationContext.getAuthentication(); } } diff --git a/src/main/java/capstone/facefriend/auth/controller/AuthController.java b/src/main/java/capstone/facefriend/auth/controller/AuthController.java index 6a3bada6cd..5902fd63c1 100644 --- a/src/main/java/capstone/facefriend/auth/controller/AuthController.java +++ b/src/main/java/capstone/facefriend/auth/controller/AuthController.java @@ -37,9 +37,4 @@ public ResponseEntity login( TokenResponse tokens = authService.generateTokens(oAuthMember); return ResponseEntity.ok(tokens); } - - @GetMapping("/oauth/google/test") - public ResponseEntity test() { - return ResponseEntity.ok("[ OAuthController ] 과연?"); - } } diff --git a/src/main/java/capstone/facefriend/auth/controller/support/AuthenticationContext.java b/src/main/java/capstone/facefriend/auth/controller/support/AuthenticationContext.java index 2a6034e5fb..3b35a0f82e 100644 --- a/src/main/java/capstone/facefriend/auth/controller/support/AuthenticationContext.java +++ b/src/main/java/capstone/facefriend/auth/controller/support/AuthenticationContext.java @@ -2,12 +2,13 @@ import capstone.facefriend.auth.exception.AuthException; -import capstone.facefriend.auth.exception.AuthExceptionType; import org.springframework.stereotype.Component; import org.springframework.web.context.annotation.RequestScope; import java.util.Objects; +import static capstone.facefriend.auth.exception.AuthExceptionType.UNAUTHORIZED; + @RequestScope @Component public class AuthenticationContext { @@ -19,14 +20,14 @@ public void setAuthentication(Long memerId) { this.memberId = memerId; } - public Long getPrincipal() { + public Long getAuthentication() { if (Objects.isNull(this.memberId)) { - throw new AuthException(AuthExceptionType.UNAUTHORIZED); + throw new AuthException(UNAUTHORIZED); } return memberId; } - public void setAnonymous() { + public void setNotAuthenticated() { this.memberId = ANONYMOUS_MEMBER; } } diff --git a/src/main/java/capstone/facefriend/auth/infrastructure/JwtProvider.java b/src/main/java/capstone/facefriend/auth/infrastructure/JwtProvider.java index 45be2e957a..01aeee6d3b 100644 --- a/src/main/java/capstone/facefriend/auth/infrastructure/JwtProvider.java +++ b/src/main/java/capstone/facefriend/auth/infrastructure/JwtProvider.java @@ -34,7 +34,7 @@ public class JwtProvider implements TokenProvider { private final RedisDao redisDao; - private static final long ACCESS_TOKEN_EXPIRATION_TIME = 60 * 2L; // 5분 // 1000 * 60 * 60 * 3L; // 3시간 + private static final long ACCESS_TOKEN_EXPIRATION_TIME = 60 * 5L; // 5분 // 1000 * 60 * 60 * 3L; // 3시간 private static final long REFRESH_TOKEN_EXPIRATION_TIME = 60 * 60 * 24 * 7L; // 7일 @PostConstruct @@ -98,6 +98,7 @@ private Date refreshTokenExpiredAt() { return Date.from(now.plusSeconds(REFRESH_TOKEN_EXPIRATION_TIME).atZone(ZoneId.systemDefault()).toInstant()); } + // 일반적인 경우에 사용합니다. @Override public Long extractId(String token) { try { @@ -119,6 +120,7 @@ public Long extractId(String token) { } } + // 재발급 시에만 사용합니다. @Override public Long extractIdIgnoringExpiration(String token) { try { @@ -128,7 +130,7 @@ public Long extractIdIgnoringExpiration(String token) { .getBody(); return claims.get("id", Long.class); } catch (ExpiredJwtException e) { - Claims expiredClaims = e.getClaims(); // 만료된 토큰일 경우 catch 후 id 를 반환해야만 합니다. + Claims expiredClaims = e.getClaims(); // catch 후 id 를 반환하고 이를 사용해 액세스 토큰을 추출할 수 있습니다. return expiredClaims.get("id", Long.class); } catch (SecurityException e) { throw new AuthException(SIGNATURE_NOT_FOUND); From 5c64fe538abfcc22815f4b57e373bc1ba6436e72 Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Thu, 21 Mar 2024 16:58:37 +0900 Subject: [PATCH 053/265] =?UTF-8?q?feat:=20isVerified=20=ED=95=84=EB=93=9C?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80=EB=A1=9C=20=EC=9D=B8=ED=95=9C=20Member=20?= =?UTF-8?q?=EA=B4=80=EB=A0=A8=20MVC=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../member/controller/MemberController.java | 18 ++++++++++++------ .../facefriend/member/domain/Member.java | 9 ++++++++- .../member/service/MemberService.java | 1 + 3 files changed, 21 insertions(+), 7 deletions(-) diff --git a/src/main/java/capstone/facefriend/member/controller/MemberController.java b/src/main/java/capstone/facefriend/member/controller/MemberController.java index 8b25bd4619..0a5e16585a 100644 --- a/src/main/java/capstone/facefriend/member/controller/MemberController.java +++ b/src/main/java/capstone/facefriend/member/controller/MemberController.java @@ -24,32 +24,38 @@ public class MemberController { private final MemberService memberService; + // 일반 유저 인증 인가 @PostMapping("/members/signup") public ResponseEntity signUp(@RequestBody SignUpRequest request) { return ResponseEntity.ok(memberService.signUp(request)); } + // 일반 유저 인증 인가 @PostMapping("/members/signin") public ResponseEntity signIn(@RequestBody SignInRequest request) { return ResponseEntity.ok(memberService.signIn(request)); } - @PostMapping("/members/reissue") + // 구글 유저, 일반 유저 공통 로직 + @PostMapping("/reissue") public ResponseEntity reissueTokens(@RequestBody ReissueRequest request, @AuthMember Long memberId) { String refreshToken = request.refreshToken(); - return ResponseEntity.ok(memberService.reissueTokens(memberId, refreshToken)); } - @DeleteMapping("/members/signout") + // 구글 유저, 일반 유저 공통 로직 + @DeleteMapping("/signout") public ResponseEntity signOut(HttpServletRequest request, @AuthMember Long memberId) { String accessToken = AuthenticationExtractor.extractAccessToken(request) .orElseThrow(() -> new MemberException(UNAUTHORIZED)); return ResponseEntity.ok(memberService.signOut(memberId, accessToken)); } - @GetMapping("/members/test") - public ResponseEntity test() { - return ResponseEntity.ok("[ MemberController ] 과연?"); + // 구글 유저, 일반 유저 공통 로직 (테스트용) + @GetMapping("/test") + public ResponseEntity test(HttpServletRequest request, @AuthMember Long memberId) { + String accessToken = AuthenticationExtractor.extractAccessToken(request) + .orElseThrow(() -> new MemberException(UNAUTHORIZED)); + return ResponseEntity.ok("토큰 = " + accessToken + " memberId = " + memberId); } } diff --git a/src/main/java/capstone/facefriend/member/domain/Member.java b/src/main/java/capstone/facefriend/member/domain/Member.java index 5fc333a27d..64fcf8d144 100644 --- a/src/main/java/capstone/facefriend/member/domain/Member.java +++ b/src/main/java/capstone/facefriend/member/domain/Member.java @@ -32,6 +32,9 @@ public class Member extends BaseEntity { @Column(unique = true) private String phone; + @Column + private Boolean isVerified; + @Enumerated(EnumType.STRING) @Column(nullable = false) private Role role; @@ -53,7 +56,11 @@ public String maskEmail() { return this.email.charAt(0) + "*".repeat(EMAIL_MASKING_LENGTH) + this.email.substring(EMAIL_MASKING_LENGTH + 1); } - public void updateRole(Role role) { + public void setRole(Role role) { this.role = role; } + + public void setIsVerified(Boolean isVerified) { + this.isVerified = isVerified; + } } \ No newline at end of file diff --git a/src/main/java/capstone/facefriend/member/service/MemberService.java b/src/main/java/capstone/facefriend/member/service/MemberService.java index 8131d90e3b..167898da5e 100644 --- a/src/main/java/capstone/facefriend/member/service/MemberService.java +++ b/src/main/java/capstone/facefriend/member/service/MemberService.java @@ -49,6 +49,7 @@ public String signUp(SignUpRequest request) { .email(email) .password(encodedPassword) .name(request.name()) + .isVerified(false) // .role(USER) .build(); memberRepository.save(member); From 90975c959070f8934cc20a88546d324599175cf9 Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Fri, 22 Mar 2024 12:37:49 +0900 Subject: [PATCH 054/265] =?UTF-8?q?fix:=20=EB=B3=B8=EC=9D=B8=EC=9D=B8?= =?UTF-8?q?=EC=A6=9D=20=ED=9B=84=20isVerified=20=ED=95=84=EB=93=9C?= =?UTF-8?q?=EA=B0=80=20=EB=B3=80=EA=B2=BD=EB=90=98=EC=A7=80=20=EC=95=8A?= =?UTF-8?q?=EB=8A=94=20=EB=B2=84=EA=B7=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../facefriend/auth/config/AuthConfig.java | 31 ++++++------------- .../TokenBlackListInterceptor.java | 7 ++++- .../auth/mail/controller/MailController.java | 21 +++++++------ .../interceptor/VerificationInterceptor.java | 8 ++--- .../auth/mail/service/MailService.java | 3 +- .../member/controller/MemberController.java | 6 ++-- .../facefriend/member/domain/Member.java | 15 +++++++-- .../member/service/MemberService.java | 2 +- .../capstone/facefriend/redis/RedisDao.java | 2 +- 9 files changed, 49 insertions(+), 46 deletions(-) diff --git a/src/main/java/capstone/facefriend/auth/config/AuthConfig.java b/src/main/java/capstone/facefriend/auth/config/AuthConfig.java index aa682e2b35..1bfeb9bad3 100644 --- a/src/main/java/capstone/facefriend/auth/config/AuthConfig.java +++ b/src/main/java/capstone/facefriend/auth/config/AuthConfig.java @@ -52,12 +52,11 @@ private HandlerInterceptor loginCheckInterceptor() { .addExcludePathPattern("/**", OPTIONS) .addExcludePathPattern("/reissue", POST) // 토큰 만료 시에는 해당 요청을 가로채지 않아야 합니다. - // 구글 유저 .addIncludePathPattern("/oauth/google/login", POST) - // 일반 유저 .addIncludePathPattern("/members/signin", POST) - // 구글 유저, 일반 유저 공통 로직 + .addIncludePathPattern("/signout", DELETE) + .addIncludePathPattern("/test", GET); } @@ -69,49 +68,37 @@ private HandlerInterceptor loginCheckInterceptor() { private HandlerInterceptor loginInterceptor() { return new PathMatchInterceptor(loginInterceptor) .addExcludePathPattern("/**", OPTIONS) + .addExcludePathPattern("/reissue", POST) // 토큰 만료 시에는 해당 요청을 가로채지 않아야 합니다. - // 구글 유저, 일반 유저 공통 로직 .addIncludePathPattern("/signout", DELETE) + .addIncludePathPattern("/test", GET); } - // 회원가입한 사용자의 id 를 추출하기 위한 인터셉터입니다. - // (1) 토큰이 만료된 경우 (2) 토큰이 위조된 경우 - // (3) 토큰이 블랙리스트에 등록된 경우 (4) 로그인하는 경우 - // 예외를 터트리지 않고 실제로 토큰 재발급을 해주기 위해 사용됩니다. - // * 해당 인터셉터는 토큰 재발급 시에만 사용되어야만 합니다. - // * 요청을 보낼 때 실려있는 토큰은 유효하지 않은 토큰(위 3가지 경우)인 것이 정상입니다. private HandlerInterceptor tokenReissueInterceptor() { return new PathMatchInterceptor(tokenReissueInterceptor) .addExcludePathPattern("/**", OPTIONS) - // 구글 유저, 일반 유저 공통 로직 .addIncludePathPattern("/reissue", POST); // 토큰 만료 시에는 해당 요청을 가로채야 합니다. } - // 재로그인 또는 토큰 재발급 이후, 토큰을 Authorization header 에 제대로 갈아끼웠는지를 확인하기 위한 인터셉터입니다. - // * 해당 인터셉터는 로그인 이후의 메서드들을 호출할 때 사용됩니다. - // * 최초 로그인 시에는 토큰이 없는게 정상입니다. 로그인 이후에 토큰을 발급받기 때문입니다. private HandlerInterceptor tokenBlackListInterceptor() { return new PathMatchInterceptor(tokenBlackListInterceptor) .addExcludePathPattern("/**", OPTIONS) - // 구글 유저, 일반 유저 공통 로직 - .addIncludePathPattern("/test", GET) - ; + .addIncludePathPattern("/test", GET); } - // 서비스를 이용하기 위해서 본인인증 여부를 판단하고 본인인증하지 않았다면 이를 강제하기 위한 인터셉터입니다. - // * 해당 인터셉터는 로그인 이후의 메서드들 호출할 때 사용됩니다, private HandlerInterceptor verificationInterceptor() { return new PathMatchInterceptor(verificationInterceptor) .addExcludePathPattern("/**", OPTIONS) - // 구글 유저, 일반 유저 공통 로직 - .addExcludePathPattern("/mail/*", POST) - .addExcludePathPattern("/mail/*", GET) .addIncludePathPattern("/test", GET); + +// .addExcludePathPattern("/mail/*", POST) // 인증 코드 발송 요청 시에는 해당 요청을 가로채지 않아야 합니다. +// .addExcludePathPattern("/mail/*", GET); // 인증 코드 발송 요청 시에는 해당 요청을 가로채지 않아야 합니다. + } @Override diff --git a/src/main/java/capstone/facefriend/auth/controller/interceptor/TokenBlackListInterceptor.java b/src/main/java/capstone/facefriend/auth/controller/interceptor/TokenBlackListInterceptor.java index eced5d9ed6..78a6435565 100644 --- a/src/main/java/capstone/facefriend/auth/controller/interceptor/TokenBlackListInterceptor.java +++ b/src/main/java/capstone/facefriend/auth/controller/interceptor/TokenBlackListInterceptor.java @@ -16,10 +16,15 @@ public class TokenBlackListInterceptor implements HandlerInterceptor { private final RedisDao redisDao; + private final static String SIGN_OUT_VALUE = "SIGN_OUT_VALUE"; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { String accessToken = AuthenticationExtractor.extractAccessToken(request).get(); - return !redisDao.isKeyOfAccessTokenInBlackList(accessToken); // 액세스 토큰이 블랙리스트에 등록되었다면 false 반환해야 합니다. + + if (accessToken != null && accessToken.equals(SIGN_OUT_VALUE)) { + return !redisDao.isKeyOfAccessTokenInBlackList(accessToken); // 액세스 토큰이 블랙리스트에 등록되었다면 false 반환해야 합니다. + } + return true; } } \ No newline at end of file diff --git a/src/main/java/capstone/facefriend/auth/mail/controller/MailController.java b/src/main/java/capstone/facefriend/auth/mail/controller/MailController.java index 17c042f431..ff93e712b7 100644 --- a/src/main/java/capstone/facefriend/auth/mail/controller/MailController.java +++ b/src/main/java/capstone/facefriend/auth/mail/controller/MailController.java @@ -1,30 +1,28 @@ package capstone.facefriend.auth.mail.controller; -import capstone.facefriend.auth.controller.support.AuthMember; import capstone.facefriend.auth.controller.support.AuthenticationExtractor; import capstone.facefriend.auth.domain.TokenProvider; import capstone.facefriend.auth.exception.AuthException; -import capstone.facefriend.auth.exception.AuthExceptionType; import capstone.facefriend.auth.mail.controller.dto.MailVerificationResponse; import capstone.facefriend.auth.mail.service.MailService; import capstone.facefriend.member.domain.Member; import capstone.facefriend.member.domain.MemberRepository; import capstone.facefriend.member.exception.MemberException; -import capstone.facefriend.member.exception.MemberExceptionType; import jakarta.servlet.http.HttpServletRequest; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; -import static capstone.facefriend.auth.exception.AuthExceptionType.*; import static capstone.facefriend.auth.exception.AuthExceptionType.UNAUTHORIZED; -import static capstone.facefriend.member.exception.MemberExceptionType.*; +import static capstone.facefriend.member.exception.MemberExceptionType.NOT_FOUND; @RestController @RequiredArgsConstructor +@Slf4j public class MailController { private final MailService mailService; @@ -40,20 +38,23 @@ public ResponseEntity sendCode( @GetMapping("/mail/verify-code") public ResponseEntity submitCode( + HttpServletRequest request, @RequestParam("mail") String mail, - @RequestParam("code") String code, - HttpServletRequest request + @RequestParam("code") String code ) { - Boolean isVerified = mailService.verifyCode(mail, code); String accessToken = AuthenticationExtractor.extractAccessToken(request) .orElseThrow(() -> new AuthException(UNAUTHORIZED)); - Long memberId = tokenProvider.extractId(accessToken); - Member member = memberRepository.findById(memberId) .orElseThrow(() -> new MemberException(NOT_FOUND)); + boolean isVerified = mailService.verifyCode(mail, code); + log.info("[ MailService ] isVerified = {}", isVerified); + member.setIsVerified(isVerified); + memberRepository.save(member); + + log.info("[ MailService ] member.getIsVerified = {}", member.isVerified()); return ResponseEntity.ok(new MailVerificationResponse(isVerified)); } diff --git a/src/main/java/capstone/facefriend/auth/mail/controller/interceptor/VerificationInterceptor.java b/src/main/java/capstone/facefriend/auth/mail/controller/interceptor/VerificationInterceptor.java index 2c09f0d0a2..8a62ef6149 100644 --- a/src/main/java/capstone/facefriend/auth/mail/controller/interceptor/VerificationInterceptor.java +++ b/src/main/java/capstone/facefriend/auth/mail/controller/interceptor/VerificationInterceptor.java @@ -40,12 +40,12 @@ public boolean preHandle(HttpServletRequest request, HttpServletResponse respons Member member = memberRepository.findById(memberId) .orElseThrow(() -> new MemberException(NOT_FOUND)); - Boolean isVerified = member.getIsVerified(); + boolean isVerified = member.isVerified(); + + log.info("[ VerificationInterceptor ] isVerified = {}", isVerified); verificationContext.setIsVerified(isVerified); - if (isVerified == null) { - throw new VerificationException(NOT_VERIFIED); - } + if (!isVerified) throw new VerificationException(NOT_VERIFIED); return true; } diff --git a/src/main/java/capstone/facefriend/auth/mail/service/MailService.java b/src/main/java/capstone/facefriend/auth/mail/service/MailService.java index e0e37f5ef0..f23263b1a7 100644 --- a/src/main/java/capstone/facefriend/auth/mail/service/MailService.java +++ b/src/main/java/capstone/facefriend/auth/mail/service/MailService.java @@ -31,6 +31,7 @@ public class MailService { private final RedisDao redisDao; private final JavaMailSender mailSender; + private final MemberRepository memberRepository; public String sendCode(String mail) { String code = createCode(); // 코드 생성 @@ -70,7 +71,7 @@ private SimpleMailMessage createMailForm(String mail, String title, String text) return message; } - public Boolean verifyCode(String mail, String codeInput) { + public boolean verifyCode(String mail, String codeInput) { String code = redisDao.getCode(mail); return code.equals(codeInput); } diff --git a/src/main/java/capstone/facefriend/member/controller/MemberController.java b/src/main/java/capstone/facefriend/member/controller/MemberController.java index 0a5e16585a..316dda0ee3 100644 --- a/src/main/java/capstone/facefriend/member/controller/MemberController.java +++ b/src/main/java/capstone/facefriend/member/controller/MemberController.java @@ -53,9 +53,7 @@ public ResponseEntity signOut(HttpServletRequest request, @AuthMember Lo // 구글 유저, 일반 유저 공통 로직 (테스트용) @GetMapping("/test") - public ResponseEntity test(HttpServletRequest request, @AuthMember Long memberId) { - String accessToken = AuthenticationExtractor.extractAccessToken(request) - .orElseThrow(() -> new MemberException(UNAUTHORIZED)); - return ResponseEntity.ok("토큰 = " + accessToken + " memberId = " + memberId); + public ResponseEntity test() { + return ResponseEntity.ok("[ MemberController ] 테스트"); } } diff --git a/src/main/java/capstone/facefriend/member/domain/Member.java b/src/main/java/capstone/facefriend/member/domain/Member.java index 64fcf8d144..2bb33807b8 100644 --- a/src/main/java/capstone/facefriend/member/domain/Member.java +++ b/src/main/java/capstone/facefriend/member/domain/Member.java @@ -3,6 +3,7 @@ import capstone.facefriend.common.domain.BaseEntity; import jakarta.persistence.*; import lombok.*; +import lombok.extern.slf4j.Slf4j; @Getter @Builder @@ -10,6 +11,7 @@ @AllArgsConstructor(access = AccessLevel.PRIVATE) @NoArgsConstructor(access = AccessLevel.PROTECTED) @Entity +@Slf4j public class Member extends BaseEntity { private static final int EMAIL_MASKING_LENGTH = 2; @@ -27,13 +29,16 @@ public class Member extends BaseEntity { @Column(nullable = false) private String password; + @Column(nullable = false) + private String name; + private String imageUrl; @Column(unique = true) private String phone; @Column - private Boolean isVerified; + private boolean isVerified; @Enumerated(EnumType.STRING) @Column(nullable = false) @@ -60,7 +65,13 @@ public void setRole(Role role) { this.role = role; } - public void setIsVerified(Boolean isVerified) { + public void setIsVerified(boolean isVerified) { + log.info("[ Member ] 현재 isVerified = {}", this.isVerified); + log.info("[ Member ] 들어온 isVerified = {}", isVerified); this.isVerified = isVerified; } + + public boolean isVerified() { + return this.isVerified == true; + } } \ No newline at end of file diff --git a/src/main/java/capstone/facefriend/member/service/MemberService.java b/src/main/java/capstone/facefriend/member/service/MemberService.java index 167898da5e..ce417934f5 100644 --- a/src/main/java/capstone/facefriend/member/service/MemberService.java +++ b/src/main/java/capstone/facefriend/member/service/MemberService.java @@ -49,7 +49,7 @@ public String signUp(SignUpRequest request) { .email(email) .password(encodedPassword) .name(request.name()) - .isVerified(false) // + .isVerified(false) .role(USER) .build(); memberRepository.save(member); diff --git a/src/main/java/capstone/facefriend/redis/RedisDao.java b/src/main/java/capstone/facefriend/redis/RedisDao.java index cfa17400f6..f40ae96916 100644 --- a/src/main/java/capstone/facefriend/redis/RedisDao.java +++ b/src/main/java/capstone/facefriend/redis/RedisDao.java @@ -17,7 +17,7 @@ public class RedisDao { private final RedisTemplate redisTemplate; - private final String SIGN_OUT_VALUE = "signOut"; + private final String SIGN_OUT_VALUE = "SIGN_OUT_VALUE"; public void setRefreshToken(String memberId, String refreshToken, long refreshTokenTime) { redisTemplate.setValueSerializer(new Jackson2JsonRedisSerializer<>(String.class)); From 054539cecca5397a20c535bcc0ea333d293439f5 Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Fri, 22 Mar 2024 12:42:21 +0900 Subject: [PATCH 055/265] =?UTF-8?q?style:=20=EC=A3=BC=EC=84=9D=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/capstone/facefriend/auth/config/AuthConfig.java | 7 ------- .../facefriend/auth/mail/controller/MailController.java | 3 --- .../controller/interceptor/VerificationInterceptor.java | 2 -- .../java/capstone/facefriend/member/domain/Member.java | 3 --- 4 files changed, 15 deletions(-) diff --git a/src/main/java/capstone/facefriend/auth/config/AuthConfig.java b/src/main/java/capstone/facefriend/auth/config/AuthConfig.java index 1bfeb9bad3..3db97a8d24 100644 --- a/src/main/java/capstone/facefriend/auth/config/AuthConfig.java +++ b/src/main/java/capstone/facefriend/auth/config/AuthConfig.java @@ -45,8 +45,6 @@ public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(verificationInterceptor()); } - // 회원가입하지 않은 사용자 id 를 -1 로 저장하기 위한 인터셉터입니다. - // * 해당 인터셉터는 로그인 시점을 포함한 그 이후의 메서드들을 호출할 때 사용됩니다. private HandlerInterceptor loginCheckInterceptor() { return new PathMatchInterceptor(loginCheckInterceptor) .addExcludePathPattern("/**", OPTIONS) @@ -60,11 +58,6 @@ private HandlerInterceptor loginCheckInterceptor() { .addIncludePathPattern("/test", GET); } - // 회원가입한 사용자의 id 를 추출하기 위한 인터셉터입니다. - // (1) 토큰이 만료된 경우 (2) 토큰이 위조된 경우 - // (3) 토큰이 블랙리스트에 등록된 경우 (4) 로그인하는 경우 - // 예외를 터트려 재로그인 또는 토큰 재발급을 강제하기 위한 인터셉터입니다. - // * 해당 인터셉터는 로그인 시점을 포함한 그 이후의 메서드들을 호출할 때 사용됩니다. private HandlerInterceptor loginInterceptor() { return new PathMatchInterceptor(loginInterceptor) .addExcludePathPattern("/**", OPTIONS) diff --git a/src/main/java/capstone/facefriend/auth/mail/controller/MailController.java b/src/main/java/capstone/facefriend/auth/mail/controller/MailController.java index ff93e712b7..78fbb7c578 100644 --- a/src/main/java/capstone/facefriend/auth/mail/controller/MailController.java +++ b/src/main/java/capstone/facefriend/auth/mail/controller/MailController.java @@ -49,13 +49,10 @@ public ResponseEntity submitCode( .orElseThrow(() -> new MemberException(NOT_FOUND)); boolean isVerified = mailService.verifyCode(mail, code); - log.info("[ MailService ] isVerified = {}", isVerified); member.setIsVerified(isVerified); memberRepository.save(member); - log.info("[ MailService ] member.getIsVerified = {}", member.isVerified()); - return ResponseEntity.ok(new MailVerificationResponse(isVerified)); } } diff --git a/src/main/java/capstone/facefriend/auth/mail/controller/interceptor/VerificationInterceptor.java b/src/main/java/capstone/facefriend/auth/mail/controller/interceptor/VerificationInterceptor.java index 8a62ef6149..24c3a37c7a 100644 --- a/src/main/java/capstone/facefriend/auth/mail/controller/interceptor/VerificationInterceptor.java +++ b/src/main/java/capstone/facefriend/auth/mail/controller/interceptor/VerificationInterceptor.java @@ -41,8 +41,6 @@ public boolean preHandle(HttpServletRequest request, HttpServletResponse respons .orElseThrow(() -> new MemberException(NOT_FOUND)); boolean isVerified = member.isVerified(); - - log.info("[ VerificationInterceptor ] isVerified = {}", isVerified); verificationContext.setIsVerified(isVerified); if (!isVerified) throw new VerificationException(NOT_VERIFIED); diff --git a/src/main/java/capstone/facefriend/member/domain/Member.java b/src/main/java/capstone/facefriend/member/domain/Member.java index 2bb33807b8..0417434376 100644 --- a/src/main/java/capstone/facefriend/member/domain/Member.java +++ b/src/main/java/capstone/facefriend/member/domain/Member.java @@ -29,9 +29,6 @@ public class Member extends BaseEntity { @Column(nullable = false) private String password; - @Column(nullable = false) - private String name; - private String imageUrl; @Column(unique = true) From 325561be802fed24ed17b6ce22bca5bff673df8d Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Fri, 22 Mar 2024 13:04:51 +0900 Subject: [PATCH 056/265] =?UTF-8?q?fix:=20VerificationInterceptor=20?= =?UTF-8?q?=EB=A1=9C=EC=A7=81=20=EC=88=9C=EC=84=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mail/controller/interceptor/VerificationInterceptor.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/capstone/facefriend/auth/mail/controller/interceptor/VerificationInterceptor.java b/src/main/java/capstone/facefriend/auth/mail/controller/interceptor/VerificationInterceptor.java index 24c3a37c7a..0ad65454a0 100644 --- a/src/main/java/capstone/facefriend/auth/mail/controller/interceptor/VerificationInterceptor.java +++ b/src/main/java/capstone/facefriend/auth/mail/controller/interceptor/VerificationInterceptor.java @@ -41,10 +41,10 @@ public boolean preHandle(HttpServletRequest request, HttpServletResponse respons .orElseThrow(() -> new MemberException(NOT_FOUND)); boolean isVerified = member.isVerified(); - verificationContext.setIsVerified(isVerified); - if (!isVerified) throw new VerificationException(NOT_VERIFIED); + verificationContext.setIsVerified(isVerified); + return true; } } From 0b97d9724725335a16ac8ce1e1f465035ab5f3bc Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Fri, 22 Mar 2024 13:08:03 +0900 Subject: [PATCH 057/265] =?UTF-8?q?style:=20=EC=98=88=EC=99=B8=EB=A5=BC=20?= =?UTF-8?q?=ED=86=B5=EA=B3=BC=ED=96=88=EB=8B=A4=EB=A9=B4=20=EB=AC=B4?= =?UTF-8?q?=EC=A1=B0=EA=B1=B4=20true=20=EC=9D=B4=EB=AF=80=EB=A1=9C=20?= =?UTF-8?q?=EC=A7=80=EC=97=AD=EB=B3=80=EC=88=98=EB=A5=BC=20true=20?= =?UTF-8?q?=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mail/controller/interceptor/VerificationInterceptor.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/capstone/facefriend/auth/mail/controller/interceptor/VerificationInterceptor.java b/src/main/java/capstone/facefriend/auth/mail/controller/interceptor/VerificationInterceptor.java index 0ad65454a0..3917108bdb 100644 --- a/src/main/java/capstone/facefriend/auth/mail/controller/interceptor/VerificationInterceptor.java +++ b/src/main/java/capstone/facefriend/auth/mail/controller/interceptor/VerificationInterceptor.java @@ -43,7 +43,7 @@ public boolean preHandle(HttpServletRequest request, HttpServletResponse respons boolean isVerified = member.isVerified(); if (!isVerified) throw new VerificationException(NOT_VERIFIED); - verificationContext.setIsVerified(isVerified); + verificationContext.setIsVerified(true); // 예외를 통과했다면 무조건 true 입니다. return true; } From 3476564aa9d0dc08c5f2362dfabd91d087e149fb Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Fri, 22 Mar 2024 13:13:53 +0900 Subject: [PATCH 058/265] =?UTF-8?q?style:=20mail=20=ED=8C=A8=ED=82=A4?= =?UTF-8?q?=EC=A7=80=EB=A5=BC=20auth=20=ED=8C=A8=ED=82=A4=EC=A7=80?= =?UTF-8?q?=EB=A1=9C=EB=B6=80=ED=84=B0=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../facefriend/auth/config/AuthConfig.java | 17 ++++++----------- .../interceptor/TokenReissueInterceptor.java | 2 +- .../mail/VerificationArgumentResolver.java | 6 +++--- .../{auth => }/mail/config/MailConfig.java | 2 +- .../mail/controller/MailController.java | 6 +++--- .../dto/MailVerificationResponse.java | 2 +- .../interceptor/VerificationInterceptor.java | 10 +++++----- .../mail/exception/MailException.java | 2 +- .../mail/exception/MailExceptionType.java | 2 +- .../mail/exception/VerificationException.java | 2 +- .../exception/VerificationExceptionType.java | 2 +- .../{auth => }/mail/service/MailService.java | 8 ++++---- .../mail/support/VerificationContext.java | 10 ++++------ .../{auth => }/mail/support/VerifiedMember.java | 2 +- 14 files changed, 33 insertions(+), 40 deletions(-) rename src/main/java/capstone/facefriend/{auth => }/mail/VerificationArgumentResolver.java (87%) rename src/main/java/capstone/facefriend/{auth => }/mail/config/MailConfig.java (97%) rename src/main/java/capstone/facefriend/{auth => }/mail/controller/MailController.java (92%) rename src/main/java/capstone/facefriend/{auth => }/mail/controller/dto/MailVerificationResponse.java (56%) rename src/main/java/capstone/facefriend/{auth => }/mail/controller/interceptor/VerificationInterceptor.java (83%) rename src/main/java/capstone/facefriend/{auth => }/mail/exception/MailException.java (84%) rename src/main/java/capstone/facefriend/{auth => }/mail/exception/MailExceptionType.java (94%) rename src/main/java/capstone/facefriend/{auth => }/mail/exception/VerificationException.java (85%) rename src/main/java/capstone/facefriend/{auth => }/mail/exception/VerificationExceptionType.java (94%) rename src/main/java/capstone/facefriend/{auth => }/mail/service/MailService.java (89%) rename src/main/java/capstone/facefriend/{auth => }/mail/support/VerificationContext.java (64%) rename src/main/java/capstone/facefriend/{auth => }/mail/support/VerifiedMember.java (85%) diff --git a/src/main/java/capstone/facefriend/auth/config/AuthConfig.java b/src/main/java/capstone/facefriend/auth/config/AuthConfig.java index 3db97a8d24..24cd24f120 100644 --- a/src/main/java/capstone/facefriend/auth/config/AuthConfig.java +++ b/src/main/java/capstone/facefriend/auth/config/AuthConfig.java @@ -2,7 +2,7 @@ import capstone.facefriend.auth.controller.AuthArgumentResolver; import capstone.facefriend.auth.controller.interceptor.*; -import capstone.facefriend.auth.mail.controller.interceptor.VerificationInterceptor; +import capstone.facefriend.mail.controller.interceptor.VerificationInterceptor; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.context.annotation.Bean; @@ -48,25 +48,24 @@ public void addInterceptors(InterceptorRegistry registry) { private HandlerInterceptor loginCheckInterceptor() { return new PathMatchInterceptor(loginCheckInterceptor) .addExcludePathPattern("/**", OPTIONS) - .addExcludePathPattern("/reissue", POST) // 토큰 만료 시에는 해당 요청을 가로채지 않아야 합니다. .addIncludePathPattern("/oauth/google/login", POST) .addIncludePathPattern("/members/signin", POST) .addIncludePathPattern("/signout", DELETE) - - .addIncludePathPattern("/test", GET); + .addIncludePathPattern("/test", GET) + + .addExcludePathPattern("/reissue", POST); // 토큰 만료 시에는 해당 요청을 가로채지 않아야 합니다. } private HandlerInterceptor loginInterceptor() { return new PathMatchInterceptor(loginInterceptor) .addExcludePathPattern("/**", OPTIONS) - .addExcludePathPattern("/reissue", POST) // 토큰 만료 시에는 해당 요청을 가로채지 않아야 합니다. - .addIncludePathPattern("/signout", DELETE) + .addIncludePathPattern("/test", GET) - .addIncludePathPattern("/test", GET); + .addExcludePathPattern("/reissue", POST); // 토큰 만료 시에는 해당 요청을 가로채지 않아야 합니다. } private HandlerInterceptor tokenReissueInterceptor() { @@ -88,10 +87,6 @@ private HandlerInterceptor verificationInterceptor() { .addExcludePathPattern("/**", OPTIONS) .addIncludePathPattern("/test", GET); - -// .addExcludePathPattern("/mail/*", POST) // 인증 코드 발송 요청 시에는 해당 요청을 가로채지 않아야 합니다. -// .addExcludePathPattern("/mail/*", GET); // 인증 코드 발송 요청 시에는 해당 요청을 가로채지 않아야 합니다. - } @Override diff --git a/src/main/java/capstone/facefriend/auth/controller/interceptor/TokenReissueInterceptor.java b/src/main/java/capstone/facefriend/auth/controller/interceptor/TokenReissueInterceptor.java index f3905518c5..f869d13ab5 100644 --- a/src/main/java/capstone/facefriend/auth/controller/interceptor/TokenReissueInterceptor.java +++ b/src/main/java/capstone/facefriend/auth/controller/interceptor/TokenReissueInterceptor.java @@ -3,7 +3,7 @@ import capstone.facefriend.auth.controller.support.AuthenticationContext; import capstone.facefriend.auth.controller.support.AuthenticationExtractor; import capstone.facefriend.auth.domain.TokenProvider; -import capstone.facefriend.auth.mail.controller.interceptor.VerificationInterceptor; +import capstone.facefriend.mail.controller.interceptor.VerificationInterceptor; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import lombok.RequiredArgsConstructor; diff --git a/src/main/java/capstone/facefriend/auth/mail/VerificationArgumentResolver.java b/src/main/java/capstone/facefriend/mail/VerificationArgumentResolver.java similarity index 87% rename from src/main/java/capstone/facefriend/auth/mail/VerificationArgumentResolver.java rename to src/main/java/capstone/facefriend/mail/VerificationArgumentResolver.java index ec1e32a087..e818a01f9e 100644 --- a/src/main/java/capstone/facefriend/auth/mail/VerificationArgumentResolver.java +++ b/src/main/java/capstone/facefriend/mail/VerificationArgumentResolver.java @@ -1,7 +1,7 @@ -package capstone.facefriend.auth.mail; +package capstone.facefriend.mail; -import capstone.facefriend.auth.mail.support.VerifiedMember; -import capstone.facefriend.auth.mail.support.VerificationContext; +import capstone.facefriend.mail.support.VerifiedMember; +import capstone.facefriend.mail.support.VerificationContext; import lombok.RequiredArgsConstructor; import org.springframework.core.MethodParameter; import org.springframework.stereotype.Component; diff --git a/src/main/java/capstone/facefriend/auth/mail/config/MailConfig.java b/src/main/java/capstone/facefriend/mail/config/MailConfig.java similarity index 97% rename from src/main/java/capstone/facefriend/auth/mail/config/MailConfig.java rename to src/main/java/capstone/facefriend/mail/config/MailConfig.java index 033d05114b..b8e21de6a8 100644 --- a/src/main/java/capstone/facefriend/auth/mail/config/MailConfig.java +++ b/src/main/java/capstone/facefriend/mail/config/MailConfig.java @@ -1,4 +1,4 @@ -package capstone.facefriend.auth.mail.config; +package capstone.facefriend.mail.config; import org.springframework.beans.factory.annotation.Value; diff --git a/src/main/java/capstone/facefriend/auth/mail/controller/MailController.java b/src/main/java/capstone/facefriend/mail/controller/MailController.java similarity index 92% rename from src/main/java/capstone/facefriend/auth/mail/controller/MailController.java rename to src/main/java/capstone/facefriend/mail/controller/MailController.java index 78fbb7c578..46f4840868 100644 --- a/src/main/java/capstone/facefriend/auth/mail/controller/MailController.java +++ b/src/main/java/capstone/facefriend/mail/controller/MailController.java @@ -1,10 +1,10 @@ -package capstone.facefriend.auth.mail.controller; +package capstone.facefriend.mail.controller; import capstone.facefriend.auth.controller.support.AuthenticationExtractor; import capstone.facefriend.auth.domain.TokenProvider; import capstone.facefriend.auth.exception.AuthException; -import capstone.facefriend.auth.mail.controller.dto.MailVerificationResponse; -import capstone.facefriend.auth.mail.service.MailService; +import capstone.facefriend.mail.controller.dto.MailVerificationResponse; +import capstone.facefriend.mail.service.MailService; import capstone.facefriend.member.domain.Member; import capstone.facefriend.member.domain.MemberRepository; import capstone.facefriend.member.exception.MemberException; diff --git a/src/main/java/capstone/facefriend/auth/mail/controller/dto/MailVerificationResponse.java b/src/main/java/capstone/facefriend/mail/controller/dto/MailVerificationResponse.java similarity index 56% rename from src/main/java/capstone/facefriend/auth/mail/controller/dto/MailVerificationResponse.java rename to src/main/java/capstone/facefriend/mail/controller/dto/MailVerificationResponse.java index e8c81ee6ae..4380c70ca3 100644 --- a/src/main/java/capstone/facefriend/auth/mail/controller/dto/MailVerificationResponse.java +++ b/src/main/java/capstone/facefriend/mail/controller/dto/MailVerificationResponse.java @@ -1,4 +1,4 @@ -package capstone.facefriend.auth.mail.controller.dto; +package capstone.facefriend.mail.controller.dto; public record MailVerificationResponse( boolean isVerified diff --git a/src/main/java/capstone/facefriend/auth/mail/controller/interceptor/VerificationInterceptor.java b/src/main/java/capstone/facefriend/mail/controller/interceptor/VerificationInterceptor.java similarity index 83% rename from src/main/java/capstone/facefriend/auth/mail/controller/interceptor/VerificationInterceptor.java rename to src/main/java/capstone/facefriend/mail/controller/interceptor/VerificationInterceptor.java index 3917108bdb..2a21e8b6ea 100644 --- a/src/main/java/capstone/facefriend/auth/mail/controller/interceptor/VerificationInterceptor.java +++ b/src/main/java/capstone/facefriend/mail/controller/interceptor/VerificationInterceptor.java @@ -1,11 +1,11 @@ -package capstone.facefriend.auth.mail.controller.interceptor; +package capstone.facefriend.mail.controller.interceptor; import capstone.facefriend.auth.controller.support.AuthenticationExtractor; import capstone.facefriend.auth.domain.TokenProvider; import capstone.facefriend.auth.exception.AuthException; -import capstone.facefriend.auth.mail.exception.VerificationException; -import capstone.facefriend.auth.mail.exception.VerificationExceptionType; -import capstone.facefriend.auth.mail.support.VerificationContext; +import capstone.facefriend.mail.exception.VerificationException; +import capstone.facefriend.mail.exception.VerificationExceptionType; +import capstone.facefriend.mail.support.VerificationContext; import capstone.facefriend.member.domain.Member; import capstone.facefriend.member.domain.MemberRepository; import capstone.facefriend.member.exception.MemberException; @@ -17,7 +17,7 @@ import org.springframework.web.servlet.HandlerInterceptor; import static capstone.facefriend.auth.exception.AuthExceptionType.UNAUTHORIZED; -import static capstone.facefriend.auth.mail.exception.VerificationExceptionType.*; +import static capstone.facefriend.mail.exception.VerificationExceptionType.*; import static capstone.facefriend.member.exception.MemberExceptionType.NOT_FOUND; diff --git a/src/main/java/capstone/facefriend/auth/mail/exception/MailException.java b/src/main/java/capstone/facefriend/mail/exception/MailException.java similarity index 84% rename from src/main/java/capstone/facefriend/auth/mail/exception/MailException.java rename to src/main/java/capstone/facefriend/mail/exception/MailException.java index d534a98ee3..6d0b13a368 100644 --- a/src/main/java/capstone/facefriend/auth/mail/exception/MailException.java +++ b/src/main/java/capstone/facefriend/mail/exception/MailException.java @@ -1,4 +1,4 @@ -package capstone.facefriend.auth.mail.exception; +package capstone.facefriend.mail.exception; import capstone.facefriend.common.exception.BaseException; import capstone.facefriend.common.exception.ExceptionType; diff --git a/src/main/java/capstone/facefriend/auth/mail/exception/MailExceptionType.java b/src/main/java/capstone/facefriend/mail/exception/MailExceptionType.java similarity index 94% rename from src/main/java/capstone/facefriend/auth/mail/exception/MailExceptionType.java rename to src/main/java/capstone/facefriend/mail/exception/MailExceptionType.java index 84917d691f..1e5241fe79 100644 --- a/src/main/java/capstone/facefriend/auth/mail/exception/MailExceptionType.java +++ b/src/main/java/capstone/facefriend/mail/exception/MailExceptionType.java @@ -1,4 +1,4 @@ -package capstone.facefriend.auth.mail.exception; +package capstone.facefriend.mail.exception; import capstone.facefriend.common.exception.ExceptionType; import capstone.facefriend.common.exception.Status; diff --git a/src/main/java/capstone/facefriend/auth/mail/exception/VerificationException.java b/src/main/java/capstone/facefriend/mail/exception/VerificationException.java similarity index 85% rename from src/main/java/capstone/facefriend/auth/mail/exception/VerificationException.java rename to src/main/java/capstone/facefriend/mail/exception/VerificationException.java index 0cb4605cf0..0d518dea45 100644 --- a/src/main/java/capstone/facefriend/auth/mail/exception/VerificationException.java +++ b/src/main/java/capstone/facefriend/mail/exception/VerificationException.java @@ -1,4 +1,4 @@ -package capstone.facefriend.auth.mail.exception; +package capstone.facefriend.mail.exception; import capstone.facefriend.common.exception.BaseException; import capstone.facefriend.common.exception.ExceptionType; diff --git a/src/main/java/capstone/facefriend/auth/mail/exception/VerificationExceptionType.java b/src/main/java/capstone/facefriend/mail/exception/VerificationExceptionType.java similarity index 94% rename from src/main/java/capstone/facefriend/auth/mail/exception/VerificationExceptionType.java rename to src/main/java/capstone/facefriend/mail/exception/VerificationExceptionType.java index c695b33415..957f68ef37 100644 --- a/src/main/java/capstone/facefriend/auth/mail/exception/VerificationExceptionType.java +++ b/src/main/java/capstone/facefriend/mail/exception/VerificationExceptionType.java @@ -1,4 +1,4 @@ -package capstone.facefriend.auth.mail.exception; +package capstone.facefriend.mail.exception; import capstone.facefriend.common.exception.ExceptionType; import capstone.facefriend.common.exception.Status; diff --git a/src/main/java/capstone/facefriend/auth/mail/service/MailService.java b/src/main/java/capstone/facefriend/mail/service/MailService.java similarity index 89% rename from src/main/java/capstone/facefriend/auth/mail/service/MailService.java rename to src/main/java/capstone/facefriend/mail/service/MailService.java index f23263b1a7..c417058a0c 100644 --- a/src/main/java/capstone/facefriend/auth/mail/service/MailService.java +++ b/src/main/java/capstone/facefriend/mail/service/MailService.java @@ -1,6 +1,6 @@ -package capstone.facefriend.auth.mail.service; +package capstone.facefriend.mail.service; -import capstone.facefriend.auth.mail.exception.MailException; +import capstone.facefriend.mail.exception.MailException; import capstone.facefriend.member.domain.MemberRepository; import capstone.facefriend.redis.RedisDao; import jakarta.transaction.Transactional; @@ -15,8 +15,8 @@ import java.security.SecureRandom; import java.util.Random; -import static capstone.facefriend.auth.mail.exception.MailExceptionType.NO_SUCH_ALGORITHM; -import static capstone.facefriend.auth.mail.exception.MailExceptionType.UNABLE_TO_SEND_MAIL; +import static capstone.facefriend.mail.exception.MailExceptionType.NO_SUCH_ALGORITHM; +import static capstone.facefriend.mail.exception.MailExceptionType.UNABLE_TO_SEND_MAIL; @Slf4j @Service diff --git a/src/main/java/capstone/facefriend/auth/mail/support/VerificationContext.java b/src/main/java/capstone/facefriend/mail/support/VerificationContext.java similarity index 64% rename from src/main/java/capstone/facefriend/auth/mail/support/VerificationContext.java rename to src/main/java/capstone/facefriend/mail/support/VerificationContext.java index b5d6efa8a8..5377826248 100644 --- a/src/main/java/capstone/facefriend/auth/mail/support/VerificationContext.java +++ b/src/main/java/capstone/facefriend/mail/support/VerificationContext.java @@ -1,15 +1,13 @@ -package capstone.facefriend.auth.mail.support; +package capstone.facefriend.mail.support; -import capstone.facefriend.auth.mail.exception.VerificationException; -import capstone.facefriend.auth.mail.exception.VerificationExceptionType; +import capstone.facefriend.mail.exception.VerificationException; +import capstone.facefriend.mail.exception.VerificationExceptionType; import org.springframework.stereotype.Component; import org.springframework.web.context.annotation.RequestScope; import java.util.Objects; -import static capstone.facefriend.auth.mail.exception.VerificationExceptionType.*; - @RequestScope @Component public class VerificationContext { @@ -23,7 +21,7 @@ public void setIsVerified(Boolean isVerified) { public Boolean getIsVerified() { if (Objects.isNull(this.isVerified)) { - throw new VerificationException(NOT_VERIFIED); + throw new VerificationException(VerificationExceptionType.NOT_VERIFIED); } return isVerified; } diff --git a/src/main/java/capstone/facefriend/auth/mail/support/VerifiedMember.java b/src/main/java/capstone/facefriend/mail/support/VerifiedMember.java similarity index 85% rename from src/main/java/capstone/facefriend/auth/mail/support/VerifiedMember.java rename to src/main/java/capstone/facefriend/mail/support/VerifiedMember.java index 94c89b759b..ce44d49809 100644 --- a/src/main/java/capstone/facefriend/auth/mail/support/VerifiedMember.java +++ b/src/main/java/capstone/facefriend/mail/support/VerifiedMember.java @@ -1,4 +1,4 @@ -package capstone.facefriend.auth.mail.support; +package capstone.facefriend.mail.support; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; From 21487ae9fd5f2f30f413398be3090df2a5bc692f Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Fri, 22 Mar 2024 13:28:27 +0900 Subject: [PATCH 059/265] =?UTF-8?q?style:=20AuthConfig=20PathPattern=20?= =?UTF-8?q?=EC=88=9C=EC=84=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/capstone/facefriend/auth/config/AuthConfig.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/capstone/facefriend/auth/config/AuthConfig.java b/src/main/java/capstone/facefriend/auth/config/AuthConfig.java index 24cd24f120..21032aca9c 100644 --- a/src/main/java/capstone/facefriend/auth/config/AuthConfig.java +++ b/src/main/java/capstone/facefriend/auth/config/AuthConfig.java @@ -54,7 +54,7 @@ private HandlerInterceptor loginCheckInterceptor() { .addIncludePathPattern("/signout", DELETE) .addIncludePathPattern("/test", GET) - + .addExcludePathPattern("/reissue", POST); // 토큰 만료 시에는 해당 요청을 가로채지 않아야 합니다. } From 83a38a5d3f78a905eead913f22daf012c274ff0a Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Sat, 23 Mar 2024 14:40:10 +0900 Subject: [PATCH 060/265] =?UTF-8?q?style:=20mail=20->=20email=20=EB=A1=9C?= =?UTF-8?q?=20=EB=94=94=EB=A0=89=ED=86=A0=EB=A6=AC,=20=ED=8C=8C=EC=9D=BC,?= =?UTF-8?q?=20=EB=A9=94=EC=84=9C=EB=93=9C=EB=AA=85=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../VerificationArgumentResolver.java | 6 +- .../config/EmailConfig.java} | 4 +- .../dto/EmailVerificationResponse.java | 7 +++ .../interceptor/VerificationInterceptor.java | 9 ++- .../exception/EmailException.java} | 6 +- .../exception/EmailExceptionType.java} | 12 ++-- .../exception/VerificationException.java | 2 +- .../exception/VerificationExceptionType.java | 2 +- .../service/EmailService.java} | 44 +++++++------- .../support/VerificationContext.java | 6 +- .../support/VerifiedMember.java | 2 +- .../mail/controller/MailController.java | 58 ------------------- .../dto/MailVerificationResponse.java | 6 -- 13 files changed, 55 insertions(+), 109 deletions(-) rename src/main/java/capstone/facefriend/{mail => email}/VerificationArgumentResolver.java (87%) rename src/main/java/capstone/facefriend/{mail/config/MailConfig.java => email/config/EmailConfig.java} (96%) create mode 100644 src/main/java/capstone/facefriend/email/controller/dto/EmailVerificationResponse.java rename src/main/java/capstone/facefriend/{mail => email}/controller/interceptor/VerificationInterceptor.java (84%) rename src/main/java/capstone/facefriend/{mail/exception/MailException.java => email/exception/EmailException.java} (50%) rename src/main/java/capstone/facefriend/{mail/exception/MailExceptionType.java => email/exception/EmailExceptionType.java} (61%) rename src/main/java/capstone/facefriend/{mail => email}/exception/VerificationException.java (86%) rename src/main/java/capstone/facefriend/{mail => email}/exception/VerificationExceptionType.java (94%) rename src/main/java/capstone/facefriend/{mail/service/MailService.java => email/service/EmailService.java} (56%) rename src/main/java/capstone/facefriend/{mail => email}/support/VerificationContext.java (79%) rename src/main/java/capstone/facefriend/{mail => email}/support/VerifiedMember.java (86%) delete mode 100644 src/main/java/capstone/facefriend/mail/controller/MailController.java delete mode 100644 src/main/java/capstone/facefriend/mail/controller/dto/MailVerificationResponse.java diff --git a/src/main/java/capstone/facefriend/mail/VerificationArgumentResolver.java b/src/main/java/capstone/facefriend/email/VerificationArgumentResolver.java similarity index 87% rename from src/main/java/capstone/facefriend/mail/VerificationArgumentResolver.java rename to src/main/java/capstone/facefriend/email/VerificationArgumentResolver.java index e818a01f9e..4ea0d37bc9 100644 --- a/src/main/java/capstone/facefriend/mail/VerificationArgumentResolver.java +++ b/src/main/java/capstone/facefriend/email/VerificationArgumentResolver.java @@ -1,7 +1,7 @@ -package capstone.facefriend.mail; +package capstone.facefriend.email; -import capstone.facefriend.mail.support.VerifiedMember; -import capstone.facefriend.mail.support.VerificationContext; +import capstone.facefriend.email.support.VerifiedMember; +import capstone.facefriend.email.support.VerificationContext; import lombok.RequiredArgsConstructor; import org.springframework.core.MethodParameter; import org.springframework.stereotype.Component; diff --git a/src/main/java/capstone/facefriend/mail/config/MailConfig.java b/src/main/java/capstone/facefriend/email/config/EmailConfig.java similarity index 96% rename from src/main/java/capstone/facefriend/mail/config/MailConfig.java rename to src/main/java/capstone/facefriend/email/config/EmailConfig.java index b8e21de6a8..a124be0493 100644 --- a/src/main/java/capstone/facefriend/mail/config/MailConfig.java +++ b/src/main/java/capstone/facefriend/email/config/EmailConfig.java @@ -1,4 +1,4 @@ -package capstone.facefriend.mail.config; +package capstone.facefriend.email.config; import org.springframework.beans.factory.annotation.Value; @@ -10,7 +10,7 @@ import java.util.Properties; @Configuration -public class MailConfig { +public class EmailConfig { @Value("${spring.mail.host}") private String host; diff --git a/src/main/java/capstone/facefriend/email/controller/dto/EmailVerificationResponse.java b/src/main/java/capstone/facefriend/email/controller/dto/EmailVerificationResponse.java new file mode 100644 index 0000000000..64942965b9 --- /dev/null +++ b/src/main/java/capstone/facefriend/email/controller/dto/EmailVerificationResponse.java @@ -0,0 +1,7 @@ +package capstone.facefriend.email.controller.dto; + +public record EmailVerificationResponse( + String email, + boolean isVerified +) { +} diff --git a/src/main/java/capstone/facefriend/mail/controller/interceptor/VerificationInterceptor.java b/src/main/java/capstone/facefriend/email/controller/interceptor/VerificationInterceptor.java similarity index 84% rename from src/main/java/capstone/facefriend/mail/controller/interceptor/VerificationInterceptor.java rename to src/main/java/capstone/facefriend/email/controller/interceptor/VerificationInterceptor.java index 2a21e8b6ea..17c03976aa 100644 --- a/src/main/java/capstone/facefriend/mail/controller/interceptor/VerificationInterceptor.java +++ b/src/main/java/capstone/facefriend/email/controller/interceptor/VerificationInterceptor.java @@ -1,11 +1,10 @@ -package capstone.facefriend.mail.controller.interceptor; +package capstone.facefriend.email.controller.interceptor; import capstone.facefriend.auth.controller.support.AuthenticationExtractor; import capstone.facefriend.auth.domain.TokenProvider; import capstone.facefriend.auth.exception.AuthException; -import capstone.facefriend.mail.exception.VerificationException; -import capstone.facefriend.mail.exception.VerificationExceptionType; -import capstone.facefriend.mail.support.VerificationContext; +import capstone.facefriend.email.exception.VerificationException; +import capstone.facefriend.email.support.VerificationContext; import capstone.facefriend.member.domain.Member; import capstone.facefriend.member.domain.MemberRepository; import capstone.facefriend.member.exception.MemberException; @@ -17,7 +16,7 @@ import org.springframework.web.servlet.HandlerInterceptor; import static capstone.facefriend.auth.exception.AuthExceptionType.UNAUTHORIZED; -import static capstone.facefriend.mail.exception.VerificationExceptionType.*; +import static capstone.facefriend.email.exception.VerificationExceptionType.*; import static capstone.facefriend.member.exception.MemberExceptionType.NOT_FOUND; diff --git a/src/main/java/capstone/facefriend/mail/exception/MailException.java b/src/main/java/capstone/facefriend/email/exception/EmailException.java similarity index 50% rename from src/main/java/capstone/facefriend/mail/exception/MailException.java rename to src/main/java/capstone/facefriend/email/exception/EmailException.java index 6d0b13a368..1f3921225b 100644 --- a/src/main/java/capstone/facefriend/mail/exception/MailException.java +++ b/src/main/java/capstone/facefriend/email/exception/EmailException.java @@ -1,11 +1,11 @@ -package capstone.facefriend.mail.exception; +package capstone.facefriend.email.exception; import capstone.facefriend.common.exception.BaseException; import capstone.facefriend.common.exception.ExceptionType; -public class MailException extends BaseException { +public class EmailException extends BaseException { - public MailException(ExceptionType exceptionType) { + public EmailException(ExceptionType exceptionType) { super(exceptionType); } } diff --git a/src/main/java/capstone/facefriend/mail/exception/MailExceptionType.java b/src/main/java/capstone/facefriend/email/exception/EmailExceptionType.java similarity index 61% rename from src/main/java/capstone/facefriend/mail/exception/MailExceptionType.java rename to src/main/java/capstone/facefriend/email/exception/EmailExceptionType.java index 1e5241fe79..df21d70adc 100644 --- a/src/main/java/capstone/facefriend/mail/exception/MailExceptionType.java +++ b/src/main/java/capstone/facefriend/email/exception/EmailExceptionType.java @@ -1,18 +1,20 @@ -package capstone.facefriend.mail.exception; +package capstone.facefriend.email.exception; import capstone.facefriend.common.exception.ExceptionType; import capstone.facefriend.common.exception.Status; -public enum MailExceptionType implements ExceptionType { +public enum EmailExceptionType implements ExceptionType { - UNABLE_TO_SEND_MAIL(Status.BAD_REQUEST, 1001, "인증 메일을 보낼 수 없습니다."), - NO_SUCH_ALGORITHM(Status.SERVER_ERROR,1002,"인증 코드 생성에 실패했습니다."); + UNABLE_TO_SEND_EMAIL(Status.BAD_REQUEST, 1001, "인증 메일을 보낼 수 없습니다."), + NO_SUCH_ALGORITHM(Status.SERVER_ERROR,1002,"인증 코드 생성에 실패했습니다."), + WRONG_EMAIL(Status.BAD_REQUEST, 1003, "올바르지 않은 이메일입니다.") + ; private final Status status; private final int exceptionCode; private final String message; - MailExceptionType(Status status, int exceptionCode, String message) { + EmailExceptionType(Status status, int exceptionCode, String message) { this.status = status; this.exceptionCode = exceptionCode; this.message = message; diff --git a/src/main/java/capstone/facefriend/mail/exception/VerificationException.java b/src/main/java/capstone/facefriend/email/exception/VerificationException.java similarity index 86% rename from src/main/java/capstone/facefriend/mail/exception/VerificationException.java rename to src/main/java/capstone/facefriend/email/exception/VerificationException.java index 0d518dea45..5662056704 100644 --- a/src/main/java/capstone/facefriend/mail/exception/VerificationException.java +++ b/src/main/java/capstone/facefriend/email/exception/VerificationException.java @@ -1,4 +1,4 @@ -package capstone.facefriend.mail.exception; +package capstone.facefriend.email.exception; import capstone.facefriend.common.exception.BaseException; import capstone.facefriend.common.exception.ExceptionType; diff --git a/src/main/java/capstone/facefriend/mail/exception/VerificationExceptionType.java b/src/main/java/capstone/facefriend/email/exception/VerificationExceptionType.java similarity index 94% rename from src/main/java/capstone/facefriend/mail/exception/VerificationExceptionType.java rename to src/main/java/capstone/facefriend/email/exception/VerificationExceptionType.java index 957f68ef37..bf406443a2 100644 --- a/src/main/java/capstone/facefriend/mail/exception/VerificationExceptionType.java +++ b/src/main/java/capstone/facefriend/email/exception/VerificationExceptionType.java @@ -1,4 +1,4 @@ -package capstone.facefriend.mail.exception; +package capstone.facefriend.email.exception; import capstone.facefriend.common.exception.ExceptionType; import capstone.facefriend.common.exception.Status; diff --git a/src/main/java/capstone/facefriend/mail/service/MailService.java b/src/main/java/capstone/facefriend/email/service/EmailService.java similarity index 56% rename from src/main/java/capstone/facefriend/mail/service/MailService.java rename to src/main/java/capstone/facefriend/email/service/EmailService.java index c417058a0c..9e46e8c59f 100644 --- a/src/main/java/capstone/facefriend/mail/service/MailService.java +++ b/src/main/java/capstone/facefriend/email/service/EmailService.java @@ -1,7 +1,7 @@ -package capstone.facefriend.mail.service; +package capstone.facefriend.email.service; -import capstone.facefriend.mail.exception.MailException; -import capstone.facefriend.member.domain.MemberRepository; +import capstone.facefriend.email.exception.EmailException; +import capstone.facefriend.email.exception.EmailExceptionType; import capstone.facefriend.redis.RedisDao; import jakarta.transaction.Transactional; import lombok.RequiredArgsConstructor; @@ -15,14 +15,13 @@ import java.security.SecureRandom; import java.util.Random; -import static capstone.facefriend.mail.exception.MailExceptionType.NO_SUCH_ALGORITHM; -import static capstone.facefriend.mail.exception.MailExceptionType.UNABLE_TO_SEND_MAIL; +import static capstone.facefriend.email.exception.EmailExceptionType.*; @Slf4j @Service @Transactional @RequiredArgsConstructor -public class MailService { +public class EmailService { @Value("${spring.mail.auth-code-expiration-millis}") private long authCodeExpirationMillis; @@ -30,13 +29,12 @@ public class MailService { private static final String MAIL_SUCCESS = "이메일로 코드를 전송했습니다."; private final RedisDao redisDao; - private final JavaMailSender mailSender; - private final MemberRepository memberRepository; + private final JavaMailSender emailSender; - public String sendCode(String mail) { + public String sendCode(String email) { String code = createCode(); // 코드 생성 - sendMail(mail, MAIL_TITLE, code); // 메일로 코드 보내기 - redisDao.setCode(mail, code, authCodeExpirationMillis); // 코드 레디스 저장 + sendEmail(email, MAIL_TITLE, code); // 메일로 코드 보내기 + redisDao.setCode(email, code, authCodeExpirationMillis); // 코드 레디스 저장 return MAIL_SUCCESS; } @@ -50,29 +48,33 @@ private String createCode() { } return sb.toString(); } catch (NoSuchAlgorithmException e) { - throw new MailException(NO_SUCH_ALGORITHM); + throw new EmailException(NO_SUCH_ALGORITHM); } } - private void sendMail(String mail, String title, String text) { - SimpleMailMessage emailForm = createMailForm(mail, title, text); + private void sendEmail(String email, String title, String text) { + SimpleMailMessage emailForm = createEmailForm(email, title, text); try { - mailSender.send(emailForm); + emailSender.send(emailForm); } catch (RuntimeException e) { - throw new MailException(UNABLE_TO_SEND_MAIL); + throw new EmailException(UNABLE_TO_SEND_EMAIL); } } - private SimpleMailMessage createMailForm(String mail, String title, String text) { + private SimpleMailMessage createEmailForm(String email, String title, String text) { SimpleMailMessage message = new SimpleMailMessage(); - message.setTo(mail); + message.setTo(email); message.setSubject(title); message.setText(text); return message; } - public boolean verifyCode(String mail, String codeInput) { - String code = redisDao.getCode(mail); - return code.equals(codeInput); + public boolean verifyCode(String email, String codeInput) { + try { + String code = redisDao.getCode(email); + return code.equals(codeInput); + } catch (NullPointerException e) { + throw new EmailException(WRONG_EMAIL); + } } } diff --git a/src/main/java/capstone/facefriend/mail/support/VerificationContext.java b/src/main/java/capstone/facefriend/email/support/VerificationContext.java similarity index 79% rename from src/main/java/capstone/facefriend/mail/support/VerificationContext.java rename to src/main/java/capstone/facefriend/email/support/VerificationContext.java index 5377826248..70b709b8d6 100644 --- a/src/main/java/capstone/facefriend/mail/support/VerificationContext.java +++ b/src/main/java/capstone/facefriend/email/support/VerificationContext.java @@ -1,8 +1,8 @@ -package capstone.facefriend.mail.support; +package capstone.facefriend.email.support; -import capstone.facefriend.mail.exception.VerificationException; -import capstone.facefriend.mail.exception.VerificationExceptionType; +import capstone.facefriend.email.exception.VerificationException; +import capstone.facefriend.email.exception.VerificationExceptionType; import org.springframework.stereotype.Component; import org.springframework.web.context.annotation.RequestScope; diff --git a/src/main/java/capstone/facefriend/mail/support/VerifiedMember.java b/src/main/java/capstone/facefriend/email/support/VerifiedMember.java similarity index 86% rename from src/main/java/capstone/facefriend/mail/support/VerifiedMember.java rename to src/main/java/capstone/facefriend/email/support/VerifiedMember.java index ce44d49809..e326153994 100644 --- a/src/main/java/capstone/facefriend/mail/support/VerifiedMember.java +++ b/src/main/java/capstone/facefriend/email/support/VerifiedMember.java @@ -1,4 +1,4 @@ -package capstone.facefriend.mail.support; +package capstone.facefriend.email.support; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; diff --git a/src/main/java/capstone/facefriend/mail/controller/MailController.java b/src/main/java/capstone/facefriend/mail/controller/MailController.java deleted file mode 100644 index 46f4840868..0000000000 --- a/src/main/java/capstone/facefriend/mail/controller/MailController.java +++ /dev/null @@ -1,58 +0,0 @@ -package capstone.facefriend.mail.controller; - -import capstone.facefriend.auth.controller.support.AuthenticationExtractor; -import capstone.facefriend.auth.domain.TokenProvider; -import capstone.facefriend.auth.exception.AuthException; -import capstone.facefriend.mail.controller.dto.MailVerificationResponse; -import capstone.facefriend.mail.service.MailService; -import capstone.facefriend.member.domain.Member; -import capstone.facefriend.member.domain.MemberRepository; -import capstone.facefriend.member.exception.MemberException; -import jakarta.servlet.http.HttpServletRequest; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; - -import static capstone.facefriend.auth.exception.AuthExceptionType.UNAUTHORIZED; -import static capstone.facefriend.member.exception.MemberExceptionType.NOT_FOUND; - -@RestController -@RequiredArgsConstructor -@Slf4j -public class MailController { - - private final MailService mailService; - private final MemberRepository memberRepository; - private final TokenProvider tokenProvider; - - @PostMapping("/mail/send-code") - public ResponseEntity sendCode( - @RequestParam("mail") String mail - ) { - return ResponseEntity.ok(mailService.sendCode(mail)); - } - - @GetMapping("/mail/verify-code") - public ResponseEntity submitCode( - HttpServletRequest request, - @RequestParam("mail") String mail, - @RequestParam("code") String code - ) { - String accessToken = AuthenticationExtractor.extractAccessToken(request) - .orElseThrow(() -> new AuthException(UNAUTHORIZED)); - Long memberId = tokenProvider.extractId(accessToken); - Member member = memberRepository.findById(memberId) - .orElseThrow(() -> new MemberException(NOT_FOUND)); - - boolean isVerified = mailService.verifyCode(mail, code); - - member.setIsVerified(isVerified); - memberRepository.save(member); - - return ResponseEntity.ok(new MailVerificationResponse(isVerified)); - } -} diff --git a/src/main/java/capstone/facefriend/mail/controller/dto/MailVerificationResponse.java b/src/main/java/capstone/facefriend/mail/controller/dto/MailVerificationResponse.java deleted file mode 100644 index 4380c70ca3..0000000000 --- a/src/main/java/capstone/facefriend/mail/controller/dto/MailVerificationResponse.java +++ /dev/null @@ -1,6 +0,0 @@ -package capstone.facefriend.mail.controller.dto; - -public record MailVerificationResponse( - boolean isVerified -) { -} From cf56bc815003deead68f5376a403c806c1e40aa6 Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Sat, 23 Mar 2024 14:44:05 +0900 Subject: [PATCH 061/265] =?UTF-8?q?feat:=20=EB=B3=B8=EC=9D=B8=EC=9D=B8?= =?UTF-8?q?=EC=A6=9D=EC=9D=84=20=ED=9A=8C=EC=9B=90=EA=B0=80=EC=9E=85?= =?UTF-8?q?=EC=97=90=20=ED=8F=AC=ED=95=A8=ED=95=98=EB=8A=94=20=EA=B2=83?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20=EB=A1=9C=EC=A7=81=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../facefriend/auth/config/AuthConfig.java | 8 +-- .../interceptor/TokenReissueInterceptor.java | 2 +- .../auth/exception/AuthExceptionType.java | 2 +- .../facefriend/auth/service/AuthService.java | 2 + .../member/controller/MemberController.java | 44 +++++++++++--- .../facefriend/member/domain/Member.java | 5 -- .../member/exception/MemberExceptionType.java | 4 +- .../member/service/MemberService.java | 58 +++++++++++++------ .../member/service/dto/FindEmailRequest.java | 7 +++ .../member/service/dto/FindEmailResponse.java | 7 +++ .../capstone/facefriend/redis/RedisDao.java | 5 +- src/main/resources/static/index.html | 4 +- 12 files changed, 108 insertions(+), 40 deletions(-) create mode 100644 src/main/java/capstone/facefriend/member/service/dto/FindEmailRequest.java create mode 100644 src/main/java/capstone/facefriend/member/service/dto/FindEmailResponse.java diff --git a/src/main/java/capstone/facefriend/auth/config/AuthConfig.java b/src/main/java/capstone/facefriend/auth/config/AuthConfig.java index 21032aca9c..6f831d536a 100644 --- a/src/main/java/capstone/facefriend/auth/config/AuthConfig.java +++ b/src/main/java/capstone/facefriend/auth/config/AuthConfig.java @@ -2,7 +2,7 @@ import capstone.facefriend.auth.controller.AuthArgumentResolver; import capstone.facefriend.auth.controller.interceptor.*; -import capstone.facefriend.mail.controller.interceptor.VerificationInterceptor; +import capstone.facefriend.email.controller.interceptor.VerificationInterceptor; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.context.annotation.Bean; @@ -49,9 +49,7 @@ private HandlerInterceptor loginCheckInterceptor() { return new PathMatchInterceptor(loginCheckInterceptor) .addExcludePathPattern("/**", OPTIONS) - .addIncludePathPattern("/oauth/google/login", POST) - .addIncludePathPattern("/members/signin", POST) - + .addIncludePathPattern("/find-email", POST) .addIncludePathPattern("/signout", DELETE) .addIncludePathPattern("/test", GET) @@ -62,6 +60,7 @@ private HandlerInterceptor loginInterceptor() { return new PathMatchInterceptor(loginInterceptor) .addExcludePathPattern("/**", OPTIONS) + .addIncludePathPattern("/find-email", POST) .addIncludePathPattern("/signout", DELETE) .addIncludePathPattern("/test", GET) @@ -79,6 +78,7 @@ private HandlerInterceptor tokenBlackListInterceptor() { return new PathMatchInterceptor(tokenBlackListInterceptor) .addExcludePathPattern("/**", OPTIONS) + .addIncludePathPattern("/find-email", POST) .addIncludePathPattern("/test", GET); } diff --git a/src/main/java/capstone/facefriend/auth/controller/interceptor/TokenReissueInterceptor.java b/src/main/java/capstone/facefriend/auth/controller/interceptor/TokenReissueInterceptor.java index f869d13ab5..76ae874ccc 100644 --- a/src/main/java/capstone/facefriend/auth/controller/interceptor/TokenReissueInterceptor.java +++ b/src/main/java/capstone/facefriend/auth/controller/interceptor/TokenReissueInterceptor.java @@ -3,7 +3,7 @@ import capstone.facefriend.auth.controller.support.AuthenticationContext; import capstone.facefriend.auth.controller.support.AuthenticationExtractor; import capstone.facefriend.auth.domain.TokenProvider; -import capstone.facefriend.mail.controller.interceptor.VerificationInterceptor; +import capstone.facefriend.email.controller.interceptor.VerificationInterceptor; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import lombok.RequiredArgsConstructor; diff --git a/src/main/java/capstone/facefriend/auth/exception/AuthExceptionType.java b/src/main/java/capstone/facefriend/auth/exception/AuthExceptionType.java index 3f04817e68..12a3d245b5 100644 --- a/src/main/java/capstone/facefriend/auth/exception/AuthExceptionType.java +++ b/src/main/java/capstone/facefriend/auth/exception/AuthExceptionType.java @@ -6,7 +6,7 @@ public enum AuthExceptionType implements ExceptionType { INVALID_AUTH_PROVIDER(Status.UNAUTHORIZED, 2001, "지원하지 않는 로그인 플랫폼입니다."), - SIGNATURE_NOT_FOUND(Status.BAD_REQUEST, 2002, "JWT 서명을 확인하지 못했습니다."), + SIGNATURE_NOT_FOUND(Status.BAD_REQUEST, 2002, "JWT 서명을 확인하지 못했습니다. 다시 로그인하시기 바랍니다."), MALFORMED_TOKEN(Status.BAD_REQUEST, 2003, "토큰의 길이 및 형식이 올바르지 않습니다."), EXPIRED_TOKEN(Status.UNAUTHORIZED, 2004, "이미 만료된 토큰입니다. 토큰을 재발급하시기 바랍니다."), UNSUPPORTED_TOKEN(Status.BAD_REQUEST, 2005, "지원되지 않는 토큰입니다."), diff --git a/src/main/java/capstone/facefriend/auth/service/AuthService.java b/src/main/java/capstone/facefriend/auth/service/AuthService.java index 5d5b7766e9..76bc23a460 100644 --- a/src/main/java/capstone/facefriend/auth/service/AuthService.java +++ b/src/main/java/capstone/facefriend/auth/service/AuthService.java @@ -9,6 +9,7 @@ import capstone.facefriend.member.domain.MemberRepository; import capstone.facefriend.member.domain.Role; import lombok.RequiredArgsConstructor; +import org.springframework.expression.ExpressionException; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -36,6 +37,7 @@ public TokenResponse generateTokens(OAuthMember oAuthMember) { .name(oAuthMember.nickname()) .password(TEMPORARY_GOOGLE_PASSWORD) .imageUrl(oAuthMember.imageUrl()) + .isVerified(true) .role(USER) .build(); Member member = memberRepository.findByEmail(oAuthMember.email()) diff --git a/src/main/java/capstone/facefriend/member/controller/MemberController.java b/src/main/java/capstone/facefriend/member/controller/MemberController.java index 316dda0ee3..c6b19ac600 100644 --- a/src/main/java/capstone/facefriend/member/controller/MemberController.java +++ b/src/main/java/capstone/facefriend/member/controller/MemberController.java @@ -4,11 +4,10 @@ import capstone.facefriend.auth.controller.dto.TokenResponse; import capstone.facefriend.auth.controller.support.AuthMember; import capstone.facefriend.auth.controller.support.AuthenticationExtractor; +import capstone.facefriend.email.controller.dto.EmailVerificationResponse; import capstone.facefriend.member.exception.MemberException; import capstone.facefriend.member.service.MemberService; -import capstone.facefriend.member.service.dto.ReissueRequest; -import capstone.facefriend.member.service.dto.SignInRequest; -import capstone.facefriend.member.service.dto.SignUpRequest; +import capstone.facefriend.member.service.dto.*; import jakarta.servlet.http.HttpServletRequest; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -24,33 +23,62 @@ public class MemberController { private final MemberService memberService; + @PostMapping("/members/send-code") + public ResponseEntity sendCode( + @RequestParam("email") String email) { + return ResponseEntity.ok(memberService.sendCode(email)); + } + + @GetMapping("/members/verify-code") + public ResponseEntity verifyCode( + @RequestParam("email") String email, + @RequestParam("code") String code) { + return ResponseEntity.ok(memberService.verifyCode(email, code)); + } + // 일반 유저 인증 인가 @PostMapping("/members/signup") - public ResponseEntity signUp(@RequestBody SignUpRequest request) { - return ResponseEntity.ok(memberService.signUp(request)); + public ResponseEntity signUp( + @RequestBody SignUpRequest request, + @RequestParam("isVerified") boolean isVerified) { + return ResponseEntity.ok(memberService.signUp(request, isVerified)); } // 일반 유저 인증 인가 @PostMapping("/members/signin") - public ResponseEntity signIn(@RequestBody SignInRequest request) { + public ResponseEntity signIn( + @RequestBody SignInRequest request) { return ResponseEntity.ok(memberService.signIn(request)); } // 구글 유저, 일반 유저 공통 로직 @PostMapping("/reissue") - public ResponseEntity reissueTokens(@RequestBody ReissueRequest request, @AuthMember Long memberId) { + public ResponseEntity reissueTokens( + @RequestBody ReissueRequest request, + @AuthMember Long memberId) { String refreshToken = request.refreshToken(); return ResponseEntity.ok(memberService.reissueTokens(memberId, refreshToken)); } // 구글 유저, 일반 유저 공통 로직 @DeleteMapping("/signout") - public ResponseEntity signOut(HttpServletRequest request, @AuthMember Long memberId) { + public ResponseEntity signOut( + HttpServletRequest request, + @AuthMember Long memberId) { String accessToken = AuthenticationExtractor.extractAccessToken(request) .orElseThrow(() -> new MemberException(UNAUTHORIZED)); return ResponseEntity.ok(memberService.signOut(memberId, accessToken)); } + @PostMapping("/find-email") + public ResponseEntity findMail( + @RequestBody FindEmailRequest request, + @AuthMember Long memberId) { + String name = request.name(); + String email = request.email(); + return ResponseEntity.ok(memberService.findEmail(name, email, memberId)); + } + // 구글 유저, 일반 유저 공통 로직 (테스트용) @GetMapping("/test") public ResponseEntity test() { diff --git a/src/main/java/capstone/facefriend/member/domain/Member.java b/src/main/java/capstone/facefriend/member/domain/Member.java index 0417434376..134f9df1a6 100644 --- a/src/main/java/capstone/facefriend/member/domain/Member.java +++ b/src/main/java/capstone/facefriend/member/domain/Member.java @@ -31,9 +31,6 @@ public class Member extends BaseEntity { private String imageUrl; - @Column(unique = true) - private String phone; - @Column private boolean isVerified; @@ -63,8 +60,6 @@ public void setRole(Role role) { } public void setIsVerified(boolean isVerified) { - log.info("[ Member ] 현재 isVerified = {}", this.isVerified); - log.info("[ Member ] 들어온 isVerified = {}", isVerified); this.isVerified = isVerified; } diff --git a/src/main/java/capstone/facefriend/member/exception/MemberExceptionType.java b/src/main/java/capstone/facefriend/member/exception/MemberExceptionType.java index 77d70ac088..83dfc8fa1c 100644 --- a/src/main/java/capstone/facefriend/member/exception/MemberExceptionType.java +++ b/src/main/java/capstone/facefriend/member/exception/MemberExceptionType.java @@ -14,7 +14,9 @@ public enum MemberExceptionType implements ExceptionType { EXPIRED_ACCESS_TOKEN(Status.BAD_REQUEST, 3008, "만료된 액세스 토큰이므로 재발급해야 합니다."), INVALID_ACCESS_TOKEN(Status.BAD_REQUEST, 3009, "유효하지 않은 액세스 토큰이므로 재발급해야 합니다."), INVALID_REFRESH_TOKEN(Status.BAD_REQUEST, 3010, "유효하지 않은 리프레시 토큰입니다. 토큰 재발급이 불가능합니다."), - ALREADY_SIGN_OUT_ACCESS_TOKEN(Status.BAD_REQUEST, 3011, "액세스 토큰이 이미 로그아웃 처리되었습니다. 재로그인하시기 바랍니다."); + ACCESS_TOKEN_IS_IN_BLACKLIST(Status.BAD_REQUEST, 3011, "액세스 토큰이 로그아웃 처리되었습니다. 재로그인하시기 바랍니다."), + NOT_VERIFIED(Status.BAD_REQUEST, 3012, "본인 인증을 먼저 완료해야 합니다.") + ; private final Status status; private final int exceptionCode; diff --git a/src/main/java/capstone/facefriend/member/service/MemberService.java b/src/main/java/capstone/facefriend/member/service/MemberService.java index ce417934f5..95182faf5d 100644 --- a/src/main/java/capstone/facefriend/member/service/MemberService.java +++ b/src/main/java/capstone/facefriend/member/service/MemberService.java @@ -2,9 +2,12 @@ import capstone.facefriend.auth.controller.dto.TokenResponse; import capstone.facefriend.auth.domain.TokenProvider; +import capstone.facefriend.email.controller.dto.EmailVerificationResponse; +import capstone.facefriend.email.service.EmailService; import capstone.facefriend.member.domain.Member; import capstone.facefriend.member.domain.MemberRepository; import capstone.facefriend.member.exception.MemberException; +import capstone.facefriend.member.service.dto.FindEmailResponse; import capstone.facefriend.member.service.dto.SignInRequest; import capstone.facefriend.member.service.dto.SignUpRequest; import capstone.facefriend.redis.RedisDao; @@ -14,8 +17,6 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import java.util.Optional; - import static capstone.facefriend.member.domain.Role.USER; import static capstone.facefriend.member.exception.MemberExceptionType.*; @@ -27,6 +28,7 @@ public class MemberService { private final TokenProvider tokenProvider; private final MemberRepository memberRepository; private final PasswordEncoder passwordEncoder; + private final EmailService emailService; private final RedisDao redisDao; @@ -35,25 +37,34 @@ public class MemberService { private static final Long SIGN_OUT_MINUTE = 1000 * 60 * 60 * 12L; // 12 시간 @Transactional - public String signUp(SignUpRequest request) { - String email = request.email(); - - Optional isPresent = memberRepository.findByEmail(email); - if (isPresent.isPresent()) { + public String sendCode(String email) { + if (memberRepository.findByEmail(email).isPresent()) { throw new MemberException(DUPLICATED_EMAIL); } + return emailService.sendCode(email); + } - String encodedPassword = passwordEncoder.encode(request.password()); - - Member member = Member.builder() - .email(email) - .password(encodedPassword) - .name(request.name()) - .isVerified(false) - .role(USER) - .build(); - memberRepository.save(member); + @Transactional + public EmailVerificationResponse verifyCode(String email, String code) { + boolean isVerified = emailService.verifyCode(email, code); + return new EmailVerificationResponse(email, isVerified); + } + @Transactional + public String signUp(SignUpRequest request, boolean isVerified) { + if (isVerified) { + String encodedPassword = passwordEncoder.encode(request.password()); + Member member = Member.builder() + .email(request.email()) + .password(encodedPassword) + .name(request.name()) + .isVerified(true) + .role(USER) + .build(); + memberRepository.save(member); + } else { + throw new MemberException(NOT_VERIFIED); + } return SIGN_UP_SUCCESS_MESSAGE; } @@ -85,4 +96,17 @@ public TokenResponse reissueTokens(Long memberId, String refreshTokenInput) { } return tokenProvider.createTokens(memberId); } + + public FindEmailResponse findEmail(String nameInput, String emailInput, Long memberId) { + Member member = memberRepository.findById(memberId) + .orElseThrow(() -> new MemberException(NOT_FOUND)); + + String name = member.getName(); + String email = member.getEmail(); + + if (name.equals(nameInput) && email.equals(emailInput)) { + return new FindEmailResponse(email, true); + } + return new FindEmailResponse(email, false); + } } diff --git a/src/main/java/capstone/facefriend/member/service/dto/FindEmailRequest.java b/src/main/java/capstone/facefriend/member/service/dto/FindEmailRequest.java new file mode 100644 index 0000000000..5370ffdd62 --- /dev/null +++ b/src/main/java/capstone/facefriend/member/service/dto/FindEmailRequest.java @@ -0,0 +1,7 @@ +package capstone.facefriend.member.service.dto; + +public record FindEmailRequest( + String name, + String email +) { +} diff --git a/src/main/java/capstone/facefriend/member/service/dto/FindEmailResponse.java b/src/main/java/capstone/facefriend/member/service/dto/FindEmailResponse.java new file mode 100644 index 0000000000..e3ebe91d2b --- /dev/null +++ b/src/main/java/capstone/facefriend/member/service/dto/FindEmailResponse.java @@ -0,0 +1,7 @@ +package capstone.facefriend.member.service.dto; + +public record FindEmailResponse( + String email, + boolean isRegistered +) { +} diff --git a/src/main/java/capstone/facefriend/redis/RedisDao.java b/src/main/java/capstone/facefriend/redis/RedisDao.java index f40ae96916..6de5146920 100644 --- a/src/main/java/capstone/facefriend/redis/RedisDao.java +++ b/src/main/java/capstone/facefriend/redis/RedisDao.java @@ -7,9 +7,10 @@ import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; import org.springframework.stereotype.Component; + import java.util.concurrent.TimeUnit; -import static capstone.facefriend.member.exception.MemberExceptionType.ALREADY_SIGN_OUT_ACCESS_TOKEN; +import static capstone.facefriend.member.exception.MemberExceptionType.ACCESS_TOKEN_IS_IN_BLACKLIST; @Component @Slf4j @@ -40,7 +41,7 @@ public void setAccessTokenSignOut(String accessToken, Long minute) { public boolean isKeyOfAccessTokenInBlackList(String accessToken) { String signOutValue = redisTemplate.opsForValue().get(accessToken); if (signOutValue.equals(SIGN_OUT_VALUE)) { - throw new MemberException(ALREADY_SIGN_OUT_ACCESS_TOKEN); + throw new MemberException(ACCESS_TOKEN_IS_IN_BLACKLIST); } return false; } diff --git a/src/main/resources/static/index.html b/src/main/resources/static/index.html index 0ee7a2abbd..b20d46d18a 100644 --- a/src/main/resources/static/index.html +++ b/src/main/resources/static/index.html @@ -1 +1,3 @@ -

하이?

\ No newline at end of file +

YOU HAVE BEEN REDIRECTED TO [ localhost:8080/index.html ]

+

THIS REDIRECT URL IS SET BY [ KIM CHAN JIN ]

+

IF YOU WANT TO BE REDIRECTED TO ANOTHER URL, YOU SHOULD LET ME KNOW

\ No newline at end of file From 1c9f85df71cb7eb7123250e0f20c21d62cf1d606 Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Sat, 23 Mar 2024 16:05:17 +0900 Subject: [PATCH 062/265] =?UTF-8?q?feat:=20=EB=B9=84=EB=B0=80=EB=B2=88?= =?UTF-8?q?=ED=98=B8=20=EC=9E=AC=EC=84=A4=EC=A0=95=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../facefriend/auth/config/AuthConfig.java | 2 - .../email/service/EmailService.java | 31 +++++++++++--- .../member/controller/MemberController.java | 41 ++++++++++++++----- .../facefriend/member/domain/Member.java | 4 ++ .../member/exception/MemberExceptionType.java | 4 +- .../member/service/MemberService.java | 39 ++++++++++++++++-- .../service/dto/ResetPasswordRequest.java | 7 ++++ 7 files changed, 105 insertions(+), 23 deletions(-) create mode 100644 src/main/java/capstone/facefriend/member/service/dto/ResetPasswordRequest.java diff --git a/src/main/java/capstone/facefriend/auth/config/AuthConfig.java b/src/main/java/capstone/facefriend/auth/config/AuthConfig.java index 6f831d536a..b22a49a323 100644 --- a/src/main/java/capstone/facefriend/auth/config/AuthConfig.java +++ b/src/main/java/capstone/facefriend/auth/config/AuthConfig.java @@ -60,7 +60,6 @@ private HandlerInterceptor loginInterceptor() { return new PathMatchInterceptor(loginInterceptor) .addExcludePathPattern("/**", OPTIONS) - .addIncludePathPattern("/find-email", POST) .addIncludePathPattern("/signout", DELETE) .addIncludePathPattern("/test", GET) @@ -78,7 +77,6 @@ private HandlerInterceptor tokenBlackListInterceptor() { return new PathMatchInterceptor(tokenBlackListInterceptor) .addExcludePathPattern("/**", OPTIONS) - .addIncludePathPattern("/find-email", POST) .addIncludePathPattern("/test", GET); } diff --git a/src/main/java/capstone/facefriend/email/service/EmailService.java b/src/main/java/capstone/facefriend/email/service/EmailService.java index 9e46e8c59f..6221d46838 100644 --- a/src/main/java/capstone/facefriend/email/service/EmailService.java +++ b/src/main/java/capstone/facefriend/email/service/EmailService.java @@ -25,17 +25,27 @@ public class EmailService { @Value("${spring.mail.auth-code-expiration-millis}") private long authCodeExpirationMillis; - private static final String MAIL_TITLE = "[ FACE FRIEND ] 본인 인증을 위한 코드가 도착했어요! \uD83D\uDE0E"; - private static final String MAIL_SUCCESS = "이메일로 코드를 전송했습니다."; + private static final String EMAIL_TITLE_OF_VERIFICATION = "[ FACE FRIEND ] 본인 인증을 위한 코드가 도착했어요! \uD83D\uDE0E"; + private static final String EMAIL_TITLE_OF_RESET_PASSWORD = "[ FACE FRIEND ] 임시 비밀번호가 도착했어요! \uD83E\uDD13"; + private static final String EMAIL_SUCCESS_OF_VERIFICATION = "이메일로 인증코드를 전송했습니다."; + private static final String EMAIL_SUCCESS_OF_RESET_PASSWORD = "이메일로 임시 비밀번호를 전송했습니다."; + private final RedisDao redisDao; private final JavaMailSender emailSender; public String sendCode(String email) { - String code = createCode(); // 코드 생성 - sendEmail(email, MAIL_TITLE, code); // 메일로 코드 보내기 - redisDao.setCode(email, code, authCodeExpirationMillis); // 코드 레디스 저장 - return MAIL_SUCCESS; + String code = createCode(); + sendEmail(email, EMAIL_TITLE_OF_VERIFICATION, code); + redisDao.setCode(email, code, authCodeExpirationMillis); + return EMAIL_SUCCESS_OF_VERIFICATION; + } + + public String sendTemporaryPassword(String email) { + String code = createCode(); + sendEmail(email, EMAIL_TITLE_OF_RESET_PASSWORD, code); + redisDao.setCode(email, code, authCodeExpirationMillis); + return EMAIL_SUCCESS_OF_RESET_PASSWORD; } private String createCode() { @@ -77,4 +87,13 @@ public boolean verifyCode(String email, String codeInput) { throw new EmailException(WRONG_EMAIL); } } + + public boolean verifyTemporaryPassword(String email, String temporaryPassword) { + try { + String code = redisDao.getCode(email); + return code.equals(temporaryPassword); + } catch (NullPointerException e) { + throw new EmailException(WRONG_EMAIL); + } + } } diff --git a/src/main/java/capstone/facefriend/member/controller/MemberController.java b/src/main/java/capstone/facefriend/member/controller/MemberController.java index c6b19ac600..6c1772ed6d 100644 --- a/src/main/java/capstone/facefriend/member/controller/MemberController.java +++ b/src/main/java/capstone/facefriend/member/controller/MemberController.java @@ -14,6 +14,7 @@ import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; +import static capstone.facefriend.member.exception.MemberExceptionType.PASSWORDS_NOT_EQUAL; import static capstone.facefriend.member.exception.MemberExceptionType.UNAUTHORIZED; @Slf4j @@ -36,7 +37,6 @@ public ResponseEntity verifyCode( return ResponseEntity.ok(memberService.verifyCode(email, code)); } - // 일반 유저 인증 인가 @PostMapping("/members/signup") public ResponseEntity signUp( @RequestBody SignUpRequest request, @@ -44,14 +44,16 @@ public ResponseEntity signUp( return ResponseEntity.ok(memberService.signUp(request, isVerified)); } - // 일반 유저 인증 인가 @PostMapping("/members/signin") public ResponseEntity signIn( @RequestBody SignInRequest request) { return ResponseEntity.ok(memberService.signIn(request)); } - // 구글 유저, 일반 유저 공통 로직 + + + + @PostMapping("/reissue") public ResponseEntity reissueTokens( @RequestBody ReissueRequest request, @@ -60,7 +62,6 @@ public ResponseEntity reissueTokens( return ResponseEntity.ok(memberService.reissueTokens(memberId, refreshToken)); } - // 구글 유저, 일반 유저 공통 로직 @DeleteMapping("/signout") public ResponseEntity signOut( HttpServletRequest request, @@ -71,15 +72,33 @@ public ResponseEntity signOut( } @PostMapping("/find-email") - public ResponseEntity findMail( - @RequestBody FindEmailRequest request, - @AuthMember Long memberId) { - String name = request.name(); - String email = request.email(); - return ResponseEntity.ok(memberService.findEmail(name, email, memberId)); + public ResponseEntity findEmail( + @RequestBody FindEmailRequest request) { + return ResponseEntity.ok(memberService.findEmail(request.name(), request.email())); } - // 구글 유저, 일반 유저 공통 로직 (테스트용) + @PostMapping("/send-temporary-password") + public ResponseEntity sendTemporaryPassword( + @RequestParam("email") String email) { + return ResponseEntity.ok(memberService.sendTemporaryPassword(email)); + } + + @PostMapping("/verify-temporary-password") + public ResponseEntity resetNewPassword( + @RequestParam("email") String email, + @RequestParam("temporaryPassword") String temporaryPassword, + @RequestBody ResetPasswordRequest request + ) { + String newPassword = request.newPassword(); + String newPassword2 = request.newPassword2(); + + if (!newPassword.equals(newPassword2)) { + throw new MemberException(PASSWORDS_NOT_EQUAL) ; + } + return ResponseEntity.ok(memberService.verifyTemporaryPassword(email, temporaryPassword, newPassword)); + } + + @GetMapping("/test") public ResponseEntity test() { return ResponseEntity.ok("[ MemberController ] 테스트"); diff --git a/src/main/java/capstone/facefriend/member/domain/Member.java b/src/main/java/capstone/facefriend/member/domain/Member.java index 134f9df1a6..488c056857 100644 --- a/src/main/java/capstone/facefriend/member/domain/Member.java +++ b/src/main/java/capstone/facefriend/member/domain/Member.java @@ -66,4 +66,8 @@ public void setIsVerified(boolean isVerified) { public boolean isVerified() { return this.isVerified == true; } + + public void setPassword(String password) { + this.password = password; + } } \ No newline at end of file diff --git a/src/main/java/capstone/facefriend/member/exception/MemberExceptionType.java b/src/main/java/capstone/facefriend/member/exception/MemberExceptionType.java index 83dfc8fa1c..3bc166d454 100644 --- a/src/main/java/capstone/facefriend/member/exception/MemberExceptionType.java +++ b/src/main/java/capstone/facefriend/member/exception/MemberExceptionType.java @@ -15,7 +15,9 @@ public enum MemberExceptionType implements ExceptionType { INVALID_ACCESS_TOKEN(Status.BAD_REQUEST, 3009, "유효하지 않은 액세스 토큰이므로 재발급해야 합니다."), INVALID_REFRESH_TOKEN(Status.BAD_REQUEST, 3010, "유효하지 않은 리프레시 토큰입니다. 토큰 재발급이 불가능합니다."), ACCESS_TOKEN_IS_IN_BLACKLIST(Status.BAD_REQUEST, 3011, "액세스 토큰이 로그아웃 처리되었습니다. 재로그인하시기 바랍니다."), - NOT_VERIFIED(Status.BAD_REQUEST, 3012, "본인 인증을 먼저 완료해야 합니다.") + NOT_VERIFIED(Status.BAD_REQUEST, 3012, "본인 인증을 먼저 완료해야 합니다."), + PASSWORDS_NOT_EQUAL(Status.BAD_REQUEST, 3013, "재설정하는 비밀번호들이 동일하지 않습니다."), + WRONG_TEMPORARY_PASSWORD(Status.BAD_REQUEST, 3014, "임시 비밀번호가 올바르지 않습니다.") ; private final Status status; diff --git a/src/main/java/capstone/facefriend/member/service/MemberService.java b/src/main/java/capstone/facefriend/member/service/MemberService.java index 95182faf5d..7005da0627 100644 --- a/src/main/java/capstone/facefriend/member/service/MemberService.java +++ b/src/main/java/capstone/facefriend/member/service/MemberService.java @@ -34,6 +34,7 @@ public class MemberService { private static final String SIGN_UP_SUCCESS_MESSAGE = "회원가입 성공"; private static final String SIGN_OUT_SUCCESS_MESSAGE = "로그아웃 성공"; + private static final String RESET_PASSWORD_SUCCESS_MESSAGE = "비밀번호 재설정 성공"; private static final Long SIGN_OUT_MINUTE = 1000 * 60 * 60 * 12L; // 12 시간 @Transactional @@ -47,7 +48,11 @@ public String sendCode(String email) { @Transactional public EmailVerificationResponse verifyCode(String email, String code) { boolean isVerified = emailService.verifyCode(email, code); - return new EmailVerificationResponse(email, isVerified); + + if (isVerified) { + return new EmailVerificationResponse(email, true); + } + return new EmailVerificationResponse(email, false); } @Transactional @@ -82,6 +87,7 @@ public TokenResponse signIn(SignInRequest request) { return tokenProvider.createTokens(member.getId()); } + @Transactional public String signOut(Long memberId, String accessToken) { redisDao.deleteRefreshToken(String.valueOf(memberId)); @@ -97,8 +103,9 @@ public TokenResponse reissueTokens(Long memberId, String refreshTokenInput) { return tokenProvider.createTokens(memberId); } - public FindEmailResponse findEmail(String nameInput, String emailInput, Long memberId) { - Member member = memberRepository.findById(memberId) + public FindEmailResponse findEmail(String nameInput, String emailInput) { + + Member member = memberRepository.findByEmail(emailInput) .orElseThrow(() -> new MemberException(NOT_FOUND)); String name = member.getName(); @@ -109,4 +116,30 @@ public FindEmailResponse findEmail(String nameInput, String emailInput, Long mem } return new FindEmailResponse(email, false); } + + @Transactional + public String sendTemporaryPassword(String email) { + if (!memberRepository.findByEmail(email).isPresent()) { + throw new MemberException(NOT_FOUND); + } + return emailService.sendTemporaryPassword(email); + } + + @Transactional + public String verifyTemporaryPassword(String email, String temporaryPassword, String newPassword) { + boolean isVerified = emailService.verifyTemporaryPassword(email, temporaryPassword); + + if (isVerified) { + Member member = memberRepository.findByEmail(email) + .orElseThrow(() -> new MemberException(NOT_FOUND)); + + String encodedPassword = passwordEncoder.encode(newPassword); + member.setPassword(encodedPassword); + memberRepository.save(member); + } else { + throw new MemberException(WRONG_TEMPORARY_PASSWORD); + } + return RESET_PASSWORD_SUCCESS_MESSAGE; + + } } diff --git a/src/main/java/capstone/facefriend/member/service/dto/ResetPasswordRequest.java b/src/main/java/capstone/facefriend/member/service/dto/ResetPasswordRequest.java new file mode 100644 index 0000000000..812f0e5a13 --- /dev/null +++ b/src/main/java/capstone/facefriend/member/service/dto/ResetPasswordRequest.java @@ -0,0 +1,7 @@ +package capstone.facefriend.member.service.dto; + +public record ResetPasswordRequest( + String newPassword, + String newPassword2 +) { +} From bebeeddeb20070b2b00c169b29ff9ba992a214c6 Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Sat, 23 Mar 2024 16:16:52 +0900 Subject: [PATCH 063/265] =?UTF-8?q?fix:=20AuthConfig=20loginInterceptor=20?= =?UTF-8?q?=EA=B2=BD=EB=A1=9C=20=EC=A4=91=20find-email=20=EC=A0=9C?= =?UTF-8?q?=EC=99=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/capstone/facefriend/auth/config/AuthConfig.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/capstone/facefriend/auth/config/AuthConfig.java b/src/main/java/capstone/facefriend/auth/config/AuthConfig.java index b22a49a323..1e01311647 100644 --- a/src/main/java/capstone/facefriend/auth/config/AuthConfig.java +++ b/src/main/java/capstone/facefriend/auth/config/AuthConfig.java @@ -49,7 +49,6 @@ private HandlerInterceptor loginCheckInterceptor() { return new PathMatchInterceptor(loginCheckInterceptor) .addExcludePathPattern("/**", OPTIONS) - .addIncludePathPattern("/find-email", POST) .addIncludePathPattern("/signout", DELETE) .addIncludePathPattern("/test", GET) From 6ddb7216d6b41713d674e4c8a20ae4751a82b577 Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Mon, 25 Mar 2024 15:30:26 +0900 Subject: [PATCH 064/265] =?UTF-8?q?refactor:=20=ED=82=A4=EB=B3=B4=EB=93=9C?= =?UTF-8?q?=20=ED=8F=AC=EC=BB=A4=EC=8A=A4=20=EC=9E=83=EC=9D=84=20=EB=95=8C?= =?UTF-8?q?=20=EC=9D=B4=EB=A9=94=EC=9D=BC=20=EC=A4=91=EB=B3=B5=EC=B2=B4?= =?UTF-8?q?=ED=81=AC=ED=95=98=EA=B8=B0=20=EC=9C=84=ED=95=B4=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../member/controller/MemberController.java | 42 +++++++++++++------ .../member/exception/MemberExceptionType.java | 3 +- .../member/service/MemberService.java | 9 ++++ 3 files changed, 41 insertions(+), 13 deletions(-) diff --git a/src/main/java/capstone/facefriend/member/controller/MemberController.java b/src/main/java/capstone/facefriend/member/controller/MemberController.java index 6c1772ed6d..22f94ed7a1 100644 --- a/src/main/java/capstone/facefriend/member/controller/MemberController.java +++ b/src/main/java/capstone/facefriend/member/controller/MemberController.java @@ -24,40 +24,56 @@ public class MemberController { private final MemberService memberService; + /** + * 일반 유저 고유 로직 + */ + @PostMapping("/members/verify-duplication") + public ResponseEntity verifyDuplication( + @RequestParam("email") String email + ) { + return ResponseEntity.ok(memberService.verifyDuplication(email)); + } + + @PostMapping("/members/send-code") public ResponseEntity sendCode( - @RequestParam("email") String email) { + @RequestParam("email") String email + ) { return ResponseEntity.ok(memberService.sendCode(email)); } @GetMapping("/members/verify-code") public ResponseEntity verifyCode( @RequestParam("email") String email, - @RequestParam("code") String code) { + @RequestParam("code") String code + ) { return ResponseEntity.ok(memberService.verifyCode(email, code)); } @PostMapping("/members/signup") public ResponseEntity signUp( @RequestBody SignUpRequest request, - @RequestParam("isVerified") boolean isVerified) { + @RequestParam("isVerified") boolean isVerified + ) { return ResponseEntity.ok(memberService.signUp(request, isVerified)); } @PostMapping("/members/signin") public ResponseEntity signIn( - @RequestBody SignInRequest request) { + @RequestBody SignInRequest request + ) { return ResponseEntity.ok(memberService.signIn(request)); } - - - + /** + * 구글 유저, 일반 유저 공통 로직 + */ @PostMapping("/reissue") public ResponseEntity reissueTokens( @RequestBody ReissueRequest request, - @AuthMember Long memberId) { + @AuthMember Long memberId + ) { String refreshToken = request.refreshToken(); return ResponseEntity.ok(memberService.reissueTokens(memberId, refreshToken)); } @@ -65,7 +81,8 @@ public ResponseEntity reissueTokens( @DeleteMapping("/signout") public ResponseEntity signOut( HttpServletRequest request, - @AuthMember Long memberId) { + @AuthMember Long memberId + ) { String accessToken = AuthenticationExtractor.extractAccessToken(request) .orElseThrow(() -> new MemberException(UNAUTHORIZED)); return ResponseEntity.ok(memberService.signOut(memberId, accessToken)); @@ -73,13 +90,15 @@ public ResponseEntity signOut( @PostMapping("/find-email") public ResponseEntity findEmail( - @RequestBody FindEmailRequest request) { + @RequestBody FindEmailRequest request + ) { return ResponseEntity.ok(memberService.findEmail(request.name(), request.email())); } @PostMapping("/send-temporary-password") public ResponseEntity sendTemporaryPassword( - @RequestParam("email") String email) { + @RequestParam("email") String email + ) { return ResponseEntity.ok(memberService.sendTemporaryPassword(email)); } @@ -98,7 +117,6 @@ public ResponseEntity resetNewPassword( return ResponseEntity.ok(memberService.verifyTemporaryPassword(email, temporaryPassword, newPassword)); } - @GetMapping("/test") public ResponseEntity test() { return ResponseEntity.ok("[ MemberController ] 테스트"); diff --git a/src/main/java/capstone/facefriend/member/exception/MemberExceptionType.java b/src/main/java/capstone/facefriend/member/exception/MemberExceptionType.java index 3bc166d454..b977a6bddc 100644 --- a/src/main/java/capstone/facefriend/member/exception/MemberExceptionType.java +++ b/src/main/java/capstone/facefriend/member/exception/MemberExceptionType.java @@ -17,7 +17,8 @@ public enum MemberExceptionType implements ExceptionType { ACCESS_TOKEN_IS_IN_BLACKLIST(Status.BAD_REQUEST, 3011, "액세스 토큰이 로그아웃 처리되었습니다. 재로그인하시기 바랍니다."), NOT_VERIFIED(Status.BAD_REQUEST, 3012, "본인 인증을 먼저 완료해야 합니다."), PASSWORDS_NOT_EQUAL(Status.BAD_REQUEST, 3013, "재설정하는 비밀번호들이 동일하지 않습니다."), - WRONG_TEMPORARY_PASSWORD(Status.BAD_REQUEST, 3014, "임시 비밀번호가 올바르지 않습니다.") + WRONG_TEMPORARY_PASSWORD(Status.BAD_REQUEST, 3014, "임시 비밀번호가 올바르지 않습니다."), + NOT_FOUND_GENDER(Status.NOT_FOUND, 3015, "일치하는 성별이 없습니다.") ; private final Status status; diff --git a/src/main/java/capstone/facefriend/member/service/MemberService.java b/src/main/java/capstone/facefriend/member/service/MemberService.java index 7005da0627..4a545c9a72 100644 --- a/src/main/java/capstone/facefriend/member/service/MemberService.java +++ b/src/main/java/capstone/facefriend/member/service/MemberService.java @@ -32,11 +32,20 @@ public class MemberService { private final RedisDao redisDao; + private static final String SIGN_UP_VALID_EMAIL = "사용 가능한 이메일입니다."; private static final String SIGN_UP_SUCCESS_MESSAGE = "회원가입 성공"; private static final String SIGN_OUT_SUCCESS_MESSAGE = "로그아웃 성공"; private static final String RESET_PASSWORD_SUCCESS_MESSAGE = "비밀번호 재설정 성공"; private static final Long SIGN_OUT_MINUTE = 1000 * 60 * 60 * 12L; // 12 시간 + @Transactional + public String verifyDuplication(String email) { + if(memberRepository.findByEmail(email).isPresent()) { + throw new MemberException(DUPLICATED_EMAIL); + } + return SIGN_UP_VALID_EMAIL; + } + @Transactional public String sendCode(String email) { if (memberRepository.findByEmail(email).isPresent()) { From 221893dfaa99ba8ed98343d9d06a36bc5b43a066 Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Mon, 25 Mar 2024 16:38:39 +0900 Subject: [PATCH 065/265] =?UTF-8?q?refactor:=20name=20=ED=95=84=EB=93=9C?= =?UTF-8?q?=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../capstone/facefriend/FacefriendApplication.java | 4 ++-- .../capstone/facefriend/auth/service/AuthService.java | 2 +- .../facefriend/member/controller/MemberController.java | 7 +++---- .../java/capstone/facefriend/member/domain/Member.java | 10 ++++++++-- .../facefriend/member/service/MemberService.java | 10 +++++----- .../facefriend/member/service/dto/SignUpRequest.java | 2 +- 6 files changed, 20 insertions(+), 15 deletions(-) diff --git a/src/main/java/capstone/facefriend/FacefriendApplication.java b/src/main/java/capstone/facefriend/FacefriendApplication.java index 78f26d506d..a197dfe24c 100644 --- a/src/main/java/capstone/facefriend/FacefriendApplication.java +++ b/src/main/java/capstone/facefriend/FacefriendApplication.java @@ -8,11 +8,11 @@ @EnableJpaAuditing @SpringBootApplication -public class FaceFriendApplication { +public class FacefriendApplication { public static void main(String[] args) { TimeZone.setDefault(TimeZone.getTimeZone("Asia/Seoul")); - SpringApplication.run(FaceFriendApplication.class, args); + SpringApplication.run(FacefriendApplication.class, args); } } diff --git a/src/main/java/capstone/facefriend/auth/service/AuthService.java b/src/main/java/capstone/facefriend/auth/service/AuthService.java index 76bc23a460..7b888cfc88 100644 --- a/src/main/java/capstone/facefriend/auth/service/AuthService.java +++ b/src/main/java/capstone/facefriend/auth/service/AuthService.java @@ -34,7 +34,7 @@ public String loginUri(String redirectUri, String provider) { public TokenResponse generateTokens(OAuthMember oAuthMember) { Member newMember = Member.builder() .email(oAuthMember.email()) - .name(oAuthMember.nickname()) + .nickname(oAuthMember.nickname()) .password(TEMPORARY_GOOGLE_PASSWORD) .imageUrl(oAuthMember.imageUrl()) .isVerified(true) diff --git a/src/main/java/capstone/facefriend/member/controller/MemberController.java b/src/main/java/capstone/facefriend/member/controller/MemberController.java index 22f94ed7a1..31a99a9a39 100644 --- a/src/main/java/capstone/facefriend/member/controller/MemberController.java +++ b/src/main/java/capstone/facefriend/member/controller/MemberController.java @@ -52,10 +52,9 @@ public ResponseEntity verifyCode( @PostMapping("/members/signup") public ResponseEntity signUp( - @RequestBody SignUpRequest request, - @RequestParam("isVerified") boolean isVerified + @RequestBody SignUpRequest request ) { - return ResponseEntity.ok(memberService.signUp(request, isVerified)); + return ResponseEntity.ok(memberService.signUp(request)); } @PostMapping("/members/signin") @@ -92,7 +91,7 @@ public ResponseEntity signOut( public ResponseEntity findEmail( @RequestBody FindEmailRequest request ) { - return ResponseEntity.ok(memberService.findEmail(request.name(), request.email())); + return ResponseEntity.ok(memberService.findEmail(request.email())); } @PostMapping("/send-temporary-password") diff --git a/src/main/java/capstone/facefriend/member/domain/Member.java b/src/main/java/capstone/facefriend/member/domain/Member.java index 488c056857..386708373a 100644 --- a/src/main/java/capstone/facefriend/member/domain/Member.java +++ b/src/main/java/capstone/facefriend/member/domain/Member.java @@ -4,6 +4,7 @@ import jakarta.persistence.*; import lombok.*; import lombok.extern.slf4j.Slf4j; +import org.hibernate.annotations.DynamicInsert; @Getter @Builder @@ -12,6 +13,7 @@ @NoArgsConstructor(access = AccessLevel.PROTECTED) @Entity @Slf4j +@DynamicInsert public class Member extends BaseEntity { private static final int EMAIL_MASKING_LENGTH = 2; @@ -23,8 +25,8 @@ public class Member extends BaseEntity { @Column(nullable = false, unique = true) private String email; - @Column(nullable = false) - private String name; + @Column + private String nickname; @Column(nullable = false) private String password; @@ -38,6 +40,10 @@ public class Member extends BaseEntity { @Column(nullable = false) private Role role; + @OneToOne + @JoinColumn(name = "BASIC_INFO_ID") + private BasicInfo basicInfo; + public Member(String email) { this.email = email; this.role = Role.USER; diff --git a/src/main/java/capstone/facefriend/member/service/MemberService.java b/src/main/java/capstone/facefriend/member/service/MemberService.java index 4a545c9a72..cb648ea590 100644 --- a/src/main/java/capstone/facefriend/member/service/MemberService.java +++ b/src/main/java/capstone/facefriend/member/service/MemberService.java @@ -65,13 +65,14 @@ public EmailVerificationResponse verifyCode(String email, String code) { } @Transactional - public String signUp(SignUpRequest request, boolean isVerified) { + public String signUp(SignUpRequest request) { + boolean isVerified = request.isVerified(); + if (isVerified) { String encodedPassword = passwordEncoder.encode(request.password()); Member member = Member.builder() .email(request.email()) .password(encodedPassword) - .name(request.name()) .isVerified(true) .role(USER) .build(); @@ -112,15 +113,14 @@ public TokenResponse reissueTokens(Long memberId, String refreshTokenInput) { return tokenProvider.createTokens(memberId); } - public FindEmailResponse findEmail(String nameInput, String emailInput) { + public FindEmailResponse findEmail(String emailInput) { Member member = memberRepository.findByEmail(emailInput) .orElseThrow(() -> new MemberException(NOT_FOUND)); - String name = member.getName(); String email = member.getEmail(); - if (name.equals(nameInput) && email.equals(emailInput)) { + if (email.equals(emailInput)) { return new FindEmailResponse(email, true); } return new FindEmailResponse(email, false); diff --git a/src/main/java/capstone/facefriend/member/service/dto/SignUpRequest.java b/src/main/java/capstone/facefriend/member/service/dto/SignUpRequest.java index ccce672f8c..84ed5f3240 100644 --- a/src/main/java/capstone/facefriend/member/service/dto/SignUpRequest.java +++ b/src/main/java/capstone/facefriend/member/service/dto/SignUpRequest.java @@ -4,6 +4,6 @@ public record SignUpRequest( String email, String password, String password2, - String name + boolean isVerified ) { } From ad3714c5977f21c0465fbed4ca4ea41cdd19d7bf Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Mon, 25 Mar 2024 21:45:10 +0900 Subject: [PATCH 066/265] =?UTF-8?q?feat:=20=EB=A9=A4=EB=B2=84=20=EA=B8=B0?= =?UTF-8?q?=EB=B3=B8=EC=A0=95=EB=B3=B4=20CRUD=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../facefriend/auth/config/AuthConfig.java | 16 ++- .../auth/exception/AuthExceptionType.java | 2 +- .../controller/BasicInfoController.java | 43 +++++++ .../member/controller/MemberController.java | 88 ++++++------- .../facefriend/member/domain/BasicInfo.java | 118 +++++++++++++++++- .../member/domain/BasicInfoRepository.java | 10 ++ .../facefriend/member/domain/Member.java | 4 +- .../member/service/BasicInfoService.java | 82 ++++++++++++ .../member/service/dto/BasicInfoRequest.java | 11 ++ .../member/service/dto/BasicInfoResponse.java | 23 ++++ .../member/service/dto/FindEmailRequest.java | 7 -- 11 files changed, 336 insertions(+), 68 deletions(-) create mode 100644 src/main/java/capstone/facefriend/member/controller/BasicInfoController.java create mode 100644 src/main/java/capstone/facefriend/member/domain/BasicInfoRepository.java create mode 100644 src/main/java/capstone/facefriend/member/service/BasicInfoService.java create mode 100644 src/main/java/capstone/facefriend/member/service/dto/BasicInfoRequest.java create mode 100644 src/main/java/capstone/facefriend/member/service/dto/BasicInfoResponse.java delete mode 100644 src/main/java/capstone/facefriend/member/service/dto/FindEmailRequest.java diff --git a/src/main/java/capstone/facefriend/auth/config/AuthConfig.java b/src/main/java/capstone/facefriend/auth/config/AuthConfig.java index 1e01311647..ed54c936fb 100644 --- a/src/main/java/capstone/facefriend/auth/config/AuthConfig.java +++ b/src/main/java/capstone/facefriend/auth/config/AuthConfig.java @@ -49,41 +49,39 @@ private HandlerInterceptor loginCheckInterceptor() { return new PathMatchInterceptor(loginCheckInterceptor) .addExcludePathPattern("/**", OPTIONS) - .addIncludePathPattern("/signout", DELETE) .addIncludePathPattern("/test", GET) + .addIncludePathPattern("/members/**", ANY) - .addExcludePathPattern("/reissue", POST); // 토큰 만료 시에는 해당 요청을 가로채지 않아야 합니다. + .addExcludePathPattern("/members/reissue", POST); // 토큰 만료 시에는 해당 요청을 가로채지 않아야 합니다. } private HandlerInterceptor loginInterceptor() { return new PathMatchInterceptor(loginInterceptor) .addExcludePathPattern("/**", OPTIONS) - .addIncludePathPattern("/signout", DELETE) - .addIncludePathPattern("/test", GET) - - .addExcludePathPattern("/reissue", POST); // 토큰 만료 시에는 해당 요청을 가로채지 않아야 합니다. + .addIncludePathPattern("/members/**", ANY) + .addExcludePathPattern("/members/reissue", POST); // 토큰 만료 시에는 해당 요청을 가로채지 않아야 합니다. } private HandlerInterceptor tokenReissueInterceptor() { return new PathMatchInterceptor(tokenReissueInterceptor) .addExcludePathPattern("/**", OPTIONS) - .addIncludePathPattern("/reissue", POST); // 토큰 만료 시에는 해당 요청을 가로채야 합니다. + .addIncludePathPattern("/members/**", ANY); } private HandlerInterceptor tokenBlackListInterceptor() { return new PathMatchInterceptor(tokenBlackListInterceptor) .addExcludePathPattern("/**", OPTIONS) - .addIncludePathPattern("/test", GET); + .addIncludePathPattern("/members/**", ANY); } private HandlerInterceptor verificationInterceptor() { return new PathMatchInterceptor(verificationInterceptor) .addExcludePathPattern("/**", OPTIONS) - .addIncludePathPattern("/test", GET); + .addIncludePathPattern("/members/**", ANY); } @Override diff --git a/src/main/java/capstone/facefriend/auth/exception/AuthExceptionType.java b/src/main/java/capstone/facefriend/auth/exception/AuthExceptionType.java index 12a3d245b5..0fb517a722 100644 --- a/src/main/java/capstone/facefriend/auth/exception/AuthExceptionType.java +++ b/src/main/java/capstone/facefriend/auth/exception/AuthExceptionType.java @@ -12,7 +12,7 @@ public enum AuthExceptionType implements ExceptionType { UNSUPPORTED_TOKEN(Status.BAD_REQUEST, 2005, "지원되지 않는 토큰입니다."), INVALID_TOKEN(Status.BAD_REQUEST, 2006, "토큰이 유효하지 않습니다."), BAD_REQUEST_TO_PROVIDER(Status.BAD_REQUEST, 2007, "토큰이 유효하지 않습니다."), - UNAUTHORIZED(Status.UNAUTHORIZED, 2008, "로그인한 정보가 없습니다."), + UNAUTHORIZED(Status.UNAUTHORIZED, 2008, "로그인한 정보가 없습니다. 로그인하시기 바랍니다."), ; private final Status status; diff --git a/src/main/java/capstone/facefriend/member/controller/BasicInfoController.java b/src/main/java/capstone/facefriend/member/controller/BasicInfoController.java new file mode 100644 index 0000000000..a82e0eafac --- /dev/null +++ b/src/main/java/capstone/facefriend/member/controller/BasicInfoController.java @@ -0,0 +1,43 @@ +package capstone.facefriend.member.controller; + + +import capstone.facefriend.auth.controller.support.AuthMember; +import capstone.facefriend.member.service.BasicInfoService; +import capstone.facefriend.member.service.dto.BasicInfoRequest; +import capstone.facefriend.member.service.dto.BasicInfoResponse; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +@Slf4j +@RestController +@RequiredArgsConstructor +@RequestMapping("/members") +public class BasicInfoController { + + private final BasicInfoService basicInfoService; + + @PostMapping("/basic-info") + public ResponseEntity setBasicInfo( + @AuthMember Long memberId, + @RequestBody BasicInfoRequest request + ) { + return ResponseEntity.ok(basicInfoService.setBasicInfo(memberId, request)); + } + + @GetMapping("/basic-info") + public ResponseEntity getBasicInfo( + @AuthMember Long memberId + ) { + return ResponseEntity.ok(basicInfoService.getBasicInfo(memberId)); + } + + @PutMapping("/basic-info") + public ResponseEntity putBasicInfo( + @AuthMember Long memberId, + @RequestBody BasicInfoRequest request + ) { + return ResponseEntity.ok(basicInfoService.putBasicInfo(memberId, request)); + } +} diff --git a/src/main/java/capstone/facefriend/member/controller/MemberController.java b/src/main/java/capstone/facefriend/member/controller/MemberController.java index 31a99a9a39..48d98bd8f2 100644 --- a/src/main/java/capstone/facefriend/member/controller/MemberController.java +++ b/src/main/java/capstone/facefriend/member/controller/MemberController.java @@ -24,25 +24,51 @@ public class MemberController { private final MemberService memberService; - /** - * 일반 유저 고유 로직 - */ - @PostMapping("/members/verify-duplication") + // 일반 유저 로직 ("/auth" 로 시작) + @PostMapping("/auth/find-email") + public ResponseEntity findEmail( + @RequestParam("email") String email + ) { + return ResponseEntity.ok(memberService.findEmail(email)); + } + + @PostMapping("/auth/send-temporary-password") + public ResponseEntity sendTemporaryPassword( + @RequestParam("email") String email + ) { + return ResponseEntity.ok(memberService.sendTemporaryPassword(email)); + } + + @PostMapping("/auth/verify-temporary-password") + public ResponseEntity resetNewPassword( + @RequestParam("email") String email, + @RequestParam("temporaryPassword") String temporaryPassword, + @RequestBody ResetPasswordRequest request + ) { + String newPassword = request.newPassword(); + String newPassword2 = request.newPassword2(); + + if (!newPassword.equals(newPassword2)) { + throw new MemberException(PASSWORDS_NOT_EQUAL); + } + return ResponseEntity.ok(memberService.verifyTemporaryPassword(email, temporaryPassword, newPassword)); + } + + @PostMapping("/auth/verify-duplication") public ResponseEntity verifyDuplication( @RequestParam("email") String email ) { return ResponseEntity.ok(memberService.verifyDuplication(email)); } - - @PostMapping("/members/send-code") + @PostMapping("/auth/send-code") public ResponseEntity sendCode( @RequestParam("email") String email ) { return ResponseEntity.ok(memberService.sendCode(email)); } - @GetMapping("/members/verify-code") + @GetMapping("/auth/verify-code") public ResponseEntity verifyCode( @RequestParam("email") String email, @RequestParam("code") String code @@ -50,14 +76,14 @@ public ResponseEntity verifyCode( return ResponseEntity.ok(memberService.verifyCode(email, code)); } - @PostMapping("/members/signup") + @PostMapping("/auth/signup") public ResponseEntity signUp( @RequestBody SignUpRequest request ) { return ResponseEntity.ok(memberService.signUp(request)); } - @PostMapping("/members/signin") + @PostMapping("/auth/signin") public ResponseEntity signIn( @RequestBody SignInRequest request ) { @@ -65,19 +91,9 @@ public ResponseEntity signIn( } - /** - * 구글 유저, 일반 유저 공통 로직 - */ - @PostMapping("/reissue") - public ResponseEntity reissueTokens( - @RequestBody ReissueRequest request, - @AuthMember Long memberId - ) { - String refreshToken = request.refreshToken(); - return ResponseEntity.ok(memberService.reissueTokens(memberId, refreshToken)); - } - @DeleteMapping("/signout") + // 구글 유저, 일반 유저 공통 로직 ("/members" 로 시작) + @DeleteMapping("/members/signout") public ResponseEntity signOut( HttpServletRequest request, @AuthMember Long memberId @@ -87,34 +103,18 @@ public ResponseEntity signOut( return ResponseEntity.ok(memberService.signOut(memberId, accessToken)); } - @PostMapping("/find-email") - public ResponseEntity findEmail( - @RequestBody FindEmailRequest request + @PostMapping("/members/reissue") + public ResponseEntity reissueTokens( + @RequestBody ReissueRequest request, + @AuthMember Long memberId ) { - return ResponseEntity.ok(memberService.findEmail(request.email())); + String refreshToken = request.refreshToken(); + return ResponseEntity.ok(memberService.reissueTokens(memberId, refreshToken)); } - @PostMapping("/send-temporary-password") - public ResponseEntity sendTemporaryPassword( - @RequestParam("email") String email - ) { - return ResponseEntity.ok(memberService.sendTemporaryPassword(email)); - } - @PostMapping("/verify-temporary-password") - public ResponseEntity resetNewPassword( - @RequestParam("email") String email, - @RequestParam("temporaryPassword") String temporaryPassword, - @RequestBody ResetPasswordRequest request - ) { - String newPassword = request.newPassword(); - String newPassword2 = request.newPassword2(); - if (!newPassword.equals(newPassword2)) { - throw new MemberException(PASSWORDS_NOT_EQUAL) ; - } - return ResponseEntity.ok(memberService.verifyTemporaryPassword(email, temporaryPassword, newPassword)); - } + @GetMapping("/test") public ResponseEntity test() { diff --git a/src/main/java/capstone/facefriend/member/domain/BasicInfo.java b/src/main/java/capstone/facefriend/member/domain/BasicInfo.java index 07793ca864..21f667da6e 100644 --- a/src/main/java/capstone/facefriend/member/domain/BasicInfo.java +++ b/src/main/java/capstone/facefriend/member/domain/BasicInfo.java @@ -1,20 +1,128 @@ package capstone.facefriend.member.domain; -import lombok.Getter; +import jakarta.persistence.*; +import lombok.*; @Getter +@Builder +@AllArgsConstructor(access = AccessLevel.PRIVATE) +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@EqualsAndHashCode(of = {"id"}, callSuper = false) +@Entity public class BasicInfo { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; + @Column(nullable = false) private String nickname; - private String gender; + @Enumerated(EnumType.STRING) + @Column(nullable = false) + private Gender gender; - private String age; + @Enumerated(EnumType.STRING) + @Column(nullable = false) + private AgeGroup ageGroup; - private String height; + @Enumerated(EnumType.STRING) + @Column(nullable = false) + private AgeDegree ageDegree; - private String region; + @Enumerated(EnumType.STRING) + @Column(nullable = false) + private HeightGroup heightGroup; + @Enumerated(EnumType.STRING) + @Column(nullable = false) + private Region region; + + public enum Gender { + MALE("남자"), + FEMALE("여자"); + + private final String value; + + Gender(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + } + + public enum AgeGroup { + TWENTIES("20 대"), + THIRTIES("30 대"), + FORTIES("40 대"), + FIFTIES("50 대"), + SIXTIES("60 대"); + + private final String value; + + AgeGroup(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + } + + public enum AgeDegree { + EARLY("초반"), + MIDDLE("중반"), + LATE("후반"); + + private final String value; + + AgeDegree(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + } + + public enum HeightGroup { + FIFTIES("150cm 대"), + SIXTIES("160cm 대"), + SEVENTIES("170cm 대"), + EIGHTIES("180cm 대"), + NINETIES("190cm 대"); + + private final String value; + + HeightGroup(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + } + + public enum Region { + GANGNAM_SEOCHO_YANGJAE("강남, 서초, 양재"), + JAMSIL_SONGPA_GANGDONG("잠실, 송파, 강동"), + DONGJAK_GWANAK_SADANG("동작, 관악, 사당"), + MAPO_SEODAEMUN_EUNPYANG("마포, 서대문, 은평"), + JONGNO_JUNGGU_YONGSAN("종로, 중구, 용산"), + NOWON_DOBONG_GANGBUK_SUNGBUK("노원, 도봉, 강북, 성북"), + GWANGJIN_SEONGDONG_JUNGRANG_DONGDAEMUN("광진, 성동, 중랑, 동대문"), + YEONGDEUNGPO_GURO_SINDORIM("영등포, 구로, 신도림"); + + private final String value; + + Region(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + } } diff --git a/src/main/java/capstone/facefriend/member/domain/BasicInfoRepository.java b/src/main/java/capstone/facefriend/member/domain/BasicInfoRepository.java new file mode 100644 index 0000000000..1b98362a14 --- /dev/null +++ b/src/main/java/capstone/facefriend/member/domain/BasicInfoRepository.java @@ -0,0 +1,10 @@ +package capstone.facefriend.member.domain; + +import org.springframework.data.repository.Repository; + +public interface BasicInfoRepository extends Repository { + + BasicInfo save(BasicInfo basicInfo); + + BasicInfo findBasicInfosById(Long id); +} diff --git a/src/main/java/capstone/facefriend/member/domain/Member.java b/src/main/java/capstone/facefriend/member/domain/Member.java index 386708373a..1bff8ae418 100644 --- a/src/main/java/capstone/facefriend/member/domain/Member.java +++ b/src/main/java/capstone/facefriend/member/domain/Member.java @@ -65,8 +65,8 @@ public void setRole(Role role) { this.role = role; } - public void setIsVerified(boolean isVerified) { - this.isVerified = isVerified; + public void setBasicInfo(BasicInfo basicInfo) { + this.basicInfo = basicInfo; } public boolean isVerified() { diff --git a/src/main/java/capstone/facefriend/member/service/BasicInfoService.java b/src/main/java/capstone/facefriend/member/service/BasicInfoService.java new file mode 100644 index 0000000000..5eaedb0b18 --- /dev/null +++ b/src/main/java/capstone/facefriend/member/service/BasicInfoService.java @@ -0,0 +1,82 @@ +package capstone.facefriend.member.service; + + +import capstone.facefriend.member.domain.BasicInfo; +import capstone.facefriend.member.domain.BasicInfoRepository; +import capstone.facefriend.member.domain.Member; +import capstone.facefriend.member.domain.MemberRepository; +import capstone.facefriend.member.exception.MemberException; +import capstone.facefriend.member.service.dto.BasicInfoRequest; +import capstone.facefriend.member.service.dto.BasicInfoResponse; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import static capstone.facefriend.member.domain.BasicInfo.*; +import static capstone.facefriend.member.exception.MemberExceptionType.NOT_FOUND; + +@Service +@Slf4j +@RequiredArgsConstructor +public class BasicInfoService { + + private final MemberRepository memberRepository; + private final BasicInfoRepository basicInfoRepository; + + private final String BASIC_INFO_SUCCESS_MESSAGE = "기본 정보 저장이 완료되었습니다."; + + + private Member findMemberById(Long memberId) { + Member member = memberRepository.findById(memberId) + .orElseThrow(() -> new MemberException(NOT_FOUND)); + return member; + } + + @Transactional + public BasicInfoResponse setBasicInfo( + Long memberId, BasicInfoRequest request + ) { + Member member = findMemberById(memberId); + BasicInfo basicInfo = builder() + .nickname(request.nickname()) + .gender(Gender.valueOf(request.gender())) + .ageGroup(AgeGroup.valueOf(request.ageGroup())) + .ageDegree(AgeDegree.valueOf(request.ageDegree())) + .heightGroup(HeightGroup.valueOf(request.heightGroup())) + .region(Region.valueOf(request.region())) + .build(); + basicInfoRepository.save(basicInfo); + member.setBasicInfo(basicInfo); + memberRepository.save(member); + + return BasicInfoResponse.of(basicInfo); + } + + @Transactional + public BasicInfoResponse getBasicInfo(Long memberId) { + Member member = findMemberById(memberId); + BasicInfo basicInfo = member.getBasicInfo(); + + return BasicInfoResponse.of(basicInfo); + } + + @Transactional + public BasicInfoResponse putBasicInfo(Long memberId, BasicInfoRequest request) { + Member member = findMemberById(memberId); + BasicInfo basicInfo = builder() + .nickname(request.nickname()) + .gender(Gender.valueOf(request.gender())) + .ageGroup(AgeGroup.valueOf(request.ageGroup())) + .ageDegree(AgeDegree.valueOf(request.ageDegree())) + .heightGroup(HeightGroup.valueOf(request.heightGroup())) + .region(Region.valueOf(request.region())) + .build(); + + basicInfoRepository.save(basicInfo); + member.setBasicInfo(basicInfo); + basicInfoRepository.save(basicInfo); + + return BasicInfoResponse.of(basicInfo); + } +} diff --git a/src/main/java/capstone/facefriend/member/service/dto/BasicInfoRequest.java b/src/main/java/capstone/facefriend/member/service/dto/BasicInfoRequest.java new file mode 100644 index 0000000000..7eb8a60e2e --- /dev/null +++ b/src/main/java/capstone/facefriend/member/service/dto/BasicInfoRequest.java @@ -0,0 +1,11 @@ +package capstone.facefriend.member.service.dto; + +public record BasicInfoRequest( + String nickname, + String gender, + String ageGroup, + String ageDegree, + String heightGroup, + String region +) { +} diff --git a/src/main/java/capstone/facefriend/member/service/dto/BasicInfoResponse.java b/src/main/java/capstone/facefriend/member/service/dto/BasicInfoResponse.java new file mode 100644 index 0000000000..791a387572 --- /dev/null +++ b/src/main/java/capstone/facefriend/member/service/dto/BasicInfoResponse.java @@ -0,0 +1,23 @@ +package capstone.facefriend.member.service.dto; + +import capstone.facefriend.member.domain.BasicInfo; + +public record BasicInfoResponse( + String nickname, + String gender, + String ageGroup, + String ageDegree, + String heightGroup, + String region +) { + public static BasicInfoResponse of(BasicInfo basicInfo) { + return new BasicInfoResponse( + basicInfo.getNickname(), + basicInfo.getGender().getValue(), + basicInfo.getAgeGroup().getValue(), + basicInfo.getAgeDegree().getValue(), + basicInfo.getHeightGroup().getValue(), + basicInfo.getRegion().getValue() + ); + } +} diff --git a/src/main/java/capstone/facefriend/member/service/dto/FindEmailRequest.java b/src/main/java/capstone/facefriend/member/service/dto/FindEmailRequest.java deleted file mode 100644 index 5370ffdd62..0000000000 --- a/src/main/java/capstone/facefriend/member/service/dto/FindEmailRequest.java +++ /dev/null @@ -1,7 +0,0 @@ -package capstone.facefriend.member.service.dto; - -public record FindEmailRequest( - String name, - String email -) { -} From 6351103c987ae844691b2d04149086a362feb22e Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Mon, 1 Apr 2024 20:54:02 +0900 Subject: [PATCH 067/265] =?UTF-8?q?feat:=20=EA=B8=B0=EB=B3=B8=EC=A0=95?= =?UTF-8?q?=EB=B3=B4=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/BasicInfoController.java | 8 --- .../facefriend/member/domain/BasicInfo.java | 6 +++ .../member/domain/BasicInfoRepository.java | 2 - .../member/service/BasicInfoService.java | 52 +++++-------------- .../member/service/MemberService.java | 17 ++++++ 5 files changed, 36 insertions(+), 49 deletions(-) diff --git a/src/main/java/capstone/facefriend/member/controller/BasicInfoController.java b/src/main/java/capstone/facefriend/member/controller/BasicInfoController.java index a82e0eafac..bf2d639daa 100644 --- a/src/main/java/capstone/facefriend/member/controller/BasicInfoController.java +++ b/src/main/java/capstone/facefriend/member/controller/BasicInfoController.java @@ -18,14 +18,6 @@ public class BasicInfoController { private final BasicInfoService basicInfoService; - @PostMapping("/basic-info") - public ResponseEntity setBasicInfo( - @AuthMember Long memberId, - @RequestBody BasicInfoRequest request - ) { - return ResponseEntity.ok(basicInfoService.setBasicInfo(memberId, request)); - } - @GetMapping("/basic-info") public ResponseEntity getBasicInfo( @AuthMember Long memberId diff --git a/src/main/java/capstone/facefriend/member/domain/BasicInfo.java b/src/main/java/capstone/facefriend/member/domain/BasicInfo.java index 21f667da6e..945fd1310a 100644 --- a/src/main/java/capstone/facefriend/member/domain/BasicInfo.java +++ b/src/main/java/capstone/facefriend/member/domain/BasicInfo.java @@ -4,6 +4,7 @@ import lombok.*; @Getter +@Setter @Builder @AllArgsConstructor(access = AccessLevel.PRIVATE) @NoArgsConstructor(access = AccessLevel.PROTECTED) @@ -39,6 +40,7 @@ public class BasicInfo { private Region region; public enum Gender { + DEFAULT(""), MALE("남자"), FEMALE("여자"); @@ -54,6 +56,7 @@ public String getValue() { } public enum AgeGroup { + DEFAULT(""), TWENTIES("20 대"), THIRTIES("30 대"), FORTIES("40 대"), @@ -72,6 +75,7 @@ public String getValue() { } public enum AgeDegree { + DEFAULT(""), EARLY("초반"), MIDDLE("중반"), LATE("후반"); @@ -88,6 +92,7 @@ public String getValue() { } public enum HeightGroup { + DEFAULT(""), FIFTIES("150cm 대"), SIXTIES("160cm 대"), SEVENTIES("170cm 대"), @@ -106,6 +111,7 @@ public String getValue() { } public enum Region { + DEFAULT(""), GANGNAM_SEOCHO_YANGJAE("강남, 서초, 양재"), JAMSIL_SONGPA_GANGDONG("잠실, 송파, 강동"), DONGJAK_GWANAK_SADANG("동작, 관악, 사당"), diff --git a/src/main/java/capstone/facefriend/member/domain/BasicInfoRepository.java b/src/main/java/capstone/facefriend/member/domain/BasicInfoRepository.java index 1b98362a14..1a1c9dc7f0 100644 --- a/src/main/java/capstone/facefriend/member/domain/BasicInfoRepository.java +++ b/src/main/java/capstone/facefriend/member/domain/BasicInfoRepository.java @@ -5,6 +5,4 @@ public interface BasicInfoRepository extends Repository { BasicInfo save(BasicInfo basicInfo); - - BasicInfo findBasicInfosById(Long id); } diff --git a/src/main/java/capstone/facefriend/member/service/BasicInfoService.java b/src/main/java/capstone/facefriend/member/service/BasicInfoService.java index 5eaedb0b18..52eefbe9d9 100644 --- a/src/main/java/capstone/facefriend/member/service/BasicInfoService.java +++ b/src/main/java/capstone/facefriend/member/service/BasicInfoService.java @@ -2,7 +2,6 @@ import capstone.facefriend.member.domain.BasicInfo; -import capstone.facefriend.member.domain.BasicInfoRepository; import capstone.facefriend.member.domain.Member; import capstone.facefriend.member.domain.MemberRepository; import capstone.facefriend.member.exception.MemberException; @@ -22,35 +21,10 @@ public class BasicInfoService { private final MemberRepository memberRepository; - private final BasicInfoRepository basicInfoRepository; - - private final String BASIC_INFO_SUCCESS_MESSAGE = "기본 정보 저장이 완료되었습니다."; - private Member findMemberById(Long memberId) { - Member member = memberRepository.findById(memberId) + return memberRepository.findById(memberId) .orElseThrow(() -> new MemberException(NOT_FOUND)); - return member; - } - - @Transactional - public BasicInfoResponse setBasicInfo( - Long memberId, BasicInfoRequest request - ) { - Member member = findMemberById(memberId); - BasicInfo basicInfo = builder() - .nickname(request.nickname()) - .gender(Gender.valueOf(request.gender())) - .ageGroup(AgeGroup.valueOf(request.ageGroup())) - .ageDegree(AgeDegree.valueOf(request.ageDegree())) - .heightGroup(HeightGroup.valueOf(request.heightGroup())) - .region(Region.valueOf(request.region())) - .build(); - basicInfoRepository.save(basicInfo); - member.setBasicInfo(basicInfo); - memberRepository.save(member); - - return BasicInfoResponse.of(basicInfo); } @Transactional @@ -64,19 +38,19 @@ public BasicInfoResponse getBasicInfo(Long memberId) { @Transactional public BasicInfoResponse putBasicInfo(Long memberId, BasicInfoRequest request) { Member member = findMemberById(memberId); - BasicInfo basicInfo = builder() - .nickname(request.nickname()) - .gender(Gender.valueOf(request.gender())) - .ageGroup(AgeGroup.valueOf(request.ageGroup())) - .ageDegree(AgeDegree.valueOf(request.ageDegree())) - .heightGroup(HeightGroup.valueOf(request.heightGroup())) - .region(Region.valueOf(request.region())) - .build(); + BasicInfo oldBasicInfo = member.getBasicInfo(); - basicInfoRepository.save(basicInfo); - member.setBasicInfo(basicInfo); - basicInfoRepository.save(basicInfo); + oldBasicInfo.setNickname(request.nickname()); + oldBasicInfo.setGender(Gender.valueOf(request.gender())); + oldBasicInfo.setAgeGroup(AgeGroup.valueOf(request.ageGroup())); + oldBasicInfo.setAgeDegree(AgeDegree.valueOf(request.ageDegree())); + oldBasicInfo.setHeightGroup(HeightGroup.valueOf(request.heightGroup())); + oldBasicInfo.setRegion(Region.valueOf(request.region())); - return BasicInfoResponse.of(basicInfo); + member.setBasicInfo(oldBasicInfo); + + memberRepository.save(member); + + return BasicInfoResponse.of(oldBasicInfo); } } diff --git a/src/main/java/capstone/facefriend/member/service/MemberService.java b/src/main/java/capstone/facefriend/member/service/MemberService.java index cb648ea590..8ff13d6c6c 100644 --- a/src/main/java/capstone/facefriend/member/service/MemberService.java +++ b/src/main/java/capstone/facefriend/member/service/MemberService.java @@ -4,6 +4,8 @@ import capstone.facefriend.auth.domain.TokenProvider; import capstone.facefriend.email.controller.dto.EmailVerificationResponse; import capstone.facefriend.email.service.EmailService; +import capstone.facefriend.member.domain.BasicInfo; +import capstone.facefriend.member.domain.BasicInfoRepository; import capstone.facefriend.member.domain.Member; import capstone.facefriend.member.domain.MemberRepository; import capstone.facefriend.member.exception.MemberException; @@ -17,6 +19,7 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import static capstone.facefriend.member.domain.BasicInfo.*; import static capstone.facefriend.member.domain.Role.USER; import static capstone.facefriend.member.exception.MemberExceptionType.*; @@ -27,6 +30,7 @@ public class MemberService { private final TokenProvider tokenProvider; private final MemberRepository memberRepository; + private final BasicInfoRepository basicInfoRepository; private final PasswordEncoder passwordEncoder; private final EmailService emailService; @@ -70,13 +74,26 @@ public String signUp(SignUpRequest request) { if (isVerified) { String encodedPassword = passwordEncoder.encode(request.password()); + + BasicInfo basicInfo = builder() + .nickname("") + .gender(Gender.DEFAULT) + .ageGroup(AgeGroup.DEFAULT) + .ageDegree(AgeDegree.DEFAULT) + .heightGroup(HeightGroup.DEFAULT) + .region(Region.DEFAULT) + .build(); + basicInfoRepository.save(basicInfo); + Member member = Member.builder() .email(request.email()) .password(encodedPassword) .isVerified(true) .role(USER) + .basicInfo(basicInfo) .build(); memberRepository.save(member); + } else { throw new MemberException(NOT_VERIFIED); } From ab80d311337660f836128ff5746bdb0396c6d564 Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Mon, 1 Apr 2024 21:25:17 +0900 Subject: [PATCH 068/265] =?UTF-8?q?feat:=20HeightGroup=20=EC=97=B4?= =?UTF-8?q?=EA=B1=B0=ED=98=95=EC=9D=98=20value=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/capstone/facefriend/member/domain/BasicInfo.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/capstone/facefriend/member/domain/BasicInfo.java b/src/main/java/capstone/facefriend/member/domain/BasicInfo.java index 945fd1310a..303cd2e265 100644 --- a/src/main/java/capstone/facefriend/member/domain/BasicInfo.java +++ b/src/main/java/capstone/facefriend/member/domain/BasicInfo.java @@ -93,11 +93,11 @@ public String getValue() { public enum HeightGroup { DEFAULT(""), - FIFTIES("150cm 대"), + FIFTIES("150cm 대 이하"), SIXTIES("160cm 대"), SEVENTIES("170cm 대"), EIGHTIES("180cm 대"), - NINETIES("190cm 대"); + NINETIES("190cm 대 이상"); private final String value; From a4180306120d3d9bd9dbf111ee7a70db3c06212f Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Wed, 10 Apr 2024 20:42:00 +0900 Subject: [PATCH 069/265] =?UTF-8?q?feat:=20FaceInfo=20=EC=97=94=ED=8B=B0?= =?UTF-8?q?=ED=8B=B0=20=EB=B0=8F=20=EB=A6=AC=ED=8F=AC=EC=A7=80=ED=86=A0?= =?UTF-8?q?=EB=A6=AC=20=EC=83=9D=EC=84=B1=20&=20Member=20=EC=97=94?= =?UTF-8?q?=ED=8B=B0=ED=8B=B0=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../facefriend/member/domain/FaceInfo.java | 26 +++++++++++++------ .../member/domain/FaceInfoRepository.java | 9 +++++++ .../facefriend/member/domain/Member.java | 15 ++++++----- 3 files changed, 36 insertions(+), 14 deletions(-) create mode 100644 src/main/java/capstone/facefriend/member/domain/FaceInfoRepository.java diff --git a/src/main/java/capstone/facefriend/member/domain/FaceInfo.java b/src/main/java/capstone/facefriend/member/domain/FaceInfo.java index e135c4a573..55f3a0cd86 100644 --- a/src/main/java/capstone/facefriend/member/domain/FaceInfo.java +++ b/src/main/java/capstone/facefriend/member/domain/FaceInfo.java @@ -1,10 +1,14 @@ package capstone.facefriend.member.domain; -import jakarta.persistence.Entity; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.GenerationType; -import jakarta.persistence.Id; +import jakarta.persistence.*; +import lombok.*; +@Getter +@Setter +@Builder +@AllArgsConstructor(access = AccessLevel.PRIVATE) +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@EqualsAndHashCode(of = {"id"}, callSuper = false) @Entity public class FaceInfo { @@ -12,11 +16,17 @@ public class FaceInfo { @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; - private String token; + @Column + private String originS3Url; - private String imageURL; + @Column + private String generatedS3url; - private String generatedImageURL; + public void setOriginS3Url(String originS3Url) { + this.originS3Url = originS3Url; + } - private String description; + public void setGeneratedS3Url(String generatedS3url) { + this.generatedS3url = generatedS3url; + } } diff --git a/src/main/java/capstone/facefriend/member/domain/FaceInfoRepository.java b/src/main/java/capstone/facefriend/member/domain/FaceInfoRepository.java new file mode 100644 index 0000000000..7cb681654c --- /dev/null +++ b/src/main/java/capstone/facefriend/member/domain/FaceInfoRepository.java @@ -0,0 +1,9 @@ +package capstone.facefriend.member.domain; + +import org.springframework.data.repository.Repository; + +public interface FaceInfoRepository extends Repository { + + FaceInfo save(FaceInfo faceInfo); + +} diff --git a/src/main/java/capstone/facefriend/member/domain/Member.java b/src/main/java/capstone/facefriend/member/domain/Member.java index 1bff8ae418..a146ce061f 100644 --- a/src/main/java/capstone/facefriend/member/domain/Member.java +++ b/src/main/java/capstone/facefriend/member/domain/Member.java @@ -25,14 +25,9 @@ public class Member extends BaseEntity { @Column(nullable = false, unique = true) private String email; - @Column - private String nickname; - @Column(nullable = false) private String password; - private String imageUrl; - @Column private boolean isVerified; @@ -41,9 +36,13 @@ public class Member extends BaseEntity { private Role role; @OneToOne - @JoinColumn(name = "BASIC_INFO_ID") + @JoinColumn(name = "BASIC_INFO_ID", nullable = false) private BasicInfo basicInfo; + @OneToOne + @JoinColumn(name = "FACE_INFO_ID", nullable = false) + private FaceInfo faceInfo; + public Member(String email) { this.email = email; this.role = Role.USER; @@ -69,6 +68,10 @@ public void setBasicInfo(BasicInfo basicInfo) { this.basicInfo = basicInfo; } + public void setFaceInfo(FaceInfo faceInfo) { + this.faceInfo = faceInfo; + } + public boolean isVerified() { return this.isVerified == true; } From 6c2266176bb67c146d3b72fa7b51c13e180699d7 Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Wed, 10 Apr 2024 20:47:10 +0900 Subject: [PATCH 070/265] =?UTF-8?q?fix:=20=EC=9D=B8=ED=84=B0=EC=85=89?= =?UTF-8?q?=ED=84=B0=20=EC=B2=B4=EC=9D=B8=EA=B1=B8=EC=96=B4=EB=91=90?= =?UTF-8?q?=EC=A7=80=20=EC=95=8A=EC=95=84=EC=84=9C=20=EC=9E=91=EB=8F=99?= =?UTF-8?q?=EB=90=98=EC=A7=80=20=EC=95=8A=EC=95=98=EB=8D=98=20=EB=B2=84?= =?UTF-8?q?=EA=B7=B8=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../facefriend/auth/config/AuthConfig.java | 28 +++++++++++++------ .../TokenBlackListInterceptor.java | 10 ++++--- .../interceptor/TokenReissueInterceptor.java | 4 +-- .../interceptor/VerificationInterceptor.java | 6 +--- 4 files changed, 29 insertions(+), 19 deletions(-) diff --git a/src/main/java/capstone/facefriend/auth/config/AuthConfig.java b/src/main/java/capstone/facefriend/auth/config/AuthConfig.java index ed54c936fb..79678f815a 100644 --- a/src/main/java/capstone/facefriend/auth/config/AuthConfig.java +++ b/src/main/java/capstone/facefriend/auth/config/AuthConfig.java @@ -49,39 +49,51 @@ private HandlerInterceptor loginCheckInterceptor() { return new PathMatchInterceptor(loginCheckInterceptor) .addExcludePathPattern("/**", OPTIONS) - .addIncludePathPattern("/test", GET) - .addIncludePathPattern("/members/**", ANY) + .addIncludePathPattern("/auth/reset-password", POST) + .addIncludePathPattern("/auth/signout", DELETE) + .addIncludePathPattern("/basic-info", ANY) + .addIncludePathPattern("/face-info", ANY) - .addExcludePathPattern("/members/reissue", POST); // 토큰 만료 시에는 해당 요청을 가로채지 않아야 합니다. + .addExcludePathPattern("/auth/reissue", POST); // 토큰 만료 시에는 해당 요청을 가로채지 않아야 합니다. } private HandlerInterceptor loginInterceptor() { return new PathMatchInterceptor(loginInterceptor) .addExcludePathPattern("/**", OPTIONS) - .addIncludePathPattern("/members/**", ANY) - .addExcludePathPattern("/members/reissue", POST); // 토큰 만료 시에는 해당 요청을 가로채지 않아야 합니다. + .addIncludePathPattern("/auth/reset-password", POST) + .addIncludePathPattern("/auth/signout", DELETE) + .addIncludePathPattern("/basic-info", ANY) + .addIncludePathPattern("/face-info", ANY) + + .addExcludePathPattern("/auth/reissue", POST); // 토큰 만료 시에는 해당 요청을 가로채지 않아야 합니다. } private HandlerInterceptor tokenReissueInterceptor() { return new PathMatchInterceptor(tokenReissueInterceptor) .addExcludePathPattern("/**", OPTIONS) - .addIncludePathPattern("/members/**", ANY); + .addIncludePathPattern("/auth/reissue", POST); // 토큰 재발급 시에는 해당 요청을 가로채야 합니다. } private HandlerInterceptor tokenBlackListInterceptor() { return new PathMatchInterceptor(tokenBlackListInterceptor) .addExcludePathPattern("/**", OPTIONS) - .addIncludePathPattern("/members/**", ANY); + .addIncludePathPattern("/auth/signout", DELETE) + .addIncludePathPattern("/auth/reset-password", POST) + .addIncludePathPattern("/basic-info", ANY) + .addIncludePathPattern("/face-info", ANY); } private HandlerInterceptor verificationInterceptor() { return new PathMatchInterceptor(verificationInterceptor) .addExcludePathPattern("/**", OPTIONS) - .addIncludePathPattern("/members/**", ANY); + .addIncludePathPattern("/auth/signout", DELETE) + .addIncludePathPattern("/auth/reset-password", POST) + .addIncludePathPattern("/basic-info", ANY) + .addIncludePathPattern("/face-info", ANY); } @Override diff --git a/src/main/java/capstone/facefriend/auth/controller/interceptor/TokenBlackListInterceptor.java b/src/main/java/capstone/facefriend/auth/controller/interceptor/TokenBlackListInterceptor.java index 78a6435565..bf0420f065 100644 --- a/src/main/java/capstone/facefriend/auth/controller/interceptor/TokenBlackListInterceptor.java +++ b/src/main/java/capstone/facefriend/auth/controller/interceptor/TokenBlackListInterceptor.java @@ -1,6 +1,7 @@ package capstone.facefriend.auth.controller.interceptor; import capstone.facefriend.auth.controller.support.AuthenticationExtractor; +import capstone.facefriend.email.controller.interceptor.VerificationInterceptor; import capstone.facefriend.redis.RedisDao; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; @@ -16,15 +17,16 @@ public class TokenBlackListInterceptor implements HandlerInterceptor { private final RedisDao redisDao; - private final static String SIGN_OUT_VALUE = "SIGN_OUT_VALUE"; + + private final VerificationInterceptor verificationInterceptor; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { String accessToken = AuthenticationExtractor.extractAccessToken(request).get(); - if (accessToken != null && accessToken.equals(SIGN_OUT_VALUE)) { - return !redisDao.isKeyOfAccessTokenInBlackList(accessToken); // 액세스 토큰이 블랙리스트에 등록되었다면 false 반환해야 합니다. + if (accessToken != null) { + return redisDao.isKeyOfAccessTokenInBlackList(accessToken); // 액세스 토큰이 블랙리스트에 등록되었다면 false 반환해야 합니다. } - return true; + return verificationInterceptor.preHandle(request, response, handler); } } \ No newline at end of file diff --git a/src/main/java/capstone/facefriend/auth/controller/interceptor/TokenReissueInterceptor.java b/src/main/java/capstone/facefriend/auth/controller/interceptor/TokenReissueInterceptor.java index 76ae874ccc..ca51815e7c 100644 --- a/src/main/java/capstone/facefriend/auth/controller/interceptor/TokenReissueInterceptor.java +++ b/src/main/java/capstone/facefriend/auth/controller/interceptor/TokenReissueInterceptor.java @@ -19,7 +19,7 @@ public class TokenReissueInterceptor implements HandlerInterceptor { private final TokenProvider tokenProvider; private final AuthenticationContext authenticationContext; - private final VerificationInterceptor verificationInterceptor; + private final TokenBlackListInterceptor tokenBlackListInterceptor; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { @@ -28,6 +28,6 @@ public boolean preHandle(HttpServletRequest request, HttpServletResponse respons Long memberId = tokenProvider.extractIdIgnoringExpiration(accessToken); authenticationContext.setAuthentication(memberId); - return verificationInterceptor.preHandle(request, response, handler); + return tokenBlackListInterceptor.preHandle(request, response, handler); } } diff --git a/src/main/java/capstone/facefriend/email/controller/interceptor/VerificationInterceptor.java b/src/main/java/capstone/facefriend/email/controller/interceptor/VerificationInterceptor.java index 17c03976aa..6da23fc411 100644 --- a/src/main/java/capstone/facefriend/email/controller/interceptor/VerificationInterceptor.java +++ b/src/main/java/capstone/facefriend/email/controller/interceptor/VerificationInterceptor.java @@ -4,7 +4,6 @@ import capstone.facefriend.auth.domain.TokenProvider; import capstone.facefriend.auth.exception.AuthException; import capstone.facefriend.email.exception.VerificationException; -import capstone.facefriend.email.support.VerificationContext; import capstone.facefriend.member.domain.Member; import capstone.facefriend.member.domain.MemberRepository; import capstone.facefriend.member.exception.MemberException; @@ -16,7 +15,7 @@ import org.springframework.web.servlet.HandlerInterceptor; import static capstone.facefriend.auth.exception.AuthExceptionType.UNAUTHORIZED; -import static capstone.facefriend.email.exception.VerificationExceptionType.*; +import static capstone.facefriend.email.exception.VerificationExceptionType.NOT_VERIFIED; import static capstone.facefriend.member.exception.MemberExceptionType.NOT_FOUND; @@ -26,7 +25,6 @@ public class VerificationInterceptor implements HandlerInterceptor { private final TokenProvider tokenProvider; - private final VerificationContext verificationContext; private final MemberRepository memberRepository; @Override @@ -42,8 +40,6 @@ public boolean preHandle(HttpServletRequest request, HttpServletResponse respons boolean isVerified = member.isVerified(); if (!isVerified) throw new VerificationException(NOT_VERIFIED); - verificationContext.setIsVerified(true); // 예외를 통과했다면 무조건 true 입니다. - return true; } } From 75a91faff7561a35af39b024b228167c11752878 Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Wed, 10 Apr 2024 20:51:23 +0900 Subject: [PATCH 071/265] =?UTF-8?q?refactor:=20=EB=B3=B8=EC=9D=B8=EC=9D=B8?= =?UTF-8?q?=EC=A6=9D=EC=9D=84=20=EC=88=9C=EC=88=98=20=EC=9D=B8=ED=84=B0?= =?UTF-8?q?=EC=85=89=ED=84=B0=20=EB=B0=A9=EC=8B=9D=EC=9C=BC=EB=A1=9C=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84=ED=95=98=EA=B8=B0=20=EC=9C=84=ED=95=9C=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../email/VerificationArgumentResolver.java | 33 ------------------- .../email/support/VerificationContext.java | 32 ------------------ .../email/support/VerifiedMember.java | 11 ------- 3 files changed, 76 deletions(-) delete mode 100644 src/main/java/capstone/facefriend/email/VerificationArgumentResolver.java delete mode 100644 src/main/java/capstone/facefriend/email/support/VerificationContext.java delete mode 100644 src/main/java/capstone/facefriend/email/support/VerifiedMember.java diff --git a/src/main/java/capstone/facefriend/email/VerificationArgumentResolver.java b/src/main/java/capstone/facefriend/email/VerificationArgumentResolver.java deleted file mode 100644 index 4ea0d37bc9..0000000000 --- a/src/main/java/capstone/facefriend/email/VerificationArgumentResolver.java +++ /dev/null @@ -1,33 +0,0 @@ -package capstone.facefriend.email; - -import capstone.facefriend.email.support.VerifiedMember; -import capstone.facefriend.email.support.VerificationContext; -import lombok.RequiredArgsConstructor; -import org.springframework.core.MethodParameter; -import org.springframework.stereotype.Component; -import org.springframework.web.bind.support.WebDataBinderFactory; -import org.springframework.web.context.request.NativeWebRequest; -import org.springframework.web.method.support.HandlerMethodArgumentResolver; -import org.springframework.web.method.support.ModelAndViewContainer; - -@RequiredArgsConstructor -@Component -public class VerificationArgumentResolver implements HandlerMethodArgumentResolver { - - private final VerificationContext verificationContext; - - @Override - public boolean supportsParameter(MethodParameter parameter) { - return parameter.hasParameterAnnotation(VerifiedMember.class) && - parameter.getParameterType().equals(Boolean.class); - } - - @Override - public Object resolveArgument( - MethodParameter parameter, - ModelAndViewContainer mavContainer, - NativeWebRequest webRequest, - WebDataBinderFactory binderFactory) { - return verificationContext.getIsVerified(); - } -} diff --git a/src/main/java/capstone/facefriend/email/support/VerificationContext.java b/src/main/java/capstone/facefriend/email/support/VerificationContext.java deleted file mode 100644 index 70b709b8d6..0000000000 --- a/src/main/java/capstone/facefriend/email/support/VerificationContext.java +++ /dev/null @@ -1,32 +0,0 @@ -package capstone.facefriend.email.support; - - -import capstone.facefriend.email.exception.VerificationException; -import capstone.facefriend.email.exception.VerificationExceptionType; -import org.springframework.stereotype.Component; -import org.springframework.web.context.annotation.RequestScope; - -import java.util.Objects; - -@RequestScope -@Component -public class VerificationContext { - - private static final boolean IS_NOT_VERIFIED = false; - private Boolean isVerified; - - public void setIsVerified(Boolean isVerified) { - this.isVerified = isVerified; - } - - public Boolean getIsVerified() { - if (Objects.isNull(this.isVerified)) { - throw new VerificationException(VerificationExceptionType.NOT_VERIFIED); - } - return isVerified; - } - - public void setNotVerified() { - this.isVerified = IS_NOT_VERIFIED; - } -} diff --git a/src/main/java/capstone/facefriend/email/support/VerifiedMember.java b/src/main/java/capstone/facefriend/email/support/VerifiedMember.java deleted file mode 100644 index e326153994..0000000000 --- a/src/main/java/capstone/facefriend/email/support/VerifiedMember.java +++ /dev/null @@ -1,11 +0,0 @@ -package capstone.facefriend.email.support; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -@Target(ElementType.PARAMETER) -@Retention(RetentionPolicy.RUNTIME) -public @interface VerifiedMember { -} From 17e61eef728c6d5d517b465db1446e33fe4233cf Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Wed, 10 Apr 2024 20:53:03 +0900 Subject: [PATCH 072/265] =?UTF-8?q?fix:=20Member=20=EC=97=94=ED=8B=B0?= =?UTF-8?q?=ED=8B=B0=EC=9D=98=20nickname=20=ED=95=84=EB=93=9C=EB=A5=BC=20B?= =?UTF-8?q?asicInfo=20=EC=97=94=ED=8B=B0=ED=8B=B0=EB=A1=9C=20=EC=9D=B4?= =?UTF-8?q?=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/capstone/facefriend/auth/service/AuthService.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/main/java/capstone/facefriend/auth/service/AuthService.java b/src/main/java/capstone/facefriend/auth/service/AuthService.java index 7b888cfc88..fb41cf7114 100644 --- a/src/main/java/capstone/facefriend/auth/service/AuthService.java +++ b/src/main/java/capstone/facefriend/auth/service/AuthService.java @@ -34,9 +34,7 @@ public String loginUri(String redirectUri, String provider) { public TokenResponse generateTokens(OAuthMember oAuthMember) { Member newMember = Member.builder() .email(oAuthMember.email()) - .nickname(oAuthMember.nickname()) .password(TEMPORARY_GOOGLE_PASSWORD) - .imageUrl(oAuthMember.imageUrl()) .isVerified(true) .role(USER) .build(); From 17faacc312f234d832374e3b803350f005e08839 Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Wed, 10 Apr 2024 20:53:43 +0900 Subject: [PATCH 073/265] =?UTF-8?q?fix:=20=EB=B9=8C=EB=93=9C=EB=90=98?= =?UTF-8?q?=EC=A7=80=20=EC=95=8A=EB=8A=94=20=EB=B2=84=EA=B7=B8=20=ED=95=B4?= =?UTF-8?q?=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/capstone/facefriend/FacefriendApplication.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/capstone/facefriend/FacefriendApplication.java b/src/main/java/capstone/facefriend/FacefriendApplication.java index a197dfe24c..ee7ff038ac 100644 --- a/src/main/java/capstone/facefriend/FacefriendApplication.java +++ b/src/main/java/capstone/facefriend/FacefriendApplication.java @@ -9,7 +9,9 @@ @EnableJpaAuditing @SpringBootApplication public class FacefriendApplication { - + static { + System.setProperty("com.amazonaws.sdk.disableEc2Metadata", "true"); + } public static void main(String[] args) { TimeZone.setDefault(TimeZone.getTimeZone("Asia/Seoul")); SpringApplication.run(FacefriendApplication.class, args); From 81585b8ed867bea6dcf90721d95ba89204d7ecb4 Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Wed, 10 Apr 2024 20:54:19 +0900 Subject: [PATCH 074/265] =?UTF-8?q?fix:=20=EC=95=A1=EC=84=B8=EC=8A=A4=20?= =?UTF-8?q?=ED=86=A0=ED=81=B0=20=EB=A7=8C=EB=A3=8C=EA=B8=B0=ED=95=9C?= =?UTF-8?q?=EC=9D=84=203=EC=8B=9C=EA=B0=84=EC=9C=BC=EB=A1=9C=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../capstone/facefriend/auth/infrastructure/JwtProvider.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/capstone/facefriend/auth/infrastructure/JwtProvider.java b/src/main/java/capstone/facefriend/auth/infrastructure/JwtProvider.java index 01aeee6d3b..b22c10a452 100644 --- a/src/main/java/capstone/facefriend/auth/infrastructure/JwtProvider.java +++ b/src/main/java/capstone/facefriend/auth/infrastructure/JwtProvider.java @@ -34,7 +34,7 @@ public class JwtProvider implements TokenProvider { private final RedisDao redisDao; - private static final long ACCESS_TOKEN_EXPIRATION_TIME = 60 * 5L; // 5분 // 1000 * 60 * 60 * 3L; // 3시간 + private static final long ACCESS_TOKEN_EXPIRATION_TIME = 60 * 60 * 3L; // 3시간 private static final long REFRESH_TOKEN_EXPIRATION_TIME = 60 * 60 * 24 * 7L; // 7일 @PostConstruct From cd2d78ea9c9b9e973e62988bf818dbb50a7c3dc7 Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Wed, 10 Apr 2024 20:56:27 +0900 Subject: [PATCH 075/265] =?UTF-8?q?fix:=20oauth2=20=EC=9D=B8=EC=A6=9D?= =?UTF-8?q?=EB=B0=A9=EC=8B=9D=20=EC=A0=9C=EA=B1=B0=EB=A1=9C=20=EC=9D=B8?= =?UTF-8?q?=ED=95=B4=20=EC=BB=A8=ED=8A=B8=EB=A1=A4=EB=9F=AC=20=ED=98=B8?= =?UTF-8?q?=EC=B6=9C=20url=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../facefriend/member/controller/BasicInfoController.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/capstone/facefriend/member/controller/BasicInfoController.java b/src/main/java/capstone/facefriend/member/controller/BasicInfoController.java index bf2d639daa..3d4aa871cf 100644 --- a/src/main/java/capstone/facefriend/member/controller/BasicInfoController.java +++ b/src/main/java/capstone/facefriend/member/controller/BasicInfoController.java @@ -13,7 +13,6 @@ @Slf4j @RestController @RequiredArgsConstructor -@RequestMapping("/members") public class BasicInfoController { private final BasicInfoService basicInfoService; From 36319718e55297b7d74f6f859cc7aab9d9cff464 Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Wed, 10 Apr 2024 20:57:27 +0900 Subject: [PATCH 076/265] =?UTF-8?q?fix:=20oauth2=20=EC=9D=B8=EC=A6=9D?= =?UTF-8?q?=EB=B0=A9=EC=8B=9D=20=EC=A0=9C=EA=B1=B0=EB=A1=9C=20=EC=9D=B8?= =?UTF-8?q?=ED=95=B4=20=EC=BB=A8=ED=8A=B8=EB=A1=A4=EB=9F=AC=20=ED=98=B8?= =?UTF-8?q?=EC=B6=9C=20url=20=EC=88=98=EC=A0=952?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../member/controller/MemberController.java | 37 +++++++++++-------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/src/main/java/capstone/facefriend/member/controller/MemberController.java b/src/main/java/capstone/facefriend/member/controller/MemberController.java index 48d98bd8f2..114f5c9775 100644 --- a/src/main/java/capstone/facefriend/member/controller/MemberController.java +++ b/src/main/java/capstone/facefriend/member/controller/MemberController.java @@ -24,7 +24,6 @@ public class MemberController { private final MemberService memberService; - // 일반 유저 로직 ("/auth" 로 시작) @PostMapping("/auth/find-email") public ResponseEntity findEmail( @RequestParam("email") String email @@ -32,6 +31,7 @@ public ResponseEntity findEmail( return ResponseEntity.ok(memberService.findEmail(email)); } + // 비로그인 상태에서 비밀번호 변경하기 위해 임시 비밀번호 발송 @PostMapping("/auth/send-temporary-password") public ResponseEntity sendTemporaryPassword( @RequestParam("email") String email @@ -39,8 +39,9 @@ public ResponseEntity sendTemporaryPassword( return ResponseEntity.ok(memberService.sendTemporaryPassword(email)); } + // 비로그인 상태에서 비밀번호 변경 @PostMapping("/auth/verify-temporary-password") - public ResponseEntity resetNewPassword( + public ResponseEntity verifyTemporaryPassword( @RequestParam("email") String email, @RequestParam("temporaryPassword") String temporaryPassword, @RequestBody ResetPasswordRequest request @@ -54,6 +55,22 @@ public ResponseEntity resetNewPassword( return ResponseEntity.ok(memberService.verifyTemporaryPassword(email, temporaryPassword, newPassword)); } + // 로그인 상태에서 비밀번호 변경 + @PostMapping("/auth/reset-password") + public ResponseEntity resetPassword( + @AuthMember Long memberId, + @RequestBody ResetPasswordRequest request + ) { + String newPassword = request.newPassword(); + String newPassword2 = request.newPassword2(); + + if (!newPassword.equals(newPassword2)) { + throw new MemberException(PASSWORDS_NOT_EQUAL); + } + + return ResponseEntity.ok(memberService.resetPassword(memberId, newPassword)); + } + @PostMapping("/auth/verify-duplication") public ResponseEntity verifyDuplication( @RequestParam("email") String email @@ -90,10 +107,7 @@ public ResponseEntity signIn( return ResponseEntity.ok(memberService.signIn(request)); } - - - // 구글 유저, 일반 유저 공통 로직 ("/members" 로 시작) - @DeleteMapping("/members/signout") + @DeleteMapping("/auth/signout") public ResponseEntity signOut( HttpServletRequest request, @AuthMember Long memberId @@ -103,7 +117,7 @@ public ResponseEntity signOut( return ResponseEntity.ok(memberService.signOut(memberId, accessToken)); } - @PostMapping("/members/reissue") + @PostMapping("/auth/reissue") public ResponseEntity reissueTokens( @RequestBody ReissueRequest request, @AuthMember Long memberId @@ -111,13 +125,4 @@ public ResponseEntity reissueTokens( String refreshToken = request.refreshToken(); return ResponseEntity.ok(memberService.reissueTokens(memberId, refreshToken)); } - - - - - - @GetMapping("/test") - public ResponseEntity test() { - return ResponseEntity.ok("[ MemberController ] 테스트"); - } } From 3273d6ea1f6aa8b97824b2d8b4c786bdb84943e3 Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Wed, 10 Apr 2024 20:59:00 +0900 Subject: [PATCH 077/265] =?UTF-8?q?fix:=20=EC=95=A1=EC=84=B8=EC=8A=A4=20?= =?UTF-8?q?=ED=86=A0=ED=81=B0=EC=9D=B4=20=EB=B8=94=EB=9E=99=EB=A6=AC?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EC=B2=98=EB=A6=AC=EA=B0=80=20=EB=90=98?= =?UTF-8?q?=EC=A7=80=20=EC=95=8A=EC=95=98=EC=9D=8C=EC=97=90=EB=8F=84=20?= =?UTF-8?q?=EB=B6=88=EA=B5=AC=ED=95=98=EA=B3=A0=20=EB=B8=94=EB=9E=99?= =?UTF-8?q?=EB=A6=AC=EC=8A=A4=ED=8A=B8=20=EC=9D=B8=ED=84=B0=EC=85=89?= =?UTF-8?q?=ED=84=B0=EC=97=90=20=EC=9E=A1=ED=9E=88=EB=8A=94=20=EB=B2=84?= =?UTF-8?q?=EA=B7=B8=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/capstone/facefriend/redis/RedisDao.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/capstone/facefriend/redis/RedisDao.java b/src/main/java/capstone/facefriend/redis/RedisDao.java index 6de5146920..a147bb70e6 100644 --- a/src/main/java/capstone/facefriend/redis/RedisDao.java +++ b/src/main/java/capstone/facefriend/redis/RedisDao.java @@ -40,10 +40,10 @@ public void setAccessTokenSignOut(String accessToken, Long minute) { public boolean isKeyOfAccessTokenInBlackList(String accessToken) { String signOutValue = redisTemplate.opsForValue().get(accessToken); - if (signOutValue.equals(SIGN_OUT_VALUE)) { + if (signOutValue != null && signOutValue.equals(SIGN_OUT_VALUE)) { throw new MemberException(ACCESS_TOKEN_IS_IN_BLACKLIST); } - return false; + return true; } public void setCode(String mail, String code, long codeTime) { From e612eaa07f6260ef7c003c12d36297630388aded Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Wed, 10 Apr 2024 20:59:46 +0900 Subject: [PATCH 078/265] =?UTF-8?q?feat:=20MultipartFile=20=EC=BB=A4?= =?UTF-8?q?=EC=8A=A4=ED=85=80=20=ED=81=B4=EB=9E=98=EC=8A=A4=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../multipartFile/ByteArrayMultipartFile.java | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 src/main/java/capstone/facefriend/member/multipartFile/ByteArrayMultipartFile.java diff --git a/src/main/java/capstone/facefriend/member/multipartFile/ByteArrayMultipartFile.java b/src/main/java/capstone/facefriend/member/multipartFile/ByteArrayMultipartFile.java new file mode 100644 index 0000000000..1ad44a6511 --- /dev/null +++ b/src/main/java/capstone/facefriend/member/multipartFile/ByteArrayMultipartFile.java @@ -0,0 +1,63 @@ +package capstone.facefriend.member.multipartFile; + +import org.springframework.web.multipart.MultipartFile; + +import java.io.*; + + +public class ByteArrayMultipartFile implements MultipartFile { + + private final byte[] bytes; + private final String name; + private final String originalFilename; + private final String contentType; + + public ByteArrayMultipartFile(byte[] bytes, String name) { + this.bytes = bytes; + this.name = name; + this.originalFilename = name; + this.contentType = "application/octet-stream"; + } + + @Override + public String getName() { + return name; + } + + @Override + public String getOriginalFilename() { + return originalFilename; + } + + @Override + public String getContentType() { + return contentType; + } + + @Override + public boolean isEmpty() { + return bytes == null || bytes.length == 0; + } + + @Override + public long getSize() { + return bytes.length; + } + + @Override + public byte[] getBytes() throws IOException { + return bytes; + } + + @Override + public InputStream getInputStream() throws IOException { + return new ByteArrayInputStream(bytes); + } + + @Override + public void transferTo(File dest) throws IOException, IllegalStateException { + try (FileOutputStream fos = new FileOutputStream(dest)) { + fos.write(bytes); + } + } +} From 39983466e361a87db7799a650196c9a9f389adb1 Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Wed, 10 Apr 2024 21:00:40 +0900 Subject: [PATCH 079/265] =?UTF-8?q?feat:=20S3=20=EC=97=B0=EB=8F=99?= =?UTF-8?q?=EC=9D=84=20=EC=9C=84=ED=95=9C=20BucketConfig=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../member/bucketConfig/BucketConfig.java | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 src/main/java/capstone/facefriend/member/bucketConfig/BucketConfig.java diff --git a/src/main/java/capstone/facefriend/member/bucketConfig/BucketConfig.java b/src/main/java/capstone/facefriend/member/bucketConfig/BucketConfig.java new file mode 100644 index 0000000000..74b2ecfdbe --- /dev/null +++ b/src/main/java/capstone/facefriend/member/bucketConfig/BucketConfig.java @@ -0,0 +1,34 @@ +package capstone.facefriend.member.bucketConfig; + +import com.amazonaws.auth.AWSCredentials; +import com.amazonaws.auth.AWSStaticCredentialsProvider; +import com.amazonaws.auth.BasicAWSCredentials; +import com.amazonaws.services.s3.AmazonS3; +import com.amazonaws.services.s3.AmazonS3ClientBuilder; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class BucketConfig { + + @Value("${cloud.aws.credentials.accessKey}") + private String accessKey; + + @Value("${cloud.aws.credentials.secretKey}") + private String secretKey; + + @Value("${cloud.aws.region.static}") + private String region; + + @Bean + public AmazonS3 amazonS3() { + AWSCredentials credentials = new BasicAWSCredentials(accessKey, secretKey); + + return AmazonS3ClientBuilder + .standard() + .withCredentials(new AWSStaticCredentialsProvider(credentials)) + .withRegion(region) + .build(); + } +} From a0ae6bf61a5802fd1a65df850f42eb5c3d82eb5d Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Wed, 10 Apr 2024 21:01:45 +0900 Subject: [PATCH 080/265] =?UTF-8?q?feat:=20FaceInfo=20=EA=B8=B0=EB=8A=A5?= =?UTF-8?q?=20=EA=B5=AC=ED=98=84=EC=97=90=20=EC=82=AC=EC=9A=A9=EB=90=A0=20?= =?UTF-8?q?DTO=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../facefriend/member/service/dto/FaceInfoResponse.java | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 src/main/java/capstone/facefriend/member/service/dto/FaceInfoResponse.java diff --git a/src/main/java/capstone/facefriend/member/service/dto/FaceInfoResponse.java b/src/main/java/capstone/facefriend/member/service/dto/FaceInfoResponse.java new file mode 100644 index 0000000000..2e4a01e1ed --- /dev/null +++ b/src/main/java/capstone/facefriend/member/service/dto/FaceInfoResponse.java @@ -0,0 +1,7 @@ +package capstone.facefriend.member.service.dto; + +public record FaceInfoResponse( + String originS3Url, + String generatedS3Url +) { +} From 1b6f4b95bbdc6c5b0aa91385b211c9c5c1deeeea Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Wed, 10 Apr 2024 21:03:15 +0900 Subject: [PATCH 081/265] =?UTF-8?q?feat:=20=ED=9A=8C=EC=9B=90=EA=B0=80?= =?UTF-8?q?=EC=9E=85=20=EC=A7=81=ED=9B=84=20S3=EC=97=90=20=EC=97=85?= =?UTF-8?q?=EB=A1=9C=EB=93=9C=EB=90=98=EC=96=B4=20=EC=9E=88=EB=8A=94=20?= =?UTF-8?q?=EA=B8=B0=EB=B3=B8=ED=94=84=EB=A1=9C=ED=95=84=EB=A1=9C=20Member?= =?UTF-8?q?=20=EC=9D=98=20FaceInfo=20=ED=95=84=EB=93=9C=20=EC=B4=88?= =?UTF-8?q?=EA=B8=B0=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../member/service/MemberService.java | 34 +++++++++++++++---- 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/src/main/java/capstone/facefriend/member/service/MemberService.java b/src/main/java/capstone/facefriend/member/service/MemberService.java index 8ff13d6c6c..6dd3840bf4 100644 --- a/src/main/java/capstone/facefriend/member/service/MemberService.java +++ b/src/main/java/capstone/facefriend/member/service/MemberService.java @@ -4,17 +4,16 @@ import capstone.facefriend.auth.domain.TokenProvider; import capstone.facefriend.email.controller.dto.EmailVerificationResponse; import capstone.facefriend.email.service.EmailService; -import capstone.facefriend.member.domain.BasicInfo; -import capstone.facefriend.member.domain.BasicInfoRepository; -import capstone.facefriend.member.domain.Member; -import capstone.facefriend.member.domain.MemberRepository; +import capstone.facefriend.member.domain.*; import capstone.facefriend.member.exception.MemberException; +import capstone.facefriend.member.exception.MemberExceptionType; import capstone.facefriend.member.service.dto.FindEmailResponse; import capstone.facefriend.member.service.dto.SignInRequest; import capstone.facefriend.member.service.dto.SignUpRequest; import capstone.facefriend.redis.RedisDao; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -31,6 +30,7 @@ public class MemberService { private final TokenProvider tokenProvider; private final MemberRepository memberRepository; private final BasicInfoRepository basicInfoRepository; + private final FaceInfoRepository faceInfoRepository; private final PasswordEncoder passwordEncoder; private final EmailService emailService; @@ -42,9 +42,12 @@ public class MemberService { private static final String RESET_PASSWORD_SUCCESS_MESSAGE = "비밀번호 재설정 성공"; private static final Long SIGN_OUT_MINUTE = 1000 * 60 * 60 * 12L; // 12 시간 + @Value(value = "${default-profile.s3-url}") + private String defaultProfileS3Url; + @Transactional public String verifyDuplication(String email) { - if(memberRepository.findByEmail(email).isPresent()) { + if (memberRepository.findByEmail(email).isPresent()) { throw new MemberException(DUPLICATED_EMAIL); } return SIGN_UP_VALID_EMAIL; @@ -75,7 +78,7 @@ public String signUp(SignUpRequest request) { if (isVerified) { String encodedPassword = passwordEncoder.encode(request.password()); - BasicInfo basicInfo = builder() + BasicInfo basicInfo = BasicInfo.builder() .nickname("") .gender(Gender.DEFAULT) .ageGroup(AgeGroup.DEFAULT) @@ -85,12 +88,19 @@ public String signUp(SignUpRequest request) { .build(); basicInfoRepository.save(basicInfo); + FaceInfo faceInfo = FaceInfo.builder() + .originS3Url(defaultProfileS3Url) + .generatedS3url(defaultProfileS3Url) + .build(); + faceInfoRepository.save(faceInfo); + Member member = Member.builder() .email(request.email()) .password(encodedPassword) .isVerified(true) .role(USER) .basicInfo(basicInfo) + .faceInfo(faceInfo) .build(); memberRepository.save(member); @@ -166,6 +176,18 @@ public String verifyTemporaryPassword(String email, String temporaryPassword, St throw new MemberException(WRONG_TEMPORARY_PASSWORD); } return RESET_PASSWORD_SUCCESS_MESSAGE; + } + @Transactional + public String resetPassword(Long memberId, String newPassword) { + Member member = memberRepository.findById(memberId) + .orElseThrow(() -> new MemberException(NOT_FOUND)); + + String encodedPassword = passwordEncoder.encode(newPassword); + + member.setPassword(encodedPassword); + memberRepository.save(member); + + return RESET_PASSWORD_SUCCESS_MESSAGE; } } From b88db0fd941fc47cf3444f9830380c1a0cac302a Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Wed, 10 Apr 2024 21:03:48 +0900 Subject: [PATCH 082/265] =?UTF-8?q?feat:=20S3=20=EC=97=B0=EB=8F=99?= =?UTF-8?q?=EC=9D=84=20=EC=9C=84=ED=95=9C=20BucketService=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../member/service/BucketService.java | 113 ++++++++++++++++++ 1 file changed, 113 insertions(+) create mode 100644 src/main/java/capstone/facefriend/member/service/BucketService.java diff --git a/src/main/java/capstone/facefriend/member/service/BucketService.java b/src/main/java/capstone/facefriend/member/service/BucketService.java new file mode 100644 index 0000000000..7d3423d9fa --- /dev/null +++ b/src/main/java/capstone/facefriend/member/service/BucketService.java @@ -0,0 +1,113 @@ +package capstone.facefriend.member.service; + + +import capstone.facefriend.member.domain.FaceInfo; +import capstone.facefriend.member.domain.FaceInfoRepository; +import capstone.facefriend.member.domain.Member; +import capstone.facefriend.member.domain.MemberRepository; +import capstone.facefriend.member.exception.MemberException; +import capstone.facefriend.member.exception.MemberExceptionType; +import capstone.facefriend.member.multipartFile.ByteArrayMultipartFile; +import capstone.facefriend.member.service.dto.FaceInfoResponse; +import com.amazonaws.services.s3.AmazonS3; +import com.amazonaws.services.s3.model.CannedAccessControlList; +import com.amazonaws.services.s3.model.DeleteObjectRequest; +import com.amazonaws.services.s3.model.ObjectMetadata; +import com.amazonaws.services.s3.model.PutObjectRequest; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; + +import java.io.IOException; + +@Slf4j +@RequiredArgsConstructor +@Service +public class BucketService { + + @Value("${cloud.aws.s3.bucket}") + private String bucketName; + + @Value("${default-profile.s3-url}") + private String defaultProfileS3Url; + + private final AmazonS3 amazonS3; + + private final MemberRepository memberRepository; + private final FaceInfoRepository faceInfoRepository; + + private static final String ORIGIN_POSTFIX = "-origin"; + private static final String GENERATED_POSTFIX = "-generated"; + + // origin 업로드 & generated 업로드 + public FaceInfoResponse upload(MultipartFile origin, ByteArrayMultipartFile generated, Long memberId) throws IOException { + /** upload origin to s3 */ + // set metadata + ObjectMetadata originMetadata = new ObjectMetadata(); + originMetadata.setContentLength(origin.getInputStream().available()); + originMetadata.setContentType("image/jpeg"); + + String originObjectName = memberId + ORIGIN_POSTFIX; + amazonS3.putObject( + new PutObjectRequest( + bucketName, + originObjectName, + origin.getInputStream(), // origin + originMetadata + ).withCannedAcl(CannedAccessControlList.PublicRead) + ); + String originS3Url = amazonS3.getUrl(bucketName, originObjectName).toString(); + + /** upload generated to s3 */ + // set metadata + ObjectMetadata generatedMetadata = new ObjectMetadata(); + generatedMetadata.setContentLength(generated.getInputStream().available()); + generatedMetadata.setContentType("image/jpeg"); + + String generatedObjectName = memberId + GENERATED_POSTFIX; + amazonS3.putObject( + new PutObjectRequest( + bucketName, + generatedObjectName, + generated.getInputStream(), // generated + generatedMetadata + ).withCannedAcl(CannedAccessControlList.PublicRead) + ); + String generatedS3Url = amazonS3.getUrl(bucketName, generatedObjectName).toString(); + + // FaceInfo 저장 + FaceInfo faceInfo = FaceInfo.builder() + .originS3Url(originS3Url) + .generatedS3url(generatedS3Url) + .build(); + faceInfoRepository.save(faceInfo); + + // Member 최신화 후 저장 + Member member = memberRepository.findById(memberId) + .orElseThrow(() -> new MemberException(MemberExceptionType.NOT_FOUND)); + member.setFaceInfo(faceInfo); + memberRepository.save(member); + + return new FaceInfoResponse(originS3Url, generatedS3Url); + } + + // origin 수정 -> generated 수정 + public FaceInfoResponse update(MultipartFile origin, ByteArrayMultipartFile generated, Long memberId) throws IOException { + delete(memberId); // 기존에 저장되어있던 사진 삭제 + return upload(origin, generated, memberId); // 새로 사진 저장 + } + + // origin 삭제 -> generated 삭제 + public FaceInfoResponse delete(Long memberId) { + String originObjectName = memberId + ORIGIN_POSTFIX; + amazonS3.deleteObject(new DeleteObjectRequest(bucketName, originObjectName)); + + String generatedObjectName = memberId + GENERATED_POSTFIX; + amazonS3.deleteObject(new DeleteObjectRequest(bucketName, generatedObjectName)); + + return new FaceInfoResponse(defaultProfileS3Url, defaultProfileS3Url); + } +} + From 85629c7a7bfdb5dd8a50f942bc4f30919cb54dcf Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Wed, 10 Apr 2024 21:04:38 +0900 Subject: [PATCH 083/265] =?UTF-8?q?faet:=20FaceInfo=20=EA=B0=9D=EC=B2=B4?= =?UTF-8?q?=20CRUD=20=EA=B8=B0=EB=8A=A5=20=EB=B0=8F=20S3=20=EC=97=B0?= =?UTF-8?q?=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../member/service/FaceInfoService.java | 123 ++++++++++++++++++ 1 file changed, 123 insertions(+) create mode 100644 src/main/java/capstone/facefriend/member/service/FaceInfoService.java diff --git a/src/main/java/capstone/facefriend/member/service/FaceInfoService.java b/src/main/java/capstone/facefriend/member/service/FaceInfoService.java new file mode 100644 index 0000000000..9052ff411e --- /dev/null +++ b/src/main/java/capstone/facefriend/member/service/FaceInfoService.java @@ -0,0 +1,123 @@ +package capstone.facefriend.member.service; + +import capstone.facefriend.member.domain.FaceInfo; +import capstone.facefriend.member.domain.FaceInfoRepository; +import capstone.facefriend.member.domain.Member; +import capstone.facefriend.member.domain.MemberRepository; +import capstone.facefriend.member.exception.MemberException; +import capstone.facefriend.member.exception.MemberExceptionType; +import capstone.facefriend.member.multipartFile.ByteArrayMultipartFile; +import capstone.facefriend.member.service.dto.FaceInfoResponse; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.core.io.ByteArrayResource; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.web.client.RestTemplate; +import org.springframework.web.multipart.MultipartFile; + +import java.io.IOException; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.util.Base64; +import java.util.Map; + + +@Slf4j +@Service +@RequiredArgsConstructor +public class FaceInfoService { + + @Value("${flask.generate-url}") + private String requestUrl; + + @Value("${default-profile.s3-url}") + private String defaultProfileS3Url; + + private final RestTemplate restTemplate; + + private final BucketService bucketService; + + private final FaceInfoRepository faceInfoRepository; + private final MemberRepository memberRepository; + + // origin 업로드 & generated 업로드 + public FaceInfoResponse upload(MultipartFile origin, Long styleId, Long memberId) throws IOException { + ByteArrayMultipartFile generated = generate(origin, styleId, memberId); + return bucketService.upload(origin, generated, memberId); + } + + // origin 삭제 & generated 삭제 -> origin 업로드 & generated 업로드 + public FaceInfoResponse update(MultipartFile origin, Long styleId, Long memberId) throws IOException { + ByteArrayMultipartFile generated = generate(origin, styleId, memberId); + return bucketService.update(origin, generated, memberId); + } + + public FaceInfoResponse get(Long memberId) { + Member member = memberRepository.findById(memberId) + .orElseThrow(() -> new MemberException(MemberExceptionType.NOT_FOUND)); + + FaceInfo faceInfo = member.getFaceInfo(); + return new FaceInfoResponse(faceInfo.getOriginS3Url(), faceInfo.getGeneratedS3url()); + } + + // origin 삭제 & generated 삭제 + public FaceInfoResponse delete(Long memberId) { + FaceInfoResponse delete = bucketService.delete(memberId); + + Member member = memberRepository.findById(memberId) + .orElseThrow(() -> new MemberException(MemberExceptionType.NOT_FOUND)); + + FaceInfo faceInfo = member.getFaceInfo(); + faceInfo.setOriginS3Url(defaultProfileS3Url); + faceInfo.setGeneratedS3Url(defaultProfileS3Url); + faceInfoRepository.save(faceInfo); + + member.setFaceInfo(faceInfo); + memberRepository.save(member); + + return delete; + } + + private ByteArrayMultipartFile generate(MultipartFile origin, Long styleId, Long memberId) throws IOException { + // convert MultipartFile into ByteArrayResource + ByteArrayResource resource = new ByteArrayResource(origin.getBytes()) { + @Override + public String getFilename() { + return URLEncoder.encode(origin.getOriginalFilename(), StandardCharsets.UTF_8); + } + }; + + // body + LinkedMultiValueMap body = new LinkedMultiValueMap<>(); + body.add("image", resource); + body.add("style_id", styleId); + body.add("user_id", memberId); + + // header + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.MULTIPART_FORM_DATA); + + // request entity + HttpEntity> requestEntity = new HttpEntity<>(body, headers); + // response entity + ResponseEntity responseEntity = restTemplate.postForEntity(requestUrl, requestEntity, JsonNode.class); + + // convert JSON into Map + ObjectMapper objectMapper = new ObjectMapper(); + Map result = objectMapper.convertValue(responseEntity.getBody(), new TypeReference<>() {}); + + byte[] imageBinary = Base64.getDecoder().decode((String)result.get("image_binary")); + + return new ByteArrayMultipartFile(imageBinary, origin.getOriginalFilename()); + } +} + From 77563cc6d72dcc11f53adfe30374059d807d2615 Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Wed, 10 Apr 2024 21:05:19 +0900 Subject: [PATCH 084/265] =?UTF-8?q?feat:=20FaceInfo=20=EA=B0=9D=EC=B2=B4?= =?UTF-8?q?=20CRUD=20=EC=BB=A8=ED=8A=B8=EB=A1=A4=EB=9F=AC=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../member/controller/FaceInfoController.java | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 src/main/java/capstone/facefriend/member/controller/FaceInfoController.java diff --git a/src/main/java/capstone/facefriend/member/controller/FaceInfoController.java b/src/main/java/capstone/facefriend/member/controller/FaceInfoController.java new file mode 100644 index 0000000000..31a431d72d --- /dev/null +++ b/src/main/java/capstone/facefriend/member/controller/FaceInfoController.java @@ -0,0 +1,52 @@ +package capstone.facefriend.member.controller; + +import capstone.facefriend.auth.controller.support.AuthMember; +import capstone.facefriend.member.service.FaceInfoService; +import capstone.facefriend.member.service.dto.FaceInfoResponse; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import java.io.IOException; + +@Slf4j +@RestController +@RequiredArgsConstructor +public class FaceInfoController { + + private final FaceInfoService faceInfoService; + + @PostMapping("/face-info") + public ResponseEntity upload( + @RequestPart("origin") MultipartFile origin, + @RequestParam("styleId") Long styleId, + @AuthMember Long memberId + ) throws IOException { + return ResponseEntity.ok(faceInfoService.upload(origin, styleId, memberId)); + } + + @GetMapping("/face-info") + public ResponseEntity get( + @AuthMember Long memberId + ) { + return ResponseEntity.ok(faceInfoService.get(memberId)); + } + + @PutMapping("/face-info") + public ResponseEntity update( + @RequestPart("origin")MultipartFile origin, + @RequestParam("styleId") Long styleId, + @AuthMember Long memberId + ) throws IOException { + return ResponseEntity.ok(faceInfoService.update(origin, styleId, memberId)); + } + + @DeleteMapping("/face-info") + public ResponseEntity delete( + @AuthMember Long memberId + ) { + return ResponseEntity.ok(faceInfoService.delete(memberId)); + } +} From 62518272899f206d5df7b42d23e904e510d45500 Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Wed, 10 Apr 2024 21:06:11 +0900 Subject: [PATCH 085/265] =?UTF-8?q?chore:=20s3=20=EC=9D=98=EC=A1=B4?= =?UTF-8?q?=EC=84=B1=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 46e45615d4..2a37e63b67 100644 --- a/build.gradle +++ b/build.gradle @@ -21,7 +21,7 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-web' annotationProcessor "org.springframework.boot:spring-boot-configuration-processor" - testImplementation 'org.springframework.boot:spring-boot-starter-test' + implementation 'org.springframework.boot:spring-boot-starter-test' // postgresql implementation 'org.postgresql:postgresql' @@ -48,6 +48,8 @@ dependencies { // 메일 인증 implementation group: 'org.springframework.boot', name: 'spring-boot-starter-mail', version: '3.0.5' + // AWS S3 + implementation 'org.springframework.cloud:spring-cloud-starter-aws:2.2.6.RELEASE' } tasks.named('test') { From 80b16b38df63ed0ff8fa10052523d4fa4073a11c Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Wed, 10 Apr 2024 21:21:54 +0900 Subject: [PATCH 086/265] =?UTF-8?q?feat:=20=ED=9A=8C=EC=9B=90=ED=83=88?= =?UTF-8?q?=ED=87=B4=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../facefriend/auth/config/AuthConfig.java | 4 ++++ .../member/controller/MemberController.java | 7 +++++++ .../member/domain/MemberRepository.java | 2 ++ .../facefriend/member/service/MemberService.java | 16 ++++++++++++++-- 4 files changed, 27 insertions(+), 2 deletions(-) diff --git a/src/main/java/capstone/facefriend/auth/config/AuthConfig.java b/src/main/java/capstone/facefriend/auth/config/AuthConfig.java index 79678f815a..52c7c3ea17 100644 --- a/src/main/java/capstone/facefriend/auth/config/AuthConfig.java +++ b/src/main/java/capstone/facefriend/auth/config/AuthConfig.java @@ -51,6 +51,7 @@ private HandlerInterceptor loginCheckInterceptor() { .addIncludePathPattern("/auth/reset-password", POST) .addIncludePathPattern("/auth/signout", DELETE) + .addIncludePathPattern("/auth/exit", DELETE) .addIncludePathPattern("/basic-info", ANY) .addIncludePathPattern("/face-info", ANY) @@ -63,6 +64,7 @@ private HandlerInterceptor loginInterceptor() { .addIncludePathPattern("/auth/reset-password", POST) .addIncludePathPattern("/auth/signout", DELETE) + .addIncludePathPattern("/auth/exit", DELETE) .addIncludePathPattern("/basic-info", ANY) .addIncludePathPattern("/face-info", ANY) @@ -81,6 +83,7 @@ private HandlerInterceptor tokenBlackListInterceptor() { .addExcludePathPattern("/**", OPTIONS) .addIncludePathPattern("/auth/signout", DELETE) + .addIncludePathPattern("/auth/exit", DELETE) .addIncludePathPattern("/auth/reset-password", POST) .addIncludePathPattern("/basic-info", ANY) .addIncludePathPattern("/face-info", ANY); @@ -91,6 +94,7 @@ private HandlerInterceptor verificationInterceptor() { .addExcludePathPattern("/**", OPTIONS) .addIncludePathPattern("/auth/signout", DELETE) + .addIncludePathPattern("/auth/exit", DELETE) .addIncludePathPattern("/auth/reset-password", POST) .addIncludePathPattern("/basic-info", ANY) .addIncludePathPattern("/face-info", ANY); diff --git a/src/main/java/capstone/facefriend/member/controller/MemberController.java b/src/main/java/capstone/facefriend/member/controller/MemberController.java index 114f5c9775..cb3caf9cda 100644 --- a/src/main/java/capstone/facefriend/member/controller/MemberController.java +++ b/src/main/java/capstone/facefriend/member/controller/MemberController.java @@ -117,6 +117,13 @@ public ResponseEntity signOut( return ResponseEntity.ok(memberService.signOut(memberId, accessToken)); } + @DeleteMapping("/auth/exit") + public ResponseEntity exit( + @AuthMember Long memberId + ) { + return ResponseEntity.ok(memberService.exit(memberId)); + } + @PostMapping("/auth/reissue") public ResponseEntity reissueTokens( @RequestBody ReissueRequest request, diff --git a/src/main/java/capstone/facefriend/member/domain/MemberRepository.java b/src/main/java/capstone/facefriend/member/domain/MemberRepository.java index 159b5974ac..cc536e0222 100644 --- a/src/main/java/capstone/facefriend/member/domain/MemberRepository.java +++ b/src/main/java/capstone/facefriend/member/domain/MemberRepository.java @@ -17,4 +17,6 @@ public interface MemberRepository extends Repository { Optional findById(Long id); Page findAll(Pageable pageable); + + void deleteById(Long id); } diff --git a/src/main/java/capstone/facefriend/member/service/MemberService.java b/src/main/java/capstone/facefriend/member/service/MemberService.java index 6dd3840bf4..6e46e238e2 100644 --- a/src/main/java/capstone/facefriend/member/service/MemberService.java +++ b/src/main/java/capstone/facefriend/member/service/MemberService.java @@ -40,7 +40,9 @@ public class MemberService { private static final String SIGN_UP_SUCCESS_MESSAGE = "회원가입 성공"; private static final String SIGN_OUT_SUCCESS_MESSAGE = "로그아웃 성공"; private static final String RESET_PASSWORD_SUCCESS_MESSAGE = "비밀번호 재설정 성공"; - private static final Long SIGN_OUT_MINUTE = 1000 * 60 * 60 * 12L; // 12 시간 + private static final String EXIT_SUCCESS_MESSAGE = "회원탈퇴 성공"; + + private static final Long BLACKLIST_REMAIN_MINUTE = 1000 * 60 * 60 * 12L; // 12 시간 @Value(value = "${default-profile.s3-url}") private String defaultProfileS3Url; @@ -128,10 +130,19 @@ public TokenResponse signIn(SignInRequest request) { @Transactional public String signOut(Long memberId, String accessToken) { redisDao.deleteRefreshToken(String.valueOf(memberId)); - redisDao.setAccessTokenSignOut(accessToken, SIGN_OUT_MINUTE); + redisDao.setAccessTokenSignOut(accessToken, BLACKLIST_REMAIN_MINUTE); return SIGN_OUT_SUCCESS_MESSAGE; } + @Transactional + public String exit(Long memberId) { + if (memberRepository.findById(memberId).isPresent()) { + memberRepository.deleteById(memberId); + } + return EXIT_SUCCESS_MESSAGE; + } + + @Transactional public TokenResponse reissueTokens(Long memberId, String refreshTokenInput) { String refreshToken = redisDao.getRefreshToken(String.valueOf(memberId)); if (!refreshToken.equals(refreshTokenInput)) { @@ -140,6 +151,7 @@ public TokenResponse reissueTokens(Long memberId, String refreshTokenInput) { return tokenProvider.createTokens(memberId); } + @Transactional public FindEmailResponse findEmail(String emailInput) { Member member = memberRepository.findByEmail(emailInput) From 5b3d63d0f2089af5ece684cc5693c7136148c2fb Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Wed, 17 Apr 2024 10:55:51 +0900 Subject: [PATCH 087/265] =?UTF-8?q?style:=20=EB=A9=94=EC=84=9C=EB=93=9C=20?= =?UTF-8?q?=EB=B0=8F=20=ED=95=84=EB=93=9C=20=EC=9D=B4=EB=A6=84=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../member/controller/FaceInfoController.java | 8 +-- .../facefriend/member/domain/Member.java | 2 + .../member/service/BucketService.java | 70 ++++++++++++++----- .../member/service/FaceInfoService.java | 16 ++--- 4 files changed, 67 insertions(+), 29 deletions(-) diff --git a/src/main/java/capstone/facefriend/member/controller/FaceInfoController.java b/src/main/java/capstone/facefriend/member/controller/FaceInfoController.java index 31a431d72d..f8f12841fe 100644 --- a/src/main/java/capstone/facefriend/member/controller/FaceInfoController.java +++ b/src/main/java/capstone/facefriend/member/controller/FaceInfoController.java @@ -24,14 +24,14 @@ public ResponseEntity upload( @RequestParam("styleId") Long styleId, @AuthMember Long memberId ) throws IOException { - return ResponseEntity.ok(faceInfoService.upload(origin, styleId, memberId)); + return ResponseEntity.ok(faceInfoService.uploadOrigin(origin, styleId, memberId)); } @GetMapping("/face-info") public ResponseEntity get( @AuthMember Long memberId ) { - return ResponseEntity.ok(faceInfoService.get(memberId)); + return ResponseEntity.ok(faceInfoService.getOriginAndGenerated(memberId)); } @PutMapping("/face-info") @@ -40,13 +40,13 @@ public ResponseEntity update( @RequestParam("styleId") Long styleId, @AuthMember Long memberId ) throws IOException { - return ResponseEntity.ok(faceInfoService.update(origin, styleId, memberId)); + return ResponseEntity.ok(faceInfoService.updateOrigin(origin, styleId, memberId)); } @DeleteMapping("/face-info") public ResponseEntity delete( @AuthMember Long memberId ) { - return ResponseEntity.ok(faceInfoService.delete(memberId)); + return ResponseEntity.ok(faceInfoService.deleteOriginAndGenerated(memberId)); } } diff --git a/src/main/java/capstone/facefriend/member/domain/Member.java b/src/main/java/capstone/facefriend/member/domain/Member.java index a146ce061f..348f7cdcec 100644 --- a/src/main/java/capstone/facefriend/member/domain/Member.java +++ b/src/main/java/capstone/facefriend/member/domain/Member.java @@ -5,6 +5,7 @@ import lombok.*; import lombok.extern.slf4j.Slf4j; import org.hibernate.annotations.DynamicInsert; +import org.hibernate.annotations.DynamicUpdate; @Getter @Builder @@ -14,6 +15,7 @@ @Entity @Slf4j @DynamicInsert +@DynamicUpdate public class Member extends BaseEntity { private static final int EMAIL_MASKING_LENGTH = 2; diff --git a/src/main/java/capstone/facefriend/member/service/BucketService.java b/src/main/java/capstone/facefriend/member/service/BucketService.java index 7d3423d9fa..bebe983d83 100644 --- a/src/main/java/capstone/facefriend/member/service/BucketService.java +++ b/src/main/java/capstone/facefriend/member/service/BucketService.java @@ -21,6 +21,8 @@ import org.springframework.web.multipart.MultipartFile; import java.io.IOException; +import java.util.ArrayList; +import java.util.List; @Slf4j @RequiredArgsConstructor @@ -30,26 +32,31 @@ public class BucketService { @Value("${cloud.aws.s3.bucket}") private String bucketName; - @Value("${default-profile.s3-url}") + @Value("${cloud.aws.s3.default-profile}") private String defaultProfileS3Url; - private final AmazonS3 amazonS3; + @Value("${cloud.aws.s3.origin-postfix}") + private String originPostfix; + + @Value("${cloud.aws.s3.generated-postfix}") + private String generatedPostfix; + @Value("${cloud.aws.s3.resume-postfix}") + private String resumePostfix; + + private final AmazonS3 amazonS3; private final MemberRepository memberRepository; private final FaceInfoRepository faceInfoRepository; - private static final String ORIGIN_POSTFIX = "-origin"; - private static final String GENERATED_POSTFIX = "-generated"; - - // origin 업로드 & generated 업로드 - public FaceInfoResponse upload(MultipartFile origin, ByteArrayMultipartFile generated, Long memberId) throws IOException { + // FaceInfo : origin 업로드 & generated 업로드 + public FaceInfoResponse uploadOriginAndGenerated(MultipartFile origin, ByteArrayMultipartFile generated, Long memberId) throws IOException { /** upload origin to s3 */ // set metadata ObjectMetadata originMetadata = new ObjectMetadata(); originMetadata.setContentLength(origin.getInputStream().available()); originMetadata.setContentType("image/jpeg"); - String originObjectName = memberId + ORIGIN_POSTFIX; + String originObjectName = memberId + originPostfix; amazonS3.putObject( new PutObjectRequest( bucketName, @@ -66,7 +73,7 @@ public FaceInfoResponse upload(MultipartFile origin, ByteArrayMultipartFile gene generatedMetadata.setContentLength(generated.getInputStream().available()); generatedMetadata.setContentType("image/jpeg"); - String generatedObjectName = memberId + GENERATED_POSTFIX; + String generatedObjectName = memberId + generatedPostfix; amazonS3.putObject( new PutObjectRequest( bucketName, @@ -93,21 +100,50 @@ public FaceInfoResponse upload(MultipartFile origin, ByteArrayMultipartFile gene return new FaceInfoResponse(originS3Url, generatedS3Url); } - // origin 수정 -> generated 수정 - public FaceInfoResponse update(MultipartFile origin, ByteArrayMultipartFile generated, Long memberId) throws IOException { - delete(memberId); // 기존에 저장되어있던 사진 삭제 - return upload(origin, generated, memberId); // 새로 사진 저장 + // FaceInfo : origin 수정 -> generated 수정 + public FaceInfoResponse updateOriginAndGenerated(MultipartFile origin, ByteArrayMultipartFile generated, Long memberId) throws IOException { + deleteOriginAndGenerated(memberId); // 기존에 저장되어있던 사진 삭제 + return uploadOriginAndGenerated(origin, generated, memberId); // 새로 사진 저장 } - // origin 삭제 -> generated 삭제 - public FaceInfoResponse delete(Long memberId) { - String originObjectName = memberId + ORIGIN_POSTFIX; + // FaceInfo : origin 삭제 -> generated 삭제 + public FaceInfoResponse deleteOriginAndGenerated(Long memberId) { + String originObjectName = memberId + originPostfix; amazonS3.deleteObject(new DeleteObjectRequest(bucketName, originObjectName)); - String generatedObjectName = memberId + GENERATED_POSTFIX; + String generatedObjectName = memberId + generatedPostfix; amazonS3.deleteObject(new DeleteObjectRequest(bucketName, generatedObjectName)); return new FaceInfoResponse(defaultProfileS3Url, defaultProfileS3Url); } + + // Resume : resumeImages 업로드 + public List uploadResumeImages(MultipartFile[] resumeImages, Long memberId) throws IOException { + /** upload resumeImage to s3 */ + + int count = 0; + List resumeS3Urls = new ArrayList<>(); + for (MultipartFile resumeImage : resumeImages) { + // set metadata + ObjectMetadata originMetadata = new ObjectMetadata(); + originMetadata.setContentLength(resumeImage.getInputStream().available()); + originMetadata.setContentType("image/jpeg"); + + String resumeImageObjectName = memberId + resumePostfix + count; // ex) bucketName/1-resume-1.jpg + amazonS3.putObject( + new PutObjectRequest( + bucketName, + resumeImageObjectName, + resumeImage.getInputStream(), // resume + originMetadata + ).withCannedAcl(CannedAccessControlList.PublicRead) + ); + resumeS3Urls.add(amazonS3.getUrl(bucketName, resumeImageObjectName).toString()); + } + +// resumeRepository.save(); + + return resumeS3Urls; + } } diff --git a/src/main/java/capstone/facefriend/member/service/FaceInfoService.java b/src/main/java/capstone/facefriend/member/service/FaceInfoService.java index 9052ff411e..44558a4f59 100644 --- a/src/main/java/capstone/facefriend/member/service/FaceInfoService.java +++ b/src/main/java/capstone/facefriend/member/service/FaceInfoService.java @@ -39,7 +39,7 @@ public class FaceInfoService { @Value("${flask.generate-url}") private String requestUrl; - @Value("${default-profile.s3-url}") + @Value("${cloud.aws.s3.default-profile}") private String defaultProfileS3Url; private final RestTemplate restTemplate; @@ -50,18 +50,18 @@ public class FaceInfoService { private final MemberRepository memberRepository; // origin 업로드 & generated 업로드 - public FaceInfoResponse upload(MultipartFile origin, Long styleId, Long memberId) throws IOException { + public FaceInfoResponse uploadOrigin(MultipartFile origin, Long styleId, Long memberId) throws IOException { ByteArrayMultipartFile generated = generate(origin, styleId, memberId); - return bucketService.upload(origin, generated, memberId); + return bucketService.uploadOriginAndGenerated(origin, generated, memberId); } // origin 삭제 & generated 삭제 -> origin 업로드 & generated 업로드 - public FaceInfoResponse update(MultipartFile origin, Long styleId, Long memberId) throws IOException { + public FaceInfoResponse updateOrigin(MultipartFile origin, Long styleId, Long memberId) throws IOException { ByteArrayMultipartFile generated = generate(origin, styleId, memberId); - return bucketService.update(origin, generated, memberId); + return bucketService.updateOriginAndGenerated(origin, generated, memberId); } - public FaceInfoResponse get(Long memberId) { + public FaceInfoResponse getOriginAndGenerated(Long memberId) { Member member = memberRepository.findById(memberId) .orElseThrow(() -> new MemberException(MemberExceptionType.NOT_FOUND)); @@ -70,8 +70,8 @@ public FaceInfoResponse get(Long memberId) { } // origin 삭제 & generated 삭제 - public FaceInfoResponse delete(Long memberId) { - FaceInfoResponse delete = bucketService.delete(memberId); + public FaceInfoResponse deleteOriginAndGenerated(Long memberId) { + FaceInfoResponse delete = bucketService.deleteOriginAndGenerated(memberId); Member member = memberRepository.findById(memberId) .orElseThrow(() -> new MemberException(MemberExceptionType.NOT_FOUND)); From e58c715176f80de3d95fcd64ed77782bcb37bd39 Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Tue, 30 Apr 2024 20:29:40 +0900 Subject: [PATCH 088/265] =?UTF-8?q?fix:=20s3=20=EC=84=A4=EC=A0=95=20?= =?UTF-8?q?=EB=93=A4=EC=97=AC=EC=93=B0=EA=B8=B0=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../facefriend/member/bucketConfig/BucketConfig.java | 6 +++--- .../facefriend/member/service/BucketService.java | 10 +++++----- .../facefriend/member/service/FaceInfoService.java | 2 +- .../facefriend/member/service/MemberService.java | 2 +- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/main/java/capstone/facefriend/member/bucketConfig/BucketConfig.java b/src/main/java/capstone/facefriend/member/bucketConfig/BucketConfig.java index 74b2ecfdbe..6a8ffa225e 100644 --- a/src/main/java/capstone/facefriend/member/bucketConfig/BucketConfig.java +++ b/src/main/java/capstone/facefriend/member/bucketConfig/BucketConfig.java @@ -12,13 +12,13 @@ @Configuration public class BucketConfig { - @Value("${cloud.aws.credentials.accessKey}") + @Value("${spring.cloud.aws.credentials.accessKey}") private String accessKey; - @Value("${cloud.aws.credentials.secretKey}") + @Value("${spring.cloud.aws.credentials.secretKey}") private String secretKey; - @Value("${cloud.aws.region.static}") + @Value("${spring.cloud.aws.region.static}") private String region; @Bean diff --git a/src/main/java/capstone/facefriend/member/service/BucketService.java b/src/main/java/capstone/facefriend/member/service/BucketService.java index bebe983d83..f3f6d0a27c 100644 --- a/src/main/java/capstone/facefriend/member/service/BucketService.java +++ b/src/main/java/capstone/facefriend/member/service/BucketService.java @@ -29,19 +29,19 @@ @Service public class BucketService { - @Value("${cloud.aws.s3.bucket}") + @Value("${spring.cloud.aws.s3.bucket}") private String bucketName; - @Value("${cloud.aws.s3.default-profile}") + @Value("${spring.cloud.aws.s3.default-profile}") private String defaultProfileS3Url; - @Value("${cloud.aws.s3.origin-postfix}") + @Value("${spring.cloud.aws.s3.origin-postfix}") private String originPostfix; - @Value("${cloud.aws.s3.generated-postfix}") + @Value("${spring.cloud.aws.s3.generated-postfix}") private String generatedPostfix; - @Value("${cloud.aws.s3.resume-postfix}") + @Value("${spring.cloud.aws.s3.resume-postfix}") private String resumePostfix; private final AmazonS3 amazonS3; diff --git a/src/main/java/capstone/facefriend/member/service/FaceInfoService.java b/src/main/java/capstone/facefriend/member/service/FaceInfoService.java index 44558a4f59..d1d803c16c 100644 --- a/src/main/java/capstone/facefriend/member/service/FaceInfoService.java +++ b/src/main/java/capstone/facefriend/member/service/FaceInfoService.java @@ -39,7 +39,7 @@ public class FaceInfoService { @Value("${flask.generate-url}") private String requestUrl; - @Value("${cloud.aws.s3.default-profile}") + @Value("${spring.cloud.aws.s3.default-profile}") private String defaultProfileS3Url; private final RestTemplate restTemplate; diff --git a/src/main/java/capstone/facefriend/member/service/MemberService.java b/src/main/java/capstone/facefriend/member/service/MemberService.java index 6e46e238e2..1ae16ceeb9 100644 --- a/src/main/java/capstone/facefriend/member/service/MemberService.java +++ b/src/main/java/capstone/facefriend/member/service/MemberService.java @@ -44,7 +44,7 @@ public class MemberService { private static final Long BLACKLIST_REMAIN_MINUTE = 1000 * 60 * 60 * 12L; // 12 시간 - @Value(value = "${default-profile.s3-url}") + @Value("${spring.cloud.aws.s3.default-profile}") private String defaultProfileS3Url; @Transactional From d2081e3846de85d02bd18e57b99c454f30c573e3 Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Thu, 2 May 2024 00:24:30 +0900 Subject: [PATCH 089/265] =?UTF-8?q?style:=20=EB=94=94=EB=A0=89=ED=86=A0?= =?UTF-8?q?=EB=A6=AC=20=EA=B5=AC=EC=A1=B0=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../member/domain/{ => basicInfo}/BasicInfo.java | 0 .../domain/{ => basicInfo}/BasicInfoRepository.java | 0 .../facefriend/member/domain/{ => faceInfo}/FaceInfo.java | 0 .../member/domain/{ => faceInfo}/FaceInfoRepository.java | 0 .../facefriend/member/domain/{ => member}/Member.java | 8 ++++++++ .../member/domain/{ => member}/MemberRepository.java | 0 .../facefriend/member/domain/{ => member}/Role.java | 0 .../member/exception/{ => member}/MemberException.java | 2 +- .../exception/{ => member}/MemberExceptionType.java | 2 +- .../service/dto/{ => basicInfo}/BasicInfoRequest.java | 0 .../service/dto/{ => basicInfo}/BasicInfoResponse.java | 0 .../service/dto/{ => faceInfo}/FaceInfoResponse.java | 0 .../service/dto/{ => member}/FindEmailResponse.java | 0 .../member/service/dto/{ => member}/ReissueRequest.java | 0 .../service/dto/{ => member}/ResetPasswordRequest.java | 0 .../member/service/dto/{ => member}/SignInRequest.java | 0 .../member/service/dto/{ => member}/SignUpRequest.java | 0 17 files changed, 10 insertions(+), 2 deletions(-) rename src/main/java/capstone/facefriend/member/domain/{ => basicInfo}/BasicInfo.java (100%) rename src/main/java/capstone/facefriend/member/domain/{ => basicInfo}/BasicInfoRepository.java (100%) rename src/main/java/capstone/facefriend/member/domain/{ => faceInfo}/FaceInfo.java (100%) rename src/main/java/capstone/facefriend/member/domain/{ => faceInfo}/FaceInfoRepository.java (100%) rename src/main/java/capstone/facefriend/member/domain/{ => member}/Member.java (89%) rename src/main/java/capstone/facefriend/member/domain/{ => member}/MemberRepository.java (100%) rename src/main/java/capstone/facefriend/member/domain/{ => member}/Role.java (100%) rename src/main/java/capstone/facefriend/member/exception/{ => member}/MemberException.java (83%) rename src/main/java/capstone/facefriend/member/exception/{ => member}/MemberExceptionType.java (97%) rename src/main/java/capstone/facefriend/member/service/dto/{ => basicInfo}/BasicInfoRequest.java (100%) rename src/main/java/capstone/facefriend/member/service/dto/{ => basicInfo}/BasicInfoResponse.java (100%) rename src/main/java/capstone/facefriend/member/service/dto/{ => faceInfo}/FaceInfoResponse.java (100%) rename src/main/java/capstone/facefriend/member/service/dto/{ => member}/FindEmailResponse.java (100%) rename src/main/java/capstone/facefriend/member/service/dto/{ => member}/ReissueRequest.java (100%) rename src/main/java/capstone/facefriend/member/service/dto/{ => member}/ResetPasswordRequest.java (100%) rename src/main/java/capstone/facefriend/member/service/dto/{ => member}/SignInRequest.java (100%) rename src/main/java/capstone/facefriend/member/service/dto/{ => member}/SignUpRequest.java (100%) diff --git a/src/main/java/capstone/facefriend/member/domain/BasicInfo.java b/src/main/java/capstone/facefriend/member/domain/basicInfo/BasicInfo.java similarity index 100% rename from src/main/java/capstone/facefriend/member/domain/BasicInfo.java rename to src/main/java/capstone/facefriend/member/domain/basicInfo/BasicInfo.java diff --git a/src/main/java/capstone/facefriend/member/domain/BasicInfoRepository.java b/src/main/java/capstone/facefriend/member/domain/basicInfo/BasicInfoRepository.java similarity index 100% rename from src/main/java/capstone/facefriend/member/domain/BasicInfoRepository.java rename to src/main/java/capstone/facefriend/member/domain/basicInfo/BasicInfoRepository.java diff --git a/src/main/java/capstone/facefriend/member/domain/FaceInfo.java b/src/main/java/capstone/facefriend/member/domain/faceInfo/FaceInfo.java similarity index 100% rename from src/main/java/capstone/facefriend/member/domain/FaceInfo.java rename to src/main/java/capstone/facefriend/member/domain/faceInfo/FaceInfo.java diff --git a/src/main/java/capstone/facefriend/member/domain/FaceInfoRepository.java b/src/main/java/capstone/facefriend/member/domain/faceInfo/FaceInfoRepository.java similarity index 100% rename from src/main/java/capstone/facefriend/member/domain/FaceInfoRepository.java rename to src/main/java/capstone/facefriend/member/domain/faceInfo/FaceInfoRepository.java diff --git a/src/main/java/capstone/facefriend/member/domain/Member.java b/src/main/java/capstone/facefriend/member/domain/member/Member.java similarity index 89% rename from src/main/java/capstone/facefriend/member/domain/Member.java rename to src/main/java/capstone/facefriend/member/domain/member/Member.java index 348f7cdcec..9ab8427e91 100644 --- a/src/main/java/capstone/facefriend/member/domain/Member.java +++ b/src/main/java/capstone/facefriend/member/domain/member/Member.java @@ -45,6 +45,10 @@ public class Member extends BaseEntity { @JoinColumn(name = "FACE_INFO_ID", nullable = false) private FaceInfo faceInfo; + @OneToOne + @JoinColumn(name = "ANALYSIS_INFO_ID", nullable = false) + private AnalysisInfo analysisInfo; + public Member(String email) { this.email = email; this.role = Role.USER; @@ -74,6 +78,10 @@ public void setFaceInfo(FaceInfo faceInfo) { this.faceInfo = faceInfo; } + public void setAnalysisInfo(AnalysisInfo analysisInfo) { + this.analysisInfo = analysisInfo; + } + public boolean isVerified() { return this.isVerified == true; } diff --git a/src/main/java/capstone/facefriend/member/domain/MemberRepository.java b/src/main/java/capstone/facefriend/member/domain/member/MemberRepository.java similarity index 100% rename from src/main/java/capstone/facefriend/member/domain/MemberRepository.java rename to src/main/java/capstone/facefriend/member/domain/member/MemberRepository.java diff --git a/src/main/java/capstone/facefriend/member/domain/Role.java b/src/main/java/capstone/facefriend/member/domain/member/Role.java similarity index 100% rename from src/main/java/capstone/facefriend/member/domain/Role.java rename to src/main/java/capstone/facefriend/member/domain/member/Role.java diff --git a/src/main/java/capstone/facefriend/member/exception/MemberException.java b/src/main/java/capstone/facefriend/member/exception/member/MemberException.java similarity index 83% rename from src/main/java/capstone/facefriend/member/exception/MemberException.java rename to src/main/java/capstone/facefriend/member/exception/member/MemberException.java index e099585bd3..ae3922a810 100644 --- a/src/main/java/capstone/facefriend/member/exception/MemberException.java +++ b/src/main/java/capstone/facefriend/member/exception/member/MemberException.java @@ -1,4 +1,4 @@ -package capstone.facefriend.member.exception; +package capstone.facefriend.member.exception.meber; import capstone.facefriend.common.exception.BaseException; import capstone.facefriend.common.exception.ExceptionType; diff --git a/src/main/java/capstone/facefriend/member/exception/MemberExceptionType.java b/src/main/java/capstone/facefriend/member/exception/member/MemberExceptionType.java similarity index 97% rename from src/main/java/capstone/facefriend/member/exception/MemberExceptionType.java rename to src/main/java/capstone/facefriend/member/exception/member/MemberExceptionType.java index b977a6bddc..2841955f98 100644 --- a/src/main/java/capstone/facefriend/member/exception/MemberExceptionType.java +++ b/src/main/java/capstone/facefriend/member/exception/member/MemberExceptionType.java @@ -1,4 +1,4 @@ -package capstone.facefriend.member.exception; +package capstone.facefriend.member.exception.meber; import capstone.facefriend.common.exception.ExceptionType; import capstone.facefriend.common.exception.Status; diff --git a/src/main/java/capstone/facefriend/member/service/dto/BasicInfoRequest.java b/src/main/java/capstone/facefriend/member/service/dto/basicInfo/BasicInfoRequest.java similarity index 100% rename from src/main/java/capstone/facefriend/member/service/dto/BasicInfoRequest.java rename to src/main/java/capstone/facefriend/member/service/dto/basicInfo/BasicInfoRequest.java diff --git a/src/main/java/capstone/facefriend/member/service/dto/BasicInfoResponse.java b/src/main/java/capstone/facefriend/member/service/dto/basicInfo/BasicInfoResponse.java similarity index 100% rename from src/main/java/capstone/facefriend/member/service/dto/BasicInfoResponse.java rename to src/main/java/capstone/facefriend/member/service/dto/basicInfo/BasicInfoResponse.java diff --git a/src/main/java/capstone/facefriend/member/service/dto/FaceInfoResponse.java b/src/main/java/capstone/facefriend/member/service/dto/faceInfo/FaceInfoResponse.java similarity index 100% rename from src/main/java/capstone/facefriend/member/service/dto/FaceInfoResponse.java rename to src/main/java/capstone/facefriend/member/service/dto/faceInfo/FaceInfoResponse.java diff --git a/src/main/java/capstone/facefriend/member/service/dto/FindEmailResponse.java b/src/main/java/capstone/facefriend/member/service/dto/member/FindEmailResponse.java similarity index 100% rename from src/main/java/capstone/facefriend/member/service/dto/FindEmailResponse.java rename to src/main/java/capstone/facefriend/member/service/dto/member/FindEmailResponse.java diff --git a/src/main/java/capstone/facefriend/member/service/dto/ReissueRequest.java b/src/main/java/capstone/facefriend/member/service/dto/member/ReissueRequest.java similarity index 100% rename from src/main/java/capstone/facefriend/member/service/dto/ReissueRequest.java rename to src/main/java/capstone/facefriend/member/service/dto/member/ReissueRequest.java diff --git a/src/main/java/capstone/facefriend/member/service/dto/ResetPasswordRequest.java b/src/main/java/capstone/facefriend/member/service/dto/member/ResetPasswordRequest.java similarity index 100% rename from src/main/java/capstone/facefriend/member/service/dto/ResetPasswordRequest.java rename to src/main/java/capstone/facefriend/member/service/dto/member/ResetPasswordRequest.java diff --git a/src/main/java/capstone/facefriend/member/service/dto/SignInRequest.java b/src/main/java/capstone/facefriend/member/service/dto/member/SignInRequest.java similarity index 100% rename from src/main/java/capstone/facefriend/member/service/dto/SignInRequest.java rename to src/main/java/capstone/facefriend/member/service/dto/member/SignInRequest.java diff --git a/src/main/java/capstone/facefriend/member/service/dto/SignUpRequest.java b/src/main/java/capstone/facefriend/member/service/dto/member/SignUpRequest.java similarity index 100% rename from src/main/java/capstone/facefriend/member/service/dto/SignUpRequest.java rename to src/main/java/capstone/facefriend/member/service/dto/member/SignUpRequest.java From 7a89f685a323863202b036146d00b9f7654a6187 Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Thu, 2 May 2024 00:26:08 +0900 Subject: [PATCH 090/265] =?UTF-8?q?style:=20=EC=9D=B4=EB=A9=94=EC=9D=BC=20?= =?UTF-8?q?=EC=9D=B8=EC=A6=9D=20=EC=9D=B8=ED=84=B0=EC=85=89=ED=84=B0=20?= =?UTF-8?q?=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../interceptor/VerificationInterceptor.java | 45 ------------------- .../exception/VerificationException.java | 11 ----- .../exception/VerificationExceptionType.java | 35 --------------- 3 files changed, 91 deletions(-) delete mode 100644 src/main/java/capstone/facefriend/email/controller/interceptor/VerificationInterceptor.java delete mode 100644 src/main/java/capstone/facefriend/email/exception/VerificationException.java delete mode 100644 src/main/java/capstone/facefriend/email/exception/VerificationExceptionType.java diff --git a/src/main/java/capstone/facefriend/email/controller/interceptor/VerificationInterceptor.java b/src/main/java/capstone/facefriend/email/controller/interceptor/VerificationInterceptor.java deleted file mode 100644 index 6da23fc411..0000000000 --- a/src/main/java/capstone/facefriend/email/controller/interceptor/VerificationInterceptor.java +++ /dev/null @@ -1,45 +0,0 @@ -package capstone.facefriend.email.controller.interceptor; - -import capstone.facefriend.auth.controller.support.AuthenticationExtractor; -import capstone.facefriend.auth.domain.TokenProvider; -import capstone.facefriend.auth.exception.AuthException; -import capstone.facefriend.email.exception.VerificationException; -import capstone.facefriend.member.domain.Member; -import capstone.facefriend.member.domain.MemberRepository; -import capstone.facefriend.member.exception.MemberException; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Component; -import org.springframework.web.servlet.HandlerInterceptor; - -import static capstone.facefriend.auth.exception.AuthExceptionType.UNAUTHORIZED; -import static capstone.facefriend.email.exception.VerificationExceptionType.NOT_VERIFIED; -import static capstone.facefriend.member.exception.MemberExceptionType.NOT_FOUND; - - -@RequiredArgsConstructor -@Component -@Slf4j -public class VerificationInterceptor implements HandlerInterceptor { - - private final TokenProvider tokenProvider; - private final MemberRepository memberRepository; - - @Override - public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { - String accessToken = AuthenticationExtractor.extractAccessToken(request) - .orElseThrow(() -> new AuthException(UNAUTHORIZED)); - - Long memberId = tokenProvider.extractId(accessToken); - - Member member = memberRepository.findById(memberId) - .orElseThrow(() -> new MemberException(NOT_FOUND)); - - boolean isVerified = member.isVerified(); - if (!isVerified) throw new VerificationException(NOT_VERIFIED); - - return true; - } -} diff --git a/src/main/java/capstone/facefriend/email/exception/VerificationException.java b/src/main/java/capstone/facefriend/email/exception/VerificationException.java deleted file mode 100644 index 5662056704..0000000000 --- a/src/main/java/capstone/facefriend/email/exception/VerificationException.java +++ /dev/null @@ -1,11 +0,0 @@ -package capstone.facefriend.email.exception; - -import capstone.facefriend.common.exception.BaseException; -import capstone.facefriend.common.exception.ExceptionType; - -public class VerificationException extends BaseException { - - public VerificationException(ExceptionType exceptionType) { - super(exceptionType); - } -} diff --git a/src/main/java/capstone/facefriend/email/exception/VerificationExceptionType.java b/src/main/java/capstone/facefriend/email/exception/VerificationExceptionType.java deleted file mode 100644 index bf406443a2..0000000000 --- a/src/main/java/capstone/facefriend/email/exception/VerificationExceptionType.java +++ /dev/null @@ -1,35 +0,0 @@ -package capstone.facefriend.email.exception; - -import capstone.facefriend.common.exception.ExceptionType; -import capstone.facefriend.common.exception.Status; - -public enum VerificationExceptionType implements ExceptionType { - - NOT_VERIFIED(Status.UNAUTHORIZED, 4001, "서비스를 사용하기 위해선 본인인증이 필요합니다.") - ; - - private final Status status; - private final int exceptionCode; - private final String message; - - VerificationExceptionType(Status status, int exceptionCode, String message) { - this.status = status; - this.exceptionCode = exceptionCode; - this.message = message; - } - - @Override - public Status status() { - return status; - } - - @Override - public int exceptionCode() { - return exceptionCode; - } - - @Override - public String message() { - return message; - } -} From 466c58221bd96ce27100074a898420bcaf1add50 Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Thu, 2 May 2024 00:29:30 +0900 Subject: [PATCH 091/265] =?UTF-8?q?style:=20=EC=BD=94=EB=93=9C=20=ED=8F=AC?= =?UTF-8?q?=EB=A7=A4=ED=8C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../capstone/facefriend/member/domain/basicInfo/BasicInfo.java | 2 +- .../member/domain/basicInfo/BasicInfoRepository.java | 2 +- .../capstone/facefriend/member/domain/faceInfo/FaceInfo.java | 2 +- .../facefriend/member/domain/faceInfo/FaceInfoRepository.java | 3 +-- 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/main/java/capstone/facefriend/member/domain/basicInfo/BasicInfo.java b/src/main/java/capstone/facefriend/member/domain/basicInfo/BasicInfo.java index 303cd2e265..2ee2127da0 100644 --- a/src/main/java/capstone/facefriend/member/domain/basicInfo/BasicInfo.java +++ b/src/main/java/capstone/facefriend/member/domain/basicInfo/BasicInfo.java @@ -1,4 +1,4 @@ -package capstone.facefriend.member.domain; +package capstone.facefriend.member.domain.basicInfo; import jakarta.persistence.*; import lombok.*; diff --git a/src/main/java/capstone/facefriend/member/domain/basicInfo/BasicInfoRepository.java b/src/main/java/capstone/facefriend/member/domain/basicInfo/BasicInfoRepository.java index 1a1c9dc7f0..29d6602dff 100644 --- a/src/main/java/capstone/facefriend/member/domain/basicInfo/BasicInfoRepository.java +++ b/src/main/java/capstone/facefriend/member/domain/basicInfo/BasicInfoRepository.java @@ -1,4 +1,4 @@ -package capstone.facefriend.member.domain; +package capstone.facefriend.member.domain.basicInfo; import org.springframework.data.repository.Repository; diff --git a/src/main/java/capstone/facefriend/member/domain/faceInfo/FaceInfo.java b/src/main/java/capstone/facefriend/member/domain/faceInfo/FaceInfo.java index 55f3a0cd86..b5ece48398 100644 --- a/src/main/java/capstone/facefriend/member/domain/faceInfo/FaceInfo.java +++ b/src/main/java/capstone/facefriend/member/domain/faceInfo/FaceInfo.java @@ -1,4 +1,4 @@ -package capstone.facefriend.member.domain; +package capstone.facefriend.member.domain.faceInfo; import jakarta.persistence.*; import lombok.*; diff --git a/src/main/java/capstone/facefriend/member/domain/faceInfo/FaceInfoRepository.java b/src/main/java/capstone/facefriend/member/domain/faceInfo/FaceInfoRepository.java index 7cb681654c..0d61b9d4d1 100644 --- a/src/main/java/capstone/facefriend/member/domain/faceInfo/FaceInfoRepository.java +++ b/src/main/java/capstone/facefriend/member/domain/faceInfo/FaceInfoRepository.java @@ -1,9 +1,8 @@ -package capstone.facefriend.member.domain; +package capstone.facefriend.member.domain.faceInfo; import org.springframework.data.repository.Repository; public interface FaceInfoRepository extends Repository { FaceInfo save(FaceInfo faceInfo); - } From 55e376245fa001d6050b03b4e46cafe7e8dfc659 Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Thu, 2 May 2024 00:30:29 +0900 Subject: [PATCH 092/265] =?UTF-8?q?refactor:=20=EC=9D=B4=EB=A9=94=EC=9D=BC?= =?UTF-8?q?=20=EC=9D=B8=EC=A6=9D=20=EC=9D=B8=ED=84=B0=EC=85=89=ED=84=B0=20?= =?UTF-8?q?=EC=A0=9C=EA=B1=B0=EB=A1=9C=20=EC=9D=B8=ED=95=9C=20AuthConfig?= =?UTF-8?q?=20=EC=88=98=EC=A0=95=20=EB=B0=8F=20=EC=9D=B8=ED=84=B0=EC=85=89?= =?UTF-8?q?=ED=84=B0=20=EC=B2=B4=EC=9D=B8=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../facefriend/auth/config/AuthConfig.java | 21 +++++-------------- .../TokenBlackListInterceptor.java | 5 +---- .../interceptor/TokenReissueInterceptor.java | 1 - .../facefriend/auth/service/AuthService.java | 9 +++----- 4 files changed, 9 insertions(+), 27 deletions(-) diff --git a/src/main/java/capstone/facefriend/auth/config/AuthConfig.java b/src/main/java/capstone/facefriend/auth/config/AuthConfig.java index 52c7c3ea17..f12feeb42e 100644 --- a/src/main/java/capstone/facefriend/auth/config/AuthConfig.java +++ b/src/main/java/capstone/facefriend/auth/config/AuthConfig.java @@ -2,7 +2,6 @@ import capstone.facefriend.auth.controller.AuthArgumentResolver; import capstone.facefriend.auth.controller.interceptor.*; -import capstone.facefriend.email.controller.interceptor.VerificationInterceptor; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.context.annotation.Bean; @@ -29,7 +28,6 @@ public class AuthConfig implements WebMvcConfigurer { private final LoginInterceptor loginInterceptor; private final TokenReissueInterceptor tokenReissueInterceptor; private final TokenBlackListInterceptor tokenBlackListInterceptor; - private final VerificationInterceptor verificationInterceptor; @Bean public PasswordEncoder passwordEncoder() { @@ -42,7 +40,6 @@ public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(loginInterceptor()); registry.addInterceptor(tokenReissueInterceptor()); registry.addInterceptor(tokenBlackListInterceptor()); - registry.addInterceptor(verificationInterceptor()); } private HandlerInterceptor loginCheckInterceptor() { @@ -54,8 +51,9 @@ private HandlerInterceptor loginCheckInterceptor() { .addIncludePathPattern("/auth/exit", DELETE) .addIncludePathPattern("/basic-info", ANY) .addIncludePathPattern("/face-info", ANY) + .addIncludePathPattern("/analysis-info", ANY) - .addExcludePathPattern("/auth/reissue", POST); // 토큰 만료 시에는 해당 요청을 가로채지 않아야 합니다. + .addExcludePathPattern("/auth/reissue/**", POST); // 토큰 만료 시에는 해당 요청을 가로채지 않아야 합니다. } private HandlerInterceptor loginInterceptor() { @@ -67,6 +65,7 @@ private HandlerInterceptor loginInterceptor() { .addIncludePathPattern("/auth/exit", DELETE) .addIncludePathPattern("/basic-info", ANY) .addIncludePathPattern("/face-info", ANY) + .addIncludePathPattern("/analysis-info/**", ANY) .addExcludePathPattern("/auth/reissue", POST); // 토큰 만료 시에는 해당 요청을 가로채지 않아야 합니다. } @@ -86,18 +85,8 @@ private HandlerInterceptor tokenBlackListInterceptor() { .addIncludePathPattern("/auth/exit", DELETE) .addIncludePathPattern("/auth/reset-password", POST) .addIncludePathPattern("/basic-info", ANY) - .addIncludePathPattern("/face-info", ANY); - } - - private HandlerInterceptor verificationInterceptor() { - return new PathMatchInterceptor(verificationInterceptor) - .addExcludePathPattern("/**", OPTIONS) - - .addIncludePathPattern("/auth/signout", DELETE) - .addIncludePathPattern("/auth/exit", DELETE) - .addIncludePathPattern("/auth/reset-password", POST) - .addIncludePathPattern("/basic-info", ANY) - .addIncludePathPattern("/face-info", ANY); + .addIncludePathPattern("/face-info", ANY) + .addIncludePathPattern("/analysis-info/**", ANY); } @Override diff --git a/src/main/java/capstone/facefriend/auth/controller/interceptor/TokenBlackListInterceptor.java b/src/main/java/capstone/facefriend/auth/controller/interceptor/TokenBlackListInterceptor.java index bf0420f065..de435296be 100644 --- a/src/main/java/capstone/facefriend/auth/controller/interceptor/TokenBlackListInterceptor.java +++ b/src/main/java/capstone/facefriend/auth/controller/interceptor/TokenBlackListInterceptor.java @@ -1,7 +1,6 @@ package capstone.facefriend.auth.controller.interceptor; import capstone.facefriend.auth.controller.support.AuthenticationExtractor; -import capstone.facefriend.email.controller.interceptor.VerificationInterceptor; import capstone.facefriend.redis.RedisDao; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; @@ -18,8 +17,6 @@ public class TokenBlackListInterceptor implements HandlerInterceptor { private final RedisDao redisDao; - private final VerificationInterceptor verificationInterceptor; - @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { String accessToken = AuthenticationExtractor.extractAccessToken(request).get(); @@ -27,6 +24,6 @@ public boolean preHandle(HttpServletRequest request, HttpServletResponse respons if (accessToken != null) { return redisDao.isKeyOfAccessTokenInBlackList(accessToken); // 액세스 토큰이 블랙리스트에 등록되었다면 false 반환해야 합니다. } - return verificationInterceptor.preHandle(request, response, handler); + return true; } } \ No newline at end of file diff --git a/src/main/java/capstone/facefriend/auth/controller/interceptor/TokenReissueInterceptor.java b/src/main/java/capstone/facefriend/auth/controller/interceptor/TokenReissueInterceptor.java index ca51815e7c..5bd23a9b2a 100644 --- a/src/main/java/capstone/facefriend/auth/controller/interceptor/TokenReissueInterceptor.java +++ b/src/main/java/capstone/facefriend/auth/controller/interceptor/TokenReissueInterceptor.java @@ -3,7 +3,6 @@ import capstone.facefriend.auth.controller.support.AuthenticationContext; import capstone.facefriend.auth.controller.support.AuthenticationExtractor; import capstone.facefriend.auth.domain.TokenProvider; -import capstone.facefriend.email.controller.interceptor.VerificationInterceptor; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import lombok.RequiredArgsConstructor; diff --git a/src/main/java/capstone/facefriend/auth/service/AuthService.java b/src/main/java/capstone/facefriend/auth/service/AuthService.java index fb41cf7114..4f6b6d730f 100644 --- a/src/main/java/capstone/facefriend/auth/service/AuthService.java +++ b/src/main/java/capstone/facefriend/auth/service/AuthService.java @@ -5,15 +5,13 @@ import capstone.facefriend.auth.domain.OAuthMember; import capstone.facefriend.auth.domain.Provider; import capstone.facefriend.auth.domain.TokenProvider; -import capstone.facefriend.member.domain.Member; -import capstone.facefriend.member.domain.MemberRepository; -import capstone.facefriend.member.domain.Role; +import capstone.facefriend.member.domain.member.Member; +import capstone.facefriend.member.domain.member.MemberRepository; import lombok.RequiredArgsConstructor; -import org.springframework.expression.ExpressionException; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import static capstone.facefriend.member.domain.Role.*; +import static capstone.facefriend.member.domain.member.Role.*; @RequiredArgsConstructor @@ -35,7 +33,6 @@ public TokenResponse generateTokens(OAuthMember oAuthMember) { Member newMember = Member.builder() .email(oAuthMember.email()) .password(TEMPORARY_GOOGLE_PASSWORD) - .isVerified(true) .role(USER) .build(); Member member = memberRepository.findByEmail(oAuthMember.email()) From a053012f3ac0324e41e7269179abdc0aac9c334c Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Thu, 2 May 2024 00:31:50 +0900 Subject: [PATCH 093/265] =?UTF-8?q?style:=20=EC=BD=94=EB=93=9C=20=ED=8F=AC?= =?UTF-8?q?=EB=A7=A4=ED=8C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../member/controller/BasicInfoController.java | 4 ++-- .../member/controller/FaceInfoController.java | 11 +---------- .../member/controller/MemberController.java | 8 ++++---- 3 files changed, 7 insertions(+), 16 deletions(-) diff --git a/src/main/java/capstone/facefriend/member/controller/BasicInfoController.java b/src/main/java/capstone/facefriend/member/controller/BasicInfoController.java index 3d4aa871cf..bbbe72a0e2 100644 --- a/src/main/java/capstone/facefriend/member/controller/BasicInfoController.java +++ b/src/main/java/capstone/facefriend/member/controller/BasicInfoController.java @@ -3,8 +3,8 @@ import capstone.facefriend.auth.controller.support.AuthMember; import capstone.facefriend.member.service.BasicInfoService; -import capstone.facefriend.member.service.dto.BasicInfoRequest; -import capstone.facefriend.member.service.dto.BasicInfoResponse; +import capstone.facefriend.member.service.dto.basicInfo.BasicInfoRequest; +import capstone.facefriend.member.service.dto.basicInfo.BasicInfoResponse; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.http.ResponseEntity; diff --git a/src/main/java/capstone/facefriend/member/controller/FaceInfoController.java b/src/main/java/capstone/facefriend/member/controller/FaceInfoController.java index f8f12841fe..ca4af52cad 100644 --- a/src/main/java/capstone/facefriend/member/controller/FaceInfoController.java +++ b/src/main/java/capstone/facefriend/member/controller/FaceInfoController.java @@ -2,7 +2,7 @@ import capstone.facefriend.auth.controller.support.AuthMember; import capstone.facefriend.member.service.FaceInfoService; -import capstone.facefriend.member.service.dto.FaceInfoResponse; +import capstone.facefriend.member.service.dto.faceInfo.FaceInfoResponse; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.http.ResponseEntity; @@ -18,15 +18,6 @@ public class FaceInfoController { private final FaceInfoService faceInfoService; - @PostMapping("/face-info") - public ResponseEntity upload( - @RequestPart("origin") MultipartFile origin, - @RequestParam("styleId") Long styleId, - @AuthMember Long memberId - ) throws IOException { - return ResponseEntity.ok(faceInfoService.uploadOrigin(origin, styleId, memberId)); - } - @GetMapping("/face-info") public ResponseEntity get( @AuthMember Long memberId diff --git a/src/main/java/capstone/facefriend/member/controller/MemberController.java b/src/main/java/capstone/facefriend/member/controller/MemberController.java index cb3caf9cda..89bf4138cc 100644 --- a/src/main/java/capstone/facefriend/member/controller/MemberController.java +++ b/src/main/java/capstone/facefriend/member/controller/MemberController.java @@ -5,17 +5,17 @@ import capstone.facefriend.auth.controller.support.AuthMember; import capstone.facefriend.auth.controller.support.AuthenticationExtractor; import capstone.facefriend.email.controller.dto.EmailVerificationResponse; -import capstone.facefriend.member.exception.MemberException; +import capstone.facefriend.member.exception.member.MemberException; import capstone.facefriend.member.service.MemberService; -import capstone.facefriend.member.service.dto.*; +import capstone.facefriend.member.service.dto.member.*; import jakarta.servlet.http.HttpServletRequest; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; -import static capstone.facefriend.member.exception.MemberExceptionType.PASSWORDS_NOT_EQUAL; -import static capstone.facefriend.member.exception.MemberExceptionType.UNAUTHORIZED; +import static capstone.facefriend.member.exception.member.MemberExceptionType.PASSWORDS_NOT_EQUAL; +import static capstone.facefriend.member.exception.member.MemberExceptionType.UNAUTHORIZED; @Slf4j @RestController From 46884d61731681c49c23efbe1b790fb957d4ba82 Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Thu, 2 May 2024 00:34:34 +0900 Subject: [PATCH 094/265] =?UTF-8?q?style:=20=EC=BD=94=EB=93=9C=20=ED=8F=AC?= =?UTF-8?q?=EB=A7=A4=ED=8C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../AnalysisInfoFullResponse.java | 8 ++++++ .../AnalysisInfoFullShortResponse.java | 10 +++++++ .../AnalysisInfoShortResponse.java | 8 ++++++ .../dto/basicInfo/BasicInfoRequest.java | 14 +++++----- .../dto/basicInfo/BasicInfoResponse.java | 28 ++++++++++--------- .../dto/faceInfo/FaceInfoResponse.java | 6 ++-- .../service/dto/member/FindEmailResponse.java | 6 ++-- .../service/dto/member/ReissueRequest.java | 4 +-- .../dto/member/ResetPasswordRequest.java | 6 ++-- .../service/dto/member/SignInRequest.java | 6 ++-- .../service/dto/member/SignUpRequest.java | 9 +++--- 11 files changed, 66 insertions(+), 39 deletions(-) create mode 100644 src/main/java/capstone/facefriend/member/service/dto/analysisInfo/AnalysisInfoFullResponse.java create mode 100644 src/main/java/capstone/facefriend/member/service/dto/analysisInfo/AnalysisInfoFullShortResponse.java create mode 100644 src/main/java/capstone/facefriend/member/service/dto/analysisInfo/AnalysisInfoShortResponse.java diff --git a/src/main/java/capstone/facefriend/member/service/dto/analysisInfo/AnalysisInfoFullResponse.java b/src/main/java/capstone/facefriend/member/service/dto/analysisInfo/AnalysisInfoFullResponse.java new file mode 100644 index 0000000000..fb4e52fc1a --- /dev/null +++ b/src/main/java/capstone/facefriend/member/service/dto/analysisInfo/AnalysisInfoFullResponse.java @@ -0,0 +1,8 @@ +package capstone.facefriend.member.service.dto.analysisInfo; + +import java.util.Map; + +public record AnalysisInfoFullResponse( + Map analysisFull +) { +} diff --git a/src/main/java/capstone/facefriend/member/service/dto/analysisInfo/AnalysisInfoFullShortResponse.java b/src/main/java/capstone/facefriend/member/service/dto/analysisInfo/AnalysisInfoFullShortResponse.java new file mode 100644 index 0000000000..c80b670182 --- /dev/null +++ b/src/main/java/capstone/facefriend/member/service/dto/analysisInfo/AnalysisInfoFullShortResponse.java @@ -0,0 +1,10 @@ +package capstone.facefriend.member.service.dto.analysisInfo; + +import java.util.List; +import java.util.Map; + +public record AnalysisInfoFullShortResponse( + Map analysisFull, + List analysisShort +) { +} diff --git a/src/main/java/capstone/facefriend/member/service/dto/analysisInfo/AnalysisInfoShortResponse.java b/src/main/java/capstone/facefriend/member/service/dto/analysisInfo/AnalysisInfoShortResponse.java new file mode 100644 index 0000000000..81917d3cfe --- /dev/null +++ b/src/main/java/capstone/facefriend/member/service/dto/analysisInfo/AnalysisInfoShortResponse.java @@ -0,0 +1,8 @@ +package capstone.facefriend.member.service.dto.analysisInfo; + +import java.util.List; + +public record AnalysisInfoShortResponse( + List analysisShort +) { +} diff --git a/src/main/java/capstone/facefriend/member/service/dto/basicInfo/BasicInfoRequest.java b/src/main/java/capstone/facefriend/member/service/dto/basicInfo/BasicInfoRequest.java index 7eb8a60e2e..9ca155e14d 100644 --- a/src/main/java/capstone/facefriend/member/service/dto/basicInfo/BasicInfoRequest.java +++ b/src/main/java/capstone/facefriend/member/service/dto/basicInfo/BasicInfoRequest.java @@ -1,11 +1,11 @@ -package capstone.facefriend.member.service.dto; +package capstone.facefriend.member.service.dto.basicInfo; public record BasicInfoRequest( - String nickname, - String gender, - String ageGroup, - String ageDegree, - String heightGroup, - String region + String nickname, + String gender, + String ageGroup, + String ageDegree, + String heightGroup, + String region ) { } diff --git a/src/main/java/capstone/facefriend/member/service/dto/basicInfo/BasicInfoResponse.java b/src/main/java/capstone/facefriend/member/service/dto/basicInfo/BasicInfoResponse.java index 791a387572..74f51f7dc8 100644 --- a/src/main/java/capstone/facefriend/member/service/dto/basicInfo/BasicInfoResponse.java +++ b/src/main/java/capstone/facefriend/member/service/dto/basicInfo/BasicInfoResponse.java @@ -1,23 +1,25 @@ -package capstone.facefriend.member.service.dto; +package capstone.facefriend.member.service.dto.basicInfo; -import capstone.facefriend.member.domain.BasicInfo; +import capstone.facefriend.member.domain.basicInfo.BasicInfo; + +import static capstone.facefriend.member.domain.basicInfo.BasicInfo.*; public record BasicInfoResponse( - String nickname, - String gender, - String ageGroup, - String ageDegree, - String heightGroup, - String region + String nickname, + Gender gender, + AgeGroup ageGroup, + AgeDegree ageDegree, + HeightGroup heightGroup, + Region region ) { public static BasicInfoResponse of(BasicInfo basicInfo) { return new BasicInfoResponse( basicInfo.getNickname(), - basicInfo.getGender().getValue(), - basicInfo.getAgeGroup().getValue(), - basicInfo.getAgeDegree().getValue(), - basicInfo.getHeightGroup().getValue(), - basicInfo.getRegion().getValue() + basicInfo.getGender(), + basicInfo.getAgeGroup(), + basicInfo.getAgeDegree(), + basicInfo.getHeightGroup(), + basicInfo.getRegion() ); } } diff --git a/src/main/java/capstone/facefriend/member/service/dto/faceInfo/FaceInfoResponse.java b/src/main/java/capstone/facefriend/member/service/dto/faceInfo/FaceInfoResponse.java index 2e4a01e1ed..6cc6660082 100644 --- a/src/main/java/capstone/facefriend/member/service/dto/faceInfo/FaceInfoResponse.java +++ b/src/main/java/capstone/facefriend/member/service/dto/faceInfo/FaceInfoResponse.java @@ -1,7 +1,7 @@ -package capstone.facefriend.member.service.dto; +package capstone.facefriend.member.service.dto.faceInfo; public record FaceInfoResponse( - String originS3Url, - String generatedS3Url + String originS3Url, + String generatedS3Url ) { } diff --git a/src/main/java/capstone/facefriend/member/service/dto/member/FindEmailResponse.java b/src/main/java/capstone/facefriend/member/service/dto/member/FindEmailResponse.java index e3ebe91d2b..07521a8f2b 100644 --- a/src/main/java/capstone/facefriend/member/service/dto/member/FindEmailResponse.java +++ b/src/main/java/capstone/facefriend/member/service/dto/member/FindEmailResponse.java @@ -1,7 +1,7 @@ -package capstone.facefriend.member.service.dto; +package capstone.facefriend.member.service.dto.member; public record FindEmailResponse( - String email, - boolean isRegistered + String email, + boolean isRegistered ) { } diff --git a/src/main/java/capstone/facefriend/member/service/dto/member/ReissueRequest.java b/src/main/java/capstone/facefriend/member/service/dto/member/ReissueRequest.java index cbb5c1c34a..1e5da08c68 100644 --- a/src/main/java/capstone/facefriend/member/service/dto/member/ReissueRequest.java +++ b/src/main/java/capstone/facefriend/member/service/dto/member/ReissueRequest.java @@ -1,6 +1,6 @@ -package capstone.facefriend.member.service.dto; +package capstone.facefriend.member.service.dto.member; public record ReissueRequest( - String refreshToken + String refreshToken ) { } diff --git a/src/main/java/capstone/facefriend/member/service/dto/member/ResetPasswordRequest.java b/src/main/java/capstone/facefriend/member/service/dto/member/ResetPasswordRequest.java index 812f0e5a13..350d06e58d 100644 --- a/src/main/java/capstone/facefriend/member/service/dto/member/ResetPasswordRequest.java +++ b/src/main/java/capstone/facefriend/member/service/dto/member/ResetPasswordRequest.java @@ -1,7 +1,7 @@ -package capstone.facefriend.member.service.dto; +package capstone.facefriend.member.service.dto.member; public record ResetPasswordRequest( - String newPassword, - String newPassword2 + String newPassword, + String newPassword2 ) { } diff --git a/src/main/java/capstone/facefriend/member/service/dto/member/SignInRequest.java b/src/main/java/capstone/facefriend/member/service/dto/member/SignInRequest.java index a1f9b59326..a77afc5062 100644 --- a/src/main/java/capstone/facefriend/member/service/dto/member/SignInRequest.java +++ b/src/main/java/capstone/facefriend/member/service/dto/member/SignInRequest.java @@ -1,7 +1,7 @@ -package capstone.facefriend.member.service.dto; +package capstone.facefriend.member.service.dto.member; public record SignInRequest( - String email, - String password + String email, + String password ) { } diff --git a/src/main/java/capstone/facefriend/member/service/dto/member/SignUpRequest.java b/src/main/java/capstone/facefriend/member/service/dto/member/SignUpRequest.java index 84ed5f3240..692decd22a 100644 --- a/src/main/java/capstone/facefriend/member/service/dto/member/SignUpRequest.java +++ b/src/main/java/capstone/facefriend/member/service/dto/member/SignUpRequest.java @@ -1,9 +1,8 @@ -package capstone.facefriend.member.service.dto; +package capstone.facefriend.member.service.dto.member; public record SignUpRequest( - String email, - String password, - String password2, - boolean isVerified + String email, + String password, + String password2 ) { } From da19bb9474b59b57c6a276dcdd157bcb56b469a2 Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Thu, 2 May 2024 00:37:11 +0900 Subject: [PATCH 095/265] =?UTF-8?q?feat:=20analysisInfo=20=EB=8F=84?= =?UTF-8?q?=EB=A9=94=EC=9D=B8=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/analysisInfo/AnalysisInfo.java | 35 +++++++++++++++++++ .../analysisInfo/AnalysisInfoRepository.java | 8 +++++ 2 files changed, 43 insertions(+) create mode 100644 src/main/java/capstone/facefriend/member/domain/analysisInfo/AnalysisInfo.java create mode 100644 src/main/java/capstone/facefriend/member/domain/analysisInfo/AnalysisInfoRepository.java diff --git a/src/main/java/capstone/facefriend/member/domain/analysisInfo/AnalysisInfo.java b/src/main/java/capstone/facefriend/member/domain/analysisInfo/AnalysisInfo.java new file mode 100644 index 0000000000..0c9fc7bd9f --- /dev/null +++ b/src/main/java/capstone/facefriend/member/domain/analysisInfo/AnalysisInfo.java @@ -0,0 +1,35 @@ +package capstone.facefriend.member.domain.analysisInfo; + +import jakarta.persistence.*; +import lombok.*; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@Getter +@Setter +@Builder +@AllArgsConstructor(access = AccessLevel.PRIVATE) +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@EqualsAndHashCode(of = {"id"}, callSuper = false) +@Entity +public class AnalysisInfo { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "ANALYSIS_INFO_ID") + private Long id; + + private Integer faceShapeIdNum; + + @ElementCollection + @CollectionTable(name = "ANALYSIS_FULL", joinColumns = @JoinColumn(name = "ANALYSIS_INFO_ID")) + private Map analysisInfoFull = new HashMap<>(); + + @ElementCollection + @CollectionTable(name = "ANALYSIS_SHORT", joinColumns = @JoinColumn(name = "ANALYSIS_INFO_ID")) + private List analysisInfoShort = new ArrayList<>(); +} + diff --git a/src/main/java/capstone/facefriend/member/domain/analysisInfo/AnalysisInfoRepository.java b/src/main/java/capstone/facefriend/member/domain/analysisInfo/AnalysisInfoRepository.java new file mode 100644 index 0000000000..4b322b666a --- /dev/null +++ b/src/main/java/capstone/facefriend/member/domain/analysisInfo/AnalysisInfoRepository.java @@ -0,0 +1,8 @@ +package capstone.facefriend.member.domain.analysisInfo; + +import org.springframework.data.repository.Repository; + +public interface AnalysisInfoRepository extends Repository { + + AnalysisInfo save(AnalysisInfo analysisInfo); +} From d6ec1bf46a2bdcbd81852af174fba76372ac609f Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Thu, 2 May 2024 00:38:08 +0900 Subject: [PATCH 096/265] =?UTF-8?q?feat:=20analysis=20service=20=EB=B0=8F?= =?UTF-8?q?=20deserializer=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../member/service/AnalysisInfoService.java | 228 ++++++++++++++++++ .../deserializer/StringListDeserializer.java | 31 +++ 2 files changed, 259 insertions(+) create mode 100644 src/main/java/capstone/facefriend/member/service/AnalysisInfoService.java create mode 100644 src/main/java/capstone/facefriend/member/service/deserializer/StringListDeserializer.java diff --git a/src/main/java/capstone/facefriend/member/service/AnalysisInfoService.java b/src/main/java/capstone/facefriend/member/service/AnalysisInfoService.java new file mode 100644 index 0000000000..03798415d1 --- /dev/null +++ b/src/main/java/capstone/facefriend/member/service/AnalysisInfoService.java @@ -0,0 +1,228 @@ +package capstone.facefriend.member.service; + + +import capstone.facefriend.member.domain.analysisInfo.AnalysisInfoRepository; +import capstone.facefriend.member.domain.member.Member; +import capstone.facefriend.member.domain.member.MemberRepository; +import capstone.facefriend.member.exception.analysis.AnalysisException; +import capstone.facefriend.member.exception.member.MemberException; +import capstone.facefriend.member.service.deserializer.StringListDeserializer; +import capstone.facefriend.member.service.dto.analysisInfo.AnalysisInfoFullResponse; +import capstone.facefriend.member.service.dto.analysisInfo.AnalysisInfoFullShortResponse; +import capstone.facefriend.member.service.dto.analysisInfo.AnalysisInfoShortResponse; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import lombok.*; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.core.io.ByteArrayResource; +import org.springframework.http.*; +import org.springframework.stereotype.Service; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.web.client.RestTemplate; +import org.springframework.web.multipart.MultipartFile; + +import java.io.IOException; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static capstone.facefriend.member.exception.analysis.AnalysisExceptionType.FAIL_TO_EXTRACT_FACE_SHAPE_ID_NUM; +import static capstone.facefriend.member.exception.member.MemberExceptionType.NOT_FOUND; + +@Slf4j +@Service +@RequiredArgsConstructor +public class AnalysisInfoService { + + @Value("${flask.analyze-url}") + private String requestUrl; + + private final RestTemplate restTemplate; + + private final MemberRepository memberRepository; + private final AnalysisInfoRepository analysisInfoRepository; + + public AnalysisInfoFullResponse analyze(MultipartFile origin, Long memberId) throws IOException { + // convert MultipartFile into ByteArrayResource + ByteArrayResource resource = new ByteArrayResource(origin.getBytes()) { + @Override + public String getFilename() { + return URLEncoder.encode(origin.getOriginalFilename(), StandardCharsets.UTF_8); + } + }; + + // body + LinkedMultiValueMap body = new LinkedMultiValueMap<>(); + body.add("image", resource); + body.add("user_id", memberId); + + // header + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.MULTIPART_FORM_DATA); + + // request entity + HttpEntity> requestEntity = new HttpEntity<>(body, headers); + // response entity + ResponseEntity responseEntity = restTemplate.exchange( + requestUrl, + HttpMethod.POST, + requestEntity, + String.class + ); + + // convert JSON into Map + ObjectMapper objectMapper = new ObjectMapper(); + AnalysisInfoTotal total = objectMapper.readValue(responseEntity.getBody(), AnalysisInfoTotal.class); + + Map analysisFull = extractAnalysisInfoFull(total); + List analysisShort = extractAnalysisInfoShort(total); + Integer faceShapeIdNum = extractFaceShapeIdNum(total); + + Member member = findMemberById(memberId); + member.getAnalysisInfo().setAnalysisInfoFull(analysisFull); + member.getAnalysisInfo().setAnalysisInfoShort(analysisShort); + member.getAnalysisInfo().setFaceShapeIdNum(faceShapeIdNum); + memberRepository.save(member); + + // return not AnalysisInfoShort, but AnalysisInfoFull + return new AnalysisInfoFullResponse(analysisFull); + } + + private Map extractAnalysisInfoFull(AnalysisInfoTotal total) { + Map analysisFull = new HashMap<>(); + + analysisFull.put(total.getEye().getName(), total.getEye().getDescription()); + analysisFull.put(total.getFaceShape().getName(), total.getFaceShape().getDescription()); + analysisFull.put(total.getLips().getName(), total.getLips().getDescription()); + analysisFull.put(total.getNose().getName(), total.getNose().getDescription()); + analysisFull.put(total.getEyebrow().getName(), total.getEyebrow().getDescription()); + + return analysisFull; + } + + private Integer extractFaceShapeIdNum(AnalysisInfoTotal total) { + String idNum = total.getFaceShape().getIdNum(); + if (idNum == null) { + throw new AnalysisException(FAIL_TO_EXTRACT_FACE_SHAPE_ID_NUM); + } else { + return Integer.parseInt(idNum); + } + } + + private List extractAnalysisInfoShort(AnalysisInfoTotal total) { + return Stream.of( + total.getEye().getTag(), + total.getFaceShape().getTag(), + total.getLips().getTag(), + total.getNose().getTag(), + total.getEyebrow().getTag() + ).flatMap(List::stream) + .collect(Collectors.toList()); + } + + public AnalysisInfoFullShortResponse getAnalysisInfoFullShort(Long memberId) { + Member member = findMemberById(memberId); + Map analysisInfoFull = member.getAnalysisInfo().getAnalysisInfoFull(); + List analysisInfoShort = member.getAnalysisInfo().getAnalysisInfoShort(); + return new AnalysisInfoFullShortResponse(analysisInfoFull, analysisInfoShort); + } + + public AnalysisInfoFullResponse getAnalysisInfoFull(Long memberId) { + Member member = findMemberById(memberId); + return new AnalysisInfoFullResponse(member.getAnalysisInfo().getAnalysisInfoFull()); + } + + public AnalysisInfoShortResponse getAnalysisInfoShort(Long memberId) { + Member member = findMemberById(memberId); + return new AnalysisInfoShortResponse(member.getAnalysisInfo().getAnalysisInfoShort()); + } + + private Member findMemberById(Long memberId) { + Member member = memberRepository.findById(memberId).orElseThrow(() -> new MemberException(NOT_FOUND)); + return member; + } + + @Getter + @Setter + @AllArgsConstructor(access = AccessLevel.PRIVATE) + @NoArgsConstructor(access = AccessLevel.PROTECTED) + public static class AnalysisInfoTotal { + @JsonProperty("face_shape") + private FaceShape faceShape; + private Eye eye; + private Lips lips; + private Nose nose; + private Eyebrow eyebrow; + } + + @Getter + @Setter + @AllArgsConstructor(access = AccessLevel.PRIVATE) + @NoArgsConstructor(access = AccessLevel.PROTECTED) + public static class FaceShape { + private String name; + private String description; + @JsonDeserialize(using = StringListDeserializer.class) + private List tag; + @JsonProperty("id_num") + private String idNum; + } + + @Getter + @Setter + @AllArgsConstructor(access = AccessLevel.PRIVATE) + @NoArgsConstructor(access = AccessLevel.PROTECTED) + public static class Eye { + private String name; + private String description; + @JsonDeserialize(using = StringListDeserializer.class) + private List tag; + @JsonProperty("id_num") + private String idNum; + } + + @Getter + @Setter + @AllArgsConstructor(access = AccessLevel.PRIVATE) + @NoArgsConstructor(access = AccessLevel.PROTECTED) + public static class Lips { + private String name; + private String description; + @JsonDeserialize(using = StringListDeserializer.class) + private List tag; + @JsonProperty("id_num") + private String idNum; + } + + @Getter + @Setter + @AllArgsConstructor(access = AccessLevel.PRIVATE) + @NoArgsConstructor(access = AccessLevel.PROTECTED) + public static class Nose { + private String name; + private String description; + @JsonDeserialize(using = StringListDeserializer.class) + private List tag; + @JsonProperty("id_num") + private String idNum; + } + + @Getter + @Setter + @AllArgsConstructor(access = AccessLevel.PRIVATE) + @NoArgsConstructor(access = AccessLevel.PROTECTED) + public static class Eyebrow { + private String name; + private String description; + @JsonDeserialize(using = StringListDeserializer.class) + private List tag; + @JsonProperty("id_num") + private String idNum; + } +} diff --git a/src/main/java/capstone/facefriend/member/service/deserializer/StringListDeserializer.java b/src/main/java/capstone/facefriend/member/service/deserializer/StringListDeserializer.java new file mode 100644 index 0000000000..138011ece5 --- /dev/null +++ b/src/main/java/capstone/facefriend/member/service/deserializer/StringListDeserializer.java @@ -0,0 +1,31 @@ +package capstone.facefriend.member.service.deserializer; + +import capstone.facefriend.member.exception.analysis.AnalysisException; +import com.fasterxml.jackson.core.JacksonException; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonToken; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; + +import java.io.IOException; +import java.util.Arrays; +import java.util.List; + +import static capstone.facefriend.member.exception.analysis.AnalysisExceptionType.FAIL_TO_DESERIALIZE_ANALYSIS; +import static com.fasterxml.jackson.core.JsonToken.START_ARRAY; +import static com.fasterxml.jackson.core.JsonToken.VALUE_STRING; + + +public class StringListDeserializer extends JsonDeserializer> { + + @Override + public List deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JacksonException { + JsonToken jt = p.getCurrentToken(); + if (jt == START_ARRAY) { + return p.readValueAs(List.class); + } else if (jt == VALUE_STRING) { + return Arrays.asList(p.getValueAsString()); + } + throw new AnalysisException(FAIL_TO_DESERIALIZE_ANALYSIS); + } +} From 6592d80d5b2dff15de0c0b04ee16e2e088715380 Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Thu, 2 May 2024 00:39:44 +0900 Subject: [PATCH 097/265] =?UTF-8?q?feat:=20analysis=20controller=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84=20=EB=B0=8F=20=EC=BB=A4=EC=8A=A4=ED=85=80=20?= =?UTF-8?q?=EC=98=88=EC=99=B8=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/AnalysisInfoController.java | 55 +++++++++++++++++++ .../member/domain/member/Member.java | 18 ++---- .../exception/analysis/AnalysisException.java | 11 ++++ .../analysis/AnalysisExceptionType.java | 38 +++++++++++++ 4 files changed, 108 insertions(+), 14 deletions(-) create mode 100644 src/main/java/capstone/facefriend/member/controller/AnalysisInfoController.java create mode 100644 src/main/java/capstone/facefriend/member/exception/analysis/AnalysisException.java create mode 100644 src/main/java/capstone/facefriend/member/exception/analysis/AnalysisExceptionType.java diff --git a/src/main/java/capstone/facefriend/member/controller/AnalysisInfoController.java b/src/main/java/capstone/facefriend/member/controller/AnalysisInfoController.java new file mode 100644 index 0000000000..07aff649cd --- /dev/null +++ b/src/main/java/capstone/facefriend/member/controller/AnalysisInfoController.java @@ -0,0 +1,55 @@ +package capstone.facefriend.member.controller; + +import capstone.facefriend.auth.controller.support.AuthMember; +import capstone.facefriend.member.service.AnalysisInfoService; +import capstone.facefriend.member.service.dto.analysisInfo.AnalysisInfoFullResponse; +import capstone.facefriend.member.service.dto.analysisInfo.AnalysisInfoFullShortResponse; +import capstone.facefriend.member.service.dto.analysisInfo.AnalysisInfoShortResponse; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestPart; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; + +import java.io.IOException; + +@Slf4j +@RestController +@RequiredArgsConstructor +public class AnalysisInfoController { + + private final AnalysisInfoService analysisInfoService; + + // 관상 분석할 때 사용 + @PutMapping("/analysis-info") + public ResponseEntity analyze( + @RequestPart("origin") MultipartFile origin, + @AuthMember Long memberId + ) throws IOException { + return ResponseEntity.ok(analysisInfoService.analyze(origin, memberId)); + } + + @GetMapping("/analysis-info") + public ResponseEntity getAnalysisInfo( + @AuthMember Long memberId + ) { + return ResponseEntity.ok(analysisInfoService.getAnalysisInfoFullShort(memberId)); + } + + @GetMapping("/analysis-info/full") + public ResponseEntity getAnalysisInfoFull( + @AuthMember Long memberId + ) { + return ResponseEntity.ok(analysisInfoService.getAnalysisInfoFull(memberId)); + } + + @GetMapping("/analysis-info/short") + public ResponseEntity getAnalysisInfoShort( + @AuthMember Long memberId + ) { + return ResponseEntity.ok(analysisInfoService.getAnalysisInfoShort(memberId)); + } +} diff --git a/src/main/java/capstone/facefriend/member/domain/member/Member.java b/src/main/java/capstone/facefriend/member/domain/member/Member.java index 9ab8427e91..a792447a32 100644 --- a/src/main/java/capstone/facefriend/member/domain/member/Member.java +++ b/src/main/java/capstone/facefriend/member/domain/member/Member.java @@ -1,6 +1,9 @@ -package capstone.facefriend.member.domain; +package capstone.facefriend.member.domain.member; import capstone.facefriend.common.domain.BaseEntity; +import capstone.facefriend.member.domain.analysisInfo.AnalysisInfo; +import capstone.facefriend.member.domain.basicInfo.BasicInfo; +import capstone.facefriend.member.domain.faceInfo.FaceInfo; import jakarta.persistence.*; import lombok.*; import lombok.extern.slf4j.Slf4j; @@ -18,8 +21,6 @@ @DynamicUpdate public class Member extends BaseEntity { - private static final int EMAIL_MASKING_LENGTH = 2; - @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @@ -30,9 +31,6 @@ public class Member extends BaseEntity { @Column(nullable = false) private String password; - @Column - private boolean isVerified; - @Enumerated(EnumType.STRING) @Column(nullable = false) private Role role; @@ -62,10 +60,6 @@ public boolean isSame(Long id) { return this.id.equals(id); } - public String maskEmail() { - return this.email.charAt(0) + "*".repeat(EMAIL_MASKING_LENGTH) + this.email.substring(EMAIL_MASKING_LENGTH + 1); - } - public void setRole(Role role) { this.role = role; } @@ -82,10 +76,6 @@ public void setAnalysisInfo(AnalysisInfo analysisInfo) { this.analysisInfo = analysisInfo; } - public boolean isVerified() { - return this.isVerified == true; - } - public void setPassword(String password) { this.password = password; } diff --git a/src/main/java/capstone/facefriend/member/exception/analysis/AnalysisException.java b/src/main/java/capstone/facefriend/member/exception/analysis/AnalysisException.java new file mode 100644 index 0000000000..6e34a76e82 --- /dev/null +++ b/src/main/java/capstone/facefriend/member/exception/analysis/AnalysisException.java @@ -0,0 +1,11 @@ +package capstone.facefriend.member.exception.analysis; + +import capstone.facefriend.common.exception.BaseException; +import capstone.facefriend.common.exception.ExceptionType; + +public class AnalysisException extends BaseException { + + public AnalysisException(ExceptionType exceptionType) { + super(exceptionType); + } +} diff --git a/src/main/java/capstone/facefriend/member/exception/analysis/AnalysisExceptionType.java b/src/main/java/capstone/facefriend/member/exception/analysis/AnalysisExceptionType.java new file mode 100644 index 0000000000..fad26645ef --- /dev/null +++ b/src/main/java/capstone/facefriend/member/exception/analysis/AnalysisExceptionType.java @@ -0,0 +1,38 @@ +package capstone.facefriend.member.exception.analysis; + + +import capstone.facefriend.common.exception.ExceptionType; +import capstone.facefriend.common.exception.Status; + +public enum AnalysisExceptionType implements ExceptionType { + + FAIL_TO_ANALYSIS(Status.NOT_FOUND, 6001, "관상 분석에 실패했습니다."), + FAIL_TO_DESERIALIZE_ANALYSIS(Status.BAD_REQUEST, 6002, "관성 분석 역직렬화에 실패했습니다."), + FAIL_TO_EXTRACT_FACE_SHAPE_ID_NUM(Status.BAD_REQUEST, 6003, "관성 분석 역직렬화에 실패했습니다.") + ; + + private final Status status; + private final int exceptionCode; + private final String message; + + AnalysisExceptionType(Status status, int exceptionCode, String message) { + this.status = status; + this.exceptionCode = exceptionCode; + this.message = message; + } + + @Override + public Status status() { + return status; + } + + @Override + public int exceptionCode() { + return exceptionCode; + } + + @Override + public String message() { + return message; + } +} From eef70cd263a65631a09159bf6bb387be26cf4d72 Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Thu, 2 May 2024 00:41:01 +0900 Subject: [PATCH 098/265] =?UTF-8?q?style:=20=EC=BD=94=EB=93=9C=20=ED=8F=AC?= =?UTF-8?q?=EB=A7=A4=ED=8C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../facefriend/FacefriendApplication.java | 4 +- .../domain/member/MemberRepository.java | 2 +- .../facefriend/member/domain/member/Role.java | 6 +- .../exception/member/MemberException.java | 2 +- .../exception/member/MemberExceptionType.java | 2 +- .../member/service/BasicInfoService.java | 17 ++- .../member/service/BucketService.java | 52 ++------ .../member/service/FaceInfoService.java | 23 ++-- .../member/service/MemberService.java | 114 +++++++++++------- 9 files changed, 106 insertions(+), 116 deletions(-) diff --git a/src/main/java/capstone/facefriend/FacefriendApplication.java b/src/main/java/capstone/facefriend/FacefriendApplication.java index ee7ff038ac..a197dfe24c 100644 --- a/src/main/java/capstone/facefriend/FacefriendApplication.java +++ b/src/main/java/capstone/facefriend/FacefriendApplication.java @@ -9,9 +9,7 @@ @EnableJpaAuditing @SpringBootApplication public class FacefriendApplication { - static { - System.setProperty("com.amazonaws.sdk.disableEc2Metadata", "true"); - } + public static void main(String[] args) { TimeZone.setDefault(TimeZone.getTimeZone("Asia/Seoul")); SpringApplication.run(FacefriendApplication.class, args); diff --git a/src/main/java/capstone/facefriend/member/domain/member/MemberRepository.java b/src/main/java/capstone/facefriend/member/domain/member/MemberRepository.java index cc536e0222..f7b31fd2bb 100644 --- a/src/main/java/capstone/facefriend/member/domain/member/MemberRepository.java +++ b/src/main/java/capstone/facefriend/member/domain/member/MemberRepository.java @@ -1,4 +1,4 @@ -package capstone.facefriend.member.domain; +package capstone.facefriend.member.domain.member; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; diff --git a/src/main/java/capstone/facefriend/member/domain/member/Role.java b/src/main/java/capstone/facefriend/member/domain/member/Role.java index 5b1b2a06e5..5bdd498cda 100644 --- a/src/main/java/capstone/facefriend/member/domain/member/Role.java +++ b/src/main/java/capstone/facefriend/member/domain/member/Role.java @@ -1,7 +1,7 @@ -package capstone.facefriend.member.domain; +package capstone.facefriend.member.domain.member; -import capstone.facefriend.member.exception.MemberException; -import capstone.facefriend.member.exception.MemberExceptionType; +import capstone.facefriend.member.exception.member.MemberException; +import capstone.facefriend.member.exception.member.MemberExceptionType; import lombok.Getter; import java.util.Arrays; diff --git a/src/main/java/capstone/facefriend/member/exception/member/MemberException.java b/src/main/java/capstone/facefriend/member/exception/member/MemberException.java index ae3922a810..13ef0cc12c 100644 --- a/src/main/java/capstone/facefriend/member/exception/member/MemberException.java +++ b/src/main/java/capstone/facefriend/member/exception/member/MemberException.java @@ -1,4 +1,4 @@ -package capstone.facefriend.member.exception.meber; +package capstone.facefriend.member.exception.member; import capstone.facefriend.common.exception.BaseException; import capstone.facefriend.common.exception.ExceptionType; diff --git a/src/main/java/capstone/facefriend/member/exception/member/MemberExceptionType.java b/src/main/java/capstone/facefriend/member/exception/member/MemberExceptionType.java index 2841955f98..42f0ae0128 100644 --- a/src/main/java/capstone/facefriend/member/exception/member/MemberExceptionType.java +++ b/src/main/java/capstone/facefriend/member/exception/member/MemberExceptionType.java @@ -1,4 +1,4 @@ -package capstone.facefriend.member.exception.meber; +package capstone.facefriend.member.exception.member; import capstone.facefriend.common.exception.ExceptionType; import capstone.facefriend.common.exception.Status; diff --git a/src/main/java/capstone/facefriend/member/service/BasicInfoService.java b/src/main/java/capstone/facefriend/member/service/BasicInfoService.java index 52eefbe9d9..f0fbea4349 100644 --- a/src/main/java/capstone/facefriend/member/service/BasicInfoService.java +++ b/src/main/java/capstone/facefriend/member/service/BasicInfoService.java @@ -1,19 +1,19 @@ package capstone.facefriend.member.service; -import capstone.facefriend.member.domain.BasicInfo; -import capstone.facefriend.member.domain.Member; -import capstone.facefriend.member.domain.MemberRepository; -import capstone.facefriend.member.exception.MemberException; -import capstone.facefriend.member.service.dto.BasicInfoRequest; -import capstone.facefriend.member.service.dto.BasicInfoResponse; +import capstone.facefriend.member.domain.basicInfo.BasicInfo; +import capstone.facefriend.member.domain.member.Member; +import capstone.facefriend.member.domain.member.MemberRepository; +import capstone.facefriend.member.exception.member.MemberException; +import capstone.facefriend.member.service.dto.basicInfo.BasicInfoRequest; +import capstone.facefriend.member.service.dto.basicInfo.BasicInfoResponse; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import static capstone.facefriend.member.domain.BasicInfo.*; -import static capstone.facefriend.member.exception.MemberExceptionType.NOT_FOUND; +import static capstone.facefriend.member.domain.basicInfo.BasicInfo.*; +import static capstone.facefriend.member.exception.member.MemberExceptionType.NOT_FOUND; @Service @Slf4j @@ -48,7 +48,6 @@ public BasicInfoResponse putBasicInfo(Long memberId, BasicInfoRequest request) { oldBasicInfo.setRegion(Region.valueOf(request.region())); member.setBasicInfo(oldBasicInfo); - memberRepository.save(member); return BasicInfoResponse.of(oldBasicInfo); diff --git a/src/main/java/capstone/facefriend/member/service/BucketService.java b/src/main/java/capstone/facefriend/member/service/BucketService.java index f3f6d0a27c..3e175536a4 100644 --- a/src/main/java/capstone/facefriend/member/service/BucketService.java +++ b/src/main/java/capstone/facefriend/member/service/BucketService.java @@ -1,14 +1,14 @@ package capstone.facefriend.member.service; -import capstone.facefriend.member.domain.FaceInfo; -import capstone.facefriend.member.domain.FaceInfoRepository; -import capstone.facefriend.member.domain.Member; -import capstone.facefriend.member.domain.MemberRepository; -import capstone.facefriend.member.exception.MemberException; -import capstone.facefriend.member.exception.MemberExceptionType; +import capstone.facefriend.member.domain.faceInfo.FaceInfo; +import capstone.facefriend.member.domain.faceInfo.FaceInfoRepository; +import capstone.facefriend.member.domain.member.Member; +import capstone.facefriend.member.domain.member.MemberRepository; +import capstone.facefriend.member.exception.member.MemberException; +import capstone.facefriend.member.exception.member.MemberExceptionType; import capstone.facefriend.member.multipartFile.ByteArrayMultipartFile; -import capstone.facefriend.member.service.dto.FaceInfoResponse; +import capstone.facefriend.member.service.dto.faceInfo.FaceInfoResponse; import com.amazonaws.services.s3.AmazonS3; import com.amazonaws.services.s3.model.CannedAccessControlList; import com.amazonaws.services.s3.model.DeleteObjectRequest; @@ -21,8 +21,6 @@ import org.springframework.web.multipart.MultipartFile; import java.io.IOException; -import java.util.ArrayList; -import java.util.List; @Slf4j @RequiredArgsConstructor @@ -54,8 +52,7 @@ public FaceInfoResponse uploadOriginAndGenerated(MultipartFile origin, ByteArray // set metadata ObjectMetadata originMetadata = new ObjectMetadata(); originMetadata.setContentLength(origin.getInputStream().available()); - originMetadata.setContentType("image/jpeg"); - + originMetadata.setContentType(origin.getContentType()); String originObjectName = memberId + originPostfix; amazonS3.putObject( new PutObjectRequest( @@ -71,7 +68,7 @@ public FaceInfoResponse uploadOriginAndGenerated(MultipartFile origin, ByteArray // set metadata ObjectMetadata generatedMetadata = new ObjectMetadata(); generatedMetadata.setContentLength(generated.getInputStream().available()); - generatedMetadata.setContentType("image/jpeg"); + generatedMetadata.setContentType(generatedMetadata.getContentType()); String generatedObjectName = memberId + generatedPostfix; amazonS3.putObject( @@ -89,7 +86,7 @@ public FaceInfoResponse uploadOriginAndGenerated(MultipartFile origin, ByteArray .originS3Url(originS3Url) .generatedS3url(generatedS3Url) .build(); - faceInfoRepository.save(faceInfo); + faceInfoRepository.save(faceInfo); // // Member 최신화 후 저장 Member member = memberRepository.findById(memberId) @@ -116,34 +113,5 @@ public FaceInfoResponse deleteOriginAndGenerated(Long memberId) { return new FaceInfoResponse(defaultProfileS3Url, defaultProfileS3Url); } - - // Resume : resumeImages 업로드 - public List uploadResumeImages(MultipartFile[] resumeImages, Long memberId) throws IOException { - /** upload resumeImage to s3 */ - - int count = 0; - List resumeS3Urls = new ArrayList<>(); - for (MultipartFile resumeImage : resumeImages) { - // set metadata - ObjectMetadata originMetadata = new ObjectMetadata(); - originMetadata.setContentLength(resumeImage.getInputStream().available()); - originMetadata.setContentType("image/jpeg"); - - String resumeImageObjectName = memberId + resumePostfix + count; // ex) bucketName/1-resume-1.jpg - amazonS3.putObject( - new PutObjectRequest( - bucketName, - resumeImageObjectName, - resumeImage.getInputStream(), // resume - originMetadata - ).withCannedAcl(CannedAccessControlList.PublicRead) - ); - resumeS3Urls.add(amazonS3.getUrl(bucketName, resumeImageObjectName).toString()); - } - -// resumeRepository.save(); - - return resumeS3Urls; - } } diff --git a/src/main/java/capstone/facefriend/member/service/FaceInfoService.java b/src/main/java/capstone/facefriend/member/service/FaceInfoService.java index d1d803c16c..13668a0637 100644 --- a/src/main/java/capstone/facefriend/member/service/FaceInfoService.java +++ b/src/main/java/capstone/facefriend/member/service/FaceInfoService.java @@ -1,13 +1,13 @@ package capstone.facefriend.member.service; -import capstone.facefriend.member.domain.FaceInfo; -import capstone.facefriend.member.domain.FaceInfoRepository; -import capstone.facefriend.member.domain.Member; -import capstone.facefriend.member.domain.MemberRepository; -import capstone.facefriend.member.exception.MemberException; -import capstone.facefriend.member.exception.MemberExceptionType; +import capstone.facefriend.member.domain.faceInfo.FaceInfo; +import capstone.facefriend.member.domain.faceInfo.FaceInfoRepository; +import capstone.facefriend.member.domain.member.Member; +import capstone.facefriend.member.domain.member.MemberRepository; +import capstone.facefriend.member.exception.member.MemberException; +import capstone.facefriend.member.exception.member.MemberExceptionType; import capstone.facefriend.member.multipartFile.ByteArrayMultipartFile; -import capstone.facefriend.member.service.dto.FaceInfoResponse; +import capstone.facefriend.member.service.dto.faceInfo.FaceInfoResponse; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; @@ -49,12 +49,6 @@ public class FaceInfoService { private final FaceInfoRepository faceInfoRepository; private final MemberRepository memberRepository; - // origin 업로드 & generated 업로드 - public FaceInfoResponse uploadOrigin(MultipartFile origin, Long styleId, Long memberId) throws IOException { - ByteArrayMultipartFile generated = generate(origin, styleId, memberId); - return bucketService.uploadOriginAndGenerated(origin, generated, memberId); - } - // origin 삭제 & generated 삭제 -> origin 업로드 & generated 업로드 public FaceInfoResponse updateOrigin(MultipartFile origin, Long styleId, Long memberId) throws IOException { ByteArrayMultipartFile generated = generate(origin, styleId, memberId); @@ -108,8 +102,9 @@ public String getFilename() { // request entity HttpEntity> requestEntity = new HttpEntity<>(body, headers); + // response entity - ResponseEntity responseEntity = restTemplate.postForEntity(requestUrl, requestEntity, JsonNode.class); + ResponseEntity responseEntity = restTemplate.postForEntity(requestUrl, requestEntity, JsonNode.class); // 문제 // convert JSON into Map ObjectMapper objectMapper = new ObjectMapper(); diff --git a/src/main/java/capstone/facefriend/member/service/MemberService.java b/src/main/java/capstone/facefriend/member/service/MemberService.java index 1ae16ceeb9..07988e515e 100644 --- a/src/main/java/capstone/facefriend/member/service/MemberService.java +++ b/src/main/java/capstone/facefriend/member/service/MemberService.java @@ -4,12 +4,18 @@ import capstone.facefriend.auth.domain.TokenProvider; import capstone.facefriend.email.controller.dto.EmailVerificationResponse; import capstone.facefriend.email.service.EmailService; -import capstone.facefriend.member.domain.*; -import capstone.facefriend.member.exception.MemberException; -import capstone.facefriend.member.exception.MemberExceptionType; -import capstone.facefriend.member.service.dto.FindEmailResponse; -import capstone.facefriend.member.service.dto.SignInRequest; -import capstone.facefriend.member.service.dto.SignUpRequest; +import capstone.facefriend.member.domain.analysisInfo.AnalysisInfo; +import capstone.facefriend.member.domain.analysisInfo.AnalysisInfoRepository; +import capstone.facefriend.member.domain.basicInfo.BasicInfo; +import capstone.facefriend.member.domain.basicInfo.BasicInfoRepository; +import capstone.facefriend.member.domain.faceInfo.FaceInfo; +import capstone.facefriend.member.domain.faceInfo.FaceInfoRepository; +import capstone.facefriend.member.domain.member.Member; +import capstone.facefriend.member.domain.member.MemberRepository; +import capstone.facefriend.member.exception.member.MemberException; +import capstone.facefriend.member.service.dto.member.FindEmailResponse; +import capstone.facefriend.member.service.dto.member.SignInRequest; +import capstone.facefriend.member.service.dto.member.SignUpRequest; import capstone.facefriend.redis.RedisDao; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -18,9 +24,12 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import static capstone.facefriend.member.domain.BasicInfo.*; -import static capstone.facefriend.member.domain.Role.USER; -import static capstone.facefriend.member.exception.MemberExceptionType.*; +import java.util.List; +import java.util.Map; + +import static capstone.facefriend.member.domain.basicInfo.BasicInfo.*; +import static capstone.facefriend.member.domain.member.Role.USER; +import static capstone.facefriend.member.exception.member.MemberExceptionType.*; @Service @Slf4j @@ -31,6 +40,8 @@ public class MemberService { private final MemberRepository memberRepository; private final BasicInfoRepository basicInfoRepository; private final FaceInfoRepository faceInfoRepository; + private final AnalysisInfoRepository analysisInfoRepository; + private final PasswordEncoder passwordEncoder; private final EmailService emailService; @@ -42,6 +53,15 @@ public class MemberService { private static final String RESET_PASSWORD_SUCCESS_MESSAGE = "비밀번호 재설정 성공"; private static final String EXIT_SUCCESS_MESSAGE = "회원탈퇴 성공"; + private static final String INITIAL_ANALYSIS_NAME_EYE = "눈"; + private static final String INITIAL_ANALYSIS_NAME_FACE_SHAPE = "얼굴형"; + private static final String INITIAL_ANALYSIS_NAME_LIPS = "입술"; + private static final String INITIAL_ANALYSIS_NAME_NOSE = "코"; + private static final String INITIAL_ANALYSIS_NAME_EYEBROW = "눈썹"; + private static final String INITIAL_ANALYSIS_DESCRIPTION = "관상 분석 설명이 없습니다!"; + private static final String INITIAL_ANALYSIS_TAG = "관상 분석 태그가 없습니다!"; + private static final Integer INITIAL_ANALYSIS_FACE_SHAPE_NUM = -1; + private static final Long BLACKLIST_REMAIN_MINUTE = 1000 * 60 * 60 * 12L; // 12 시간 @Value("${spring.cloud.aws.s3.default-profile}") @@ -75,40 +95,50 @@ public EmailVerificationResponse verifyCode(String email, String code) { @Transactional public String signUp(SignUpRequest request) { - boolean isVerified = request.isVerified(); - - if (isVerified) { - String encodedPassword = passwordEncoder.encode(request.password()); - - BasicInfo basicInfo = BasicInfo.builder() - .nickname("") - .gender(Gender.DEFAULT) - .ageGroup(AgeGroup.DEFAULT) - .ageDegree(AgeDegree.DEFAULT) - .heightGroup(HeightGroup.DEFAULT) - .region(Region.DEFAULT) - .build(); - basicInfoRepository.save(basicInfo); - - FaceInfo faceInfo = FaceInfo.builder() - .originS3Url(defaultProfileS3Url) - .generatedS3url(defaultProfileS3Url) - .build(); - faceInfoRepository.save(faceInfo); - - Member member = Member.builder() - .email(request.email()) - .password(encodedPassword) - .isVerified(true) - .role(USER) - .basicInfo(basicInfo) - .faceInfo(faceInfo) - .build(); - memberRepository.save(member); + String encodedPassword = passwordEncoder.encode(request.password()); + + // 기본정보 초기값 + BasicInfo basicInfo = BasicInfo.builder() + .nickname("") + .gender(Gender.DEFAULT) + .ageGroup(AgeGroup.DEFAULT) + .ageDegree(AgeDegree.DEFAULT) + .heightGroup(HeightGroup.DEFAULT) + .region(Region.DEFAULT) + .build(); + basicInfoRepository.save(basicInfo); + + // 관상 이미지 초기값 + FaceInfo faceInfo = FaceInfo.builder() + .originS3Url(defaultProfileS3Url) + .generatedS3url(defaultProfileS3Url) + .build(); + faceInfoRepository.save(faceInfo); + + // 관상 분석 초기값 + AnalysisInfo analysisInfo = AnalysisInfo.builder() + .analysisInfoFull(Map.of( + INITIAL_ANALYSIS_NAME_EYE, INITIAL_ANALYSIS_DESCRIPTION, + INITIAL_ANALYSIS_NAME_FACE_SHAPE, INITIAL_ANALYSIS_DESCRIPTION, + INITIAL_ANALYSIS_NAME_LIPS, INITIAL_ANALYSIS_DESCRIPTION, + INITIAL_ANALYSIS_NAME_NOSE, INITIAL_ANALYSIS_DESCRIPTION, + INITIAL_ANALYSIS_NAME_EYEBROW, INITIAL_ANALYSIS_DESCRIPTION)) + .analysisInfoShort(List.of(INITIAL_ANALYSIS_TAG)) + .faceShapeIdNum(INITIAL_ANALYSIS_FACE_SHAPE_NUM) + .build(); + analysisInfoRepository.save(analysisInfo); + + // 저장 + Member member = Member.builder() + .email(request.email()) + .password(encodedPassword) + .role(USER) + .basicInfo(basicInfo) // 기본정보 + .faceInfo(faceInfo) // 관상 이미지 + .analysisInfo(analysisInfo) // 관상 분석 + .build(); + memberRepository.save(member); - } else { - throw new MemberException(NOT_VERIFIED); - } return SIGN_UP_SUCCESS_MESSAGE; } From c4fef6272b232c565dc9a6073fc2bbebcef77610 Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Thu, 2 May 2024 00:41:59 +0900 Subject: [PATCH 099/265] =?UTF-8?q?refactor:=20=ED=86=A0=ED=81=B0=20?= =?UTF-8?q?=EB=B8=94=EB=9E=99=EB=A6=AC=EC=8A=A4=ED=8A=B8=20=EC=A1=B0?= =?UTF-8?q?=EA=B1=B4=EB=AC=B8=20=EB=B3=B4=EC=99=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/capstone/facefriend/redis/RedisDao.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/capstone/facefriend/redis/RedisDao.java b/src/main/java/capstone/facefriend/redis/RedisDao.java index a147bb70e6..cbe2b404a5 100644 --- a/src/main/java/capstone/facefriend/redis/RedisDao.java +++ b/src/main/java/capstone/facefriend/redis/RedisDao.java @@ -1,7 +1,7 @@ package capstone.facefriend.redis; -import capstone.facefriend.member.exception.MemberException; +import capstone.facefriend.member.exception.member.MemberException; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.data.redis.core.RedisTemplate; @@ -10,7 +10,7 @@ import java.util.concurrent.TimeUnit; -import static capstone.facefriend.member.exception.MemberExceptionType.ACCESS_TOKEN_IS_IN_BLACKLIST; +import static capstone.facefriend.member.exception.member.MemberExceptionType.ACCESS_TOKEN_IS_IN_BLACKLIST; @Component @Slf4j From febc07a9f0dc5e265925fe06b5263f63833a43e0 Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Thu, 2 May 2024 10:41:09 +0900 Subject: [PATCH 100/265] =?UTF-8?q?style:=20AnalysisInfo=20=ED=85=8C?= =?UTF-8?q?=EC=9D=B4=EB=B8=94=EB=AA=85=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../facefriend/member/domain/analysisInfo/AnalysisInfo.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/capstone/facefriend/member/domain/analysisInfo/AnalysisInfo.java b/src/main/java/capstone/facefriend/member/domain/analysisInfo/AnalysisInfo.java index 0c9fc7bd9f..40525ad56d 100644 --- a/src/main/java/capstone/facefriend/member/domain/analysisInfo/AnalysisInfo.java +++ b/src/main/java/capstone/facefriend/member/domain/analysisInfo/AnalysisInfo.java @@ -25,11 +25,11 @@ public class AnalysisInfo { private Integer faceShapeIdNum; @ElementCollection - @CollectionTable(name = "ANALYSIS_FULL", joinColumns = @JoinColumn(name = "ANALYSIS_INFO_ID")) + @CollectionTable(name = "ANALYSIS_INFO_FULL", joinColumns = @JoinColumn(name = "ANALYSIS_INFO_ID")) private Map analysisInfoFull = new HashMap<>(); @ElementCollection - @CollectionTable(name = "ANALYSIS_SHORT", joinColumns = @JoinColumn(name = "ANALYSIS_INFO_ID")) + @CollectionTable(name = "ANALYSIS_INFO_SHORT", joinColumns = @JoinColumn(name = "ANALYSIS_INFO_ID")) private List analysisInfoShort = new ArrayList<>(); } From 6b9cf5a5fea4810d321dc0f4301680ff5100005c Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Thu, 2 May 2024 22:12:37 +0900 Subject: [PATCH 101/265] be-backup --- .gitignore | 3 + build.gradle | 40 +++ .../facefriend/FacefriendApplication.java | 8 +- .../bucketConfig => bucket}/BucketConfig.java | 2 +- .../service => bucket}/BucketService.java | 45 ++- .../facefriend/config/JasyptConfig.java | 31 ++ .../config/QuerydslConfiguration.java | 15 + .../domain/analysisInfo/AnalysisInfo.java | 2 + .../member/domain/faceInfo/FaceInfo.java | 6 +- .../member/domain/resume/Resume.java | 4 + .../domain/resume/ResumeRepository.java | 4 + .../member/service/BasicInfoService.java | 1 - .../member/service/FaceInfoService.java | 5 +- .../member/service/MemberService.java | 2 +- .../resume/controller/ResumeController.java | 61 ++++ .../facefriend/resume/domain/Resume.java | 69 +++++ .../resume/domain/ResumeRepository.java | 15 + .../resume/domain/ResumeRepositoryCustom.java | 19 ++ .../resume/domain/ResumeRepositoryImpl.java | 272 ++++++++++++++++++ .../domain/dto/ResumeHomeDetailResponse.java | 7 + .../resume/domain/dto/ResumeRequest.java | 8 + .../resume/domain/dto/ResumeResponse.java | 17 ++ .../resume/exception/ResumeException.java | 12 + .../resume/exception/ResumeExceptionType.java | 37 +++ .../resume/service/ResumeService.java | 125 ++++++++ src/main/resources/application-prod.yml | 77 +++++ .../FacefriendApplicationTests.java | 16 ++ 27 files changed, 886 insertions(+), 17 deletions(-) rename src/main/java/capstone/facefriend/{member/bucketConfig => bucket}/BucketConfig.java (95%) rename src/main/java/capstone/facefriend/{member/service => bucket}/BucketService.java (76%) create mode 100644 src/main/java/capstone/facefriend/config/JasyptConfig.java create mode 100644 src/main/java/capstone/facefriend/config/QuerydslConfiguration.java create mode 100644 src/main/java/capstone/facefriend/member/domain/resume/Resume.java create mode 100644 src/main/java/capstone/facefriend/member/domain/resume/ResumeRepository.java create mode 100644 src/main/java/capstone/facefriend/resume/controller/ResumeController.java create mode 100644 src/main/java/capstone/facefriend/resume/domain/Resume.java create mode 100644 src/main/java/capstone/facefriend/resume/domain/ResumeRepository.java create mode 100644 src/main/java/capstone/facefriend/resume/domain/ResumeRepositoryCustom.java create mode 100644 src/main/java/capstone/facefriend/resume/domain/ResumeRepositoryImpl.java create mode 100644 src/main/java/capstone/facefriend/resume/domain/dto/ResumeHomeDetailResponse.java create mode 100644 src/main/java/capstone/facefriend/resume/domain/dto/ResumeRequest.java create mode 100644 src/main/java/capstone/facefriend/resume/domain/dto/ResumeResponse.java create mode 100644 src/main/java/capstone/facefriend/resume/exception/ResumeException.java create mode 100644 src/main/java/capstone/facefriend/resume/exception/ResumeExceptionType.java create mode 100644 src/main/java/capstone/facefriend/resume/service/ResumeService.java create mode 100644 src/main/resources/application-prod.yml diff --git a/.gitignore b/.gitignore index 64bc76fe20..bf4f94b05d 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,9 @@ # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 +#querydsl +/src/main/generated + # User-specific stuff .idea/**/workspace.xml .idea/**/tasks.xml diff --git a/build.gradle b/build.gradle index 2a37e63b67..5aeb5a5c62 100644 --- a/build.gradle +++ b/build.gradle @@ -11,6 +11,12 @@ java { sourceCompatibility = '17' } +configurations { + compileOnly { + extendsFrom annotationProcessor + } +} + repositories { mavenCentral() } @@ -50,8 +56,42 @@ dependencies { // AWS S3 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:${dependencyManagement.importedProperties['querydsl.version']}:jakarta" + implementation "com.querydsl:querydsl-core" + implementation "com.querydsl:querydsl-collections" + annotationProcessor "jakarta.annotation:jakarta.annotation-api" // java.lang.NoClassDefFoundError (javax.annotation.Generated) 에러 대응 코드 + annotationProcessor "jakarta.persistence:jakarta.persistence-api" // java.lang.NoClassDefFoundError (javax.annotation.Entity) 에러 대응 코드 + + // jasypt + implementation 'com.github.ulisesbocchio:jasypt-spring-boot-starter:3.0.5' } tasks.named('test') { useJUnitPlatform() } + +///// Querydsl 빌드 옵션 (옵셔널) +def generated = 'src/main/generated' + +///// querydsl QClass 파일 생성 위치를 지정 +tasks.withType(JavaCompile) { + options.getGeneratedSourceOutputDirectory().set(file(generated)) +} + +///// java source set 에 querydsl QClass 위치 추가 +sourceSets { + main.java.srcDirs += [ generated ] +} + +///// gradle clean 시에 QClass 디렉토리 삭제 +clean { + delete file(generated) +} + +test { + useJUnitPlatform() + systemProperty 'jasypt.encryptor.password', findProperty("jasypt.encryptor.password") +} \ No newline at end of file diff --git a/src/main/java/capstone/facefriend/FacefriendApplication.java b/src/main/java/capstone/facefriend/FacefriendApplication.java index a197dfe24c..c87661e736 100644 --- a/src/main/java/capstone/facefriend/FacefriendApplication.java +++ b/src/main/java/capstone/facefriend/FacefriendApplication.java @@ -10,9 +10,9 @@ @SpringBootApplication public class FacefriendApplication { - public static void main(String[] args) { - TimeZone.setDefault(TimeZone.getTimeZone("Asia/Seoul")); - SpringApplication.run(FacefriendApplication.class, args); - } + public static void main(String[] args) { + TimeZone.setDefault(TimeZone.getTimeZone("Asia/Seoul")); + SpringApplication.run(FacefriendApplication.class, args); + } } diff --git a/src/main/java/capstone/facefriend/member/bucketConfig/BucketConfig.java b/src/main/java/capstone/facefriend/bucket/BucketConfig.java similarity index 95% rename from src/main/java/capstone/facefriend/member/bucketConfig/BucketConfig.java rename to src/main/java/capstone/facefriend/bucket/BucketConfig.java index 6a8ffa225e..59e6643b6d 100644 --- a/src/main/java/capstone/facefriend/member/bucketConfig/BucketConfig.java +++ b/src/main/java/capstone/facefriend/bucket/BucketConfig.java @@ -1,4 +1,4 @@ -package capstone.facefriend.member.bucketConfig; +package capstone.facefriend.bucket; import com.amazonaws.auth.AWSCredentials; import com.amazonaws.auth.AWSStaticCredentialsProvider; diff --git a/src/main/java/capstone/facefriend/member/service/BucketService.java b/src/main/java/capstone/facefriend/bucket/BucketService.java similarity index 76% rename from src/main/java/capstone/facefriend/member/service/BucketService.java rename to src/main/java/capstone/facefriend/bucket/BucketService.java index 3e175536a4..0a7daffb1f 100644 --- a/src/main/java/capstone/facefriend/member/service/BucketService.java +++ b/src/main/java/capstone/facefriend/bucket/BucketService.java @@ -1,4 +1,4 @@ -package capstone.facefriend.member.service; +package capstone.facefriend.bucket; import capstone.facefriend.member.domain.faceInfo.FaceInfo; @@ -21,6 +21,8 @@ import org.springframework.web.multipart.MultipartFile; import java.io.IOException; +import java.util.ArrayList; +import java.util.List; @Slf4j @RequiredArgsConstructor @@ -40,7 +42,7 @@ public class BucketService { private String generatedPostfix; @Value("${spring.cloud.aws.s3.resume-postfix}") - private String resumePostfix; + private String resumeInfix; private final AmazonS3 amazonS3; private final MemberRepository memberRepository; @@ -62,7 +64,7 @@ public FaceInfoResponse uploadOriginAndGenerated(MultipartFile origin, ByteArray originMetadata ).withCannedAcl(CannedAccessControlList.PublicRead) ); - String originS3Url = amazonS3.getUrl(bucketName, originObjectName).toString(); + String originS3url = amazonS3.getUrl(bucketName, originObjectName).toString(); /** upload generated to s3 */ // set metadata @@ -83,7 +85,7 @@ public FaceInfoResponse uploadOriginAndGenerated(MultipartFile origin, ByteArray // FaceInfo 저장 FaceInfo faceInfo = FaceInfo.builder() - .originS3Url(originS3Url) + .originS3url(originS3url) .generatedS3url(generatedS3Url) .build(); faceInfoRepository.save(faceInfo); // @@ -94,7 +96,7 @@ public FaceInfoResponse uploadOriginAndGenerated(MultipartFile origin, ByteArray member.setFaceInfo(faceInfo); memberRepository.save(member); - return new FaceInfoResponse(originS3Url, generatedS3Url); + return new FaceInfoResponse(originS3url, generatedS3Url); } // FaceInfo : origin 수정 -> generated 수정 @@ -113,5 +115,38 @@ public FaceInfoResponse deleteOriginAndGenerated(Long memberId) { return new FaceInfoResponse(defaultProfileS3Url, defaultProfileS3Url); } + + public List uploadResumeImages(List images, Long memberId) throws IOException { + int start = 0; + List resumeImageS3urls = new ArrayList<>(); + + for (MultipartFile image : images) { + ObjectMetadata metadata = new ObjectMetadata(); + metadata.setContentLength(image.getInputStream().available()); + metadata.setContentType(image.getContentType()); + + String imageObjectName = memberId + resumeInfix + start; + amazonS3.putObject( + new PutObjectRequest( + bucketName, + imageObjectName, + image.getInputStream(), + metadata + ).withCannedAcl(CannedAccessControlList.PublicRead) + ); + + resumeImageS3urls.add(amazonS3.getUrl(bucketName, imageObjectName).toString()); + } + + return resumeImageS3urls; + } + + public List updateResumeImages() { + return null; + } + + public List deleteResumeImages(Long memberId) { + String imageObjectName = memberId + resumeInfix + + } } diff --git a/src/main/java/capstone/facefriend/config/JasyptConfig.java b/src/main/java/capstone/facefriend/config/JasyptConfig.java new file mode 100644 index 0000000000..7cc1ad13ba --- /dev/null +++ b/src/main/java/capstone/facefriend/config/JasyptConfig.java @@ -0,0 +1,31 @@ +package capstone.facefriend.config; + +import org.jasypt.encryption.StringEncryptor; +import org.jasypt.encryption.pbe.PooledPBEStringEncryptor; +import org.jasypt.encryption.pbe.config.SimpleStringPBEConfig; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class JasyptConfig { + + @Value("${jasypt.encryptor.password}") + private String password; + + @Bean("jasyptStringEncryptor") + public StringEncryptor stringEncryptor() { + PooledPBEStringEncryptor encryptor = new PooledPBEStringEncryptor(); + SimpleStringPBEConfig config = new SimpleStringPBEConfig(); + config.setPassword(password); + config.setAlgorithm("PBEWithMD5AndDES"); + config.setKeyObtentionIterations("1000"); + config.setPoolSize("1"); + config.setProviderName("SunJCE"); + config.setSaltGeneratorClassName("org.jasypt.salt.RandomSaltGenerator"); + config.setIvGeneratorClassName("org.jasypt.iv.NoIvGenerator"); + config.setStringOutputType("base64"); + encryptor.setConfig(config); + return encryptor; + } +} diff --git a/src/main/java/capstone/facefriend/config/QuerydslConfiguration.java b/src/main/java/capstone/facefriend/config/QuerydslConfiguration.java new file mode 100644 index 0000000000..760e350e8c --- /dev/null +++ b/src/main/java/capstone/facefriend/config/QuerydslConfiguration.java @@ -0,0 +1,15 @@ +package capstone.facefriend.config; + +import com.querydsl.jpa.impl.JPAQueryFactory; +import jakarta.persistence.EntityManager; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class QuerydslConfiguration { + + @Bean + public JPAQueryFactory jpaQueryFactory(EntityManager em) { + return new JPAQueryFactory(em); + } +} diff --git a/src/main/java/capstone/facefriend/member/domain/analysisInfo/AnalysisInfo.java b/src/main/java/capstone/facefriend/member/domain/analysisInfo/AnalysisInfo.java index 40525ad56d..666a2dab79 100644 --- a/src/main/java/capstone/facefriend/member/domain/analysisInfo/AnalysisInfo.java +++ b/src/main/java/capstone/facefriend/member/domain/analysisInfo/AnalysisInfo.java @@ -24,10 +24,12 @@ public class AnalysisInfo { private Integer faceShapeIdNum; + @Builder.Default @ElementCollection @CollectionTable(name = "ANALYSIS_INFO_FULL", joinColumns = @JoinColumn(name = "ANALYSIS_INFO_ID")) private Map analysisInfoFull = new HashMap<>(); + @Builder.Default @ElementCollection @CollectionTable(name = "ANALYSIS_INFO_SHORT", joinColumns = @JoinColumn(name = "ANALYSIS_INFO_ID")) private List analysisInfoShort = new ArrayList<>(); diff --git a/src/main/java/capstone/facefriend/member/domain/faceInfo/FaceInfo.java b/src/main/java/capstone/facefriend/member/domain/faceInfo/FaceInfo.java index b5ece48398..1070b67c74 100644 --- a/src/main/java/capstone/facefriend/member/domain/faceInfo/FaceInfo.java +++ b/src/main/java/capstone/facefriend/member/domain/faceInfo/FaceInfo.java @@ -17,13 +17,13 @@ public class FaceInfo { private Long id; @Column - private String originS3Url; + private String originS3url; @Column private String generatedS3url; - public void setOriginS3Url(String originS3Url) { - this.originS3Url = originS3Url; + public void setOriginS3url(String originS3Url) { + this.originS3url = originS3url; } public void setGeneratedS3Url(String generatedS3url) { diff --git a/src/main/java/capstone/facefriend/member/domain/resume/Resume.java b/src/main/java/capstone/facefriend/member/domain/resume/Resume.java new file mode 100644 index 0000000000..20a4487d75 --- /dev/null +++ b/src/main/java/capstone/facefriend/member/domain/resume/Resume.java @@ -0,0 +1,4 @@ +package capstone.facefriend.member.domain.resume; + +public class Resume { +} diff --git a/src/main/java/capstone/facefriend/member/domain/resume/ResumeRepository.java b/src/main/java/capstone/facefriend/member/domain/resume/ResumeRepository.java new file mode 100644 index 0000000000..74768626ee --- /dev/null +++ b/src/main/java/capstone/facefriend/member/domain/resume/ResumeRepository.java @@ -0,0 +1,4 @@ +package capstone.facefriend.member.domain.resume; + +public interface ResumeRepository { +} diff --git a/src/main/java/capstone/facefriend/member/service/BasicInfoService.java b/src/main/java/capstone/facefriend/member/service/BasicInfoService.java index f0fbea4349..e9841707ec 100644 --- a/src/main/java/capstone/facefriend/member/service/BasicInfoService.java +++ b/src/main/java/capstone/facefriend/member/service/BasicInfoService.java @@ -27,7 +27,6 @@ private Member findMemberById(Long memberId) { .orElseThrow(() -> new MemberException(NOT_FOUND)); } - @Transactional public BasicInfoResponse getBasicInfo(Long memberId) { Member member = findMemberById(memberId); BasicInfo basicInfo = member.getBasicInfo(); diff --git a/src/main/java/capstone/facefriend/member/service/FaceInfoService.java b/src/main/java/capstone/facefriend/member/service/FaceInfoService.java index 13668a0637..7520d2eee2 100644 --- a/src/main/java/capstone/facefriend/member/service/FaceInfoService.java +++ b/src/main/java/capstone/facefriend/member/service/FaceInfoService.java @@ -1,5 +1,6 @@ package capstone.facefriend.member.service; +import capstone.facefriend.bucket.BucketService; import capstone.facefriend.member.domain.faceInfo.FaceInfo; import capstone.facefriend.member.domain.faceInfo.FaceInfoRepository; import capstone.facefriend.member.domain.member.Member; @@ -60,7 +61,7 @@ public FaceInfoResponse getOriginAndGenerated(Long memberId) { .orElseThrow(() -> new MemberException(MemberExceptionType.NOT_FOUND)); FaceInfo faceInfo = member.getFaceInfo(); - return new FaceInfoResponse(faceInfo.getOriginS3Url(), faceInfo.getGeneratedS3url()); + return new FaceInfoResponse(faceInfo.getOriginS3url(), faceInfo.getGeneratedS3url()); } // origin 삭제 & generated 삭제 @@ -71,7 +72,7 @@ public FaceInfoResponse deleteOriginAndGenerated(Long memberId) { .orElseThrow(() -> new MemberException(MemberExceptionType.NOT_FOUND)); FaceInfo faceInfo = member.getFaceInfo(); - faceInfo.setOriginS3Url(defaultProfileS3Url); + faceInfo.setOriginS3url(defaultProfileS3Url); faceInfo.setGeneratedS3Url(defaultProfileS3Url); faceInfoRepository.save(faceInfo); diff --git a/src/main/java/capstone/facefriend/member/service/MemberService.java b/src/main/java/capstone/facefriend/member/service/MemberService.java index 07988e515e..c37af0f9e2 100644 --- a/src/main/java/capstone/facefriend/member/service/MemberService.java +++ b/src/main/java/capstone/facefriend/member/service/MemberService.java @@ -110,7 +110,7 @@ public String signUp(SignUpRequest request) { // 관상 이미지 초기값 FaceInfo faceInfo = FaceInfo.builder() - .originS3Url(defaultProfileS3Url) + .originS3url(defaultProfileS3Url) .generatedS3url(defaultProfileS3Url) .build(); faceInfoRepository.save(faceInfo); diff --git a/src/main/java/capstone/facefriend/resume/controller/ResumeController.java b/src/main/java/capstone/facefriend/resume/controller/ResumeController.java new file mode 100644 index 0000000000..408e820137 --- /dev/null +++ b/src/main/java/capstone/facefriend/resume/controller/ResumeController.java @@ -0,0 +1,61 @@ +package capstone.facefriend.resume.controller; + +import capstone.facefriend.auth.controller.support.AuthMember; +import capstone.facefriend.resume.domain.dto.ResumeHomeDetailResponse; +import capstone.facefriend.resume.domain.dto.ResumeRequest; +import capstone.facefriend.resume.service.ResumeService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import java.io.IOException; +import java.util.List; + + +@Slf4j +@RestController +@RequiredArgsConstructor +public class ResumeController { + + private final ResumeService resumeService; + + /** 특정 페이지 **/ + @PostMapping("/resume") + public ResponseEntity postResume( + @RequestPart List images, + @AuthMember Long memberId, + ResumeRequest request + ) throws IOException { + resumeService.postResume(memberId, images, request); + + } + + + /** 홈 페이지 **/ + @GetMapping("/resume") + public List getHomeResumesByGoodCombi( + @AuthMember Long memberId + ) { + return resumeService.getHomeResumesByGoodCombi(memberId); + } + + @GetMapping("/resume") + public List getHomeResumesByBadCombi( + @AuthMember Long memberId + ) { + return resumeService.getHomeResumesByBadCombi(memberId) + } + + @GetMapping("/resume") + public List getDetailResumesByCategory( + @AuthMember Long memberId, + String category + ) { + return resumeService.getDetailResumesByCategory(category) + } + + /** 디테일 페이지 **/ + +} diff --git a/src/main/java/capstone/facefriend/resume/domain/Resume.java b/src/main/java/capstone/facefriend/resume/domain/Resume.java new file mode 100644 index 0000000000..08286469b5 --- /dev/null +++ b/src/main/java/capstone/facefriend/resume/domain/Resume.java @@ -0,0 +1,69 @@ +package capstone.facefriend.resume.domain; + +import capstone.facefriend.member.domain.member.Member; +import jakarta.persistence.*; +import lombok.*; +import lombok.extern.slf4j.Slf4j; +import org.hibernate.annotations.DynamicInsert; +import org.hibernate.annotations.DynamicUpdate; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@Getter +@Builder +@EqualsAndHashCode(of = "id", callSuper = false) +@AllArgsConstructor(access = AccessLevel.PRIVATE) +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@Entity +@Slf4j +@DynamicInsert +@DynamicUpdate +public class Resume { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "RESUME_ID") + private Long id; + + @OneToOne + @JoinColumn(name = "MEMBER_ID", nullable = false) + private Member member; // 본인 + + @ElementCollection + @CollectionTable(name = "RESUME_IMAGE_S3_URLS", joinColumns = @JoinColumn(name = "RESUME_ID")) + private List resumeImageS3urls; + + @Enumerated(EnumType.STRING) + @Column(nullable = false) + private Category category; + + private String content; + + @Builder.Default + @ElementCollection + @CollectionTable(name = "OPEN_MEMBER", joinColumns = @JoinColumn(name = "RESUME_ID")) + @MapKeyColumn(name = "MEMBER_ID") // key + @Column(name = "IS_OPEN") // value + private Map friends = new HashMap<>(); // 타멤버id : 공개여부 + + public enum Category { + SIMILAR_ANALYSIS("비슷한 관상"), + DIFFERENT_ANALYSIS("다른 관상"), + FOOD("음식"), + WORKOUT("운동"), + MOVIE("영화"), + FASHION("패션"), + DATING("연애"), + MUSIC("음악"), + STUDY("영화"), + ETC("기타"); + + private final String value; + + Category(String value) { + this.value = value; + } + } +} diff --git a/src/main/java/capstone/facefriend/resume/domain/ResumeRepository.java b/src/main/java/capstone/facefriend/resume/domain/ResumeRepository.java new file mode 100644 index 0000000000..5dcf272f3a --- /dev/null +++ b/src/main/java/capstone/facefriend/resume/domain/ResumeRepository.java @@ -0,0 +1,15 @@ +package capstone.facefriend.resume.domain; + +import capstone.facefriend.member.domain.member.Member; +import org.springframework.data.repository.Repository; + +import java.util.Optional; + +public interface ResumeRepository extends Repository, ResumeRepositoryCustom { + + Resume save(Resume resume); + + Optional findResumeByMember(Member member); + + Optional findResumeById(Long id); +} diff --git a/src/main/java/capstone/facefriend/resume/domain/ResumeRepositoryCustom.java b/src/main/java/capstone/facefriend/resume/domain/ResumeRepositoryCustom.java new file mode 100644 index 0000000000..9c7861f067 --- /dev/null +++ b/src/main/java/capstone/facefriend/resume/domain/ResumeRepositoryCustom.java @@ -0,0 +1,19 @@ +package capstone.facefriend.resume.domain; + +import capstone.facefriend.resume.domain.dto.ResumeHomeDetailResponse; +import org.springframework.data.domain.Pageable; + +import java.util.List; + +public interface ResumeRepositoryCustom { + + // 홈 페이지 : 20개 + List getHomeResumesByGoodCombi(Long memberId, Pageable pageable); // 관상 궁합 좋은 20개 + List getHomeResumesByBadCombi(Long memberId, Pageable pageable); // 관상 궁합 나쁜 20개 + List getHomeResumesByCategory(String category, Pageable pageable); // 카테고리별 20개 + + // 디테일 페이지 : 10개 + List getDetailResumesByGoodCombi(Long memberId, Pageable pageable); // 관상 궁합 좋은 10개 + List getDetailResumesByBadCombi(Long memberId, Pageable pageable); // 관상 궁합 나쁜 10개 + List getDetailResumesByCategory(String category, Pageable pageable); // 카테고리별 10개 +} diff --git a/src/main/java/capstone/facefriend/resume/domain/ResumeRepositoryImpl.java b/src/main/java/capstone/facefriend/resume/domain/ResumeRepositoryImpl.java new file mode 100644 index 0000000000..92e3c2cd39 --- /dev/null +++ b/src/main/java/capstone/facefriend/resume/domain/ResumeRepositoryImpl.java @@ -0,0 +1,272 @@ +package capstone.facefriend.resume.domain; + +import capstone.facefriend.bucket.BucketService; +import capstone.facefriend.member.domain.member.Member; +import capstone.facefriend.member.domain.member.MemberRepository; +import capstone.facefriend.member.domain.member.QMember; +import capstone.facefriend.member.exception.member.MemberException; +import capstone.facefriend.resume.domain.dto.ResumeHomeDetailResponse; +import capstone.facefriend.resume.domain.dto.ResumeResponse; +import capstone.facefriend.resume.exception.ResumeException; +import capstone.facefriend.resume.exception.ResumeExceptionType; +import com.querydsl.core.BooleanBuilder; +import com.querydsl.core.QueryResults; +import com.querydsl.core.types.Projections; +import com.querydsl.jpa.impl.JPAQueryFactory; +import jakarta.persistence.EntityManager; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Pageable; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Repository; +import org.springframework.web.multipart.MultipartFile; + +import java.util.List; + +import static capstone.facefriend.member.exception.member.MemberExceptionType.NOT_FOUND; +import static capstone.facefriend.resume.domain.QResume.resume; +import static capstone.facefriend.resume.exception.ResumeExceptionType.*; +import static capstone.facefriend.resume.exception.ResumeExceptionType.NO_RESUME; + +@Repository +@RequiredArgsConstructor +public class ResumeRepositoryImpl implements ResumeRepositoryCustom { + + private final EntityManager em; + private final JPAQueryFactory queryFactory; + + private final EntityManager em; + private final JPAQueryFactory queryFactory; + + private final MemberRepository memberRepository; + private final ResumeRepository resumeRepository; + private final BucketService bucketService; + + private static final List GOOD_COMBI_IN_CASE_0 = List.of(2,4); // 화 + private static final List GOOD_COMBI_IN_CASE_1 = List.of(2,4); // 수 + private static final List GOOD_COMBI_IN_CASE_2 = List.of(0,1); // 목 + private static final List GOOD_COMBI_IN_CASE_3 = List.of(1,4); // 금 + private static final List GOOD_COMBI_IN_CASE_4 = List.of(0,3); // 토 + + private static final List BAD_COMBI_IN_CASE_0 = List.of(1,3); // 화 + private static final List BAD_COMBI_IN_CASE_1 = List.of(0,4); // 수 + private static final List BAD_COMBI_IN_CASE_2 = List.of(3,4); // 목 + private static final List BAD_COMBI_IN_CASE_3 = List.of(0,2); // 금 + private static final List BAD_COMBI_IN_CASE_4 = List.of(1,2); // 토 + + + private Member findMemberById(Long memberId) { + return memberRepository.findById(memberId) + .orElseThrow(() -> new MemberException(NOT_FOUND)); + } + + private Resume findResumeByMember(Member member) { + return resumeRepository.findResumeByMember(member) + .orElseThrow(() -> new ResumeException(NO_RESUME)); + } + + /** 특정 페이지 **/ + public ResumeResponse postResume() { + + } + + public ResumeResponse getResume(Long resumeId) { + + } + + public ResumeResponse putResume(Long resumeId) { + + } + + public ResumeResponse deleteResume(Long resumeId) { + + } + + /** 홈 페이지 **/ + // 홈 페이지에서 궁합 좋은 관상 조회하는 동적 쿼리 (20개) + public List getHomeResumesByGoodCombi(Long memberId, Pageable pageable) { + Member me = findMemberById(memberId); + Integer faceShapeIdNum = me.getAnalysisInfo().getFaceShapeIdNum(); + + BooleanBuilder builder = new BooleanBuilder(); + switch (faceShapeIdNum) { + case 0: // 화 + builder.and(resume.member.analysisInfo.faceShapeIdNum.in(GOOD_COMBI_IN_CASE_0)); + break; + case 1: // 수 + builder.and(resume.member.analysisInfo.faceShapeIdNum.in(GOOD_COMBI_IN_CASE_1)); + break; + case 2: // 목 + builder.and(resume.member.analysisInfo.faceShapeIdNum.in(GOOD_COMBI_IN_CASE_2)); + break; + case 3: // 금 + builder.and(resume.member.analysisInfo.faceShapeIdNum.in(GOOD_COMBI_IN_CASE_3)); + break; + case 4: // 토 + builder.and(resume.member.analysisInfo.faceShapeIdNum.in(GOOD_COMBI_IN_CASE_4)); + break; + } + + List results = queryFactory + .select(Projections.bean(ResumeHomeDetailResponse.class, + resume.id, + resume.member.faceInfo.generatedS3url + )) + .from(resume) + .leftJoin(resume.member, QMember.member) // join + .where(builder) + .orderBy(resume.id.desc()) + .offset(pageable.getOffset()) + .limit(pageable.getPageSize()) + .fetch(); + + + } + + // 홈페이지에서 궁합 나쁜 관상 조회하는 동적 쿼리 (20개) + public List getHomeResumesByBadCombi(Long memberId, Pageable pageable) { + Member me = findMemberById(memberId); + Integer faceShapeIdNum = me.getAnalysisInfo().getFaceShapeIdNum(); + + BooleanBuilder builder = new BooleanBuilder(); + switch (faceShapeIdNum) { + case 0: // 화 + builder.and(resume.member.analysisInfo.faceShapeIdNum.in(BAD_COMBI_IN_CASE_0)); + break; + case 1: // 수 + builder.and(resume.member.analysisInfo.faceShapeIdNum.in(BAD_COMBI_IN_CASE_1)); + break; + case 2: // 목 + builder.and(resume.member.analysisInfo.faceShapeIdNum.in(BAD_COMBI_IN_CASE_2)); + break; + case 3: // 금 + builder.and(resume.member.analysisInfo.faceShapeIdNum.in(BAD_COMBI_IN_CASE_3)); + break; + case 4: // 토 + builder.and(resume.member.analysisInfo.faceShapeIdNum.in(BAD_COMBI_IN_CASE_4)); + break; + } + + return queryFactory + .select(Projections.bean(ResumeHomeDetailResponse.class, + resume.id, + resume.member.faceInfo.generatedS3url + )) + .from(resume) + .leftJoin(resume.member, QMember.member) // join + .where(builder) + .orderBy(resume.id.desc()) + .offset(0) + .limit(10) + .fetch(); + } +} + + // 홈 페이지에서 카테고리별 조회하는 동적 쿼리 (10개) + public List getHomeResumesByCategory(String category, Pageable pageable) { + return queryFactory + .select(Projections.bean(ResumeHomeDetailResponse.class, + resume.id, + resume.member.faceInfo.generatedS3url + )) + .from(resume) + .leftJoin(resume.member, QMember.member) // join + .where(resume.category.eq(Resume.Category.valueOf(category))) + .orderBy(resume.id.desc()) + .offset(0) + .limit(10) + .fetch(); + } + + + /** 디테일 페이지 **/ + // 디테일 페이지에서 궁합 좋은 관상 조회하는 동적 쿼리 (20개) + public List getDetailResumesByGoodCombi(Long memberId, Pageable pageable) { + Member member = findMemberById(memberId); + Integer faceShapeIdNum = member.getAnalysisInfo().getFaceShapeIdNum(); + + BooleanBuilder builder = new BooleanBuilder(); + switch (faceShapeIdNum) { + case 0: // 화 + builder.and(resume.member.analysisInfo.faceShapeIdNum.in(GOOD_COMBI_IN_CASE_0)); + break; + case 1: // 수 + builder.and(resume.member.analysisInfo.faceShapeIdNum.in(GOOD_COMBI_IN_CASE_1)); + break; + case 2: // 목 + builder.and(resume.member.analysisInfo.faceShapeIdNum.in(GOOD_COMBI_IN_CASE_2)); + break; + case 3: // 금 + builder.and(resume.member.analysisInfo.faceShapeIdNum.in(GOOD_COMBI_IN_CASE_3)); + break; + case 4: // 토 + builder.and(resume.member.analysisInfo.faceShapeIdNum.in(GOOD_COMBI_IN_CASE_4)); + break; + } + + return queryFactory + .select(Projections.bean(ResumeHomeDetailResponse.class, + resume.id, + resume.member.faceInfo.generatedS3url + )) + .from(resume) + .leftJoin(resume.member, QMember.member) // join + .where(builder) + .orderBy(resume.id.desc()) + .offset(0) + .limit(20) + .fetch(); + } + + // 디테일 페이지에서 궁합 나쁜 관상 조회하는 동적 쿼리 (20개) + public List getDetailResumesByBadCombi(Long memberId, Pageable pageable) { + Member member = findMemberById(memberId); + Integer faceShapeIdNum = member.getAnalysisInfo().getFaceShapeIdNum(); + + BooleanBuilder builder = new BooleanBuilder(); + switch (faceShapeIdNum) { + case 0: // 화 + builder.and(resume.member.analysisInfo.faceShapeIdNum.in(BAD_COMBI_IN_CASE_0)); + break; + case 1: // 수 + builder.and(resume.member.analysisInfo.faceShapeIdNum.in(BAD_COMBI_IN_CASE_1)); + break; + case 2: // 목 + builder.and(resume.member.analysisInfo.faceShapeIdNum.in(BAD_COMBI_IN_CASE_2)); + break; + case 3: // 금 + builder.and(resume.member.analysisInfo.faceShapeIdNum.in(BAD_COMBI_IN_CASE_3)); + break; + case 4: // 토 + builder.and(resume.member.analysisInfo.faceShapeIdNum.in(BAD_COMBI_IN_CASE_4)); + break; + } + + return queryFactory + .select(Projections.bean(ResumeHomeDetailResponse.class, + resume.id, + resume.member.faceInfo.generatedS3url + )) + .from(resume) + .leftJoin(resume.member, QMember.member) // join + .where(builder) + .orderBy(resume.id.desc()) + .offset(0) + .limit(20) + .fetch(); + } + + // 디테일 페이지에서 카테고리별 동적쿼리 (20개) + public List getDetailResumesByCategory(String category, Pageable pageable) { + return queryFactory.select(Projections.bean(ResumeHomeDetailResponse.class, + resume.id, + resume.member.faceInfo.generatedS3url + )) + .from(resume) + .leftJoin(resume.member, QMember.member) // join + .where(resume.category.eq(Resume.Category.valueOf(category))) + .orderBy(resume.id.desc()) + .offset(0) + .limit(20) + .fetch(); + } +} diff --git a/src/main/java/capstone/facefriend/resume/domain/dto/ResumeHomeDetailResponse.java b/src/main/java/capstone/facefriend/resume/domain/dto/ResumeHomeDetailResponse.java new file mode 100644 index 0000000000..fc656ee352 --- /dev/null +++ b/src/main/java/capstone/facefriend/resume/domain/dto/ResumeHomeDetailResponse.java @@ -0,0 +1,7 @@ +package capstone.facefriend.resume.domain.dto; + +public record ResumeHomeDetailResponse( + Long resumeId, + String thumbnailS3url +){ +} diff --git a/src/main/java/capstone/facefriend/resume/domain/dto/ResumeRequest.java b/src/main/java/capstone/facefriend/resume/domain/dto/ResumeRequest.java new file mode 100644 index 0000000000..abab0582a1 --- /dev/null +++ b/src/main/java/capstone/facefriend/resume/domain/dto/ResumeRequest.java @@ -0,0 +1,8 @@ +package capstone.facefriend.resume.domain.dto; + +public record ResumeRequest( + Long resumeId, + String category, + String content +) { +} diff --git a/src/main/java/capstone/facefriend/resume/domain/dto/ResumeResponse.java b/src/main/java/capstone/facefriend/resume/domain/dto/ResumeResponse.java new file mode 100644 index 0000000000..c841ae39f1 --- /dev/null +++ b/src/main/java/capstone/facefriend/resume/domain/dto/ResumeResponse.java @@ -0,0 +1,17 @@ +package capstone.facefriend.resume.domain.dto; + +import capstone.facefriend.member.domain.analysisInfo.AnalysisInfo; +import capstone.facefriend.member.domain.basicInfo.BasicInfo; +import capstone.facefriend.member.domain.faceInfo.FaceInfo; + +import static capstone.facefriend.resume.domain.Resume.Category; + +public record ResumeResponse( + Long resumeId, + BasicInfo basicInfo, + FaceInfo faceInfo, + AnalysisInfo analysisInfo, + Category category, + String content +) { +} diff --git a/src/main/java/capstone/facefriend/resume/exception/ResumeException.java b/src/main/java/capstone/facefriend/resume/exception/ResumeException.java new file mode 100644 index 0000000000..bead02b533 --- /dev/null +++ b/src/main/java/capstone/facefriend/resume/exception/ResumeException.java @@ -0,0 +1,12 @@ +package capstone.facefriend.resume.exception; + +import capstone.facefriend.common.exception.BaseException; +import capstone.facefriend.common.exception.ExceptionType; + +public class ResumeException extends BaseException { + + public ResumeException(ExceptionType exceptionType) { + super(exceptionType); + } +} + diff --git a/src/main/java/capstone/facefriend/resume/exception/ResumeExceptionType.java b/src/main/java/capstone/facefriend/resume/exception/ResumeExceptionType.java new file mode 100644 index 0000000000..65a35f7f71 --- /dev/null +++ b/src/main/java/capstone/facefriend/resume/exception/ResumeExceptionType.java @@ -0,0 +1,37 @@ +package capstone.facefriend.resume.exception; + +import capstone.facefriend.common.exception.ExceptionType; +import capstone.facefriend.common.exception.Status; + +public enum ResumeExceptionType implements ExceptionType { + + NO_RESUME(Status.NOT_FOUND, 7001, "자기소개서가 없습니다!."), + ALREADY_HAVE_RESUME(Status.BAD_REQUEST, 7002, "자기소개서는 1인당 1개만 생성할 수 있습니다!") + ; + + private final Status status; + private final int exceptionCode; + private final String message; + + ResumeExceptionType(Status status, int exceptionCode, String message) { + this.status = status; + this.exceptionCode = exceptionCode; + this.message = message; + } + + @Override + public Status status() { + return status; + } + + @Override + public int exceptionCode() { + return exceptionCode; + } + + @Override + public String message() { + return message; + } +} + diff --git a/src/main/java/capstone/facefriend/resume/service/ResumeService.java b/src/main/java/capstone/facefriend/resume/service/ResumeService.java new file mode 100644 index 0000000000..a273d5030b --- /dev/null +++ b/src/main/java/capstone/facefriend/resume/service/ResumeService.java @@ -0,0 +1,125 @@ +package capstone.facefriend.resume.service; + + +import capstone.facefriend.auth.controller.support.AuthMember; +import capstone.facefriend.bucket.BucketService; +import capstone.facefriend.member.domain.member.Member; +import capstone.facefriend.member.domain.member.MemberRepository; +import capstone.facefriend.member.exception.member.MemberException; +import capstone.facefriend.resume.domain.Resume; +import capstone.facefriend.resume.domain.ResumeRepository; +import capstone.facefriend.resume.domain.dto.ResumeHomeDetailResponse; +import capstone.facefriend.resume.domain.dto.ResumeRequest; +import capstone.facefriend.resume.domain.dto.ResumeResponse; +import capstone.facefriend.resume.exception.ResumeException; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; + +import java.io.IOException; +import java.util.List; + +import static capstone.facefriend.member.exception.member.MemberExceptionType.NOT_FOUND; +import static capstone.facefriend.resume.domain.Resume.*; +import static capstone.facefriend.resume.exception.ResumeExceptionType.ALREADY_HAVE_RESUME; +import static capstone.facefriend.resume.exception.ResumeExceptionType.NO_RESUME; + +@Service +@Slf4j +@RequiredArgsConstructor +public class ResumeService { + + private final ResumeRepository resumeRepository; + private final MemberRepository memberRepository; + private final BucketService bucketService; + + /** 특정 페이지 **/ + // 나 + public ResumeResponse postResume(Long memberId, List images, ResumeRequest request) throws IOException { + + Member member = findMemberById(memberId); // 본인 + if(!resumeRepository.findResumeByMember(member).isPresent()) { + throw new ResumeException(ALREADY_HAVE_RESUME); + } + + List resumeImagesS3url = bucketService.uploadResumeImages(images, memberId); + Resume resume = builder() + .member(member) + .resumeImageS3urls(resumeImagesS3url) + .category(Category.valueOf(request.category())) + .content(request.content()) + .build(); + resumeRepository.save(resume); + + return new ResumeResponse( + resume.getId(), + resume.getMember().getBasicInfo(), + resume.getMember().getFaceInfo(), + resume.getMember().getAnalysisInfo(), + resume.getCategory(), + resume.getContent() + ); + } + + // 나 또는 상대 + public ResumeResponse getResume( + Long resumeId + ) { + resumeRepository.findResumeById(resumeId) + .orElseThrow(() -> new ResumeException(NO_RESUME)); + } + + // 나 + public ResumeResponse putResume(Long resumeId) { + + } + + // 나 + public ResumeResponse deleteResume(Long resumeId) { + + } + + /** 홈 페이지 **/ + // 홈 페이지에서 궁합 좋은 관상 조회하는 동적 쿼리 (20개) + public List getHomeResumesByGoodCombi(Long memberId) { + + } + + // 홈페이지에서 궁합 나쁜 관상 조회하는 동적 쿼리 (20개) + public List getHomeResumesByBadCombi(Long memberId) { + + } + + // 홈 페이지에서 카테고리별 조회하는 동적 쿼리 (10개) + public List getHomeResumesByCategory(String category) { + + } + + + /** 디테일 페이지 **/ + // 디테일 페이지에서 궁합 좋은 관상 조회하는 동적 쿼리 (20개) + public List getDetailResumesByGoodCombi(Long memberId) { + + } + + // 디테일 페이지에서 궁합 나쁜 관상 조회하는 동적 쿼리 (20개) + public List getDetailResumesByBadCombi(Long memberId) { + + } + + // 디테일 페이지에서 카테고리별 동적쿼리 (20개) + public List getDetailResumesByCategory(String category) { + + } + + private Member findMemberById(Long memberId) { + return memberRepository.findById(memberId) + .orElseThrow(() -> new MemberException(NOT_FOUND)); + } + + private Resume findResumeByMember(Member member) { + return resumeRepository.findResumeByMember(member) + .orElseThrow(() -> new ResumeException(NO_RESUME)); + } +} diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml new file mode 100644 index 0000000000..83e9f7b7e7 --- /dev/null +++ b/src/main/resources/application-prod.yml @@ -0,0 +1,77 @@ +spring: + servlet: + multipart: + max-file-size: ENC(QEdqgrkc1SQ1MQiXhyAgxw==) + max-request-size: ENC(LPYSKFia9XgqXJ3LGkU7Hw==) + jpa: + properties: + hibernate: + format_sql: ENC(/k2lAOOiToZinXyjPs043A==) + dialect: ENC(hp39xonPsDFfQh0uP+QagZp+eTRY3ZAbQwHKlZn8sF4qdgSFLFfy/S3t7JZiU1qp) + hibernate: + ddl-auto: ENC(aYCGcw68x/rbuYvEdL4Lhg==) + database: ENC(lqzJGxyaruG3vO12BiGSnjFhVXtQ/+Fo) + datasource: + url: ENC(wV42lv7kzXkiXlnYQ1a2bG1dONwCCIv2bBMaPQ4lhYAgukbfTQ7bTfWnPJTLkYQ2GabqhbvF04k=) + driver-class-name: ENC(iqVYNwUdkSnuRBOTVhE4/vFEFLVHrzRqgat0OWx6wsA=) + username: ENC(JMdQNyqE900MZFuuxX+EqHGrFzc8wbpG) + password: ENC(boj14VTRgt8U1VxDmizlgW6H+4OgUKnm) + data: + redis: + host: ENC(m/yqU98jpYwgO4T0cn+cbj84vU3Df5LB) + port: ENC(Xmvsj7NQ241FjmCVl+CoJA==) + cloud: + aws: + credentials: + accessKey: ENC(AKIA4PECHEV4U7C4UN6X) + secretKey: ENC(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) + s3: + bucket: ENC(SEJS5JtGMpaGfNbv0bo5/Z6lBlWUbjagZvXHZkCEVI8=) + default-profile: ENC(86nUxZdzl1eLO73dgWZ/0nQ97ufNG4wHH+gAb7YXnsE/k8bx7w/Kxto9rJbgTkl/9sD39B/59r9pXlDyxh8/rxwIyWoY762DHdtI4vALuUMkoIOXy6bphDj7/7Zj+4mg) + origin-postfix: ENC(p0xrguoxJfBD/ni12bsBCg==) + generated-postfix: ENC(Puyqdr1silILjwFaVgy/6TWlyttYD85u) + resume-postfix: ENC(Dg6Zti3FGjEBEHuX/YSqI0Cp6P/LFSLY) + region: + static: ENC(hcMa/BJepQBT0E9O7EiVpXd9V4raCcNe) + auto: ENC(XRRrHE8GwP+ZEKyYkm+TBg==) + stack: + auto: ENC(7qYWPZ8gY4UWFUCFMe9IBQ==) + mail: + host: ENC(jZYnjrEyHZZ4o8h8LZidGkB2Xhgm/Vit) + port: ENC(JuFjkiiT6DHuIaayqED9Ig==) + username: ENC(FsUEuNq53OtzT7AVF6ljEkda1uM+UzU3DIoKwd8odsc=) + password: ENC(9VcbNyNuTcOtbMjVV5chkLoAFVYy+BL+0x6OyJ6Rb9A=) + properties: + mail: + smtp: + auth: ENC(qP6dP401owtpbmLO9ZLsqQ==) + starttls: + enable: ENC(2/iPAAN2gOI7acxeL/Ae4w==) + required: ENC(ylH6cx5TFINzE+Iaejk05g==) + connectiontimeout: ENC(5VmkTIOsxDFGqFYJxpeRxQ==) + timeout: ENC(1vRJaeCxUbBooMhkNGYAgA==) + writetimeout: ENC(HgCUlmd2sIuCbfMtnMIDiw==) + auth-code-expiration-millis: ENC(TiTMV9IAw23s+lb3rg5cEg==) + + +logging.level: + org.hibernate.type: ENC(zpXUtRSonoW/8iNULYl71A==) + + +jwt: + header: ENC(um8d5ji3oU1r1B+F1pK/hfIS3V0D9JmH) + secret: ENC(KhoKRnYvfMbkW6KOxF2957PgwNBHw2xk4XvdogWgyp6gKr/nkQPNnWyQeOT41HBnDmwemvypAzA6Wa1Lf8he/8N89Tbogx3BbHafuAlY928=) + + +oauth2: + provider: + google: + id: ENC(Rxxd9KP4koB4a3/M1NJVn0VgVkMounxvLanizrDFHPYC4fpBtXQICFnuNHUaKXPkZc2qiRelcHX9lfMucQi6Zv+J/azrksfjReRIPFIGBK42qyAqFHL0iA==) + secret: ENC(yiPxkw/nRQr+aYBGy8/MO8wO/pEycXYqucC5cSPJ/qV7oaD1wJbYeqJfbB02CFXi) + token-url: ENC(q8uAMwvhAPIGJCiXMXX1j9YSitxh5mIUFKUbvln8AFxanfCHUH3OI4/q35+oTxOr) + info-url: ENC(i4B4j83Tp6Sg0bmIb5PXACW04oET9g0PMNZ0Gi2x7G4PQWpLcHWWFQn0h9j0sown0zSQhjs6KCg=) + login-url: ENC(6cISkrFhAk1pqC2LNJwUfOjv83B85WVRnxH5cXRhsVGhNjpwB9aAmApxHNUE5r+YTZOqOYOnYSo=) + scope: ENC(CCQMgZpF+j4QkzQtm/o8Xb+iSJ5E5YDx) +flask: + generate-url: ENC(OzXrY2PX8uaoonLpKTWI8qnGJR3tFH7rfC5tspp6RYRNq/75DfOj++64bJta2Cf+2iC5wFzMJHo=) + analyze-url: ENC(9D6R6D2eJFmhgwagCtzSRVmz+HLEq6VD8C1e3bBe92aTL05YhlZIAjLz4DB9JA09) \ No newline at end of file diff --git a/src/test/java/capstone/facefriend/FacefriendApplicationTests.java b/src/test/java/capstone/facefriend/FacefriendApplicationTests.java index 5233b8ef1a..ec4ffc1519 100644 --- a/src/test/java/capstone/facefriend/FacefriendApplicationTests.java +++ b/src/test/java/capstone/facefriend/FacefriendApplicationTests.java @@ -3,11 +3,27 @@ import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; +import java.util.ArrayList; +import java.util.List; + @SpringBootTest class FacefriendApplicationTests { @Test void contextLoads() { + List int1 = new ArrayList<>(); + int1.add(1); + int1.add(2); + + List int2 = new ArrayList<>(); + int2.add(3); + int2.add(4); + int2.add(5); + + int1.addAll(int2); + + System.out.println(int2); + } } From 9b580ed4a42c9113119f66b9160a47881e875556 Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Thu, 2 May 2024 22:34:53 +0900 Subject: [PATCH 102/265] =?UTF-8?q?chore:=20ci=20=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/be-docker-buildx-ci.yml | 35 +++++++++ .gitignore | 3 - Dockerfile | 26 +++++++ build.gradle | 3 + .../facefriend/config/JasyptConfig.java | 31 ++++++++ src/main/resources/application-prod.yml | 77 +++++++++++++++++++ src/main/resources/application.yml | 4 + 7 files changed, 176 insertions(+), 3 deletions(-) create mode 100644 .github/workflows/be-docker-buildx-ci.yml create mode 100644 Dockerfile create mode 100644 src/main/java/capstone/facefriend/config/JasyptConfig.java create mode 100644 src/main/resources/application-prod.yml create mode 100644 src/main/resources/application.yml diff --git a/.github/workflows/be-docker-buildx-ci.yml b/.github/workflows/be-docker-buildx-ci.yml new file mode 100644 index 0000000000..a51bf2ce99 --- /dev/null +++ b/.github/workflows/be-docker-buildx-ci.yml @@ -0,0 +1,35 @@ +name: be-docker-buildx-ci + +on: + push: + branches: + - 'be' + +jobs: + docker: + runs-on: ubuntu-latest + steps: + - + name: Checkout + uses: actions/checkout@v3 + - + name: Set up QEMU + uses: docker/setup-qemu-action@v3 + - + name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + - + name: Login to Docker Hub + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKER_HUB_USERNAME }} + password: ${{ secrets.DOCKER_HUB_SECRET_TOKEN }} + - + name: Build and push + uses: docker/build-push-action@v5 + with: + push: true + tags: cjkimhello97/facefriend-be:latest + platforms: linux/amd64 + build-args: | + "ENCRYPT_KEY=${{ secrets.ENCRYPT_KEY }}" \ No newline at end of file diff --git a/.gitignore b/.gitignore index 64bc76fe20..fd5ec37bee 100644 --- a/.gitignore +++ b/.gitignore @@ -56,9 +56,6 @@ cmake-build-*/ # IntelliJ out/ -# Application.yml -application.yml - # mpeltonen/sbt-idea plugin .idea_modules/ diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000000..986cb047dc --- /dev/null +++ b/Dockerfile @@ -0,0 +1,26 @@ +FROM bellsoft/liberica-openjdk-debian:17 AS builder +ENV WORK /workspace +COPY build.gradle settings.gradle gradlew $WORK/ +COPY gradle $WORK/gradle +COPY ./src $WORK/src + +WORKDIR $WORK + +RUN apt-get update && apt-get install -y dos2unix && dos2unix gradlew +RUN chmod +x gradlew +RUN ./gradlew clean bootJar --parallel --no-daemon + +FROM bellsoft/liberica-openjdk-debian:17 + +ARG JAR_FILE=workspace/build/libs/*.jar +COPY --from=builder $JAR_FILE app.jar + +ARG ENCRYPT_KEY + +ENTRYPOINT ["java", \ + "-Xms1400m", \ + "-Xmx1400m", \ + "-jar", \ + "-Dspring.profiles.active=prod", \ + "--ENCRYPT_KEY=$ENCRYPT_KEY", \ + "/app.jar"] \ No newline at end of file diff --git a/build.gradle b/build.gradle index 2a37e63b67..c92fbe932f 100644 --- a/build.gradle +++ b/build.gradle @@ -50,6 +50,9 @@ dependencies { // AWS S3 implementation 'org.springframework.cloud:spring-cloud-starter-aws:2.2.6.RELEASE' + + // jasypt + implementation 'com.github.ulisesbocchio:jasypt-spring-boot-starter:3.0.5' } tasks.named('test') { diff --git a/src/main/java/capstone/facefriend/config/JasyptConfig.java b/src/main/java/capstone/facefriend/config/JasyptConfig.java new file mode 100644 index 0000000000..b6d75f11a2 --- /dev/null +++ b/src/main/java/capstone/facefriend/config/JasyptConfig.java @@ -0,0 +1,31 @@ +package capstone.facefriend.config; + +import org.jasypt.encryption.StringEncryptor; +import org.jasypt.encryption.pbe.PooledPBEStringEncryptor; +import org.jasypt.encryption.pbe.config.SimpleStringPBEConfig; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class JasyptConfig { + + @Value("${jasypt.encryptor.password}") + private String password; + + @Bean("jasyptStringEncryptor") + public StringEncryptor stringEncryptor() { + PooledPBEStringEncryptor encryptor = new PooledPBEStringEncryptor(); + SimpleStringPBEConfig config = new SimpleStringPBEConfig(); + config.setPassword(password); + config.setAlgorithm("PBEWithMD5AndDES"); + config.setKeyObtentionIterations("1000"); + config.setPoolSize("1"); + config.setProviderName("SunJCE"); + config.setSaltGeneratorClassName("org.jasypt.salt.RandomSaltGenerator"); + config.setIvGeneratorClassName("org.jasypt.iv.NoIvGenerator"); + config.setStringOutputType("base64"); + encryptor.setConfig(config); + return encryptor; + } +} \ No newline at end of file diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml new file mode 100644 index 0000000000..83e9f7b7e7 --- /dev/null +++ b/src/main/resources/application-prod.yml @@ -0,0 +1,77 @@ +spring: + servlet: + multipart: + max-file-size: ENC(QEdqgrkc1SQ1MQiXhyAgxw==) + max-request-size: ENC(LPYSKFia9XgqXJ3LGkU7Hw==) + jpa: + properties: + hibernate: + format_sql: ENC(/k2lAOOiToZinXyjPs043A==) + dialect: ENC(hp39xonPsDFfQh0uP+QagZp+eTRY3ZAbQwHKlZn8sF4qdgSFLFfy/S3t7JZiU1qp) + hibernate: + ddl-auto: ENC(aYCGcw68x/rbuYvEdL4Lhg==) + database: ENC(lqzJGxyaruG3vO12BiGSnjFhVXtQ/+Fo) + datasource: + url: ENC(wV42lv7kzXkiXlnYQ1a2bG1dONwCCIv2bBMaPQ4lhYAgukbfTQ7bTfWnPJTLkYQ2GabqhbvF04k=) + driver-class-name: ENC(iqVYNwUdkSnuRBOTVhE4/vFEFLVHrzRqgat0OWx6wsA=) + username: ENC(JMdQNyqE900MZFuuxX+EqHGrFzc8wbpG) + password: ENC(boj14VTRgt8U1VxDmizlgW6H+4OgUKnm) + data: + redis: + host: ENC(m/yqU98jpYwgO4T0cn+cbj84vU3Df5LB) + port: ENC(Xmvsj7NQ241FjmCVl+CoJA==) + cloud: + aws: + credentials: + accessKey: ENC(AKIA4PECHEV4U7C4UN6X) + secretKey: ENC(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) + s3: + bucket: ENC(SEJS5JtGMpaGfNbv0bo5/Z6lBlWUbjagZvXHZkCEVI8=) + default-profile: ENC(86nUxZdzl1eLO73dgWZ/0nQ97ufNG4wHH+gAb7YXnsE/k8bx7w/Kxto9rJbgTkl/9sD39B/59r9pXlDyxh8/rxwIyWoY762DHdtI4vALuUMkoIOXy6bphDj7/7Zj+4mg) + origin-postfix: ENC(p0xrguoxJfBD/ni12bsBCg==) + generated-postfix: ENC(Puyqdr1silILjwFaVgy/6TWlyttYD85u) + resume-postfix: ENC(Dg6Zti3FGjEBEHuX/YSqI0Cp6P/LFSLY) + region: + static: ENC(hcMa/BJepQBT0E9O7EiVpXd9V4raCcNe) + auto: ENC(XRRrHE8GwP+ZEKyYkm+TBg==) + stack: + auto: ENC(7qYWPZ8gY4UWFUCFMe9IBQ==) + mail: + host: ENC(jZYnjrEyHZZ4o8h8LZidGkB2Xhgm/Vit) + port: ENC(JuFjkiiT6DHuIaayqED9Ig==) + username: ENC(FsUEuNq53OtzT7AVF6ljEkda1uM+UzU3DIoKwd8odsc=) + password: ENC(9VcbNyNuTcOtbMjVV5chkLoAFVYy+BL+0x6OyJ6Rb9A=) + properties: + mail: + smtp: + auth: ENC(qP6dP401owtpbmLO9ZLsqQ==) + starttls: + enable: ENC(2/iPAAN2gOI7acxeL/Ae4w==) + required: ENC(ylH6cx5TFINzE+Iaejk05g==) + connectiontimeout: ENC(5VmkTIOsxDFGqFYJxpeRxQ==) + timeout: ENC(1vRJaeCxUbBooMhkNGYAgA==) + writetimeout: ENC(HgCUlmd2sIuCbfMtnMIDiw==) + auth-code-expiration-millis: ENC(TiTMV9IAw23s+lb3rg5cEg==) + + +logging.level: + org.hibernate.type: ENC(zpXUtRSonoW/8iNULYl71A==) + + +jwt: + header: ENC(um8d5ji3oU1r1B+F1pK/hfIS3V0D9JmH) + secret: ENC(KhoKRnYvfMbkW6KOxF2957PgwNBHw2xk4XvdogWgyp6gKr/nkQPNnWyQeOT41HBnDmwemvypAzA6Wa1Lf8he/8N89Tbogx3BbHafuAlY928=) + + +oauth2: + provider: + google: + id: ENC(Rxxd9KP4koB4a3/M1NJVn0VgVkMounxvLanizrDFHPYC4fpBtXQICFnuNHUaKXPkZc2qiRelcHX9lfMucQi6Zv+J/azrksfjReRIPFIGBK42qyAqFHL0iA==) + secret: ENC(yiPxkw/nRQr+aYBGy8/MO8wO/pEycXYqucC5cSPJ/qV7oaD1wJbYeqJfbB02CFXi) + token-url: ENC(q8uAMwvhAPIGJCiXMXX1j9YSitxh5mIUFKUbvln8AFxanfCHUH3OI4/q35+oTxOr) + info-url: ENC(i4B4j83Tp6Sg0bmIb5PXACW04oET9g0PMNZ0Gi2x7G4PQWpLcHWWFQn0h9j0sown0zSQhjs6KCg=) + login-url: ENC(6cISkrFhAk1pqC2LNJwUfOjv83B85WVRnxH5cXRhsVGhNjpwB9aAmApxHNUE5r+YTZOqOYOnYSo=) + scope: ENC(CCQMgZpF+j4QkzQtm/o8Xb+iSJ5E5YDx) +flask: + generate-url: ENC(OzXrY2PX8uaoonLpKTWI8qnGJR3tFH7rfC5tspp6RYRNq/75DfOj++64bJta2Cf+2iC5wFzMJHo=) + analyze-url: ENC(9D6R6D2eJFmhgwagCtzSRVmz+HLEq6VD8C1e3bBe92aTL05YhlZIAjLz4DB9JA09) \ No newline at end of file diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml new file mode 100644 index 0000000000..43a857c01b --- /dev/null +++ b/src/main/resources/application.yml @@ -0,0 +1,4 @@ +jasypt: + encryptor: + bean: jasyptStringEncryptor + password: ${ENCRYPT_KEY} \ No newline at end of file From d36706ce85aece1fc9f690d70cf91e34ef4aba7c Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Thu, 2 May 2024 22:38:43 +0900 Subject: [PATCH 103/265] test ci --- .../capstone/facefriend/auth/controller/AuthController.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/java/capstone/facefriend/auth/controller/AuthController.java b/src/main/java/capstone/facefriend/auth/controller/AuthController.java index 5902fd63c1..03e7424dc6 100644 --- a/src/main/java/capstone/facefriend/auth/controller/AuthController.java +++ b/src/main/java/capstone/facefriend/auth/controller/AuthController.java @@ -19,6 +19,11 @@ public class AuthController { private final AuthService authService; private final OAuthRequester oAuthRequester; + @GetMapping("/ping") + public String ping() { + return "pong"; + } + @GetMapping("/oauth/{provider}/login-uri") public ResponseEntity loginUri( @PathVariable String provider, From badd5cee1cd0fd2704a8754c948cb8b587d72bbc Mon Sep 17 00:00:00 2001 From: imjanghyeok Date: Fri, 3 May 2024 09:04:15 +0900 Subject: [PATCH 104/265] =?UTF-8?q?Chore:=20gitignore=20=EC=88=98=EC=A0=95?= =?UTF-8?q?=20=EB=B0=8F=20chat=20=ED=8C=A8=ED=82=A4=EC=A7=80=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 8 +- .../gradle-8.0-bin.zip.lck | 0 .../gradle-8.0-bin.zip.ok | 0 .../gradle-8.0/LICENSE | 420 ++++++++++++++++++ .../gradle-8.0/NOTICE | 21 + .../gradle-8.0/README | 11 + .../gradle-8.0/bin/gradle | 244 ++++++++++ .../gradle-8.0/bin/gradle.bat | 92 ++++ .../gradle-8.0/init.d/readme.txt | 1 + 9 files changed, 796 insertions(+), 1 deletion(-) create mode 100644 gradle/wrapper/dists/gradle-8.0-bin/ca5e32bp14vu59qr306oxotwh/gradle-8.0-bin.zip.lck create mode 100644 gradle/wrapper/dists/gradle-8.0-bin/ca5e32bp14vu59qr306oxotwh/gradle-8.0-bin.zip.ok create mode 100644 gradle/wrapper/dists/gradle-8.0-bin/ca5e32bp14vu59qr306oxotwh/gradle-8.0/LICENSE create mode 100644 gradle/wrapper/dists/gradle-8.0-bin/ca5e32bp14vu59qr306oxotwh/gradle-8.0/NOTICE create mode 100644 gradle/wrapper/dists/gradle-8.0-bin/ca5e32bp14vu59qr306oxotwh/gradle-8.0/README create mode 100644 gradle/wrapper/dists/gradle-8.0-bin/ca5e32bp14vu59qr306oxotwh/gradle-8.0/bin/gradle create mode 100644 gradle/wrapper/dists/gradle-8.0-bin/ca5e32bp14vu59qr306oxotwh/gradle-8.0/bin/gradle.bat create mode 100644 gradle/wrapper/dists/gradle-8.0-bin/ca5e32bp14vu59qr306oxotwh/gradle-8.0/init.d/readme.txt diff --git a/.gitignore b/.gitignore index fd5ec37bee..1a6145ee49 100644 --- a/.gitignore +++ b/.gitignore @@ -284,4 +284,10 @@ gradle-app.setting # Java heap dump *.hprof -# End of https://www.toptal.com/developers/gitignore/api/java,intellij,gradle,macos,windows,intellij+all \ No newline at end of file +# End of https://www.toptal.com/developers/gitignore/api/java,intellij,gradle,macos,windows,intellij+all + +gradle/caches +gradle/.tmp +gradle/daemon +gradle/jdks +gradle/native \ No newline at end of file diff --git a/gradle/wrapper/dists/gradle-8.0-bin/ca5e32bp14vu59qr306oxotwh/gradle-8.0-bin.zip.lck b/gradle/wrapper/dists/gradle-8.0-bin/ca5e32bp14vu59qr306oxotwh/gradle-8.0-bin.zip.lck new file mode 100644 index 0000000000..e69de29bb2 diff --git a/gradle/wrapper/dists/gradle-8.0-bin/ca5e32bp14vu59qr306oxotwh/gradle-8.0-bin.zip.ok b/gradle/wrapper/dists/gradle-8.0-bin/ca5e32bp14vu59qr306oxotwh/gradle-8.0-bin.zip.ok new file mode 100644 index 0000000000..e69de29bb2 diff --git a/gradle/wrapper/dists/gradle-8.0-bin/ca5e32bp14vu59qr306oxotwh/gradle-8.0/LICENSE b/gradle/wrapper/dists/gradle-8.0-bin/ca5e32bp14vu59qr306oxotwh/gradle-8.0/LICENSE new file mode 100644 index 0000000000..f013fd5ddb --- /dev/null +++ b/gradle/wrapper/dists/gradle-8.0-bin/ca5e32bp14vu59qr306oxotwh/gradle-8.0/LICENSE @@ -0,0 +1,420 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + +============================================================================== +Licenses for included components: + +------------------------------------------------------------------------------ +Eclipse Public License 1.0 +https://opensource.org/licenses/EPL-1.0 + +junit:junit +org.sonatype.aether:aether-api +org.sonatype.aether:aether-connector-wagon +org.sonatype.aether:aether-impl +org.sonatype.aether:aether-spi +org.sonatype.aether:aether-util + +------------------------------------------------------------------------------ +3-Clause BSD +https://opensource.org/licenses/BSD-3-Clause + +com.google.code.findbugs:jsr305 + +org.hamcrest:hamcrest-core +BSD License + +Copyright (c) 2000-2015 www.hamcrest.org +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this list of +conditions and the following disclaimer. Redistributions in binary form must reproduce +the above copyright notice, this list of conditions and the following disclaimer in +the documentation and/or other materials provided with the distribution. + +Neither the name of Hamcrest nor the names of its contributors may be used to endorse +or promote products derived from this software without specific prior written +permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT +SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY +WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + +com.esotericsoftware.kryo:kryo +com.esotericsoftware.minlog:minlog +Copyright (c) 2008-2018, Nathan Sweet All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. +Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. +Neither the name of Esoteric Software nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +org.ow2.asm:asm +org.ow2.asm:asm-analysis +org.ow2.asm:asm-commons +org.ow2.asm:asm-tree +org.ow2.asm:asm-util +ASM: a very small and fast Java bytecode manipulation framework + Copyright (c) 2000-2011 INRIA, France Telecom + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + THE POSSIBILITY OF SUCH DAMAGE. + +------------------------------------------------------------------------------ +MIT + +com.googlecode.plist:dd-plist +dd-plist - An open source library to parse and generate property lists +Copyright (C) 2016 Daniel Dreibrodt + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +org.bouncycastle:bcpg-jdk15on +org.bouncycastle:bcprov-jdk15on +Copyright (c) 2000 - 2019 The Legion of the Bouncy Castle Inc. (https://www.bouncycastle.org) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +org.slf4j:jcl-over-slf4j +org.slf4j:jul-to-slf4j +org.slf4j:log4j-over-slf4j +org.slf4j:slf4j-api + Copyright (c) 2004-2017 QOS.ch + All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +------------------------------------------------------------------------------ +CDDL +https://opensource.org/licenses/CDDL-1.0 + +com.sun.xml.bind:jaxb-impl + +------------------------------------------------------------------------------ +LGPL 2.1 +https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html + +org.samba.jcifs:jcifs + +org.jetbrains.intellij.deps:trove4j + +------------------------------------------------------------------------------ +License for the GNU Trove library included by the Kotlin embeddable compiler +------------------------------------------------------------------------------ +The source code for GNU Trove is licensed under the Lesser GNU Public License (LGPL). + + Copyright (c) 2001, Eric D. Friedman All Rights Reserved. This library is free software; you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without + even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + +Two classes (HashFunctions and PrimeFinder) included in Trove are licensed under the following terms: + + Copyright (c) 1999 CERN - European Organization for Nuclear Research. Permission to use, copy, modify, distribute and sell this software + and its documentation for any purpose is hereby granted without fee, provided that the above copyright notice appear in all copies and + that both that copyright notice and this permission notice appear in supporting documentation. CERN makes no representations about the + suitability of this software for any purpose. It is provided "as is" without expressed or implied warranty. + +The source code of modified GNU Trove library is available at + https://github.com/JetBrains/intellij-deps-trove4j (with trove4j_changes.txt describing the changes) + +------------------------------------------------------------------------------ +Eclipse Distribution License 1.0 +https://www.eclipse.org/org/documents/edl-v10.php + +org.eclipse.jgit:org.eclipse.jgit + +------------------------------------------------------------------------------ +BSD-style + +com.jcraft:jsch +com.jcraft:jzlib + +Copyright (c) 2000-2011 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +------------------------------------------------------------------------------ +Eclipse Public License 2.0 +https://www.eclipse.org/legal/epl-2.0/ + +org.junit.platform:junit-platform-launcher + +------------------------------------------------------------------------------ +Mozilla Public License 2.0 +https://www.mozilla.org/en-US/MPL/2.0/ + +org.mozilla:rhino diff --git a/gradle/wrapper/dists/gradle-8.0-bin/ca5e32bp14vu59qr306oxotwh/gradle-8.0/NOTICE b/gradle/wrapper/dists/gradle-8.0-bin/ca5e32bp14vu59qr306oxotwh/gradle-8.0/NOTICE new file mode 100644 index 0000000000..00a36efcde --- /dev/null +++ b/gradle/wrapper/dists/gradle-8.0-bin/ca5e32bp14vu59qr306oxotwh/gradle-8.0/NOTICE @@ -0,0 +1,21 @@ +========================================================================= +== NOTICE file corresponding to the section 4 d of == +== the Apache License, Version 2.0, == +== in this case for the Gradle distribution. == +========================================================================= + +This product includes software developed by +The Apache Software Foundation (http://www.apache.org/). + +It includes the following other software: + +Groovy (http://groovy-lang.org) +SLF4J (http://www.slf4j.org) +JUnit (http://www.junit.org) +JCIFS (http://jcifs.samba.org) +HttpClient (https://hc.apache.org/httpcomponents-client-4.5.x/) + +For licenses, see the LICENSE file. + +If any software distributed with Gradle does not have an Apache 2 License, its license is explicitly listed in the +LICENSE file. diff --git a/gradle/wrapper/dists/gradle-8.0-bin/ca5e32bp14vu59qr306oxotwh/gradle-8.0/README b/gradle/wrapper/dists/gradle-8.0-bin/ca5e32bp14vu59qr306oxotwh/gradle-8.0/README new file mode 100644 index 0000000000..97d48bdc51 --- /dev/null +++ b/gradle/wrapper/dists/gradle-8.0-bin/ca5e32bp14vu59qr306oxotwh/gradle-8.0/README @@ -0,0 +1,11 @@ +Gradle is a build tool with a focus on build automation and support for multi-language development. If you are building, testing, publishing, and deploying software on any platform, Gradle offers a flexible model that can support the entire development lifecycle from compiling and packaging code to publishing web sites. Gradle has been designed to support build automation across multiple languages and platforms including Java, Scala, Android, C/C++, and Groovy, and is closely integrated with development tools and continuous integration servers including Eclipse, IntelliJ, and Jenkins. + +For more information about Gradle, please visit: https://gradle.org + +If you are using the "all" distribution, the User Manual is included in your distribution. + +If you are using the "bin" distribution, a copy of the User Manual is available on https://docs.gradle.org. + +Typing `gradle help` prints the command line help. + +Typing `gradle tasks` shows all the tasks of a Gradle build. diff --git a/gradle/wrapper/dists/gradle-8.0-bin/ca5e32bp14vu59qr306oxotwh/gradle-8.0/bin/gradle b/gradle/wrapper/dists/gradle-8.0-bin/ca5e32bp14vu59qr306oxotwh/gradle-8.0/bin/gradle new file mode 100644 index 0000000000..5e152d7195 --- /dev/null +++ b/gradle/wrapper/dists/gradle-8.0-bin/ca5e32bp14vu59qr306oxotwh/gradle-8.0/bin/gradle @@ -0,0 +1,244 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +APP_HOME=$( cd "${APP_HOME:-./}.." && pwd -P ) || exit + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/lib/gradle-launcher-8.0.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.launcher.GradleMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/gradle/wrapper/dists/gradle-8.0-bin/ca5e32bp14vu59qr306oxotwh/gradle-8.0/bin/gradle.bat b/gradle/wrapper/dists/gradle-8.0-bin/ca5e32bp14vu59qr306oxotwh/gradle-8.0/bin/gradle.bat new file mode 100644 index 0000000000..3d017f471e --- /dev/null +++ b/gradle/wrapper/dists/gradle-8.0-bin/ca5e32bp14vu59qr306oxotwh/gradle-8.0/bin/gradle.bat @@ -0,0 +1,92 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME%.. + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\lib\gradle-launcher-8.0.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.launcher.GradleMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/gradle/wrapper/dists/gradle-8.0-bin/ca5e32bp14vu59qr306oxotwh/gradle-8.0/init.d/readme.txt b/gradle/wrapper/dists/gradle-8.0-bin/ca5e32bp14vu59qr306oxotwh/gradle-8.0/init.d/readme.txt new file mode 100644 index 0000000000..d8e210f840 --- /dev/null +++ b/gradle/wrapper/dists/gradle-8.0-bin/ca5e32bp14vu59qr306oxotwh/gradle-8.0/init.d/readme.txt @@ -0,0 +1 @@ +You can add .gradle (e.g. test.gradle) init scripts to this directory. Each one is executed at the start of the build. From f814e3c41d509a01427c679b17b6837e4e1994bb Mon Sep 17 00:00:00 2001 From: imjanghyeok Date: Fri, 3 May 2024 09:17:05 +0900 Subject: [PATCH 105/265] =?UTF-8?q?Feat:=20ChatRoom=20=EB=8F=84=EB=A9=94?= =?UTF-8?q?=EC=9D=B8=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../facefriend/chat/domain/ChatRoom.java | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 src/main/java/capstone/facefriend/chat/domain/ChatRoom.java diff --git a/src/main/java/capstone/facefriend/chat/domain/ChatRoom.java b/src/main/java/capstone/facefriend/chat/domain/ChatRoom.java new file mode 100644 index 0000000000..e2daef07ed --- /dev/null +++ b/src/main/java/capstone/facefriend/chat/domain/ChatRoom.java @@ -0,0 +1,50 @@ +package capstone.facefriend.chat.domain; + +import capstone.facefriend.common.domain.BaseEntity; +import jakarta.persistence.*; +import lombok.*; +import lombok.extern.slf4j.Slf4j; +import org.hibernate.annotations.DynamicInsert; + +@Getter +@Setter +@Builder +@EqualsAndHashCode(of = "id", callSuper = false) +@AllArgsConstructor(access = AccessLevel.PRIVATE) +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@Entity +@Slf4j +@DynamicInsert +public class ChatRoom extends BaseEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Enumerated(EnumType.STRING) + @Column + private Status status = Status.set; + + @Column + private boolean isPublic; + + public enum Status { + set("Set"), + open("Open"), + + progress("Progres"), + close("Close"), + delete("Delete"); + private final String value; + + Status(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + } + + +} \ No newline at end of file From 673924aec086f207bd73deff520781f8f0349d1b Mon Sep 17 00:00:00 2001 From: imjanghyeok Date: Fri, 3 May 2024 09:17:42 +0900 Subject: [PATCH 106/265] =?UTF-8?q?Feat:=20ChatRoomMember=20=EB=8F=84?= =?UTF-8?q?=EB=A9=94=EC=9D=B8=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../chat/domain/ChatRoomMember.java | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 src/main/java/capstone/facefriend/chat/domain/ChatRoomMember.java diff --git a/src/main/java/capstone/facefriend/chat/domain/ChatRoomMember.java b/src/main/java/capstone/facefriend/chat/domain/ChatRoomMember.java new file mode 100644 index 0000000000..9762e0d698 --- /dev/null +++ b/src/main/java/capstone/facefriend/chat/domain/ChatRoomMember.java @@ -0,0 +1,65 @@ +package capstone.facefriend.chat.domain; + +import capstone.facefriend.common.domain.BaseEntity; +import capstone.facefriend.member.domain.Member; +import jakarta.persistence.*; +import lombok.*; +import lombok.extern.slf4j.Slf4j; +import org.hibernate.annotations.DynamicInsert; + +@Getter +@Setter +@Builder +@EqualsAndHashCode(of = "id", callSuper = false) +@AllArgsConstructor(access = AccessLevel.PRIVATE) +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@Entity +@Slf4j +@DynamicInsert +public class ChatRoomMember extends BaseEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @ManyToOne + @JoinColumn(name = "Room_ID") + private ChatRoom chatRoom; + + @ManyToOne + @JoinColumn(name = "SENDER_ID") + private Member sender; + + @ManyToOne + @JoinColumn(name = "RECEIVER_ID") + private Member receiver; + + @Column + private boolean isSenderExist; + + @Column + private boolean isReceiverExist; + + @Column + private boolean isSenderPublic; + + @Column + private boolean isReceiverPublic; + + public void setChatRoom(ChatRoom chatRoom) { + this.chatRoom = chatRoom; + } + public void setSender(Member Sender) { + this.sender = sender; + } + public void setReceiver(Member Sender) { + this.receiver = receiver; + } + + public boolean isSenderExist() {return this.isSenderExist == true;} + public boolean isReceiverExist() {return this.isReceiverExist == true;} + public boolean isSenderPublic() {return this.isSenderPublic == true;} + public boolean isReceiverPublic() {return this.isReceiverPublic == true;} + + +} \ No newline at end of file From e2ddee64f14111618204072f410ac492d435919e Mon Sep 17 00:00:00 2001 From: imjanghyeok Date: Fri, 3 May 2024 09:17:49 +0900 Subject: [PATCH 107/265] =?UTF-8?q?Feat:=20ChatMessage=20=EB=8F=84?= =?UTF-8?q?=EB=A9=94=EC=9D=B8=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../facefriend/chat/domain/ChatMessage.java | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 src/main/java/capstone/facefriend/chat/domain/ChatMessage.java diff --git a/src/main/java/capstone/facefriend/chat/domain/ChatMessage.java b/src/main/java/capstone/facefriend/chat/domain/ChatMessage.java new file mode 100644 index 0000000000..f01c4382ca --- /dev/null +++ b/src/main/java/capstone/facefriend/chat/domain/ChatMessage.java @@ -0,0 +1,51 @@ +package capstone.facefriend.chat.domain; + +import capstone.facefriend.common.domain.BaseEntity; +import capstone.facefriend.member.domain.Member; +import jakarta.persistence.*; +import lombok.*; +import lombok.extern.slf4j.Slf4j; +import org.hibernate.annotations.DynamicInsert; + +import java.time.LocalDateTime; + +@Getter +@Setter +@Builder +@EqualsAndHashCode(of = "id", callSuper = false) +@AllArgsConstructor(access = AccessLevel.PRIVATE) +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@Entity +@Slf4j +@DynamicInsert +public class ChatMessage extends BaseEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(nullable = false) + private String content; + + @Column(nullable = false) + private LocalDateTime sendTime; + + @Column + private boolean isRead; + + @ManyToOne + @JoinColumn(name = "SENDER_ID") + private Member sender; + + @ManyToOne + @JoinColumn(name = "ROOM_ID") + private ChatRoom chatRoom; + + public void setMember(Member sender) { + this.sender = sender; + } + + public void setChatRoom(ChatRoom ChatRoom) { + this.chatRoom = ChatRoom; + } +} \ No newline at end of file From 28c06a8e169ffdf67f411dd7b14349e8ef9f18fd Mon Sep 17 00:00:00 2001 From: imjanghyeok Date: Fri, 3 May 2024 09:20:55 +0900 Subject: [PATCH 108/265] =?UTF-8?q?Feat:=20build.gradle=20dependencies=20?= =?UTF-8?q?=EC=84=B8=EC=85=98=EC=97=90=20websocket=EC=9D=84=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/build.gradle b/build.gradle index c92fbe932f..099c062793 100644 --- a/build.gradle +++ b/build.gradle @@ -51,6 +51,12 @@ dependencies { // AWS S3 implementation 'org.springframework.cloud:spring-cloud-starter-aws:2.2.6.RELEASE' + // WebSocket + implementation 'org.springframework.boot:spring-boot-starter-websocket' + + // SockJs + implementation 'org.webjars:sockjs-client:1.5.1' + // jasypt implementation 'com.github.ulisesbocchio:jasypt-spring-boot-starter:3.0.5' } From cdc37f9b612de270aa9644e98b2efc4f7a1f555c Mon Sep 17 00:00:00 2001 From: imjanghyeok Date: Fri, 3 May 2024 09:40:32 +0900 Subject: [PATCH 109/265] =?UTF-8?q?Feat:=20WebSocket=20=EC=82=AC=EC=9A=A9?= =?UTF-8?q?=EC=9D=84=20=EC=9C=84=ED=95=9C=20=EC=84=A4=EC=A0=95=20=ED=8C=8C?= =?UTF-8?q?=EC=9D=BC=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../facefriend/chat/config/ChatConfig.java | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 src/main/java/capstone/facefriend/chat/config/ChatConfig.java diff --git a/src/main/java/capstone/facefriend/chat/config/ChatConfig.java b/src/main/java/capstone/facefriend/chat/config/ChatConfig.java new file mode 100644 index 0000000000..d54818d4ba --- /dev/null +++ b/src/main/java/capstone/facefriend/chat/config/ChatConfig.java @@ -0,0 +1,35 @@ +package capstone.facefriend.chat.config; + +import org.springframework.context.annotation.Configuration; +import org.springframework.messaging.simp.config.MessageBrokerRegistry; +import org.springframework.web.socket.config.annotation.EnableWebSocket; +import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker; +import org.springframework.web.socket.config.annotation.StompEndpointRegistry; +import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer; + +@Configuration +@EnableWebSocket +@EnableWebSocketMessageBroker +public class ChatConfig implements WebSocketMessageBrokerConfigurer { + + // sockJS Fallback을 이용해 노출할 endpoint 설정 + @Override + public void registerStompEndpoints(StompEndpointRegistry registry) { + // 웹소켓이 handshake를 하기 위해 연결하는 endpoint + registry.addEndpoint("/ws") + .setAllowedOriginPatterns("*"); +// .withSockJS(); + + } + + //메세지 브로커에 관한 설정 + @Override + public void configureMessageBroker(MessageBrokerRegistry registry) { + // 서버 -> 클라이언트로 발행하는 메세지에 대한 endpoint 설정 : 구독 + registry.enableSimpleBroker("/sub"); + + // 클라이언트->서버로 발행하는 메세지에 대한 endpoint 설정 : 구독에 대한 메세지 + registry.setApplicationDestinationPrefixes("/pub"); + } +} + From 1faaefd38f926d986b9fad9783a23ce173ea396f Mon Sep 17 00:00:00 2001 From: imjanghyeok Date: Fri, 3 May 2024 09:42:25 +0900 Subject: [PATCH 110/265] =?UTF-8?q?Feat:=20ChatRoom=EC=97=90=20=EB=8C=80?= =?UTF-8?q?=ED=95=9C=20Controller=20class=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../chat/controller/ChatRoomController.java | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 src/main/java/capstone/facefriend/chat/controller/ChatRoomController.java diff --git a/src/main/java/capstone/facefriend/chat/controller/ChatRoomController.java b/src/main/java/capstone/facefriend/chat/controller/ChatRoomController.java new file mode 100644 index 0000000000..1d8d14d2f9 --- /dev/null +++ b/src/main/java/capstone/facefriend/chat/controller/ChatRoomController.java @@ -0,0 +1,11 @@ +package capstone.facefriend.chat.controller; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@Slf4j +@RequiredArgsConstructor +public class ChatRoomController { +} \ No newline at end of file From 139a065132e68dce14c1c7e4f11d442a85df07c9 Mon Sep 17 00:00:00 2001 From: imjanghyeok Date: Fri, 3 May 2024 09:43:27 +0900 Subject: [PATCH 111/265] =?UTF-8?q?Feat:=20ChatRoom=EC=97=90=20=EB=8C=80?= =?UTF-8?q?=ED=95=9C=20Service=20class=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../capstone/facefriend/chat/service/ChatRoomService.java | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 src/main/java/capstone/facefriend/chat/service/ChatRoomService.java diff --git a/src/main/java/capstone/facefriend/chat/service/ChatRoomService.java b/src/main/java/capstone/facefriend/chat/service/ChatRoomService.java new file mode 100644 index 0000000000..39f48aff83 --- /dev/null +++ b/src/main/java/capstone/facefriend/chat/service/ChatRoomService.java @@ -0,0 +1,4 @@ +package capstone.facefriend.chat.service; + +public class ChatRoomService { +} From 95bc67313d65b27a86d9e62e4d188d1106a32f80 Mon Sep 17 00:00:00 2001 From: imjanghyeok Date: Fri, 3 May 2024 09:44:11 +0900 Subject: [PATCH 112/265] =?UTF-8?q?Feat:=20=EC=9E=91=EC=84=B1=ED=95=9C=20?= =?UTF-8?q?=EB=8F=84=EB=A9=94=EC=9D=B8=EC=97=90=20=EB=8C=80=ED=95=9C=20rep?= =?UTF-8?q?ository=20interface=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../facefriend/chat/repository/ChatMessageRepository.java | 4 ++++ .../facefriend/chat/repository/ChatRoomMemberRepository.java | 4 ++++ .../facefriend/chat/repository/ChatRoomRepository.java | 4 ++++ 3 files changed, 12 insertions(+) create mode 100644 src/main/java/capstone/facefriend/chat/repository/ChatMessageRepository.java create mode 100644 src/main/java/capstone/facefriend/chat/repository/ChatRoomMemberRepository.java create mode 100644 src/main/java/capstone/facefriend/chat/repository/ChatRoomRepository.java diff --git a/src/main/java/capstone/facefriend/chat/repository/ChatMessageRepository.java b/src/main/java/capstone/facefriend/chat/repository/ChatMessageRepository.java new file mode 100644 index 0000000000..7e1ec098e3 --- /dev/null +++ b/src/main/java/capstone/facefriend/chat/repository/ChatMessageRepository.java @@ -0,0 +1,4 @@ +package capstone.facefriend.chat.repository; + +public interface ChatMessageRepository { +} diff --git a/src/main/java/capstone/facefriend/chat/repository/ChatRoomMemberRepository.java b/src/main/java/capstone/facefriend/chat/repository/ChatRoomMemberRepository.java new file mode 100644 index 0000000000..db472c61e7 --- /dev/null +++ b/src/main/java/capstone/facefriend/chat/repository/ChatRoomMemberRepository.java @@ -0,0 +1,4 @@ +package capstone.facefriend.chat.repository; + +public interface ChatRoomMemberRepository { +} diff --git a/src/main/java/capstone/facefriend/chat/repository/ChatRoomRepository.java b/src/main/java/capstone/facefriend/chat/repository/ChatRoomRepository.java new file mode 100644 index 0000000000..62a8e94d7c --- /dev/null +++ b/src/main/java/capstone/facefriend/chat/repository/ChatRoomRepository.java @@ -0,0 +1,4 @@ +package capstone.facefriend.chat.repository; + +public interface ChatRoomRepository { +} From ef99e90c55b79c4dc19b5edbf8a3865e09a2acc7 Mon Sep 17 00:00:00 2001 From: imjanghyeok Date: Fri, 3 May 2024 09:44:39 +0900 Subject: [PATCH 113/265] =?UTF-8?q?Feat:=20Message=EC=97=90=20=EB=8C=80?= =?UTF-8?q?=ED=95=9C=20Controller=20class=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../chat/controller/MessageController.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 src/main/java/capstone/facefriend/chat/controller/MessageController.java diff --git a/src/main/java/capstone/facefriend/chat/controller/MessageController.java b/src/main/java/capstone/facefriend/chat/controller/MessageController.java new file mode 100644 index 0000000000..9dffed43d1 --- /dev/null +++ b/src/main/java/capstone/facefriend/chat/controller/MessageController.java @@ -0,0 +1,12 @@ +package capstone.facefriend.chat.controller; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@Slf4j +@RequiredArgsConstructor +public class MessageController { + +} \ No newline at end of file From 4314c882e2cf8eacb6d2abfeb5f8a20c07a050ff Mon Sep 17 00:00:00 2001 From: imjanghyeok Date: Fri, 3 May 2024 09:44:47 +0900 Subject: [PATCH 114/265] =?UTF-8?q?Feat:=20Message=EC=97=90=20=EB=8C=80?= =?UTF-8?q?=ED=95=9C=20Service=20class=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../facefriend/chat/service/MessageService.java | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 src/main/java/capstone/facefriend/chat/service/MessageService.java diff --git a/src/main/java/capstone/facefriend/chat/service/MessageService.java b/src/main/java/capstone/facefriend/chat/service/MessageService.java new file mode 100644 index 0000000000..f927460676 --- /dev/null +++ b/src/main/java/capstone/facefriend/chat/service/MessageService.java @@ -0,0 +1,11 @@ +package capstone.facefriend.chat.service; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +@Service +@Slf4j +@RequiredArgsConstructor +public class MessageService { +} \ No newline at end of file From 714e71922765eab9dbb4100f180aa84677ed6f26 Mon Sep 17 00:00:00 2001 From: imjanghyeok Date: Fri, 3 May 2024 09:51:27 +0900 Subject: [PATCH 115/265] =?UTF-8?q?Feat:=20=ED=95=84=EC=9A=94=ED=95=9C=20J?= =?UTF-8?q?PA=20method=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../chat/repository/ChatMessageRepository.java | 10 ++++++++-- .../chat/repository/ChatRoomMemberRepository.java | 12 ++++++++++-- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/src/main/java/capstone/facefriend/chat/repository/ChatMessageRepository.java b/src/main/java/capstone/facefriend/chat/repository/ChatMessageRepository.java index 7e1ec098e3..b4ed24bbb5 100644 --- a/src/main/java/capstone/facefriend/chat/repository/ChatMessageRepository.java +++ b/src/main/java/capstone/facefriend/chat/repository/ChatMessageRepository.java @@ -1,4 +1,10 @@ package capstone.facefriend.chat.repository; -public interface ChatMessageRepository { -} +import capstone.facefriend.chat.domain.ChatMessage; +import org.springframework.data.jpa.repository.JpaRepository; + + +public interface ChatMessageRepository extends JpaRepository { + ChatMessage findFirstByChatRoomIdOrderBySendTimeDesc(Long roomId); + +} \ No newline at end of file diff --git a/src/main/java/capstone/facefriend/chat/repository/ChatRoomMemberRepository.java b/src/main/java/capstone/facefriend/chat/repository/ChatRoomMemberRepository.java index db472c61e7..f93f7aa943 100644 --- a/src/main/java/capstone/facefriend/chat/repository/ChatRoomMemberRepository.java +++ b/src/main/java/capstone/facefriend/chat/repository/ChatRoomMemberRepository.java @@ -1,4 +1,12 @@ package capstone.facefriend.chat.repository; -public interface ChatRoomMemberRepository { -} +import capstone.facefriend.chat.domain.ChatRoomMember; +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.List; +import java.util.Optional; + +public interface ChatRoomMemberRepository extends JpaRepository { + Optional> findAllBySenderId(Long senderId); + Optional> findAllByReceiverId(Long senderId); +} \ No newline at end of file From 5e4b2b9320e65ef9549bd398d1892c1de2a3bd81 Mon Sep 17 00:00:00 2001 From: imjanghyeok Date: Fri, 3 May 2024 09:53:42 +0900 Subject: [PATCH 116/265] =?UTF-8?q?Feat:=20=EC=B1=84=ED=8C=85=EB=B0=A9=20?= =?UTF-8?q?=EC=97=86=EC=9D=84=20=EB=95=8C=20=EC=82=AC=EC=9A=A9=ED=95=98?= =?UTF-8?q?=EB=8A=94=20ChatRoomEmptyResponse=20dto=20class=20=EC=9E=91?= =?UTF-8?q?=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/dto/chatroom/ChatRoomEmptyResponse.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 src/main/java/capstone/facefriend/chat/service/dto/chatroom/ChatRoomEmptyResponse.java diff --git a/src/main/java/capstone/facefriend/chat/service/dto/chatroom/ChatRoomEmptyResponse.java b/src/main/java/capstone/facefriend/chat/service/dto/chatroom/ChatRoomEmptyResponse.java new file mode 100644 index 0000000000..ae7050b979 --- /dev/null +++ b/src/main/java/capstone/facefriend/chat/service/dto/chatroom/ChatRoomEmptyResponse.java @@ -0,0 +1,13 @@ +package capstone.facefriend.chat.service.dto.chatroom; + +public record ChatRoomEmptyResponse( + Boolean isEmpty, + String message +){ + public static ChatRoomEmptyResponse of(String message) { + return new ChatRoomEmptyResponse( + true, + message + ); + } +} \ No newline at end of file From 5dd878baa570980611b4b0bce8d7c83ac23a5456 Mon Sep 17 00:00:00 2001 From: imjanghyeok Date: Fri, 3 May 2024 09:55:13 +0900 Subject: [PATCH 117/265] =?UTF-8?q?Feat:=20=EC=B1=84=ED=8C=85=EB=B0=A9?= =?UTF-8?q?=EC=9D=B4=20=EC=9E=88=EC=9D=84=20=EB=95=8C,=20=EC=83=81?= =?UTF-8?q?=ED=83=9C=EC=97=90=20=EB=94=B0=EB=A5=B8=20=ED=95=84=EC=9A=94=20?= =?UTF-8?q?dto=20class=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/chatroom/ChatRoomHeartResponse.java | 22 +++++++++++++ .../dto/chatroom/ChatRoomMessageResponse.java | 32 +++++++++++++++++++ .../dto/heart/GetSendHeartResponse.java | 31 ++++++++++++++++++ 3 files changed, 85 insertions(+) create mode 100644 src/main/java/capstone/facefriend/chat/service/dto/chatroom/ChatRoomHeartResponse.java create mode 100644 src/main/java/capstone/facefriend/chat/service/dto/chatroom/ChatRoomMessageResponse.java create mode 100644 src/main/java/capstone/facefriend/chat/service/dto/heart/GetSendHeartResponse.java diff --git a/src/main/java/capstone/facefriend/chat/service/dto/chatroom/ChatRoomHeartResponse.java b/src/main/java/capstone/facefriend/chat/service/dto/chatroom/ChatRoomHeartResponse.java new file mode 100644 index 0000000000..d3a9457690 --- /dev/null +++ b/src/main/java/capstone/facefriend/chat/service/dto/chatroom/ChatRoomHeartResponse.java @@ -0,0 +1,22 @@ +package capstone.facefriend.chat.service.dto.chatroom; + +import capstone.facefriend.chat.domain.ChatRoom; +import capstone.facefriend.chat.domain.ChatRoomMember; +import capstone.facefriend.chat.service.dto.heart.GetSendHeartResponse; + +public record ChatRoomHeartResponse ( + Long sender, + Long receiver, + ChatRoom chatRoom, + GetSendHeartResponse sendHeart + +) { + public static ChatRoomHeartResponse of(ChatRoomMember chatRoomMember, GetSendHeartResponse sendHeartResponse) { + return new ChatRoomHeartResponse( + chatRoomMember.getSender().getId(), + chatRoomMember.getReceiver().getId(), + chatRoomMember.getChatRoom(), + sendHeartResponse + ); + } +} \ No newline at end of file diff --git a/src/main/java/capstone/facefriend/chat/service/dto/chatroom/ChatRoomMessageResponse.java b/src/main/java/capstone/facefriend/chat/service/dto/chatroom/ChatRoomMessageResponse.java new file mode 100644 index 0000000000..528dbd750c --- /dev/null +++ b/src/main/java/capstone/facefriend/chat/service/dto/chatroom/ChatRoomMessageResponse.java @@ -0,0 +1,32 @@ +package capstone.facefriend.chat.service.dto.chatroom; + +import capstone.facefriend.chat.domain.ChatMessage; +import capstone.facefriend.chat.domain.ChatRoom; +import capstone.facefriend.chat.domain.ChatRoomMember; + +import java.time.LocalDateTime; + +public record ChatRoomMessageResponse( + Long sender, + Long receiver, + ChatRoom chatRoom, + String content, + LocalDateTime sendTime, + String senderNickname, + String senderGeneratedFaceInfo, + String senderOriginalFaceInfo + +){ + public static ChatRoomMessageResponse of(ChatRoomMember chatRoomMember, ChatMessage message) { + return new ChatRoomMessageResponse( + chatRoomMember.getSender().getId(), + chatRoomMember.getReceiver().getId(), + chatRoomMember.getChatRoom(), + message.getContent(), + message.getSendTime(), + message.getSender().getBasicInfo().getNickname(), + message.getSender().getFaceInfo().getGeneratedS3url(), + message.getSender().getFaceInfo().getOriginS3Url() + ); + } +} \ No newline at end of file diff --git a/src/main/java/capstone/facefriend/chat/service/dto/heart/GetSendHeartResponse.java b/src/main/java/capstone/facefriend/chat/service/dto/heart/GetSendHeartResponse.java new file mode 100644 index 0000000000..8e510750aa --- /dev/null +++ b/src/main/java/capstone/facefriend/chat/service/dto/heart/GetSendHeartResponse.java @@ -0,0 +1,31 @@ +package capstone.facefriend.chat.service.dto.heart; + +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.time.LocalDateTime; + +@Setter +@Data +@NoArgsConstructor +public class GetSendHeartResponse { + private String senderName; + private Long senderId; + private Long receiveId; + private String type; + private Long roomId; + @JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss", timezone = "Asia/Seoul") + private LocalDateTime createdAt; + + + public GetSendHeartResponse(SendHeartResponse sendHeartResponse) { + this.senderName = sendHeartResponse.getSenderName(); + this.senderId = sendHeartResponse.getSenderId(); + this.receiveId = sendHeartResponse.getReceiveId(); + this.type = sendHeartResponse.getType(); + this.roomId = sendHeartResponse.getRoomId(); + this.createdAt = sendHeartResponse.getCreatedAt(); + } +} \ No newline at end of file From dba13588e0de7b8214d22de744af4669471c973e Mon Sep 17 00:00:00 2001 From: imjanghyeok Date: Fri, 3 May 2024 09:56:13 +0900 Subject: [PATCH 118/265] =?UTF-8?q?Feat:=20=EC=B1=84=ED=8C=85=EB=B0=A9?= =?UTF-8?q?=EC=9D=B4=20=EC=9E=88=EC=9D=84=20=EB=95=8C,=20=EC=83=81?= =?UTF-8?q?=ED=83=9C=EC=97=90=20=EB=94=B0=EB=A5=B8=20=ED=95=84=EC=9A=94=20?= =?UTF-8?q?dto=20class=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/chatroom/ChatRoomOpenResponse.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 src/main/java/capstone/facefriend/chat/service/dto/chatroom/ChatRoomOpenResponse.java diff --git a/src/main/java/capstone/facefriend/chat/service/dto/chatroom/ChatRoomOpenResponse.java b/src/main/java/capstone/facefriend/chat/service/dto/chatroom/ChatRoomOpenResponse.java new file mode 100644 index 0000000000..393f9a8880 --- /dev/null +++ b/src/main/java/capstone/facefriend/chat/service/dto/chatroom/ChatRoomOpenResponse.java @@ -0,0 +1,15 @@ +package capstone.facefriend.chat.service.dto.chatroom; + +public record ChatRoomOpenResponse ( + Long senderId, + String senderNickname, + String message +) { + public static ChatRoomOpenResponse of(Long senderId, String senderNickname, String message) { + return new ChatRoomOpenResponse( + senderId, + senderNickname, + message + ); + } +} \ No newline at end of file From 1b8cfa26e7123edce9002cac85d464edf4346ca2 Mon Sep 17 00:00:00 2001 From: imjanghyeok Date: Fri, 3 May 2024 09:57:35 +0900 Subject: [PATCH 119/265] =?UTF-8?q?Feat:=20=EC=B1=84=ED=8C=85=EB=B0=A9=20l?= =?UTF-8?q?ist=EB=A5=BC=20=EB=B3=B4=EC=97=AC=EC=A3=BC=EA=B8=B0=20=EC=9C=84?= =?UTF-8?q?=ED=95=9C=20service=20code=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../chat/service/ChatRoomService.java | 91 ++++++++++++++++++- 1 file changed, 90 insertions(+), 1 deletion(-) diff --git a/src/main/java/capstone/facefriend/chat/service/ChatRoomService.java b/src/main/java/capstone/facefriend/chat/service/ChatRoomService.java index 39f48aff83..2cfd9dfb7e 100644 --- a/src/main/java/capstone/facefriend/chat/service/ChatRoomService.java +++ b/src/main/java/capstone/facefriend/chat/service/ChatRoomService.java @@ -1,4 +1,93 @@ package capstone.facefriend.chat.service; +import capstone.facefriend.chat.domain.ChatMessage; +import capstone.facefriend.chat.domain.ChatRoom; +import capstone.facefriend.chat.domain.ChatRoomMember; +import capstone.facefriend.chat.repository.ChatMessageRepository; +import capstone.facefriend.chat.repository.ChatRoomMemberRepository; +import capstone.facefriend.chat.service.dto.chatroom.ChatRoomEmptyResponse; +import capstone.facefriend.chat.service.dto.chatroom.ChatRoomHeartResponse; +import capstone.facefriend.chat.service.dto.chatroom.ChatRoomMessageResponse; +import capstone.facefriend.chat.service.dto.chatroom.ChatRoomOpenResponse; +import capstone.facefriend.chat.service.dto.heart.GetSendHeartResponse; +import capstone.facefriend.member.domain.Member; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@Service +@Slf4j +@RequiredArgsConstructor public class ChatRoomService { -} + + private final ChatRoomMemberRepository chatRoomMemberRepository; + private final ChatMessageRepository chatMessageRepository; + private static final String EMPTY_MESSAGE = "채팅을 시작하지 않았습니다."; + private static final String OPEN_MESSAGE = "채팅을 시작해보세요!"; + + private List findAllChatRoomMemberBySenderId(Long memberId) { + return chatRoomMemberRepository.findAllBySenderId(memberId).orElse(new ArrayList<>()); + } + + private List findAllChatRoomMemberByReceiverId(Long memberId) { + return chatRoomMemberRepository.findAllByReceiverId(memberId).orElse(new ArrayList<>()); + } + + @Transactional + public Map getChatRoomList(Long memberId) { + + List chatRoomMemberList = new ArrayList<>(); + chatRoomMemberList.addAll(findAllChatRoomMemberByReceiverId(memberId)); + chatRoomMemberList.addAll(findAllChatRoomMemberBySenderId(memberId)); + + Map chatRooms = new HashMap<>(); + + if (chatRoomMemberList.isEmpty()) { + ChatRoomEmptyResponse chatRoomEmptyResponse = ChatRoomEmptyResponse.of(EMPTY_MESSAGE); + chatRooms.put("chatRoomList", chatRoomEmptyResponse); + return chatRooms; + } + + List chatRoomsMessage = new ArrayList<>(); + List chatRoomsHeart = new ArrayList<>(); + List chatRoomsOpen = new ArrayList<>(); + + for (ChatRoomMember chatRoomMember : chatRoomMemberList) { + ChatRoom.Status status = chatRoomMember.getChatRoom().getStatus(); + + if (status == ChatRoom.Status.set) { + GetSendHeartResponse sendHeartResponse = new GetSendHeartResponse(); + sendHeartResponse.setType("Heart"); + sendHeartResponse.setRoomId(chatRoomMember.getChatRoom().getId()); + sendHeartResponse.setSenderId(chatRoomMember.getSender().getId()); + sendHeartResponse.setReceiveId(chatRoomMember.getReceiver().getId()); + sendHeartResponse.setSenderName(chatRoomMember.getSender().getBasicInfo().getNickname()); + log.info("sendHeart:{}", sendHeartResponse.toString()); + ChatRoomHeartResponse chatRoomHeartResponse = ChatRoomHeartResponse.of(chatRoomMember, sendHeartResponse); + chatRoomsHeart.add(chatRoomHeartResponse); + + } else if (status == ChatRoom.Status.progress) { + ChatMessage chatMessage = chatMessageRepository.findFirstByChatRoomIdOrderBySendTimeDesc(chatRoomMember.getChatRoom().getId()); + ChatRoomMessageResponse chatRoomResponse = ChatRoomMessageResponse.of(chatRoomMember, chatMessage); + chatRoomsMessage.add(chatRoomResponse); + + } else if (status == ChatRoom.Status.open) { + Member Sender = chatRoomMember.getSender(); + ChatRoomOpenResponse chatRoomOpenResponse = ChatRoomOpenResponse.of(Sender.getId(), Sender.getBasicInfo().getNickname(), OPEN_MESSAGE); + chatRoomsOpen.add(chatRoomOpenResponse); + } + } + + chatRooms.put("chatRoomHeartList", chatRoomsHeart); + chatRooms.put("chatRoomMessageList", chatRoomsMessage); + chatRooms.put("chatRoomOpenList", chatRoomsOpen); + + return chatRooms; + } +} \ No newline at end of file From 131f137872e4ac1f176ffe976b12d28388bd8db4 Mon Sep 17 00:00:00 2001 From: imjanghyeok Date: Fri, 3 May 2024 09:57:51 +0900 Subject: [PATCH 120/265] =?UTF-8?q?Feat:=20=EC=B1=84=ED=8C=85=EB=B0=A9=20l?= =?UTF-8?q?ist=EB=A5=BC=20=EB=B3=B4=EC=97=AC=EC=A3=BC=EB=8A=94=20controlle?= =?UTF-8?q?r=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../chat/controller/ChatRoomController.java | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/main/java/capstone/facefriend/chat/controller/ChatRoomController.java b/src/main/java/capstone/facefriend/chat/controller/ChatRoomController.java index 1d8d14d2f9..3ccfb34806 100644 --- a/src/main/java/capstone/facefriend/chat/controller/ChatRoomController.java +++ b/src/main/java/capstone/facefriend/chat/controller/ChatRoomController.java @@ -1,11 +1,25 @@ package capstone.facefriend.chat.controller; +import capstone.facefriend.auth.controller.support.AuthMember; +import capstone.facefriend.chat.service.ChatRoomService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; +import java.util.Map; + @RestController @Slf4j @RequiredArgsConstructor public class ChatRoomController { + private final ChatRoomService chatRoomService; + + @GetMapping("/room/list") + ResponseEntity> getChatRoomList( + @AuthMember Long memberId + ) { + return ResponseEntity.ok(chatRoomService.getChatRoomList(memberId)); + } } \ No newline at end of file From fd6e25bc06b381c13cd6382d9ba526aea8102adf Mon Sep 17 00:00:00 2001 From: imjanghyeok Date: Fri, 3 May 2024 09:58:42 +0900 Subject: [PATCH 121/265] =?UTF-8?q?Feat:=20stomp=20=EC=99=B8=EB=B6=80?= =?UTF-8?q?=EB=B8=8C=EB=A1=9C=EC=BB=A4=20=EC=82=AC=EC=9A=A9=EC=9D=84=20?= =?UTF-8?q?=EC=9C=84=ED=95=9C=20redis=20=EC=84=A4=EC=A0=95=20=ED=8C=8C?= =?UTF-8?q?=EC=9D=BC=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../facefriend/chat/config/RedisConfig.java | 80 +++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 src/main/java/capstone/facefriend/chat/config/RedisConfig.java diff --git a/src/main/java/capstone/facefriend/chat/config/RedisConfig.java b/src/main/java/capstone/facefriend/chat/config/RedisConfig.java new file mode 100644 index 0000000000..52fd742c81 --- /dev/null +++ b/src/main/java/capstone/facefriend/chat/config/RedisConfig.java @@ -0,0 +1,80 @@ +package capstone.facefriend.chat.config; + + +import capstone.facefriend.chat.service.RedisSubscriber; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.connection.RedisStandaloneConfiguration; +import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.listener.ChannelTopic; +import org.springframework.data.redis.listener.RedisMessageListenerContainer; +import org.springframework.data.redis.listener.adapter.MessageListenerAdapter; +import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; +import org.springframework.data.redis.serializer.StringRedisSerializer; + +@Configuration +@RequiredArgsConstructor +public class RedisConfig { + @Value("${spring.data.redis.host}") + private String redisHost; + @Value("${spring.data.redis.port}") + private int redisPort; + + + @Bean + public RedisConnectionFactory redisConnectionFactory() { + RedisStandaloneConfiguration redisStandaloneConfiguration = + new RedisStandaloneConfiguration(redisHost, redisPort); + return new LettuceConnectionFactory(redisStandaloneConfiguration); + } + + @Bean + public RedisMessageListenerContainer redisMessageListenerContainer( + RedisConnectionFactory connectionFactory, + MessageListenerAdapter listenerAdapter, + ChannelTopic channelTopic + ) { + RedisMessageListenerContainer container = new RedisMessageListenerContainer(); + container.setConnectionFactory(connectionFactory); + // RedisMessageListenerContainer 에 Bean 으로 등록한 listenerAdapter, channelTopic 추가 + container.addMessageListener(listenerAdapter, channelTopic); + return container; + } + + @Bean + public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) { + RedisTemplate redisTemplate = new RedisTemplate<>(); + redisTemplate.setConnectionFactory(redisConnectionFactory); + + // Jackson2JsonRedisSerializer를 사용하여 RedisTemplate 설정 + Jackson2JsonRedisSerializer serializer = new Jackson2JsonRedisSerializer<>(Object.class); + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.registerModule(new JavaTimeModule()); // Java 8 시간 모듈 등록 + objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); // 날짜를 타임스탬프로 변환하지 않도록 설정 + serializer.setObjectMapper(objectMapper); + + // 문자열 직렬화 설정 + redisTemplate.setKeySerializer(new StringRedisSerializer()); + redisTemplate.setValueSerializer(serializer); + + redisTemplate.afterPropertiesSet(); + return redisTemplate; + } + + @Bean + public MessageListenerAdapter listenerAdapter(RedisSubscriber subscriber) { + return new MessageListenerAdapter(subscriber, "sendMessage"); + } + + + @Bean + public ChannelTopic channelTopic() {return new ChannelTopic("chatroom");} + +} \ No newline at end of file From 0b9db392a401a886941e98638d0bc723373063f7 Mon Sep 17 00:00:00 2001 From: imjanghyeok Date: Fri, 3 May 2024 09:59:13 +0900 Subject: [PATCH 122/265] =?UTF-8?q?Feat:=20RedisSubscriber=20class=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../capstone/facefriend/chat/service/RedisSubscriber.java | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 src/main/java/capstone/facefriend/chat/service/RedisSubscriber.java diff --git a/src/main/java/capstone/facefriend/chat/service/RedisSubscriber.java b/src/main/java/capstone/facefriend/chat/service/RedisSubscriber.java new file mode 100644 index 0000000000..25b3394680 --- /dev/null +++ b/src/main/java/capstone/facefriend/chat/service/RedisSubscriber.java @@ -0,0 +1,4 @@ +package capstone.facefriend.chat.service; + +public class RedisSubscriber { +} From 5ca1edbd698cb8ef4afd625f8877003f48712082 Mon Sep 17 00:00:00 2001 From: imjanghyeok Date: Fri, 3 May 2024 10:05:02 +0900 Subject: [PATCH 123/265] =?UTF-8?q?Feat:=20message=20=EC=A0=84=EC=86=A1?= =?UTF-8?q?=EC=9D=84=20=EC=9C=84=ED=95=9C=20=ED=95=84=EC=9A=94=20dto=20cla?= =?UTF-8?q?ss=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/dto/message/MessageRequest.java | 14 +++++++++++ .../service/dto/message/MessageResponse.java | 24 +++++++++++++++++++ 2 files changed, 38 insertions(+) create mode 100644 src/main/java/capstone/facefriend/chat/service/dto/message/MessageRequest.java create mode 100644 src/main/java/capstone/facefriend/chat/service/dto/message/MessageResponse.java diff --git a/src/main/java/capstone/facefriend/chat/service/dto/message/MessageRequest.java b/src/main/java/capstone/facefriend/chat/service/dto/message/MessageRequest.java new file mode 100644 index 0000000000..a8b8222c4d --- /dev/null +++ b/src/main/java/capstone/facefriend/chat/service/dto/message/MessageRequest.java @@ -0,0 +1,14 @@ +package capstone.facefriend.chat.service.dto.message; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class MessageRequest { + private Long roomId; + + private Long receiveId; + + private String content; +} \ No newline at end of file diff --git a/src/main/java/capstone/facefriend/chat/service/dto/message/MessageResponse.java b/src/main/java/capstone/facefriend/chat/service/dto/message/MessageResponse.java new file mode 100644 index 0000000000..0a12830f2f --- /dev/null +++ b/src/main/java/capstone/facefriend/chat/service/dto/message/MessageResponse.java @@ -0,0 +1,24 @@ +package capstone.facefriend.chat.service.dto.message; + +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; +import java.time.LocalDateTime; + + +@Data +@NoArgsConstructor +@AllArgsConstructor +public class MessageResponse implements Serializable { + private Long roomId; + private Long receiveId; + private Long senderId; + private String content; + private String type; + @JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss", timezone = "Asia/Seoul") + private LocalDateTime createdAt; + private Boolean isRead; +} \ No newline at end of file From 6d135bde01c97f1031c1ecc9acc52b9871f2dd82 Mon Sep 17 00:00:00 2001 From: imjanghyeok Date: Fri, 3 May 2024 10:05:24 +0900 Subject: [PATCH 124/265] =?UTF-8?q?Feat:=20message=20=EC=A0=84=EC=86=A1=20?= =?UTF-8?q?service=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../chat/service/MessageService.java | 93 +++++++++++++++++++ 1 file changed, 93 insertions(+) diff --git a/src/main/java/capstone/facefriend/chat/service/MessageService.java b/src/main/java/capstone/facefriend/chat/service/MessageService.java index f927460676..ef9de64b2a 100644 --- a/src/main/java/capstone/facefriend/chat/service/MessageService.java +++ b/src/main/java/capstone/facefriend/chat/service/MessageService.java @@ -1,11 +1,104 @@ package capstone.facefriend.chat.service; +import capstone.facefriend.chat.domain.ChatMessage; +import capstone.facefriend.chat.domain.ChatRoom; +import capstone.facefriend.chat.domain.ChatRoomMember; +import capstone.facefriend.chat.exception.ChatException; +import capstone.facefriend.chat.exception.ChatExceptionType; +import capstone.facefriend.chat.repository.ChatMessageRepository; +import capstone.facefriend.chat.repository.ChatRoomMemberRepository; +import capstone.facefriend.chat.repository.ChatRoomRepository; +import capstone.facefriend.chat.service.dto.message.MessageRequest; +import capstone.facefriend.chat.service.dto.message.MessageResponse; +import capstone.facefriend.member.domain.Member; +import capstone.facefriend.member.domain.MemberRepository; +import capstone.facefriend.member.exception.MemberException; +import capstone.facefriend.member.exception.MemberExceptionType; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.listener.ChannelTopic; +import org.springframework.messaging.simp.SimpMessagingTemplate; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.time.LocalDateTime; @Service @Slf4j @RequiredArgsConstructor public class MessageService { + + private final ChatMessageRepository chatMessageRepository; + private final ChatRoomMemberRepository chatRoomMemberRepository; + private final MemberRepository memberRepository; + private final ChatRoomRepository chatRoomRepository; + private final RedisTemplate redisTemplate; + private final ChannelTopic channelTopic; + private final SimpMessagingTemplate messagingTemplate; + + private Member findMemberById(Long memberId) { + Member member = memberRepository.findById(memberId) + .orElseThrow(() -> new MemberException(MemberExceptionType.NOT_FOUND)); + return member; + } + + private ChatRoom findRoomById(Long roomId) { + ChatRoom chatRoom = chatRoomRepository.findById(roomId) + .orElseThrow(()-> new ChatException(ChatExceptionType.NOT_FOUND)); + return chatRoom; + } + + private ChatRoomMember findSenderReceiver(Long senderId, Long receiveId) { + ChatRoomMember chatRoomMember = chatRoomMemberRepository.findBySenderAndReceiver(senderId, receiveId) + .orElseThrow(()-> new ChatException(ChatExceptionType.NOT_FOUND)); + return chatRoomMember; + } + + + @Transactional + public void sendMessage(MessageRequest messageRequest, Long senderId) { + Member sender = findMemberById(senderId); + Member receiver = findMemberById(messageRequest.getReceiveId()); + ChatRoomMember chatRoomMember = findSenderReceiver(sender.getId(), receiver.getId()); + ChatRoom chatRoom = findRoomById(messageRequest.getRoomId()); + + if (chatRoom.getStatus() == ChatRoom.Status.open) { + chatRoom.setStatus(ChatRoom.Status.progress); + chatRoomRepository.save(chatRoom); + chatRoomMemberRepository.save(chatRoomMember); + } else if ((chatRoom.getStatus() == ChatRoom.Status.close)) { + throw new ChatException(ChatExceptionType.INVALIDED_CHATROOM); + } else if ((chatRoom.getStatus() == ChatRoom.Status.set)) { + throw new ChatException(ChatExceptionType.INVALIDED_CHATROOM); + } else if ((chatRoom.getStatus() == ChatRoom.Status.delete)) { + throw new ChatException(ChatExceptionType.INVALIDED_CHATROOM); + } + + + ChatMessage chatMessage = ChatMessage.builder() + .content(messageRequest.getContent()) + .sender(sender) + .chatRoom(chatRoom) + .sendTime(LocalDateTime.now()) + .isRead(false) + .build(); + + chatMessageRepository.save(chatMessage); + + MessageResponse messageResponse = new MessageResponse(); + messageResponse.setRoomId(chatMessage.getChatRoom().getId()); + messageResponse.setSenderId(senderId); + messageResponse.setReceiveId(receiver.getId()); + messageResponse.setContent(chatMessage.getContent()); + messageResponse.setType("message"); + messageResponse.setCreatedAt(chatMessage.getSendTime()); + messageResponse.setIsRead(chatMessage.isRead()); + + String topic = channelTopic.getTopic(); + + redisTemplate.convertAndSend(topic, messageResponse); + + } + } \ No newline at end of file From 2809e083f38d4e43be392b65513a847b3cb79041 Mon Sep 17 00:00:00 2001 From: imjanghyeok Date: Fri, 3 May 2024 10:05:33 +0900 Subject: [PATCH 125/265] =?UTF-8?q?Feat:=20message=20=EC=A0=84=EC=86=A1=20?= =?UTF-8?q?controller=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../chat/controller/MessageController.java | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/src/main/java/capstone/facefriend/chat/controller/MessageController.java b/src/main/java/capstone/facefriend/chat/controller/MessageController.java index 9dffed43d1..9d3ee29e2b 100644 --- a/src/main/java/capstone/facefriend/chat/controller/MessageController.java +++ b/src/main/java/capstone/facefriend/chat/controller/MessageController.java @@ -1,7 +1,17 @@ package capstone.facefriend.chat.controller; +import capstone.facefriend.auth.infrastructure.JwtProvider; +import capstone.facefriend.chat.service.dto.heart.HeartReplyRequest; +import capstone.facefriend.chat.service.dto.message.MessageRequest; +import capstone.facefriend.chat.service.dto.heart.SendHeartRequest; +import capstone.facefriend.chat.service.MessageService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; +import org.springframework.messaging.handler.annotation.MessageMapping; +import org.springframework.messaging.handler.annotation.SendTo; +import org.springframework.messaging.simp.stomp.StompHeaderAccessor; +import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; @RestController @@ -9,4 +19,31 @@ @RequiredArgsConstructor public class MessageController { + private static final String BEARER_PREFIX = "Bearer "; + private final MessageService messageService; + private final MappingJackson2HttpMessageConverter converter; + private final JwtProvider jwtProvider; + + @MessageMapping("/test") + @SendTo("/sub/test") + public String test() { + return "테스트 메시지"; + } + + @MessageMapping("/chat/messages") + public void message( + StompHeaderAccessor headerAccessor, + MessageRequest messageRequest + ) { + String authorizationHeader = headerAccessor.getFirstNativeHeader("Authorization"); + String token = authorizationHeader.substring(BEARER_PREFIX.length()); + log.info("token: {}",token); + Long senderId = jwtProvider.extractId(token); + log.info("senderId: {}",senderId.toString()); + String destination = headerAccessor.getDestination(); + messageService.sendMessage(messageRequest, senderId); + } + + + } \ No newline at end of file From 4115ae3abafd29878dab172829c5c5d8f4b576fa Mon Sep 17 00:00:00 2001 From: imjanghyeok Date: Fri, 3 May 2024 10:05:58 +0900 Subject: [PATCH 126/265] =?UTF-8?q?Feat:=20message=20=EC=A0=84=EC=86=A1=20?= =?UTF-8?q?service=EC=97=90=EC=84=9C=20=EC=93=B0=EC=9D=B4=EB=8A=94=20jpa?= =?UTF-8?q?=20method=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../chat/repository/ChatMessageRepository.java | 1 + .../chat/repository/ChatRoomMemberRepository.java | 11 +++++++++++ .../chat/repository/ChatRoomRepository.java | 10 ++++++++-- 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/src/main/java/capstone/facefriend/chat/repository/ChatMessageRepository.java b/src/main/java/capstone/facefriend/chat/repository/ChatMessageRepository.java index b4ed24bbb5..f6a20281f5 100644 --- a/src/main/java/capstone/facefriend/chat/repository/ChatMessageRepository.java +++ b/src/main/java/capstone/facefriend/chat/repository/ChatMessageRepository.java @@ -6,5 +6,6 @@ public interface ChatMessageRepository extends JpaRepository { ChatMessage findFirstByChatRoomIdOrderBySendTimeDesc(Long roomId); + ChatMessage save(ChatMessage chatMessage); } \ No newline at end of file diff --git a/src/main/java/capstone/facefriend/chat/repository/ChatRoomMemberRepository.java b/src/main/java/capstone/facefriend/chat/repository/ChatRoomMemberRepository.java index f93f7aa943..7cb178a2ad 100644 --- a/src/main/java/capstone/facefriend/chat/repository/ChatRoomMemberRepository.java +++ b/src/main/java/capstone/facefriend/chat/repository/ChatRoomMemberRepository.java @@ -1,12 +1,23 @@ package capstone.facefriend.chat.repository; +import capstone.facefriend.chat.domain.ChatRoom; import capstone.facefriend.chat.domain.ChatRoomMember; +import io.lettuce.core.dynamic.annotation.Param; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; import java.util.List; import java.util.Optional; public interface ChatRoomMemberRepository extends JpaRepository { + Optional findByChatRoom(ChatRoom chatRoom); + + @Query("SELECT c FROM ChatRoomMember c WHERE c.sender.id = :senderId AND c.receiver.id = :receiverId") + Optional findBySenderAndReceiver(@Param("senderId") Long senderId, @Param("receiverId") Long receiverId); + + // Optional findChatRoomMemberBySenderAAndReceiver(Member sender, Member receiver); + ChatRoomMember save(ChatRoomMember chatRoomMember); + Optional> findAllBySenderId(Long senderId); Optional> findAllByReceiverId(Long senderId); } \ No newline at end of file diff --git a/src/main/java/capstone/facefriend/chat/repository/ChatRoomRepository.java b/src/main/java/capstone/facefriend/chat/repository/ChatRoomRepository.java index 62a8e94d7c..8a7da8f716 100644 --- a/src/main/java/capstone/facefriend/chat/repository/ChatRoomRepository.java +++ b/src/main/java/capstone/facefriend/chat/repository/ChatRoomRepository.java @@ -1,4 +1,10 @@ package capstone.facefriend.chat.repository; -public interface ChatRoomRepository { -} +import capstone.facefriend.chat.domain.ChatRoom; +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.Optional; +public interface ChatRoomRepository extends JpaRepository { + Optional findById(Long id); + ChatRoom save(ChatRoom chatRoom); +} \ No newline at end of file From 2b508855b532a98ee88dafc3420351fa66919765 Mon Sep 17 00:00:00 2001 From: imjanghyeok Date: Fri, 3 May 2024 10:06:27 +0900 Subject: [PATCH 127/265] =?UTF-8?q?Feat:=20message=20=EC=A0=84=EC=86=A1=20?= =?UTF-8?q?service=20=EC=98=88=EC=99=B8=EC=B2=98=EB=A6=AC=EB=A5=BC=20?= =?UTF-8?q?=EC=9C=84=ED=95=9C=20exception=20class=20=EB=B0=8F=20enum=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../chat/exception/ChatException.java | 10 ++++ .../chat/exception/ChatExceptionType.java | 46 +++++++++++++++++++ 2 files changed, 56 insertions(+) create mode 100644 src/main/java/capstone/facefriend/chat/exception/ChatException.java create mode 100644 src/main/java/capstone/facefriend/chat/exception/ChatExceptionType.java diff --git a/src/main/java/capstone/facefriend/chat/exception/ChatException.java b/src/main/java/capstone/facefriend/chat/exception/ChatException.java new file mode 100644 index 0000000000..d82e698859 --- /dev/null +++ b/src/main/java/capstone/facefriend/chat/exception/ChatException.java @@ -0,0 +1,10 @@ +package capstone.facefriend.chat.exception; + +import capstone.facefriend.common.exception.BaseException; +import capstone.facefriend.common.exception.ExceptionType; + +public class ChatException extends BaseException { + public ChatException(ExceptionType exceptionType) { + super(exceptionType); + } +} \ No newline at end of file diff --git a/src/main/java/capstone/facefriend/chat/exception/ChatExceptionType.java b/src/main/java/capstone/facefriend/chat/exception/ChatExceptionType.java new file mode 100644 index 0000000000..afea94e745 --- /dev/null +++ b/src/main/java/capstone/facefriend/chat/exception/ChatExceptionType.java @@ -0,0 +1,46 @@ +package capstone.facefriend.chat.exception; + +import capstone.facefriend.common.exception.ExceptionType; +import capstone.facefriend.common.exception.Status; + +public enum ChatExceptionType implements ExceptionType { + NOT_FOUND(Status.NOT_FOUND, 5001, "일치하는 채팅방이 없습니다."), + INVALIDED_CHATROOM(Status.BAD_REQUEST, 5002, "유효한 채팅방이 아닙니다"), + INVALID_ACCESS(Status.FORBIDDEN, 5003, "본인의 계정이 아닙니다."), + UNAUTHORIZED(Status.UNAUTHORIZED, 5005, "접근 정보가 잘못되었습니다."), + ALREADY_CHATROOM(Status.BAD_REQUEST, 5006, "이미 존재하는 채팅방입니다."), + WRONG_PASSWORD(Status.BAD_REQUEST, 5007, "잘못된 비밀번호입니다."), + EXPIRED_ACCESS_TOKEN(Status.BAD_REQUEST, 5008, "만료된 액세스 토큰이므로 재발급해야 합니다."), + INVALID_ACCESS_TOKEN(Status.BAD_REQUEST, 5009, "유효하지 않은 액세스 토큰이므로 재발급해야 합니다."), + INVALID_REFRESH_TOKEN(Status.BAD_REQUEST, 5010, "유효하지 않은 리프레시 토큰입니다. 토큰 재발급이 불가능합니다."), + ACCESS_TOKEN_IS_IN_BLACKLIST(Status.BAD_REQUEST, 5011, "액세스 토큰이 로그아웃 처리되었습니다. 재로그인하시기 바랍니다."), + NOT_VERIFIED(Status.BAD_REQUEST, 5012, "본인 인증을 먼저 완료해야 합니다."), + PASSWORDS_NOT_EQUAL(Status.BAD_REQUEST, 5013, "재설정하는 비밀번호들이 동일하지 않습니다."), + WRONG_TEMPORARY_PASSWORD(Status.BAD_REQUEST, 5014, "임시 비밀번호가 올바르지 않습니다."), + NOT_FOUND_GENDER(Status.NOT_FOUND, 5015, "일치하는 성별이 없습니다.") + ; + private final Status status; + private final int exceptionCode; + private final String message; + + ChatExceptionType(Status status, int exceptionCode, String message) { + this.status = status; + this.exceptionCode = exceptionCode; + this.message = message; + } + + @Override + public Status status() { + return status; + } + + @Override + public int exceptionCode() { + return exceptionCode; + } + + @Override + public String message() { + return message; + } +} \ No newline at end of file From c82968f521652f7835fb29db13336dea219ae13b Mon Sep 17 00:00:00 2001 From: imjanghyeok Date: Fri, 3 May 2024 10:08:05 +0900 Subject: [PATCH 128/265] =?UTF-8?q?Feat:=20RedisSubscriber=EB=A5=BC=20?= =?UTF-8?q?=ED=86=B5=ED=95=B4=20=EC=99=B8=EB=B6=80=20=EB=B8=8C=EB=A1=9C?= =?UTF-8?q?=EC=BB=A4=EB=A1=9C=EB=B6=80=ED=84=B0=20=EB=B0=9B=EC=9D=80=20?= =?UTF-8?q?=EB=A9=94=EC=8B=9C=EC=A7=80=EB=A5=BC=20destination=EC=97=90=20?= =?UTF-8?q?=EC=A0=84=EB=8B=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../chat/service/RedisSubscriber.java | 50 ++++++++++++++++++- 1 file changed, 48 insertions(+), 2 deletions(-) diff --git a/src/main/java/capstone/facefriend/chat/service/RedisSubscriber.java b/src/main/java/capstone/facefriend/chat/service/RedisSubscriber.java index 25b3394680..7f578c8839 100644 --- a/src/main/java/capstone/facefriend/chat/service/RedisSubscriber.java +++ b/src/main/java/capstone/facefriend/chat/service/RedisSubscriber.java @@ -1,4 +1,50 @@ package capstone.facefriend.chat.service; -public class RedisSubscriber { -} +import capstone.facefriend.chat.service.dto.message.GetMessageResponse; +import capstone.facefriend.chat.service.dto.heart.GetSendHeartResponse; +import capstone.facefriend.chat.service.dto.message.MessageResponse; +import capstone.facefriend.chat.service.dto.heart.SendHeartResponse; +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.data.redis.connection.Message; +import org.springframework.data.redis.connection.MessageListener; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.messaging.simp.SimpMessageSendingOperations; +import org.springframework.stereotype.Service; + +import java.io.IOException; + +@Slf4j +@RequiredArgsConstructor +@Service +public class RedisSubscriber implements MessageListener { + + private final ObjectMapper objectMapper; + private final RedisTemplate redisTemplate; + private final SimpMessageSendingOperations messagingTemplate; + + @Override + public void onMessage(Message message, byte[] pattern) { + try { + + // redis에서 발행된 데이터를 받아 역직렬화 + String publishMessage = (String) redisTemplate.getStringSerializer().deserialize(message.getBody()); + + log.info("Received message from Redis: {}", publishMessage); // 메시지 내용 로깅 + + if (publishMessage.contains("message")) { + MessageResponse messageResponse = objectMapper.readValue(publishMessage, MessageResponse.class); + + log.info("Received message: {}", messageResponse.toString()); // message 보내지는 지 확인 + + GetMessageResponse chatMessageResponse = new GetMessageResponse(messageResponse); + + messagingTemplate.convertAndSend("/sub/chat/" + messageResponse.getReceiveId(), chatMessageResponse); + + } + } catch (IOException e) { + throw new RuntimeException("Failed to process message", e); + } + } +} \ No newline at end of file From 5b8ffb6e74c218b56ce41421d6804657d4f181f9 Mon Sep 17 00:00:00 2001 From: imjanghyeok Date: Fri, 3 May 2024 10:09:03 +0900 Subject: [PATCH 129/265] =?UTF-8?q?Feat:=20RedisSubscriber=EC=97=90?= =?UTF-8?q?=EC=84=9C=20destination=EC=9C=BC=EB=A1=9C=20=EB=B3=B4=EB=82=B4?= =?UTF-8?q?=EC=A7=80=EB=8A=94=20dto=20class=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/message/GetMessageResponse.java | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 src/main/java/capstone/facefriend/chat/service/dto/message/GetMessageResponse.java diff --git a/src/main/java/capstone/facefriend/chat/service/dto/message/GetMessageResponse.java b/src/main/java/capstone/facefriend/chat/service/dto/message/GetMessageResponse.java new file mode 100644 index 0000000000..04749cab84 --- /dev/null +++ b/src/main/java/capstone/facefriend/chat/service/dto/message/GetMessageResponse.java @@ -0,0 +1,31 @@ +package capstone.facefriend.chat.service.dto.message; + +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.LocalDateTime; + + +@Data +@NoArgsConstructor +public class GetMessageResponse { + private Long roomId; + private Long senderId; + private Long receiveId; + private String type; + private String content; + @JsonFormat(pattern = "yyyy-MM-dd hh:mm", timezone = "Asia/Seoul") + private LocalDateTime createdAt; + private Boolean isRead; + + public GetMessageResponse(MessageResponse messageResponse) { + this.roomId = messageResponse.getRoomId(); + this.senderId = messageResponse.getSenderId(); + this.receiveId = messageResponse.getReceiveId(); + this.type = messageResponse.getType(); + this.content = messageResponse.getContent(); + this.createdAt = LocalDateTime.now(); + this.isRead = messageResponse.getIsRead(); + } +} \ No newline at end of file From b9f2fe944ffc06fc37245adef08d7aed9f5bcf56 Mon Sep 17 00:00:00 2001 From: imjanghyeok Date: Fri, 3 May 2024 10:11:42 +0900 Subject: [PATCH 130/265] =?UTF-8?q?Feat:=20=EB=8C=80=ED=99=94=EB=A5=BC=20?= =?UTF-8?q?=EC=9A=94=EC=B2=AD=ED=95=98=EB=8A=94=20Sendheart=20service=20me?= =?UTF-8?q?thod=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../chat/service/MessageService.java | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/src/main/java/capstone/facefriend/chat/service/MessageService.java b/src/main/java/capstone/facefriend/chat/service/MessageService.java index ef9de64b2a..7e8f497b58 100644 --- a/src/main/java/capstone/facefriend/chat/service/MessageService.java +++ b/src/main/java/capstone/facefriend/chat/service/MessageService.java @@ -8,6 +8,8 @@ import capstone.facefriend.chat.repository.ChatMessageRepository; import capstone.facefriend.chat.repository.ChatRoomMemberRepository; import capstone.facefriend.chat.repository.ChatRoomRepository; +import capstone.facefriend.chat.service.dto.heart.HeartReplyRequest; +import capstone.facefriend.chat.service.dto.heart.SendHeartResponse; import capstone.facefriend.chat.service.dto.message.MessageRequest; import capstone.facefriend.chat.service.dto.message.MessageResponse; import capstone.facefriend.member.domain.Member; @@ -100,5 +102,47 @@ public void sendMessage(MessageRequest messageRequest, Long senderId) { redisTemplate.convertAndSend(topic, messageResponse); } + @Transactional + public void sendHeart(Long senderId, Long receiveId) { + + + ChatRoom chatRoom = ChatRoom.builder() + .status(ChatRoom.Status.set) + .isPublic(false) + .build(); + chatRoomRepository.save(chatRoom); + + Member sender = findMemberById(senderId); + Member receiver = findMemberById(receiveId); + + + if (chatRoomMemberRepository.findBySenderAndReceiver(senderId, receiveId).isPresent()) + throw new ChatException(ChatExceptionType.ALREADY_CHATROOM); + + + ChatRoomMember chatRoomMember = ChatRoomMember.builder() + .chatRoom(chatRoom) + .sender(sender) + .receiver(receiver) + .isSenderExist(false) + .isReceiverExist(false) + .isSenderPublic(false) + .isReceiverPublic(false) + .build(); + + chatRoomMemberRepository.save(chatRoomMember); + + SendHeartResponse sendHeartResponse = new SendHeartResponse(); + sendHeartResponse.setRoomId(chatRoom.getId()); + sendHeartResponse.setSenderId(sender.getId()); + sendHeartResponse.setReceiveId(receiveId); + sendHeartResponse.setSenderName(sender.getBasicInfo().getNickname()); + sendHeartResponse.setCreatedAt(LocalDateTime.now()); + sendHeartResponse.setType("Heart"); + + String topic = channelTopic.getTopic(); + log.info("-------------------send-heart---------------"); + redisTemplate.convertAndSend(topic, sendHeartResponse); + } } \ No newline at end of file From 3134d73a7fd67fbc507e552c600cec75dd455eee Mon Sep 17 00:00:00 2001 From: imjanghyeok Date: Fri, 3 May 2024 10:12:01 +0900 Subject: [PATCH 131/265] =?UTF-8?q?Feat:=20=EB=8C=80=ED=99=94=EB=A5=BC=20?= =?UTF-8?q?=EC=9A=94=EC=B2=AD=ED=95=98=EB=8A=94=20Sendheart=20=EA=B4=80?= =?UTF-8?q?=EB=A0=A8=20dto=20class=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/dto/heart/SendHeartRequest.java | 10 +++++++++ .../service/dto/heart/SendHeartResponse.java | 22 +++++++++++++++++++ 2 files changed, 32 insertions(+) create mode 100644 src/main/java/capstone/facefriend/chat/service/dto/heart/SendHeartRequest.java create mode 100644 src/main/java/capstone/facefriend/chat/service/dto/heart/SendHeartResponse.java diff --git a/src/main/java/capstone/facefriend/chat/service/dto/heart/SendHeartRequest.java b/src/main/java/capstone/facefriend/chat/service/dto/heart/SendHeartRequest.java new file mode 100644 index 0000000000..d572e66238 --- /dev/null +++ b/src/main/java/capstone/facefriend/chat/service/dto/heart/SendHeartRequest.java @@ -0,0 +1,10 @@ +package capstone.facefriend.chat.service.dto.heart; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class SendHeartRequest { + private Long receiveId; +} \ No newline at end of file diff --git a/src/main/java/capstone/facefriend/chat/service/dto/heart/SendHeartResponse.java b/src/main/java/capstone/facefriend/chat/service/dto/heart/SendHeartResponse.java new file mode 100644 index 0000000000..438e842d61 --- /dev/null +++ b/src/main/java/capstone/facefriend/chat/service/dto/heart/SendHeartResponse.java @@ -0,0 +1,22 @@ +package capstone.facefriend.chat.service.dto.heart; + +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.*; + +import java.io.Serializable; +import java.time.LocalDateTime; + +@Data +@NoArgsConstructor +@AllArgsConstructor +public class SendHeartResponse implements Serializable { + private Long roomId; + private Long senderId; + private Long receiveId; + private String senderName; + private String type; + private String sessionId; + @JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss", timezone = "Asia/Seoul") + private LocalDateTime createdAt; + +} \ No newline at end of file From 571719d5195288f6bfe8c8ecec08a99ca82ce577 Mon Sep 17 00:00:00 2001 From: imjanghyeok Date: Fri, 3 May 2024 10:13:17 +0900 Subject: [PATCH 132/265] =?UTF-8?q?Feat:=20controller=EC=97=90=20=EB=8C=80?= =?UTF-8?q?=ED=99=94=EB=A5=BC=20=EC=9A=94=EC=B2=AD=ED=95=98=EB=8A=94=20Sen?= =?UTF-8?q?dheart=20mapping=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../chat/controller/MessageController.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/main/java/capstone/facefriend/chat/controller/MessageController.java b/src/main/java/capstone/facefriend/chat/controller/MessageController.java index 9d3ee29e2b..af93ca8a99 100644 --- a/src/main/java/capstone/facefriend/chat/controller/MessageController.java +++ b/src/main/java/capstone/facefriend/chat/controller/MessageController.java @@ -44,6 +44,21 @@ public void message( messageService.sendMessage(messageRequest, senderId); } + @MessageMapping("/chat/send-heart") + public void sendheart( + StompHeaderAccessor headerAccessor, + @RequestBody SendHeartRequest sendHeartRequest + ){ + String authorizationHeader = headerAccessor.getFirstNativeHeader("Authorization"); + String token = authorizationHeader.substring(BEARER_PREFIX.length()); + log.info("token: {}",token); + Long senderId = jwtProvider.extractId(token); + log.info("senderId: {}",senderId.toString()); + String destination = headerAccessor.getDestination(); + messageService.sendHeart(senderId, sendHeartRequest.getReceiveId()); + } + + } \ No newline at end of file From 47514aa6a45819fd6c12b0a1f45eca40803375b5 Mon Sep 17 00:00:00 2001 From: imjanghyeok Date: Fri, 3 May 2024 10:14:18 +0900 Subject: [PATCH 133/265] =?UTF-8?q?Feat:=20type=20=EB=94=B0=EB=9D=BC=20?= =?UTF-8?q?=EC=A0=84=EC=86=A1=ED=95=98=EB=8A=94=20=EA=B0=9D=EC=B2=B4?= =?UTF-8?q?=EB=A5=BC=20=EB=B0=94=EA=BE=B8=EB=8A=94=20subscribe=20=EC=BD=94?= =?UTF-8?q?=EB=93=9C=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../capstone/facefriend/chat/service/RedisSubscriber.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/main/java/capstone/facefriend/chat/service/RedisSubscriber.java b/src/main/java/capstone/facefriend/chat/service/RedisSubscriber.java index 7f578c8839..08138b79ad 100644 --- a/src/main/java/capstone/facefriend/chat/service/RedisSubscriber.java +++ b/src/main/java/capstone/facefriend/chat/service/RedisSubscriber.java @@ -42,6 +42,12 @@ public void onMessage(Message message, byte[] pattern) { messagingTemplate.convertAndSend("/sub/chat/" + messageResponse.getReceiveId(), chatMessageResponse); + } else if (publishMessage.contains("Heart")) { + SendHeartResponse sendHeartResponse = objectMapper.readValue(publishMessage, SendHeartResponse.class); + + GetSendHeartResponse chatSendHeartResponse = new GetSendHeartResponse(sendHeartResponse); + + messagingTemplate.convertAndSend("/sub/chat/" + sendHeartResponse.getReceiveId(), chatSendHeartResponse); } } catch (IOException e) { throw new RuntimeException("Failed to process message", e); From fd974c77caf9390b91d1c1a525d93b325236a563 Mon Sep 17 00:00:00 2001 From: imjanghyeok Date: Fri, 3 May 2024 10:16:14 +0900 Subject: [PATCH 134/265] =?UTF-8?q?Feat:=20=EB=8C=80=ED=99=94=20=EC=9A=94?= =?UTF-8?q?=EC=B2=AD/=EA=B1=B0=EC=A0=88=EC=97=90=20=EB=8C=80=ED=95=9C=20se?= =?UTF-8?q?rvice=20method=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../chat/service/MessageService.java | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/src/main/java/capstone/facefriend/chat/service/MessageService.java b/src/main/java/capstone/facefriend/chat/service/MessageService.java index 7e8f497b58..c3eb0752b4 100644 --- a/src/main/java/capstone/facefriend/chat/service/MessageService.java +++ b/src/main/java/capstone/facefriend/chat/service/MessageService.java @@ -144,5 +144,34 @@ public void sendHeart(Long senderId, Long receiveId) { log.info("-------------------send-heart---------------"); redisTemplate.convertAndSend(topic, sendHeartResponse); } + @Transactional + public void heartReply(HeartReplyRequest heartReplyRequest, Long receiveId) { + String message = null; + + Member receiver = findMemberById(receiveId); + Member sender = findMemberById(heartReplyRequest.getSenderId()); + ChatRoomMember chatRoomMember = findSenderReceiver(sender.getId(), receiver.getId()); + ChatRoom chatRoom = findRoomById(chatRoomMember.getChatRoom().getId()); + + if (heartReplyRequest.getIntention().equals("positive")) { + chatRoom.setStatus(ChatRoom.Status.open); + chatRoomRepository.save(chatRoom); + chatRoomMemberRepository.save(chatRoomMember); + + message = receiver.getBasicInfo().getNickname() + "님이 수락했습니다."; + } else if (heartReplyRequest.getIntention().equals("negative")) { + chatRoomMemberRepository.delete(chatRoomMember); + chatRoomRepository.delete(chatRoom); + + message = receiver.getBasicInfo().getNickname() + "님이 거절했습니다."; + } else { + throw new ChatException(ChatExceptionType.ALREADY_CHATROOM); + } + // 동적으로 목적지 설정 + String destination = "/sub/chat/" + sender.getId(); + + // 메시지 전송 + messagingTemplate.convertAndSend(destination, message); + } } \ No newline at end of file From 57660d0aceb678e2da269347bb1ece94eef539d4 Mon Sep 17 00:00:00 2001 From: imjanghyeok Date: Fri, 3 May 2024 10:16:26 +0900 Subject: [PATCH 135/265] =?UTF-8?q?Feat:=20=EB=8C=80=ED=99=94=20=EC=9A=94?= =?UTF-8?q?=EC=B2=AD/=EA=B1=B0=EC=A0=88=EC=97=90=20=EB=8C=80=ED=95=9C=20?= =?UTF-8?q?=ED=95=84=EC=9A=94=20dto=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../chat/service/dto/heart/HeartReplyRequest.java | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 src/main/java/capstone/facefriend/chat/service/dto/heart/HeartReplyRequest.java diff --git a/src/main/java/capstone/facefriend/chat/service/dto/heart/HeartReplyRequest.java b/src/main/java/capstone/facefriend/chat/service/dto/heart/HeartReplyRequest.java new file mode 100644 index 0000000000..22b42fdfa4 --- /dev/null +++ b/src/main/java/capstone/facefriend/chat/service/dto/heart/HeartReplyRequest.java @@ -0,0 +1,11 @@ +package capstone.facefriend.chat.service.dto.heart; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class HeartReplyRequest { + Long senderId; + String intention; +} \ No newline at end of file From a9de68b4efd98f9959feb05747ad015d7656ecd6 Mon Sep 17 00:00:00 2001 From: imjanghyeok Date: Fri, 3 May 2024 10:16:58 +0900 Subject: [PATCH 136/265] =?UTF-8?q?Feat:=20controller=EC=97=90=20=EB=8C=80?= =?UTF-8?q?=ED=99=94=20=EC=9A=94=EC=B2=AD/=EA=B1=B0=EC=A0=88=20mapping=20?= =?UTF-8?q?=EB=93=B1=EB=A1=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../chat/controller/MessageController.java | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/main/java/capstone/facefriend/chat/controller/MessageController.java b/src/main/java/capstone/facefriend/chat/controller/MessageController.java index af93ca8a99..9ff7558b7b 100644 --- a/src/main/java/capstone/facefriend/chat/controller/MessageController.java +++ b/src/main/java/capstone/facefriend/chat/controller/MessageController.java @@ -58,7 +58,18 @@ public void sendheart( messageService.sendHeart(senderId, sendHeartRequest.getReceiveId()); } + @MessageMapping("/chat/heart-reply") + public void heartreply( + StompHeaderAccessor headerAccessor, + HeartReplyRequest heartReplyRequest + ) { + String authorizationHeader = headerAccessor.getFirstNativeHeader("Authorization"); + String token = authorizationHeader.substring(BEARER_PREFIX.length()); + log.info("token: {}",token); + Long receiveId = jwtProvider.extractId(token); + log.info("receiveId: {}",receiveId.toString()); - + messageService.heartReply(heartReplyRequest, receiveId); + } } \ No newline at end of file From 974fd5456025a0d8ace3368fed77a74fdac29926 Mon Sep 17 00:00:00 2001 From: imjanghyeok Date: Fri, 3 May 2024 11:11:29 +0900 Subject: [PATCH 137/265] =?UTF-8?q?Feat:=20=EB=8B=89=EB=84=A4=EC=9E=84=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=EB=A5=BC=20=EC=9C=84=ED=95=9C=20response=20d?= =?UTF-8?q?to=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../facefriend/chat/service/dto/message/GetMessageResponse.java | 2 ++ .../facefriend/chat/service/dto/message/MessageResponse.java | 1 + 2 files changed, 3 insertions(+) diff --git a/src/main/java/capstone/facefriend/chat/service/dto/message/GetMessageResponse.java b/src/main/java/capstone/facefriend/chat/service/dto/message/GetMessageResponse.java index 04749cab84..2b80749085 100644 --- a/src/main/java/capstone/facefriend/chat/service/dto/message/GetMessageResponse.java +++ b/src/main/java/capstone/facefriend/chat/service/dto/message/GetMessageResponse.java @@ -13,6 +13,7 @@ public class GetMessageResponse { private Long roomId; private Long senderId; private Long receiveId; + private String senderNickname; private String type; private String content; @JsonFormat(pattern = "yyyy-MM-dd hh:mm", timezone = "Asia/Seoul") @@ -23,6 +24,7 @@ public GetMessageResponse(MessageResponse messageResponse) { this.roomId = messageResponse.getRoomId(); this.senderId = messageResponse.getSenderId(); this.receiveId = messageResponse.getReceiveId(); + this.senderNickname = messageResponse.getSenderNickname(); this.type = messageResponse.getType(); this.content = messageResponse.getContent(); this.createdAt = LocalDateTime.now(); diff --git a/src/main/java/capstone/facefriend/chat/service/dto/message/MessageResponse.java b/src/main/java/capstone/facefriend/chat/service/dto/message/MessageResponse.java index 0a12830f2f..cb55138300 100644 --- a/src/main/java/capstone/facefriend/chat/service/dto/message/MessageResponse.java +++ b/src/main/java/capstone/facefriend/chat/service/dto/message/MessageResponse.java @@ -18,6 +18,7 @@ public class MessageResponse implements Serializable { private Long senderId; private String content; private String type; + private String senderNickname; @JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss", timezone = "Asia/Seoul") private LocalDateTime createdAt; private Boolean isRead; From cb487954dea03957734f312ccc7c49fcf41696d2 Mon Sep 17 00:00:00 2001 From: imjanghyeok Date: Fri, 3 May 2024 11:12:00 +0900 Subject: [PATCH 138/265] =?UTF-8?q?Feat:=20service=20method=EC=97=90=20?= =?UTF-8?q?=EB=8B=89=EB=84=A4=EC=9E=84=EB=8F=84=20=EA=B0=99=EC=9D=B4=20?= =?UTF-8?q?=EB=B3=B4=EB=82=B4=EB=8A=94=20=EB=A1=9C=EC=A7=81=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/capstone/facefriend/chat/service/MessageService.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/capstone/facefriend/chat/service/MessageService.java b/src/main/java/capstone/facefriend/chat/service/MessageService.java index c3eb0752b4..b8495494e6 100644 --- a/src/main/java/capstone/facefriend/chat/service/MessageService.java +++ b/src/main/java/capstone/facefriend/chat/service/MessageService.java @@ -92,6 +92,7 @@ public void sendMessage(MessageRequest messageRequest, Long senderId) { messageResponse.setRoomId(chatMessage.getChatRoom().getId()); messageResponse.setSenderId(senderId); messageResponse.setReceiveId(receiver.getId()); + messageResponse.setSenderNickname(sender.getBasicInfo().getNickname()); messageResponse.setContent(chatMessage.getContent()); messageResponse.setType("message"); messageResponse.setCreatedAt(chatMessage.getSendTime()); From e321f4aec248cf0ea23d3e30b4ac91412d016722 Mon Sep 17 00:00:00 2001 From: imjanghyeok Date: Fri, 3 May 2024 11:13:49 +0900 Subject: [PATCH 139/265] =?UTF-8?q?Chore:=20dev=20application.yml=20?= =?UTF-8?q?=EC=95=88=EC=98=AC=EB=9D=BC=EA=B0=80=EA=B2=8C=20ignore=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/.gitignore b/.gitignore index 1a6145ee49..6fcbd2dd3a 100644 --- a/.gitignore +++ b/.gitignore @@ -30,6 +30,11 @@ # Gradle .idea/**/gradle.xml .idea/**/libraries +gradle/caches +gradle/.tmp +gradle/daemon +gradle/jdks +gradle/native # Gradle and Maven with auto-import # When using Gradle or Maven with auto-import, you should exclude module files, @@ -286,8 +291,4 @@ gradle-app.setting # End of https://www.toptal.com/developers/gitignore/api/java,intellij,gradle,macos,windows,intellij+all -gradle/caches -gradle/.tmp -gradle/daemon -gradle/jdks -gradle/native \ No newline at end of file +application-dev.yml \ No newline at end of file From 0c893fed5e45675e3cfad864e901604e6ba967d3 Mon Sep 17 00:00:00 2001 From: imjanghyeok Date: Fri, 3 May 2024 11:14:24 +0900 Subject: [PATCH 140/265] =?UTF-8?q?Chore:=20dev,=20prod=20=EA=B5=AC?= =?UTF-8?q?=EB=B6=84=20=EB=B0=8F=20=EC=A3=BC=EC=84=9D=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../capstone/facefriend/config/JasyptConfig.java | 2 ++ src/main/resources/application.yml | 15 +++++++++++---- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/main/java/capstone/facefriend/config/JasyptConfig.java b/src/main/java/capstone/facefriend/config/JasyptConfig.java index b6d75f11a2..3c95b047f3 100644 --- a/src/main/java/capstone/facefriend/config/JasyptConfig.java +++ b/src/main/java/capstone/facefriend/config/JasyptConfig.java @@ -6,8 +6,10 @@ import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Profile; @Configuration +@Profile({"prod"}) // profile 설정을 통해 prod, dev를 구분 public class JasyptConfig { @Value("${jasypt.encryptor.password}") diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 43a857c01b..92d6385fe5 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -1,4 +1,11 @@ -jasypt: - encryptor: - bean: jasyptStringEncryptor - password: ${ENCRYPT_KEY} \ No newline at end of file +#production에서 실행할 때 +#jasypt: +# encryptor: +# bean: jasyptStringEncryptor +# password: ${ENCRYPT_KEY} + + +# local에서 실행할 때 +spring: + profiles: + active: dev \ No newline at end of file From bf7e88dc72f59aef36064c18bc87e4dd5c234697 Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Fri, 3 May 2024 14:15:37 +0900 Subject: [PATCH 141/265] =?UTF-8?q?fix:=20ci=20=EB=B2=84=EA=B7=B8=EB=A1=9C?= =?UTF-8?q?=20=EB=A1=A4=EB=B0=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../capstone/facefriend/DummyInitializer.java | 29 +++ .../facefriend/FacefriendApplication.java | 3 + .../facefriend/bucket/BucketService.java | 48 +++- .../member/domain/member/Member.java | 21 +- .../resume/controller/ResumeController.java | 68 ++++-- .../facefriend/resume/domain/Resume.java | 1 + .../resume/domain/ResumeRepository.java | 4 + .../resume/domain/ResumeRepositoryCustom.java | 13 +- .../resume/domain/ResumeRepositoryImpl.java | 217 +++++------------- .../resume/domain/dto/ResumeCondition.java | 6 + .../domain/dto/ResumeDeleteResponse.java | 6 + .../resume/domain/dto/ResumePostRequest.java | 13 ++ ...sumeRequest.java => ResumePutRequest.java} | 2 +- .../resume/domain/dto/ResumeResponse.java | 9 +- .../resume/exception/ResumeExceptionType.java | 4 +- .../resume/service/ResumeService.java | 118 ++++++---- 16 files changed, 299 insertions(+), 263 deletions(-) create mode 100644 src/main/java/capstone/facefriend/DummyInitializer.java create mode 100644 src/main/java/capstone/facefriend/resume/domain/dto/ResumeCondition.java create mode 100644 src/main/java/capstone/facefriend/resume/domain/dto/ResumeDeleteResponse.java create mode 100644 src/main/java/capstone/facefriend/resume/domain/dto/ResumePostRequest.java rename src/main/java/capstone/facefriend/resume/domain/dto/{ResumeRequest.java => ResumePutRequest.java} (79%) diff --git a/src/main/java/capstone/facefriend/DummyInitializer.java b/src/main/java/capstone/facefriend/DummyInitializer.java new file mode 100644 index 0000000000..37f24f6c42 --- /dev/null +++ b/src/main/java/capstone/facefriend/DummyInitializer.java @@ -0,0 +1,29 @@ +package capstone.facefriend; + +import capstone.facefriend.member.domain.member.Member; +import capstone.facefriend.member.domain.member.MemberRepository; +import capstone.facefriend.resume.domain.Resume; +import capstone.facefriend.resume.domain.ResumeRepository; +import jakarta.annotation.PostConstruct; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +@Component +@RequiredArgsConstructor +public class DummyInitializer { + + private final MemberRepository memberRepository; + private final ResumeRepository resumeRepository; + + @PostConstruct + public void init() { + + for (long i = 0; i < 100; i++) { + Member member = Member.builder().id(i).build(); + memberRepository.save(member); + + Resume resume = Resume.builder().id(i).member(member).build(); + resumeRepository.save(resume); + } + } +} diff --git a/src/main/java/capstone/facefriend/FacefriendApplication.java b/src/main/java/capstone/facefriend/FacefriendApplication.java index c87661e736..0bab914e34 100644 --- a/src/main/java/capstone/facefriend/FacefriendApplication.java +++ b/src/main/java/capstone/facefriend/FacefriendApplication.java @@ -1,5 +1,8 @@ package capstone.facefriend; +import capstone.facefriend.member.domain.member.Member; +import capstone.facefriend.resume.domain.Resume; +import jakarta.annotation.PostConstruct; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.data.jpa.repository.config.EnableJpaAuditing; diff --git a/src/main/java/capstone/facefriend/bucket/BucketService.java b/src/main/java/capstone/facefriend/bucket/BucketService.java index 0a7daffb1f..4eb6b54618 100644 --- a/src/main/java/capstone/facefriend/bucket/BucketService.java +++ b/src/main/java/capstone/facefriend/bucket/BucketService.java @@ -49,7 +49,11 @@ public class BucketService { private final FaceInfoRepository faceInfoRepository; // FaceInfo : origin 업로드 & generated 업로드 - public FaceInfoResponse uploadOriginAndGenerated(MultipartFile origin, ByteArrayMultipartFile generated, Long memberId) throws IOException { + public FaceInfoResponse uploadOriginAndGenerated( + MultipartFile origin, + ByteArrayMultipartFile generated, + Long memberId + ) throws IOException { /** upload origin to s3 */ // set metadata ObjectMetadata originMetadata = new ObjectMetadata(); @@ -100,13 +104,19 @@ public FaceInfoResponse uploadOriginAndGenerated(MultipartFile origin, ByteArray } // FaceInfo : origin 수정 -> generated 수정 - public FaceInfoResponse updateOriginAndGenerated(MultipartFile origin, ByteArrayMultipartFile generated, Long memberId) throws IOException { + public FaceInfoResponse updateOriginAndGenerated( + MultipartFile origin, + ByteArrayMultipartFile generated, + Long memberId + ) throws IOException { deleteOriginAndGenerated(memberId); // 기존에 저장되어있던 사진 삭제 return uploadOriginAndGenerated(origin, generated, memberId); // 새로 사진 저장 } // FaceInfo : origin 삭제 -> generated 삭제 - public FaceInfoResponse deleteOriginAndGenerated(Long memberId) { + public FaceInfoResponse deleteOriginAndGenerated( + Long memberId + ) { String originObjectName = memberId + originPostfix; amazonS3.deleteObject(new DeleteObjectRequest(bucketName, originObjectName)); @@ -116,8 +126,12 @@ public FaceInfoResponse deleteOriginAndGenerated(Long memberId) { return new FaceInfoResponse(defaultProfileS3Url, defaultProfileS3Url); } - public List uploadResumeImages(List images, Long memberId) throws IOException { - int start = 0; + // Resume : images 업로드 + public List uploadResumeImages( + List images, + Long memberId + ) throws IOException { + int idx = 0; List resumeImageS3urls = new ArrayList<>(); for (MultipartFile image : images) { @@ -125,7 +139,7 @@ public List uploadResumeImages(List images, Long memberId metadata.setContentLength(image.getInputStream().available()); metadata.setContentType(image.getContentType()); - String imageObjectName = memberId + resumeInfix + start; + String imageObjectName = memberId + resumeInfix + idx; amazonS3.putObject( new PutObjectRequest( bucketName, @@ -136,17 +150,31 @@ public List uploadResumeImages(List images, Long memberId ); resumeImageS3urls.add(amazonS3.getUrl(bucketName, imageObjectName).toString()); + idx++; } return resumeImageS3urls; } - public List updateResumeImages() { - return null; + // Resume : images 삭제 -> images 업로드 + public List updateResumeImages( + List images, + Long memberId, + int size + ) throws IOException { + deleteResumeImages(memberId,size); + return uploadResumeImages(images, memberId); } - public List deleteResumeImages(Long memberId) { - String imageObjectName = memberId + resumeInfix + + // Resume : images 삭제 + public void deleteResumeImages( + Long memberId, + int size + ) { + for (int idx = 0; idx < size; idx++) { + String imageObjectName = memberId + resumeInfix + idx; + amazonS3.deleteObject(new DeleteObjectRequest(bucketName, imageObjectName)); + } } } diff --git a/src/main/java/capstone/facefriend/member/domain/member/Member.java b/src/main/java/capstone/facefriend/member/domain/member/Member.java index a792447a32..347d49bfc8 100644 --- a/src/main/java/capstone/facefriend/member/domain/member/Member.java +++ b/src/main/java/capstone/facefriend/member/domain/member/Member.java @@ -11,6 +11,7 @@ import org.hibernate.annotations.DynamicUpdate; @Getter +@Setter @Builder @EqualsAndHashCode(of = "id", callSuper = false) @AllArgsConstructor(access = AccessLevel.PRIVATE) @@ -59,24 +60,4 @@ public boolean isAdmin() { public boolean isSame(Long id) { return this.id.equals(id); } - - public void setRole(Role role) { - this.role = role; - } - - public void setBasicInfo(BasicInfo basicInfo) { - this.basicInfo = basicInfo; - } - - public void setFaceInfo(FaceInfo faceInfo) { - this.faceInfo = faceInfo; - } - - public void setAnalysisInfo(AnalysisInfo analysisInfo) { - this.analysisInfo = analysisInfo; - } - - public void setPassword(String password) { - this.password = password; - } } \ No newline at end of file diff --git a/src/main/java/capstone/facefriend/resume/controller/ResumeController.java b/src/main/java/capstone/facefriend/resume/controller/ResumeController.java index 408e820137..2e9c2c9cc0 100644 --- a/src/main/java/capstone/facefriend/resume/controller/ResumeController.java +++ b/src/main/java/capstone/facefriend/resume/controller/ResumeController.java @@ -1,11 +1,12 @@ package capstone.facefriend.resume.controller; import capstone.facefriend.auth.controller.support.AuthMember; -import capstone.facefriend.resume.domain.dto.ResumeHomeDetailResponse; -import capstone.facefriend.resume.domain.dto.ResumeRequest; +import capstone.facefriend.resume.domain.dto.*; import capstone.facefriend.resume.service.ResumeService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; @@ -21,41 +22,64 @@ public class ResumeController { private final ResumeService resumeService; - /** 특정 페이지 **/ + // 정적 쿼리 @PostMapping("/resume") - public ResponseEntity postResume( - @RequestPart List images, + public ResponseEntity postResume( @AuthMember Long memberId, - ResumeRequest request + @RequestPart("images") List images, + @RequestPart("request") ResumePostRequest request ) throws IOException { - resumeService.postResume(memberId, images, request); - + return ResponseEntity.ok(resumeService.postResume(memberId, images, request)); } - - /** 홈 페이지 **/ @GetMapping("/resume") - public List getHomeResumesByGoodCombi( - @AuthMember Long memberId + public ResponseEntity getResume( + @AuthMember Long memberId, + @RequestParam("resumeId") Long resumeId ) { - return resumeService.getHomeResumesByGoodCombi(memberId); + return ResponseEntity.ok(resumeService.getResume(memberId, resumeId)); } - @GetMapping("/resume") - public List getHomeResumesByBadCombi( - @AuthMember Long memberId + @PutMapping("/resume") + public ResponseEntity putResume( + @AuthMember Long memberId, + @RequestPart("images") List images, + @RequestPart("request") ResumePutRequest request + ) throws IOException { + return ResponseEntity.ok(resumeService.putResume(memberId, images, request)); + } + + @DeleteMapping("/resume") + public ResponseEntity deleteResume( + @AuthMember Long memberId, + @RequestParam("resumeId") Long resumeId ) { - return resumeService.getHomeResumesByBadCombi(memberId) + return ResponseEntity.ok(resumeService.deleteResume(memberId, resumeId)); } - @GetMapping("/resume") - public List getDetailResumesByCategory( + // 동적 쿼리 + @GetMapping("/resume-by-good-combi") + public Page getResumesByGoodCombi( @AuthMember Long memberId, - String category + Pageable pageable ) { - return resumeService.getDetailResumesByCategory(category) + return resumeService.getResumesByGoodCombi(memberId, pageable); } - /** 디테일 페이지 **/ + @GetMapping("/resume-by-bad-combi") + public Page getResumesByBadCombi( + @AuthMember Long memberId, + Pageable pageable + ) { + return resumeService.getResumesByBadCombi(memberId, pageable); + } + @GetMapping("/resume-by-category") + public Page getResumesByCategory( + @AuthMember Long memberId, + @RequestParam String category, + Pageable pageable + ) { + return resumeService.getResumesByCategory(category, pageable); + } } diff --git a/src/main/java/capstone/facefriend/resume/domain/Resume.java b/src/main/java/capstone/facefriend/resume/domain/Resume.java index 08286469b5..5f2dad934b 100644 --- a/src/main/java/capstone/facefriend/resume/domain/Resume.java +++ b/src/main/java/capstone/facefriend/resume/domain/Resume.java @@ -12,6 +12,7 @@ import java.util.Map; @Getter +@Setter @Builder @EqualsAndHashCode(of = "id", callSuper = false) @AllArgsConstructor(access = AccessLevel.PRIVATE) diff --git a/src/main/java/capstone/facefriend/resume/domain/ResumeRepository.java b/src/main/java/capstone/facefriend/resume/domain/ResumeRepository.java index 5dcf272f3a..369520470e 100644 --- a/src/main/java/capstone/facefriend/resume/domain/ResumeRepository.java +++ b/src/main/java/capstone/facefriend/resume/domain/ResumeRepository.java @@ -1,8 +1,10 @@ package capstone.facefriend.resume.domain; import capstone.facefriend.member.domain.member.Member; +import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.Repository; +import java.util.List; import java.util.Optional; public interface ResumeRepository extends Repository, ResumeRepositoryCustom { @@ -12,4 +14,6 @@ public interface ResumeRepository extends Repository, ResumeReposi Optional findResumeByMember(Member member); Optional findResumeById(Long id); + + void deleteResumeById(Long id); } diff --git a/src/main/java/capstone/facefriend/resume/domain/ResumeRepositoryCustom.java b/src/main/java/capstone/facefriend/resume/domain/ResumeRepositoryCustom.java index 9c7861f067..462581c91a 100644 --- a/src/main/java/capstone/facefriend/resume/domain/ResumeRepositoryCustom.java +++ b/src/main/java/capstone/facefriend/resume/domain/ResumeRepositoryCustom.java @@ -1,19 +1,14 @@ package capstone.facefriend.resume.domain; import capstone.facefriend.resume.domain.dto.ResumeHomeDetailResponse; +import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import java.util.List; public interface ResumeRepositoryCustom { - // 홈 페이지 : 20개 - List getHomeResumesByGoodCombi(Long memberId, Pageable pageable); // 관상 궁합 좋은 20개 - List getHomeResumesByBadCombi(Long memberId, Pageable pageable); // 관상 궁합 나쁜 20개 - List getHomeResumesByCategory(String category, Pageable pageable); // 카테고리별 20개 - - // 디테일 페이지 : 10개 - List getDetailResumesByGoodCombi(Long memberId, Pageable pageable); // 관상 궁합 좋은 10개 - List getDetailResumesByBadCombi(Long memberId, Pageable pageable); // 관상 궁합 나쁜 10개 - List getDetailResumesByCategory(String category, Pageable pageable); // 카테고리별 10개 + Page getResumesByGoodCombi(Long memberId, Pageable pageable); // 좋은 궁합 + Page getResumesByBadCombi(Long memberId, Pageable pageable); // 나쁜 궁합 + Page getResumesByCategory(String category, Pageable pageable); // 카테고리별 } diff --git a/src/main/java/capstone/facefriend/resume/domain/ResumeRepositoryImpl.java b/src/main/java/capstone/facefriend/resume/domain/ResumeRepositoryImpl.java index 92e3c2cd39..c2356cf982 100644 --- a/src/main/java/capstone/facefriend/resume/domain/ResumeRepositoryImpl.java +++ b/src/main/java/capstone/facefriend/resume/domain/ResumeRepositoryImpl.java @@ -8,23 +8,20 @@ import capstone.facefriend.resume.domain.dto.ResumeHomeDetailResponse; import capstone.facefriend.resume.domain.dto.ResumeResponse; import capstone.facefriend.resume.exception.ResumeException; -import capstone.facefriend.resume.exception.ResumeExceptionType; import com.querydsl.core.BooleanBuilder; -import com.querydsl.core.QueryResults; import com.querydsl.core.types.Projections; import com.querydsl.jpa.impl.JPAQueryFactory; import jakarta.persistence.EntityManager; import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.Pageable; -import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Repository; -import org.springframework.web.multipart.MultipartFile; import java.util.List; import static capstone.facefriend.member.exception.member.MemberExceptionType.NOT_FOUND; import static capstone.facefriend.resume.domain.QResume.resume; -import static capstone.facefriend.resume.exception.ResumeExceptionType.*; import static capstone.facefriend.resume.exception.ResumeExceptionType.NO_RESUME; @Repository @@ -34,56 +31,24 @@ public class ResumeRepositoryImpl implements ResumeRepositoryCustom { private final EntityManager em; private final JPAQueryFactory queryFactory; - private final EntityManager em; - private final JPAQueryFactory queryFactory; - private final MemberRepository memberRepository; private final ResumeRepository resumeRepository; private final BucketService bucketService; - private static final List GOOD_COMBI_IN_CASE_0 = List.of(2,4); // 화 - private static final List GOOD_COMBI_IN_CASE_1 = List.of(2,4); // 수 - private static final List GOOD_COMBI_IN_CASE_2 = List.of(0,1); // 목 - private static final List GOOD_COMBI_IN_CASE_3 = List.of(1,4); // 금 - private static final List GOOD_COMBI_IN_CASE_4 = List.of(0,3); // 토 - - private static final List BAD_COMBI_IN_CASE_0 = List.of(1,3); // 화 - private static final List BAD_COMBI_IN_CASE_1 = List.of(0,4); // 수 - private static final List BAD_COMBI_IN_CASE_2 = List.of(3,4); // 목 - private static final List BAD_COMBI_IN_CASE_3 = List.of(0,2); // 금 - private static final List BAD_COMBI_IN_CASE_4 = List.of(1,2); // 토 - - - private Member findMemberById(Long memberId) { - return memberRepository.findById(memberId) - .orElseThrow(() -> new MemberException(NOT_FOUND)); - } + private static final List GOOD_COMBI_IN_CASE_0 = List.of(2, 4); // 화 + private static final List GOOD_COMBI_IN_CASE_1 = List.of(2, 4); // 수 + private static final List GOOD_COMBI_IN_CASE_2 = List.of(0, 1); // 목 + private static final List GOOD_COMBI_IN_CASE_3 = List.of(1, 4); // 금 + private static final List GOOD_COMBI_IN_CASE_4 = List.of(0, 3); // 토 - private Resume findResumeByMember(Member member) { - return resumeRepository.findResumeByMember(member) - .orElseThrow(() -> new ResumeException(NO_RESUME)); - } + private static final List BAD_COMBI_IN_CASE_0 = List.of(1, 3); // 화 + private static final List BAD_COMBI_IN_CASE_1 = List.of(0, 4); // 수 + private static final List BAD_COMBI_IN_CASE_2 = List.of(3, 4); // 목 + private static final List BAD_COMBI_IN_CASE_3 = List.of(0, 2); // 금 + private static final List BAD_COMBI_IN_CASE_4 = List.of(1, 2); // 토 - /** 특정 페이지 **/ - public ResumeResponse postResume() { - - } - - public ResumeResponse getResume(Long resumeId) { - - } - - public ResumeResponse putResume(Long resumeId) { - - } - - public ResumeResponse deleteResume(Long resumeId) { - - } - - /** 홈 페이지 **/ - // 홈 페이지에서 궁합 좋은 관상 조회하는 동적 쿼리 (20개) - public List getHomeResumesByGoodCombi(Long memberId, Pageable pageable) { + // 좋은 궁합 동적 쿼리 + public Page getResumesByGoodCombi(Long memberId, Pageable pageable) { Member me = findMemberById(memberId); Integer faceShapeIdNum = me.getAnalysisInfo().getFaceShapeIdNum(); @@ -106,24 +71,32 @@ public List getHomeResumesByGoodCombi(Long memberId, P break; } - List results = queryFactory + List content = queryFactory .select(Projections.bean(ResumeHomeDetailResponse.class, resume.id, resume.member.faceInfo.generatedS3url )) .from(resume) - .leftJoin(resume.member, QMember.member) // join - .where(builder) + .leftJoin(resume.member, QMember.member) // left join + .where(builder) // boolean builder .orderBy(resume.id.desc()) .offset(pageable.getOffset()) .limit(pageable.getPageSize()) .fetch(); + int total = queryFactory + .select(resume) + .from(resume) + .leftJoin(resume.member, QMember.member) // left join + .where(builder) // boolean builder + .fetch() + .size(); + return new PageImpl<>(content, pageable, total); } - // 홈페이지에서 궁합 나쁜 관상 조회하는 동적 쿼리 (20개) - public List getHomeResumesByBadCombi(Long memberId, Pageable pageable) { + // 나쁜 궁합 동적 쿼리 + public Page getResumesByBadCombi(Long memberId, Pageable pageable) { Member me = findMemberById(memberId); Integer faceShapeIdNum = me.getAnalysisInfo().getFaceShapeIdNum(); @@ -146,127 +119,63 @@ public List getHomeResumesByBadCombi(Long memberId, Pa break; } - return queryFactory - .select(Projections.bean(ResumeHomeDetailResponse.class, - resume.id, - resume.member.faceInfo.generatedS3url - )) - .from(resume) - .leftJoin(resume.member, QMember.member) // join - .where(builder) - .orderBy(resume.id.desc()) - .offset(0) - .limit(10) - .fetch(); - } -} - - // 홈 페이지에서 카테고리별 조회하는 동적 쿼리 (10개) - public List getHomeResumesByCategory(String category, Pageable pageable) { - return queryFactory + List content = queryFactory .select(Projections.bean(ResumeHomeDetailResponse.class, resume.id, resume.member.faceInfo.generatedS3url )) .from(resume) - .leftJoin(resume.member, QMember.member) // join - .where(resume.category.eq(Resume.Category.valueOf(category))) + .leftJoin(resume.member, QMember.member) // left join + .where(builder) // boolean builder .orderBy(resume.id.desc()) - .offset(0) - .limit(10) + .offset(pageable.getOffset()) + .limit(pageable.getPageSize()) .fetch(); - } + int total = queryFactory + .select(resume) + .from(resume) + .leftJoin(resume.member, QMember.member) // left join + .where(builder) // boolean builder + .fetch() + .size(); - /** 디테일 페이지 **/ - // 디테일 페이지에서 궁합 좋은 관상 조회하는 동적 쿼리 (20개) - public List getDetailResumesByGoodCombi(Long memberId, Pageable pageable) { - Member member = findMemberById(memberId); - Integer faceShapeIdNum = member.getAnalysisInfo().getFaceShapeIdNum(); - - BooleanBuilder builder = new BooleanBuilder(); - switch (faceShapeIdNum) { - case 0: // 화 - builder.and(resume.member.analysisInfo.faceShapeIdNum.in(GOOD_COMBI_IN_CASE_0)); - break; - case 1: // 수 - builder.and(resume.member.analysisInfo.faceShapeIdNum.in(GOOD_COMBI_IN_CASE_1)); - break; - case 2: // 목 - builder.and(resume.member.analysisInfo.faceShapeIdNum.in(GOOD_COMBI_IN_CASE_2)); - break; - case 3: // 금 - builder.and(resume.member.analysisInfo.faceShapeIdNum.in(GOOD_COMBI_IN_CASE_3)); - break; - case 4: // 토 - builder.and(resume.member.analysisInfo.faceShapeIdNum.in(GOOD_COMBI_IN_CASE_4)); - break; - } + return new PageImpl<>(content, pageable, total); + } - return queryFactory + // 카테고리별 동적 쿼리 + public Page getResumesByCategory(String category, Pageable pageable) { + List content = queryFactory .select(Projections.bean(ResumeHomeDetailResponse.class, resume.id, resume.member.faceInfo.generatedS3url )) .from(resume) - .leftJoin(resume.member, QMember.member) // join - .where(builder) + .leftJoin(resume.member, QMember.member) // left join + .where(resume.category.eq(Resume.Category.valueOf(category))) .orderBy(resume.id.desc()) - .offset(0) - .limit(20) + .offset(pageable.getOffset()) + .limit(pageable.getPageSize()) .fetch(); - } - // 디테일 페이지에서 궁합 나쁜 관상 조회하는 동적 쿼리 (20개) - public List getDetailResumesByBadCombi(Long memberId, Pageable pageable) { - Member member = findMemberById(memberId); - Integer faceShapeIdNum = member.getAnalysisInfo().getFaceShapeIdNum(); + int total = queryFactory + .select(resume) + .from(resume) + .leftJoin(resume.member, QMember.member) // left join + .where(resume.category.eq(Resume.Category.valueOf(category))) + .fetch() + .size(); - BooleanBuilder builder = new BooleanBuilder(); - switch (faceShapeIdNum) { - case 0: // 화 - builder.and(resume.member.analysisInfo.faceShapeIdNum.in(BAD_COMBI_IN_CASE_0)); - break; - case 1: // 수 - builder.and(resume.member.analysisInfo.faceShapeIdNum.in(BAD_COMBI_IN_CASE_1)); - break; - case 2: // 목 - builder.and(resume.member.analysisInfo.faceShapeIdNum.in(BAD_COMBI_IN_CASE_2)); - break; - case 3: // 금 - builder.and(resume.member.analysisInfo.faceShapeIdNum.in(BAD_COMBI_IN_CASE_3)); - break; - case 4: // 토 - builder.and(resume.member.analysisInfo.faceShapeIdNum.in(BAD_COMBI_IN_CASE_4)); - break; - } + return new PageImpl<>(content, pageable, total); + } - return queryFactory - .select(Projections.bean(ResumeHomeDetailResponse.class, - resume.id, - resume.member.faceInfo.generatedS3url - )) - .from(resume) - .leftJoin(resume.member, QMember.member) // join - .where(builder) - .orderBy(resume.id.desc()) - .offset(0) - .limit(20) - .fetch(); + private Member findMemberById(Long memberId) { + return memberRepository.findById(memberId) + .orElseThrow(() -> new MemberException(NOT_FOUND)); } - // 디테일 페이지에서 카테고리별 동적쿼리 (20개) - public List getDetailResumesByCategory(String category, Pageable pageable) { - return queryFactory.select(Projections.bean(ResumeHomeDetailResponse.class, - resume.id, - resume.member.faceInfo.generatedS3url - )) - .from(resume) - .leftJoin(resume.member, QMember.member) // join - .where(resume.category.eq(Resume.Category.valueOf(category))) - .orderBy(resume.id.desc()) - .offset(0) - .limit(20) - .fetch(); + private Resume findResumeByMember(Member member) { + return resumeRepository.findResumeByMember(member) + .orElseThrow(() -> new ResumeException(NO_RESUME)); } } diff --git a/src/main/java/capstone/facefriend/resume/domain/dto/ResumeCondition.java b/src/main/java/capstone/facefriend/resume/domain/dto/ResumeCondition.java new file mode 100644 index 0000000000..937ec5ab09 --- /dev/null +++ b/src/main/java/capstone/facefriend/resume/domain/dto/ResumeCondition.java @@ -0,0 +1,6 @@ +package capstone.facefriend.resume.domain.dto; + +public record ResumeCondition( + String category +) { +} diff --git a/src/main/java/capstone/facefriend/resume/domain/dto/ResumeDeleteResponse.java b/src/main/java/capstone/facefriend/resume/domain/dto/ResumeDeleteResponse.java new file mode 100644 index 0000000000..871e0ee430 --- /dev/null +++ b/src/main/java/capstone/facefriend/resume/domain/dto/ResumeDeleteResponse.java @@ -0,0 +1,6 @@ +package capstone.facefriend.resume.domain.dto; + +public record ResumeDeleteResponse( + String message +) { +} diff --git a/src/main/java/capstone/facefriend/resume/domain/dto/ResumePostRequest.java b/src/main/java/capstone/facefriend/resume/domain/dto/ResumePostRequest.java new file mode 100644 index 0000000000..4fc229e238 --- /dev/null +++ b/src/main/java/capstone/facefriend/resume/domain/dto/ResumePostRequest.java @@ -0,0 +1,13 @@ +package capstone.facefriend.resume.domain.dto; + +import org.springframework.web.multipart.MultipartFile; + +import java.util.List; + +public record ResumePostRequest( + Long resumeId, + List images, + String category, + String content +) { +} diff --git a/src/main/java/capstone/facefriend/resume/domain/dto/ResumeRequest.java b/src/main/java/capstone/facefriend/resume/domain/dto/ResumePutRequest.java similarity index 79% rename from src/main/java/capstone/facefriend/resume/domain/dto/ResumeRequest.java rename to src/main/java/capstone/facefriend/resume/domain/dto/ResumePutRequest.java index abab0582a1..400736b5fe 100644 --- a/src/main/java/capstone/facefriend/resume/domain/dto/ResumeRequest.java +++ b/src/main/java/capstone/facefriend/resume/domain/dto/ResumePutRequest.java @@ -1,6 +1,6 @@ package capstone.facefriend.resume.domain.dto; -public record ResumeRequest( +public record ResumePutRequest( Long resumeId, String category, String content diff --git a/src/main/java/capstone/facefriend/resume/domain/dto/ResumeResponse.java b/src/main/java/capstone/facefriend/resume/domain/dto/ResumeResponse.java index c841ae39f1..0506d6c5c2 100644 --- a/src/main/java/capstone/facefriend/resume/domain/dto/ResumeResponse.java +++ b/src/main/java/capstone/facefriend/resume/domain/dto/ResumeResponse.java @@ -4,14 +4,17 @@ import capstone.facefriend.member.domain.basicInfo.BasicInfo; import capstone.facefriend.member.domain.faceInfo.FaceInfo; +import java.util.List; + import static capstone.facefriend.resume.domain.Resume.Category; public record ResumeResponse( Long resumeId, - BasicInfo basicInfo, + List resumeImageS3urls, // resume FaceInfo faceInfo, + BasicInfo basicInfo, AnalysisInfo analysisInfo, - Category category, - String content + Category category, // resume + String content // resume ) { } diff --git a/src/main/java/capstone/facefriend/resume/exception/ResumeExceptionType.java b/src/main/java/capstone/facefriend/resume/exception/ResumeExceptionType.java index 65a35f7f71..f68d0159f5 100644 --- a/src/main/java/capstone/facefriend/resume/exception/ResumeExceptionType.java +++ b/src/main/java/capstone/facefriend/resume/exception/ResumeExceptionType.java @@ -6,7 +6,9 @@ public enum ResumeExceptionType implements ExceptionType { NO_RESUME(Status.NOT_FOUND, 7001, "자기소개서가 없습니다!."), - ALREADY_HAVE_RESUME(Status.BAD_REQUEST, 7002, "자기소개서는 1인당 1개만 생성할 수 있습니다!") + ALREADY_HAS_RESUME(Status.BAD_REQUEST, 7002, "자기소개서는 1인당 1개만 생성할 수 있습니다!"), + UNAUTHORIZED(Status.UNAUTHORIZED, 7003, "나의 자기소개서가 아닙니다!"), + FAIL_TO_DELETE(Status.BAD_REQUEST, 7004, "자기소개서 삭제 실패") ; private final Status status; diff --git a/src/main/java/capstone/facefriend/resume/service/ResumeService.java b/src/main/java/capstone/facefriend/resume/service/ResumeService.java index a273d5030b..5035ceef12 100644 --- a/src/main/java/capstone/facefriend/resume/service/ResumeService.java +++ b/src/main/java/capstone/facefriend/resume/service/ResumeService.java @@ -1,19 +1,18 @@ package capstone.facefriend.resume.service; -import capstone.facefriend.auth.controller.support.AuthMember; import capstone.facefriend.bucket.BucketService; import capstone.facefriend.member.domain.member.Member; import capstone.facefriend.member.domain.member.MemberRepository; import capstone.facefriend.member.exception.member.MemberException; import capstone.facefriend.resume.domain.Resume; import capstone.facefriend.resume.domain.ResumeRepository; -import capstone.facefriend.resume.domain.dto.ResumeHomeDetailResponse; -import capstone.facefriend.resume.domain.dto.ResumeRequest; -import capstone.facefriend.resume.domain.dto.ResumeResponse; +import capstone.facefriend.resume.domain.dto.*; import capstone.facefriend.resume.exception.ResumeException; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; import org.springframework.web.multipart.MultipartFile; @@ -21,9 +20,8 @@ import java.util.List; import static capstone.facefriend.member.exception.member.MemberExceptionType.NOT_FOUND; -import static capstone.facefriend.resume.domain.Resume.*; -import static capstone.facefriend.resume.exception.ResumeExceptionType.ALREADY_HAVE_RESUME; -import static capstone.facefriend.resume.exception.ResumeExceptionType.NO_RESUME; +import static capstone.facefriend.resume.domain.Resume.Category; +import static capstone.facefriend.resume.exception.ResumeExceptionType.*; @Service @Slf4j @@ -34,17 +32,22 @@ public class ResumeService { private final MemberRepository memberRepository; private final BucketService bucketService; - /** 특정 페이지 **/ - // 나 - public ResumeResponse postResume(Long memberId, List images, ResumeRequest request) throws IOException { + private static final String DELETE_SUCCESS_MESSAGE = "자기소개서 삭제 완료!"; - Member member = findMemberById(memberId); // 본인 - if(!resumeRepository.findResumeByMember(member).isPresent()) { - throw new ResumeException(ALREADY_HAVE_RESUME); + // 정적 쿼리 + public ResumeResponse postResume( + Long memberId, + List images, + ResumePostRequest request + ) throws IOException { + + Member member = findMemberById(memberId); + if (!resumeRepository.findResumeByMember(member).isPresent()) { + throw new ResumeException(ALREADY_HAS_RESUME); } List resumeImagesS3url = bucketService.uploadResumeImages(images, memberId); - Resume resume = builder() + Resume resume = Resume.builder() .member(member) .resumeImageS3urls(resumeImagesS3url) .category(Category.valueOf(request.category())) @@ -54,65 +57,94 @@ public ResumeResponse postResume(Long memberId, List images, Resu return new ResumeResponse( resume.getId(), - resume.getMember().getBasicInfo(), + resume.getResumeImageS3urls(), resume.getMember().getFaceInfo(), + resume.getMember().getBasicInfo(), resume.getMember().getAnalysisInfo(), resume.getCategory(), resume.getContent() ); } - // 나 또는 상대 public ResumeResponse getResume( + Long memberId, Long resumeId ) { - resumeRepository.findResumeById(resumeId) + Resume resume = resumeRepository.findResumeById(resumeId) .orElseThrow(() -> new ResumeException(NO_RESUME)); - } - - // 나 - public ResumeResponse putResume(Long resumeId) { + return new ResumeResponse( + resume.getId(), + resume.getResumeImageS3urls(), + resume.getMember().getFaceInfo(), + resume.getMember().getBasicInfo(), + resume.getMember().getAnalysisInfo(), + resume.getCategory(), + resume.getContent() + ); } - // 나 - public ResumeResponse deleteResume(Long resumeId) { - - } + public ResumeResponse putResume( + Long memberId, + List images, + ResumePutRequest request + ) throws IOException { + Member member = findMemberById(memberId); // me + Resume resume = findResumeByMember(member); // mine - /** 홈 페이지 **/ - // 홈 페이지에서 궁합 좋은 관상 조회하는 동적 쿼리 (20개) - public List getHomeResumesByGoodCombi(Long memberId) { + if (request.resumeId() != resume.getId()) { // if resumeId is not mine + throw new ResumeException(UNAUTHORIZED); + } - } + List resumeImageS3urls = bucketService.updateResumeImages(images, memberId, images.size()); - // 홈페이지에서 궁합 나쁜 관상 조회하는 동적 쿼리 (20개) - public List getHomeResumesByBadCombi(Long memberId) { + resume.setResumeImageS3urls(resumeImageS3urls); + resume.setCategory(Category.valueOf(request.category())); + resume.setContent(request.content()); + return new ResumeResponse( + resume.getId(), + resume.getResumeImageS3urls(), + resume.getMember().getFaceInfo(), + resume.getMember().getBasicInfo(), + resume.getMember().getAnalysisInfo(), + resume.getCategory(), + resume.getContent() + ); } - // 홈 페이지에서 카테고리별 조회하는 동적 쿼리 (10개) - public List getHomeResumesByCategory(String category) { - - } + public ResumeDeleteResponse deleteResume( + Long memberId, + Long resumeId + ) { + Member member = findMemberById(memberId); // me + Resume resume = findResumeByMember(member); // mine + if (resumeId != resume.getId()) { // if resumeId is not mine + throw new ResumeException(UNAUTHORIZED); + } - /** 디테일 페이지 **/ - // 디테일 페이지에서 궁합 좋은 관상 조회하는 동적 쿼리 (20개) - public List getDetailResumesByGoodCombi(Long memberId) { + int size = resume.getResumeImageS3urls().size(); + bucketService.deleteResumeImages(memberId, size); + resumeRepository.deleteResumeById(resumeId); + return new ResumeDeleteResponse(DELETE_SUCCESS_MESSAGE); } - // 디테일 페이지에서 궁합 나쁜 관상 조회하는 동적 쿼리 (20개) - public List getDetailResumesByBadCombi(Long memberId) { - + // 동적 쿼리 + public Page getResumesByGoodCombi(Long memberId, Pageable pageable) { + return resumeRepository.getResumesByGoodCombi(memberId, pageable); } - // 디테일 페이지에서 카테고리별 동적쿼리 (20개) - public List getDetailResumesByCategory(String category) { + public Page getResumesByBadCombi(Long memberId, Pageable pageable) { + return resumeRepository.getResumesByBadCombi(memberId, pageable); + } + public Page getResumesByCategory(String category, Pageable pageable) { + return resumeRepository.getResumesByCategory(category, pageable); } + // 내부 로직용 private Member findMemberById(Long memberId) { return memberRepository.findById(memberId) .orElseThrow(() -> new MemberException(NOT_FOUND)); From fdda84710ecca910174344b181e54f5be81d1217 Mon Sep 17 00:00:00 2001 From: KimChanJin97 <102044895+KimChanJin97@users.noreply.github.com> Date: Fri, 3 May 2024 16:22:57 +0900 Subject: [PATCH 142/265] Delete src/main/java/capstone/facefriend/config/JasyptConfig.java --- .../facefriend/config/JasyptConfig.java | 31 ------------------- 1 file changed, 31 deletions(-) delete mode 100644 src/main/java/capstone/facefriend/config/JasyptConfig.java diff --git a/src/main/java/capstone/facefriend/config/JasyptConfig.java b/src/main/java/capstone/facefriend/config/JasyptConfig.java deleted file mode 100644 index b6d75f11a2..0000000000 --- a/src/main/java/capstone/facefriend/config/JasyptConfig.java +++ /dev/null @@ -1,31 +0,0 @@ -package capstone.facefriend.config; - -import org.jasypt.encryption.StringEncryptor; -import org.jasypt.encryption.pbe.PooledPBEStringEncryptor; -import org.jasypt.encryption.pbe.config.SimpleStringPBEConfig; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -@Configuration -public class JasyptConfig { - - @Value("${jasypt.encryptor.password}") - private String password; - - @Bean("jasyptStringEncryptor") - public StringEncryptor stringEncryptor() { - PooledPBEStringEncryptor encryptor = new PooledPBEStringEncryptor(); - SimpleStringPBEConfig config = new SimpleStringPBEConfig(); - config.setPassword(password); - config.setAlgorithm("PBEWithMD5AndDES"); - config.setKeyObtentionIterations("1000"); - config.setPoolSize("1"); - config.setProviderName("SunJCE"); - config.setSaltGeneratorClassName("org.jasypt.salt.RandomSaltGenerator"); - config.setIvGeneratorClassName("org.jasypt.iv.NoIvGenerator"); - config.setStringOutputType("base64"); - encryptor.setConfig(config); - return encryptor; - } -} \ No newline at end of file From aafe3202781db3efc5a6a25671ac6fcfa02c4cef Mon Sep 17 00:00:00 2001 From: KimChanJin97 <102044895+KimChanJin97@users.noreply.github.com> Date: Fri, 3 May 2024 16:23:46 +0900 Subject: [PATCH 143/265] Update Dockerfile --- Dockerfile | 30 ++++-------------------------- 1 file changed, 4 insertions(+), 26 deletions(-) diff --git a/Dockerfile b/Dockerfile index 986cb047dc..629e52aead 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,26 +1,4 @@ -FROM bellsoft/liberica-openjdk-debian:17 AS builder -ENV WORK /workspace -COPY build.gradle settings.gradle gradlew $WORK/ -COPY gradle $WORK/gradle -COPY ./src $WORK/src - -WORKDIR $WORK - -RUN apt-get update && apt-get install -y dos2unix && dos2unix gradlew -RUN chmod +x gradlew -RUN ./gradlew clean bootJar --parallel --no-daemon - -FROM bellsoft/liberica-openjdk-debian:17 - -ARG JAR_FILE=workspace/build/libs/*.jar -COPY --from=builder $JAR_FILE app.jar - -ARG ENCRYPT_KEY - -ENTRYPOINT ["java", \ - "-Xms1400m", \ - "-Xmx1400m", \ - "-jar", \ - "-Dspring.profiles.active=prod", \ - "--ENCRYPT_KEY=$ENCRYPT_KEY", \ - "/app.jar"] \ No newline at end of file +FROM openjdk:17-jdk-alpine +ARG JAR_FILE=build/libs/*.jar +COPY ${JAR_FILE} facefriend-0.0.1-SNAPSHOT.jar +ENTRYPOINT ["java","-jar","/facefriend-0.0.1-SNAPSHOT.jar"] From 9572f1bc8265285258c5cd75dbec64e77995263c Mon Sep 17 00:00:00 2001 From: KimChanJin97 <102044895+KimChanJin97@users.noreply.github.com> Date: Fri, 3 May 2024 16:25:18 +0900 Subject: [PATCH 144/265] Delete src/main/resources/application-prod.yml --- src/main/resources/application-prod.yml | 77 ------------------------- 1 file changed, 77 deletions(-) delete mode 100644 src/main/resources/application-prod.yml diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml deleted file mode 100644 index 83e9f7b7e7..0000000000 --- a/src/main/resources/application-prod.yml +++ /dev/null @@ -1,77 +0,0 @@ -spring: - servlet: - multipart: - max-file-size: ENC(QEdqgrkc1SQ1MQiXhyAgxw==) - max-request-size: ENC(LPYSKFia9XgqXJ3LGkU7Hw==) - jpa: - properties: - hibernate: - format_sql: ENC(/k2lAOOiToZinXyjPs043A==) - dialect: ENC(hp39xonPsDFfQh0uP+QagZp+eTRY3ZAbQwHKlZn8sF4qdgSFLFfy/S3t7JZiU1qp) - hibernate: - ddl-auto: ENC(aYCGcw68x/rbuYvEdL4Lhg==) - database: ENC(lqzJGxyaruG3vO12BiGSnjFhVXtQ/+Fo) - datasource: - url: ENC(wV42lv7kzXkiXlnYQ1a2bG1dONwCCIv2bBMaPQ4lhYAgukbfTQ7bTfWnPJTLkYQ2GabqhbvF04k=) - driver-class-name: ENC(iqVYNwUdkSnuRBOTVhE4/vFEFLVHrzRqgat0OWx6wsA=) - username: ENC(JMdQNyqE900MZFuuxX+EqHGrFzc8wbpG) - password: ENC(boj14VTRgt8U1VxDmizlgW6H+4OgUKnm) - data: - redis: - host: ENC(m/yqU98jpYwgO4T0cn+cbj84vU3Df5LB) - port: ENC(Xmvsj7NQ241FjmCVl+CoJA==) - cloud: - aws: - credentials: - accessKey: ENC(AKIA4PECHEV4U7C4UN6X) - secretKey: ENC(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) - s3: - bucket: ENC(SEJS5JtGMpaGfNbv0bo5/Z6lBlWUbjagZvXHZkCEVI8=) - default-profile: ENC(86nUxZdzl1eLO73dgWZ/0nQ97ufNG4wHH+gAb7YXnsE/k8bx7w/Kxto9rJbgTkl/9sD39B/59r9pXlDyxh8/rxwIyWoY762DHdtI4vALuUMkoIOXy6bphDj7/7Zj+4mg) - origin-postfix: ENC(p0xrguoxJfBD/ni12bsBCg==) - generated-postfix: ENC(Puyqdr1silILjwFaVgy/6TWlyttYD85u) - resume-postfix: ENC(Dg6Zti3FGjEBEHuX/YSqI0Cp6P/LFSLY) - region: - static: ENC(hcMa/BJepQBT0E9O7EiVpXd9V4raCcNe) - auto: ENC(XRRrHE8GwP+ZEKyYkm+TBg==) - stack: - auto: ENC(7qYWPZ8gY4UWFUCFMe9IBQ==) - mail: - host: ENC(jZYnjrEyHZZ4o8h8LZidGkB2Xhgm/Vit) - port: ENC(JuFjkiiT6DHuIaayqED9Ig==) - username: ENC(FsUEuNq53OtzT7AVF6ljEkda1uM+UzU3DIoKwd8odsc=) - password: ENC(9VcbNyNuTcOtbMjVV5chkLoAFVYy+BL+0x6OyJ6Rb9A=) - properties: - mail: - smtp: - auth: ENC(qP6dP401owtpbmLO9ZLsqQ==) - starttls: - enable: ENC(2/iPAAN2gOI7acxeL/Ae4w==) - required: ENC(ylH6cx5TFINzE+Iaejk05g==) - connectiontimeout: ENC(5VmkTIOsxDFGqFYJxpeRxQ==) - timeout: ENC(1vRJaeCxUbBooMhkNGYAgA==) - writetimeout: ENC(HgCUlmd2sIuCbfMtnMIDiw==) - auth-code-expiration-millis: ENC(TiTMV9IAw23s+lb3rg5cEg==) - - -logging.level: - org.hibernate.type: ENC(zpXUtRSonoW/8iNULYl71A==) - - -jwt: - header: ENC(um8d5ji3oU1r1B+F1pK/hfIS3V0D9JmH) - secret: ENC(KhoKRnYvfMbkW6KOxF2957PgwNBHw2xk4XvdogWgyp6gKr/nkQPNnWyQeOT41HBnDmwemvypAzA6Wa1Lf8he/8N89Tbogx3BbHafuAlY928=) - - -oauth2: - provider: - google: - id: ENC(Rxxd9KP4koB4a3/M1NJVn0VgVkMounxvLanizrDFHPYC4fpBtXQICFnuNHUaKXPkZc2qiRelcHX9lfMucQi6Zv+J/azrksfjReRIPFIGBK42qyAqFHL0iA==) - secret: ENC(yiPxkw/nRQr+aYBGy8/MO8wO/pEycXYqucC5cSPJ/qV7oaD1wJbYeqJfbB02CFXi) - token-url: ENC(q8uAMwvhAPIGJCiXMXX1j9YSitxh5mIUFKUbvln8AFxanfCHUH3OI4/q35+oTxOr) - info-url: ENC(i4B4j83Tp6Sg0bmIb5PXACW04oET9g0PMNZ0Gi2x7G4PQWpLcHWWFQn0h9j0sown0zSQhjs6KCg=) - login-url: ENC(6cISkrFhAk1pqC2LNJwUfOjv83B85WVRnxH5cXRhsVGhNjpwB9aAmApxHNUE5r+YTZOqOYOnYSo=) - scope: ENC(CCQMgZpF+j4QkzQtm/o8Xb+iSJ5E5YDx) -flask: - generate-url: ENC(OzXrY2PX8uaoonLpKTWI8qnGJR3tFH7rfC5tspp6RYRNq/75DfOj++64bJta2Cf+2iC5wFzMJHo=) - analyze-url: ENC(9D6R6D2eJFmhgwagCtzSRVmz+HLEq6VD8C1e3bBe92aTL05YhlZIAjLz4DB9JA09) \ No newline at end of file From c47e83bd47439b1a0740406035590b8de01d026a Mon Sep 17 00:00:00 2001 From: KimChanJin97 <102044895+KimChanJin97@users.noreply.github.com> Date: Fri, 3 May 2024 16:25:37 +0900 Subject: [PATCH 145/265] Delete src/main/resources/application.yml --- src/main/resources/application.yml | 4 ---- 1 file changed, 4 deletions(-) delete mode 100644 src/main/resources/application.yml diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml deleted file mode 100644 index 43a857c01b..0000000000 --- a/src/main/resources/application.yml +++ /dev/null @@ -1,4 +0,0 @@ -jasypt: - encryptor: - bean: jasyptStringEncryptor - password: ${ENCRYPT_KEY} \ No newline at end of file From 13da4417d8a81f0204454cead4cdb0b6b5922bdf Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Sat, 4 May 2024 08:34:47 +0900 Subject: [PATCH 146/265] =?UTF-8?q?feat:=20JpaQueryConfig=20=EB=93=B1?= =?UTF-8?q?=EB=A1=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../facefriend/config/JasyptConfig.java | 31 -------- ...Configuration.java => JpaQueryConfig.java} | 2 +- src/main/resources/application-prod.yml | 77 ------------------- 3 files changed, 1 insertion(+), 109 deletions(-) delete mode 100644 src/main/java/capstone/facefriend/config/JasyptConfig.java rename src/main/java/capstone/facefriend/config/{QuerydslConfiguration.java => JpaQueryConfig.java} (91%) delete mode 100644 src/main/resources/application-prod.yml diff --git a/src/main/java/capstone/facefriend/config/JasyptConfig.java b/src/main/java/capstone/facefriend/config/JasyptConfig.java deleted file mode 100644 index 7cc1ad13ba..0000000000 --- a/src/main/java/capstone/facefriend/config/JasyptConfig.java +++ /dev/null @@ -1,31 +0,0 @@ -package capstone.facefriend.config; - -import org.jasypt.encryption.StringEncryptor; -import org.jasypt.encryption.pbe.PooledPBEStringEncryptor; -import org.jasypt.encryption.pbe.config.SimpleStringPBEConfig; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -@Configuration -public class JasyptConfig { - - @Value("${jasypt.encryptor.password}") - private String password; - - @Bean("jasyptStringEncryptor") - public StringEncryptor stringEncryptor() { - PooledPBEStringEncryptor encryptor = new PooledPBEStringEncryptor(); - SimpleStringPBEConfig config = new SimpleStringPBEConfig(); - config.setPassword(password); - config.setAlgorithm("PBEWithMD5AndDES"); - config.setKeyObtentionIterations("1000"); - config.setPoolSize("1"); - config.setProviderName("SunJCE"); - config.setSaltGeneratorClassName("org.jasypt.salt.RandomSaltGenerator"); - config.setIvGeneratorClassName("org.jasypt.iv.NoIvGenerator"); - config.setStringOutputType("base64"); - encryptor.setConfig(config); - return encryptor; - } -} diff --git a/src/main/java/capstone/facefriend/config/QuerydslConfiguration.java b/src/main/java/capstone/facefriend/config/JpaQueryConfig.java similarity index 91% rename from src/main/java/capstone/facefriend/config/QuerydslConfiguration.java rename to src/main/java/capstone/facefriend/config/JpaQueryConfig.java index 760e350e8c..792bd3f148 100644 --- a/src/main/java/capstone/facefriend/config/QuerydslConfiguration.java +++ b/src/main/java/capstone/facefriend/config/JpaQueryConfig.java @@ -6,7 +6,7 @@ import org.springframework.context.annotation.Configuration; @Configuration -public class QuerydslConfiguration { +public class JpaQueryConfig { @Bean public JPAQueryFactory jpaQueryFactory(EntityManager em) { diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml deleted file mode 100644 index 83e9f7b7e7..0000000000 --- a/src/main/resources/application-prod.yml +++ /dev/null @@ -1,77 +0,0 @@ -spring: - servlet: - multipart: - max-file-size: ENC(QEdqgrkc1SQ1MQiXhyAgxw==) - max-request-size: ENC(LPYSKFia9XgqXJ3LGkU7Hw==) - jpa: - properties: - hibernate: - format_sql: ENC(/k2lAOOiToZinXyjPs043A==) - dialect: ENC(hp39xonPsDFfQh0uP+QagZp+eTRY3ZAbQwHKlZn8sF4qdgSFLFfy/S3t7JZiU1qp) - hibernate: - ddl-auto: ENC(aYCGcw68x/rbuYvEdL4Lhg==) - database: ENC(lqzJGxyaruG3vO12BiGSnjFhVXtQ/+Fo) - datasource: - url: ENC(wV42lv7kzXkiXlnYQ1a2bG1dONwCCIv2bBMaPQ4lhYAgukbfTQ7bTfWnPJTLkYQ2GabqhbvF04k=) - driver-class-name: ENC(iqVYNwUdkSnuRBOTVhE4/vFEFLVHrzRqgat0OWx6wsA=) - username: ENC(JMdQNyqE900MZFuuxX+EqHGrFzc8wbpG) - password: ENC(boj14VTRgt8U1VxDmizlgW6H+4OgUKnm) - data: - redis: - host: ENC(m/yqU98jpYwgO4T0cn+cbj84vU3Df5LB) - port: ENC(Xmvsj7NQ241FjmCVl+CoJA==) - cloud: - aws: - credentials: - accessKey: ENC(AKIA4PECHEV4U7C4UN6X) - secretKey: ENC(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) - s3: - bucket: ENC(SEJS5JtGMpaGfNbv0bo5/Z6lBlWUbjagZvXHZkCEVI8=) - default-profile: ENC(86nUxZdzl1eLO73dgWZ/0nQ97ufNG4wHH+gAb7YXnsE/k8bx7w/Kxto9rJbgTkl/9sD39B/59r9pXlDyxh8/rxwIyWoY762DHdtI4vALuUMkoIOXy6bphDj7/7Zj+4mg) - origin-postfix: ENC(p0xrguoxJfBD/ni12bsBCg==) - generated-postfix: ENC(Puyqdr1silILjwFaVgy/6TWlyttYD85u) - resume-postfix: ENC(Dg6Zti3FGjEBEHuX/YSqI0Cp6P/LFSLY) - region: - static: ENC(hcMa/BJepQBT0E9O7EiVpXd9V4raCcNe) - auto: ENC(XRRrHE8GwP+ZEKyYkm+TBg==) - stack: - auto: ENC(7qYWPZ8gY4UWFUCFMe9IBQ==) - mail: - host: ENC(jZYnjrEyHZZ4o8h8LZidGkB2Xhgm/Vit) - port: ENC(JuFjkiiT6DHuIaayqED9Ig==) - username: ENC(FsUEuNq53OtzT7AVF6ljEkda1uM+UzU3DIoKwd8odsc=) - password: ENC(9VcbNyNuTcOtbMjVV5chkLoAFVYy+BL+0x6OyJ6Rb9A=) - properties: - mail: - smtp: - auth: ENC(qP6dP401owtpbmLO9ZLsqQ==) - starttls: - enable: ENC(2/iPAAN2gOI7acxeL/Ae4w==) - required: ENC(ylH6cx5TFINzE+Iaejk05g==) - connectiontimeout: ENC(5VmkTIOsxDFGqFYJxpeRxQ==) - timeout: ENC(1vRJaeCxUbBooMhkNGYAgA==) - writetimeout: ENC(HgCUlmd2sIuCbfMtnMIDiw==) - auth-code-expiration-millis: ENC(TiTMV9IAw23s+lb3rg5cEg==) - - -logging.level: - org.hibernate.type: ENC(zpXUtRSonoW/8iNULYl71A==) - - -jwt: - header: ENC(um8d5ji3oU1r1B+F1pK/hfIS3V0D9JmH) - secret: ENC(KhoKRnYvfMbkW6KOxF2957PgwNBHw2xk4XvdogWgyp6gKr/nkQPNnWyQeOT41HBnDmwemvypAzA6Wa1Lf8he/8N89Tbogx3BbHafuAlY928=) - - -oauth2: - provider: - google: - id: ENC(Rxxd9KP4koB4a3/M1NJVn0VgVkMounxvLanizrDFHPYC4fpBtXQICFnuNHUaKXPkZc2qiRelcHX9lfMucQi6Zv+J/azrksfjReRIPFIGBK42qyAqFHL0iA==) - secret: ENC(yiPxkw/nRQr+aYBGy8/MO8wO/pEycXYqucC5cSPJ/qV7oaD1wJbYeqJfbB02CFXi) - token-url: ENC(q8uAMwvhAPIGJCiXMXX1j9YSitxh5mIUFKUbvln8AFxanfCHUH3OI4/q35+oTxOr) - info-url: ENC(i4B4j83Tp6Sg0bmIb5PXACW04oET9g0PMNZ0Gi2x7G4PQWpLcHWWFQn0h9j0sown0zSQhjs6KCg=) - login-url: ENC(6cISkrFhAk1pqC2LNJwUfOjv83B85WVRnxH5cXRhsVGhNjpwB9aAmApxHNUE5r+YTZOqOYOnYSo=) - scope: ENC(CCQMgZpF+j4QkzQtm/o8Xb+iSJ5E5YDx) -flask: - generate-url: ENC(OzXrY2PX8uaoonLpKTWI8qnGJR3tFH7rfC5tspp6RYRNq/75DfOj++64bJta2Cf+2iC5wFzMJHo=) - analyze-url: ENC(9D6R6D2eJFmhgwagCtzSRVmz+HLEq6VD8C1e3bBe92aTL05YhlZIAjLz4DB9JA09) \ No newline at end of file From f28c3373ffe8546dafcc5049b805f0095b357e6a Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Sat, 4 May 2024 08:36:24 +0900 Subject: [PATCH 147/265] =?UTF-8?q?fix:=20=EB=B2=84=ED=82=B7=20=EC=98=A4?= =?UTF-8?q?=EB=B8=8C=EC=A0=9D=ED=8A=B8=20=EC=88=98=EC=A0=95,=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C=EB=90=98=EC=A7=80=20=EC=95=8A=EB=8A=94=20=EB=B2=84?= =?UTF-8?q?=EA=B7=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../facefriend/bucket/BucketService.java | 103 +++++++++--------- 1 file changed, 53 insertions(+), 50 deletions(-) diff --git a/src/main/java/capstone/facefriend/bucket/BucketService.java b/src/main/java/capstone/facefriend/bucket/BucketService.java index 4eb6b54618..8e19088331 100644 --- a/src/main/java/capstone/facefriend/bucket/BucketService.java +++ b/src/main/java/capstone/facefriend/bucket/BucketService.java @@ -9,6 +9,7 @@ import capstone.facefriend.member.exception.member.MemberExceptionType; import capstone.facefriend.member.multipartFile.ByteArrayMultipartFile; import capstone.facefriend.member.service.dto.faceInfo.FaceInfoResponse; +import capstone.facefriend.resume.domain.Resume; import com.amazonaws.services.s3.AmazonS3; import com.amazonaws.services.s3.model.CannedAccessControlList; import com.amazonaws.services.s3.model.DeleteObjectRequest; @@ -18,12 +19,15 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import org.springframework.web.multipart.MultipartFile; import java.io.IOException; import java.util.ArrayList; import java.util.List; +import java.util.UUID; +@Transactional @Slf4j @RequiredArgsConstructor @Service @@ -32,34 +36,30 @@ public class BucketService { @Value("${spring.cloud.aws.s3.bucket}") private String bucketName; - @Value("${spring.cloud.aws.s3.default-profile}") - private String defaultProfileS3Url; - + @Value("${spring.cloud.aws.s3.default-faceInfo-s3url}") + private String defaultFaceInfoS3url; @Value("${spring.cloud.aws.s3.origin-postfix}") private String originPostfix; - @Value("${spring.cloud.aws.s3.generated-postfix}") private String generatedPostfix; - @Value("${spring.cloud.aws.s3.resume-postfix}") - private String resumeInfix; + private String resumePostfix; private final AmazonS3 amazonS3; private final MemberRepository memberRepository; - private final FaceInfoRepository faceInfoRepository; // FaceInfo : origin 업로드 & generated 업로드 - public FaceInfoResponse uploadOriginAndGenerated( + public List uploadOriginAndGenerated( MultipartFile origin, - ByteArrayMultipartFile generated, - Long memberId + ByteArrayMultipartFile generated ) throws IOException { /** upload origin to s3 */ // set metadata ObjectMetadata originMetadata = new ObjectMetadata(); originMetadata.setContentLength(origin.getInputStream().available()); originMetadata.setContentType(origin.getContentType()); - String originObjectName = memberId + originPostfix; + + String originObjectName = UUID.randomUUID() + originPostfix; amazonS3.putObject( new PutObjectRequest( bucketName, @@ -76,7 +76,7 @@ public FaceInfoResponse uploadOriginAndGenerated( generatedMetadata.setContentLength(generated.getInputStream().available()); generatedMetadata.setContentType(generatedMetadata.getContentType()); - String generatedObjectName = memberId + generatedPostfix; + String generatedObjectName = UUID.randomUUID() + generatedPostfix; amazonS3.putObject( new PutObjectRequest( bucketName, @@ -85,53 +85,53 @@ public FaceInfoResponse uploadOriginAndGenerated( generatedMetadata ).withCannedAcl(CannedAccessControlList.PublicRead) ); - String generatedS3Url = amazonS3.getUrl(bucketName, generatedObjectName).toString(); - - // FaceInfo 저장 - FaceInfo faceInfo = FaceInfo.builder() - .originS3url(originS3url) - .generatedS3url(generatedS3Url) - .build(); - faceInfoRepository.save(faceInfo); // + String generatedS3url = amazonS3.getUrl(bucketName, generatedObjectName).toString(); - // Member 최신화 후 저장 - Member member = memberRepository.findById(memberId) - .orElseThrow(() -> new MemberException(MemberExceptionType.NOT_FOUND)); - member.setFaceInfo(faceInfo); - memberRepository.save(member); - - return new FaceInfoResponse(originS3url, generatedS3Url); + return List.of(originS3url, generatedS3url); } // FaceInfo : origin 수정 -> generated 수정 - public FaceInfoResponse updateOriginAndGenerated( + public List updateOriginAndGenerated( MultipartFile origin, ByteArrayMultipartFile generated, Long memberId ) throws IOException { - deleteOriginAndGenerated(memberId); // 기존에 저장되어있던 사진 삭제 - return uploadOriginAndGenerated(origin, generated, memberId); // 새로 사진 저장 + Member member = findMemberById(memberId); + + String originS3url = member.getFaceInfo().getOriginS3url(); + String generatedS3url = member.getFaceInfo().getGeneratedS3url(); + + if (originS3url.equals(defaultFaceInfoS3url) || generatedS3url.equals(defaultFaceInfoS3url)) { + return uploadOriginAndGenerated(origin, generated); + } + + deleteOriginAndGenerated(memberId); + return uploadOriginAndGenerated(origin, generated); } // FaceInfo : origin 삭제 -> generated 삭제 - public FaceInfoResponse deleteOriginAndGenerated( + public String deleteOriginAndGenerated( Long memberId ) { - String originObjectName = memberId + originPostfix; + Member member = findMemberById(memberId); + + String originS3url = member.getFaceInfo().getOriginS3url(); + String originObjectName = originS3url.substring(originS3url.lastIndexOf("/") + 1); amazonS3.deleteObject(new DeleteObjectRequest(bucketName, originObjectName)); - String generatedObjectName = memberId + generatedPostfix; + String generatedS3url = member.getFaceInfo().getGeneratedS3url(); + String generatedObjectName = generatedS3url.substring(generatedS3url.lastIndexOf("/") + 1); amazonS3.deleteObject(new DeleteObjectRequest(bucketName, generatedObjectName)); - return new FaceInfoResponse(defaultProfileS3Url, defaultProfileS3Url); + return defaultFaceInfoS3url; } + + // Resume : images 업로드 public List uploadResumeImages( - List images, - Long memberId + List images ) throws IOException { - int idx = 0; List resumeImageS3urls = new ArrayList<>(); for (MultipartFile image : images) { @@ -139,7 +139,8 @@ public List uploadResumeImages( metadata.setContentLength(image.getInputStream().available()); metadata.setContentType(image.getContentType()); - String imageObjectName = memberId + resumeInfix + idx; + String imageObjectName = UUID.randomUUID() + resumePostfix; + amazonS3.putObject( new PutObjectRequest( bucketName, @@ -148,33 +149,35 @@ public List uploadResumeImages( metadata ).withCannedAcl(CannedAccessControlList.PublicRead) ); - resumeImageS3urls.add(amazonS3.getUrl(bucketName, imageObjectName).toString()); - idx++; } - return resumeImageS3urls; } // Resume : images 삭제 -> images 업로드 public List updateResumeImages( List images, - Long memberId, - int size + Resume resume ) throws IOException { - deleteResumeImages(memberId,size); - return uploadResumeImages(images, memberId); + deleteResumeImages(resume); + return uploadResumeImages(images); } // Resume : images 삭제 public void deleteResumeImages( - Long memberId, - int size + Resume resume ) { - for (int idx = 0; idx < size; idx++) { - String imageObjectName = memberId + resumeInfix + idx; - amazonS3.deleteObject(new DeleteObjectRequest(bucketName, imageObjectName)); + List resumeImageS3urls = resume.getResumeImageS3urls(); + + for (String resumeImageS3url : resumeImageS3urls) { + String resumeImageObjectName = resumeImageS3url.substring(resumeImageS3url.lastIndexOf("/") + 1); + amazonS3.deleteObject(new DeleteObjectRequest(bucketName, resumeImageObjectName)); } } + + private Member findMemberById(Long memberId) { + return memberRepository.findById(memberId) + .orElseThrow(() -> new MemberException(MemberExceptionType.NOT_FOUND)); + } } From 7000f149840d743875c2a2daa5accbec585b7f05 Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Sat, 4 May 2024 08:40:50 +0900 Subject: [PATCH 148/265] =?UTF-8?q?feat:=20=EC=8A=A4=ED=94=84=EB=A7=81=20r?= =?UTF-8?q?epository=20=EC=97=90=EC=84=9C=20find,=20delete=20=EB=A9=94?= =?UTF-8?q?=EC=84=9C=EB=93=9C=20=EC=84=A0=EC=96=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../facefriend/member/domain/analysisInfo/AnalysisInfo.java | 1 + .../member/domain/analysisInfo/AnalysisInfoRepository.java | 2 ++ 2 files changed, 3 insertions(+) diff --git a/src/main/java/capstone/facefriend/member/domain/analysisInfo/AnalysisInfo.java b/src/main/java/capstone/facefriend/member/domain/analysisInfo/AnalysisInfo.java index 666a2dab79..2bf08b2352 100644 --- a/src/main/java/capstone/facefriend/member/domain/analysisInfo/AnalysisInfo.java +++ b/src/main/java/capstone/facefriend/member/domain/analysisInfo/AnalysisInfo.java @@ -22,6 +22,7 @@ public class AnalysisInfo { @Column(name = "ANALYSIS_INFO_ID") private Long id; + @Column private Integer faceShapeIdNum; @Builder.Default diff --git a/src/main/java/capstone/facefriend/member/domain/analysisInfo/AnalysisInfoRepository.java b/src/main/java/capstone/facefriend/member/domain/analysisInfo/AnalysisInfoRepository.java index 4b322b666a..b3596e5e85 100644 --- a/src/main/java/capstone/facefriend/member/domain/analysisInfo/AnalysisInfoRepository.java +++ b/src/main/java/capstone/facefriend/member/domain/analysisInfo/AnalysisInfoRepository.java @@ -5,4 +5,6 @@ public interface AnalysisInfoRepository extends Repository { AnalysisInfo save(AnalysisInfo analysisInfo); + + AnalysisInfo findAnalysisInfoById(Long id); } From c4a1fe99f86d8e85b4b69b2c7b88a0b71f7e9063 Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Sat, 4 May 2024 08:45:37 +0900 Subject: [PATCH 149/265] =?UTF-8?q?fix:=20=EB=8D=94=ED=8B=B0=EC=B2=B4?= =?UTF-8?q?=ED=82=B9=EC=9C=BC=EB=A1=9C=20=EC=9D=B8=ED=95=9C=20save()=20?= =?UTF-8?q?=EC=A3=BC=EC=84=9D=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../member/controller/AnalysisInfoController.java | 4 ++-- .../facefriend/member/service/AnalysisInfoService.java | 6 ++++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/main/java/capstone/facefriend/member/controller/AnalysisInfoController.java b/src/main/java/capstone/facefriend/member/controller/AnalysisInfoController.java index 07aff649cd..097899cf07 100644 --- a/src/main/java/capstone/facefriend/member/controller/AnalysisInfoController.java +++ b/src/main/java/capstone/facefriend/member/controller/AnalysisInfoController.java @@ -32,8 +32,8 @@ public ResponseEntity analyze( return ResponseEntity.ok(analysisInfoService.analyze(origin, memberId)); } - @GetMapping("/analysis-info") - public ResponseEntity getAnalysisInfo( + @GetMapping("/analysis-info/full-short") + public ResponseEntity getAnalysisInfoFullShort( @AuthMember Long memberId ) { return ResponseEntity.ok(analysisInfoService.getAnalysisInfoFullShort(memberId)); diff --git a/src/main/java/capstone/facefriend/member/service/AnalysisInfoService.java b/src/main/java/capstone/facefriend/member/service/AnalysisInfoService.java index 03798415d1..171272260d 100644 --- a/src/main/java/capstone/facefriend/member/service/AnalysisInfoService.java +++ b/src/main/java/capstone/facefriend/member/service/AnalysisInfoService.java @@ -19,6 +19,7 @@ import org.springframework.core.io.ByteArrayResource; import org.springframework.http.*; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import org.springframework.util.LinkedMultiValueMap; import org.springframework.web.client.RestTemplate; import org.springframework.web.multipart.MultipartFile; @@ -35,6 +36,8 @@ import static capstone.facefriend.member.exception.analysis.AnalysisExceptionType.FAIL_TO_EXTRACT_FACE_SHAPE_ID_NUM; import static capstone.facefriend.member.exception.member.MemberExceptionType.NOT_FOUND; + +@Transactional @Slf4j @Service @RequiredArgsConstructor @@ -88,9 +91,8 @@ public String getFilename() { member.getAnalysisInfo().setAnalysisInfoFull(analysisFull); member.getAnalysisInfo().setAnalysisInfoShort(analysisShort); member.getAnalysisInfo().setFaceShapeIdNum(faceShapeIdNum); - memberRepository.save(member); +// memberRepository.save(member); - // return not AnalysisInfoShort, but AnalysisInfoFull return new AnalysisInfoFullResponse(analysisFull); } From 94c501f4daa0888753263286e96285ea5eadf720 Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Sat, 4 May 2024 08:47:07 +0900 Subject: [PATCH 150/265] =?UTF-8?q?fix:=20lombok=20setter=20=EC=99=80=20?= =?UTF-8?q?=EC=82=AC=EC=9A=A9=EC=9E=90=20=EC=A0=95=EC=9D=98=20setter=20?= =?UTF-8?q?=EC=A4=91=EB=B3=B5=EC=9C=BC=EB=A1=9C=20=EC=9D=B8=ED=95=9C=20?= =?UTF-8?q?=EB=B2=84=EA=B7=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../facefriend/member/domain/faceInfo/FaceInfo.java | 8 -------- .../member/domain/faceInfo/FaceInfoRepository.java | 4 ++++ 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/src/main/java/capstone/facefriend/member/domain/faceInfo/FaceInfo.java b/src/main/java/capstone/facefriend/member/domain/faceInfo/FaceInfo.java index 1070b67c74..5c2a5a8526 100644 --- a/src/main/java/capstone/facefriend/member/domain/faceInfo/FaceInfo.java +++ b/src/main/java/capstone/facefriend/member/domain/faceInfo/FaceInfo.java @@ -21,12 +21,4 @@ public class FaceInfo { @Column private String generatedS3url; - - public void setOriginS3url(String originS3Url) { - this.originS3url = originS3url; - } - - public void setGeneratedS3Url(String generatedS3url) { - this.generatedS3url = generatedS3url; - } } diff --git a/src/main/java/capstone/facefriend/member/domain/faceInfo/FaceInfoRepository.java b/src/main/java/capstone/facefriend/member/domain/faceInfo/FaceInfoRepository.java index 0d61b9d4d1..4123be4b04 100644 --- a/src/main/java/capstone/facefriend/member/domain/faceInfo/FaceInfoRepository.java +++ b/src/main/java/capstone/facefriend/member/domain/faceInfo/FaceInfoRepository.java @@ -5,4 +5,8 @@ public interface FaceInfoRepository extends Repository { FaceInfo save(FaceInfo faceInfo); + + FaceInfo findFaceInfoById(Long id); + + void deleteFaceInfoById(Long id); } From 1b6f5866938c37e935f1cbc1006bff5cb3c01e46 Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Sat, 4 May 2024 08:48:44 +0900 Subject: [PATCH 151/265] =?UTF-8?q?fix:=20=EB=B2=84=ED=82=B7=20=EC=98=A4?= =?UTF-8?q?=EB=B8=8C=EC=A0=9D=ED=8A=B8=20=EC=88=98=EC=A0=95,=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C=EA=B0=80=20=EB=90=98=EC=A7=80=20=EC=95=8A=EB=8A=94=20?= =?UTF-8?q?=EB=B2=84=EA=B7=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../member/service/FaceInfoService.java | 61 ++++++++++++------- 1 file changed, 40 insertions(+), 21 deletions(-) diff --git a/src/main/java/capstone/facefriend/member/service/FaceInfoService.java b/src/main/java/capstone/facefriend/member/service/FaceInfoService.java index 7520d2eee2..02c0e916d7 100644 --- a/src/main/java/capstone/facefriend/member/service/FaceInfoService.java +++ b/src/main/java/capstone/facefriend/member/service/FaceInfoService.java @@ -21,6 +21,7 @@ import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import org.springframework.util.LinkedMultiValueMap; import org.springframework.web.client.RestTemplate; import org.springframework.web.multipart.MultipartFile; @@ -29,9 +30,11 @@ import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import java.util.Base64; +import java.util.List; import java.util.Map; +@Transactional @Slf4j @Service @RequiredArgsConstructor @@ -39,47 +42,57 @@ public class FaceInfoService { @Value("${flask.generate-url}") private String requestUrl; - - @Value("${spring.cloud.aws.s3.default-profile}") - private String defaultProfileS3Url; - private final RestTemplate restTemplate; - private final BucketService bucketService; - - private final FaceInfoRepository faceInfoRepository; private final MemberRepository memberRepository; + private final FaceInfoRepository faceInfoRepository; + // origin 삭제 & generated 삭제 -> origin 업로드 & generated 업로드 public FaceInfoResponse updateOrigin(MultipartFile origin, Long styleId, Long memberId) throws IOException { + // bucket update ByteArrayMultipartFile generated = generate(origin, styleId, memberId); - return bucketService.updateOriginAndGenerated(origin, generated, memberId); + List s3urls = bucketService.updateOriginAndGenerated(origin, generated, memberId); + + // entity update + Member member = findMemberById(memberId); + FaceInfo faceInfo = faceInfoRepository.findFaceInfoById(member.getFaceInfo().getId()); + + String originS3url = s3urls.get(0); + String generatedS3url = s3urls.get(1); + + faceInfo.setOriginS3url(originS3url); + faceInfo.setGeneratedS3url(generatedS3url); + + member.setFaceInfo(faceInfo); + + return new FaceInfoResponse(originS3url, generatedS3url); } public FaceInfoResponse getOriginAndGenerated(Long memberId) { - Member member = memberRepository.findById(memberId) - .orElseThrow(() -> new MemberException(MemberExceptionType.NOT_FOUND)); - + Member member = findMemberById(memberId); FaceInfo faceInfo = member.getFaceInfo(); return new FaceInfoResponse(faceInfo.getOriginS3url(), faceInfo.getGeneratedS3url()); } // origin 삭제 & generated 삭제 public FaceInfoResponse deleteOriginAndGenerated(Long memberId) { - FaceInfoResponse delete = bucketService.deleteOriginAndGenerated(memberId); + String defaultFaceInfoS3url = bucketService.deleteOriginAndGenerated(memberId); - Member member = memberRepository.findById(memberId) - .orElseThrow(() -> new MemberException(MemberExceptionType.NOT_FOUND)); + Member member = findMemberById(memberId); + faceInfoRepository.deleteFaceInfoById(member.getFaceInfo().getId()); - FaceInfo faceInfo = member.getFaceInfo(); - faceInfo.setOriginS3url(defaultProfileS3Url); - faceInfo.setGeneratedS3Url(defaultProfileS3Url); + FaceInfo faceInfo = FaceInfo.builder() + .originS3url(defaultFaceInfoS3url) + .generatedS3url(defaultFaceInfoS3url) + .build(); + faceInfo.setOriginS3url(defaultFaceInfoS3url); + faceInfo.setGeneratedS3url(defaultFaceInfoS3url); faceInfoRepository.save(faceInfo); member.setFaceInfo(faceInfo); - memberRepository.save(member); - return delete; + return new FaceInfoResponse(defaultFaceInfoS3url, defaultFaceInfoS3url); } private ByteArrayMultipartFile generate(MultipartFile origin, Long styleId, Long memberId) throws IOException { @@ -109,11 +122,17 @@ public String getFilename() { // convert JSON into Map ObjectMapper objectMapper = new ObjectMapper(); - Map result = objectMapper.convertValue(responseEntity.getBody(), new TypeReference<>() {}); + Map result = objectMapper.convertValue(responseEntity.getBody(), new TypeReference<>() { + }); - byte[] imageBinary = Base64.getDecoder().decode((String)result.get("image_binary")); + byte[] imageBinary = Base64.getDecoder().decode((String) result.get("image_binary")); return new ByteArrayMultipartFile(imageBinary, origin.getOriginalFilename()); } + + private Member findMemberById(Long memberId) { + return memberRepository.findById(memberId) + .orElseThrow(() -> new MemberException(MemberExceptionType.NOT_FOUND)); + } } From 54af0efc2612534bbd9dfbf64e95d04b14401812 Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Sat, 4 May 2024 08:49:37 +0900 Subject: [PATCH 152/265] =?UTF-8?q?style:=20=EC=BD=94=EB=93=9C=20=ED=8F=AC?= =?UTF-8?q?=EB=A7=A4=ED=8C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../capstone/facefriend/FacefriendApplication.java | 4 ---- .../member/service/BasicInfoService.java | 14 ++++++++------ .../facefriend/member/service/MemberService.java | 8 ++++---- 3 files changed, 12 insertions(+), 14 deletions(-) diff --git a/src/main/java/capstone/facefriend/FacefriendApplication.java b/src/main/java/capstone/facefriend/FacefriendApplication.java index 0bab914e34..723fc9435d 100644 --- a/src/main/java/capstone/facefriend/FacefriendApplication.java +++ b/src/main/java/capstone/facefriend/FacefriendApplication.java @@ -1,8 +1,5 @@ package capstone.facefriend; -import capstone.facefriend.member.domain.member.Member; -import capstone.facefriend.resume.domain.Resume; -import jakarta.annotation.PostConstruct; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.data.jpa.repository.config.EnableJpaAuditing; @@ -16,6 +13,5 @@ public class FacefriendApplication { public static void main(String[] args) { TimeZone.setDefault(TimeZone.getTimeZone("Asia/Seoul")); SpringApplication.run(FacefriendApplication.class, args); - } } diff --git a/src/main/java/capstone/facefriend/member/service/BasicInfoService.java b/src/main/java/capstone/facefriend/member/service/BasicInfoService.java index e9841707ec..8a613d7ad4 100644 --- a/src/main/java/capstone/facefriend/member/service/BasicInfoService.java +++ b/src/main/java/capstone/facefriend/member/service/BasicInfoService.java @@ -15,6 +15,8 @@ import static capstone.facefriend.member.domain.basicInfo.BasicInfo.*; import static capstone.facefriend.member.exception.member.MemberExceptionType.NOT_FOUND; + +@Transactional @Service @Slf4j @RequiredArgsConstructor @@ -22,11 +24,6 @@ public class BasicInfoService { private final MemberRepository memberRepository; - private Member findMemberById(Long memberId) { - return memberRepository.findById(memberId) - .orElseThrow(() -> new MemberException(NOT_FOUND)); - } - public BasicInfoResponse getBasicInfo(Long memberId) { Member member = findMemberById(memberId); BasicInfo basicInfo = member.getBasicInfo(); @@ -47,8 +44,13 @@ public BasicInfoResponse putBasicInfo(Long memberId, BasicInfoRequest request) { oldBasicInfo.setRegion(Region.valueOf(request.region())); member.setBasicInfo(oldBasicInfo); - memberRepository.save(member); +// memberRepository.save(member); return BasicInfoResponse.of(oldBasicInfo); } + + private Member findMemberById(Long memberId) { + return memberRepository.findById(memberId) + .orElseThrow(() -> new MemberException(NOT_FOUND)); + } } diff --git a/src/main/java/capstone/facefriend/member/service/MemberService.java b/src/main/java/capstone/facefriend/member/service/MemberService.java index c37af0f9e2..9d8b8b40fd 100644 --- a/src/main/java/capstone/facefriend/member/service/MemberService.java +++ b/src/main/java/capstone/facefriend/member/service/MemberService.java @@ -64,8 +64,8 @@ public class MemberService { private static final Long BLACKLIST_REMAIN_MINUTE = 1000 * 60 * 60 * 12L; // 12 시간 - @Value("${spring.cloud.aws.s3.default-profile}") - private String defaultProfileS3Url; + @Value("${spring.cloud.aws.s3.default-faceInfo-s3url}") + private String defaultFaceInfoS3url; @Transactional public String verifyDuplication(String email) { @@ -110,8 +110,8 @@ public String signUp(SignUpRequest request) { // 관상 이미지 초기값 FaceInfo faceInfo = FaceInfo.builder() - .originS3url(defaultProfileS3Url) - .generatedS3url(defaultProfileS3Url) + .originS3url(defaultFaceInfoS3url) + .generatedS3url(defaultFaceInfoS3url) .build(); faceInfoRepository.save(faceInfo); From f57ee7fff21f1f3875979148430a30a40da820dc Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Sat, 4 May 2024 08:51:45 +0900 Subject: [PATCH 153/265] =?UTF-8?q?feat:=20querydsl=20=EC=82=AC=EC=9A=A9?= =?UTF-8?q?=EC=9D=84=20=EC=9C=84=ED=95=9C=20=EC=9D=B8=ED=84=B0=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=8A=A4=20=EB=B0=8F=20=EC=BB=A4=EC=8A=A4=ED=85=80=20?= =?UTF-8?q?=ED=81=B4=EB=9E=98=EC=8A=A4=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../facefriend/resume/domain/Resume.java | 2 - .../resume/domain/ResumeRepositoryCustom.java | 3 - .../resume/domain/ResumeRepositoryImpl.java | 84 ++----------------- 3 files changed, 7 insertions(+), 82 deletions(-) diff --git a/src/main/java/capstone/facefriend/resume/domain/Resume.java b/src/main/java/capstone/facefriend/resume/domain/Resume.java index 5f2dad934b..366db93f81 100644 --- a/src/main/java/capstone/facefriend/resume/domain/Resume.java +++ b/src/main/java/capstone/facefriend/resume/domain/Resume.java @@ -50,8 +50,6 @@ public class Resume { private Map friends = new HashMap<>(); // 타멤버id : 공개여부 public enum Category { - SIMILAR_ANALYSIS("비슷한 관상"), - DIFFERENT_ANALYSIS("다른 관상"), FOOD("음식"), WORKOUT("운동"), MOVIE("영화"), diff --git a/src/main/java/capstone/facefriend/resume/domain/ResumeRepositoryCustom.java b/src/main/java/capstone/facefriend/resume/domain/ResumeRepositoryCustom.java index 462581c91a..61509ceffb 100644 --- a/src/main/java/capstone/facefriend/resume/domain/ResumeRepositoryCustom.java +++ b/src/main/java/capstone/facefriend/resume/domain/ResumeRepositoryCustom.java @@ -4,11 +4,8 @@ import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; -import java.util.List; - public interface ResumeRepositoryCustom { Page getResumesByGoodCombi(Long memberId, Pageable pageable); // 좋은 궁합 - Page getResumesByBadCombi(Long memberId, Pageable pageable); // 나쁜 궁합 Page getResumesByCategory(String category, Pageable pageable); // 카테고리별 } diff --git a/src/main/java/capstone/facefriend/resume/domain/ResumeRepositoryImpl.java b/src/main/java/capstone/facefriend/resume/domain/ResumeRepositoryImpl.java index c2356cf982..b15eb45ad0 100644 --- a/src/main/java/capstone/facefriend/resume/domain/ResumeRepositoryImpl.java +++ b/src/main/java/capstone/facefriend/resume/domain/ResumeRepositoryImpl.java @@ -1,17 +1,13 @@ package capstone.facefriend.resume.domain; -import capstone.facefriend.bucket.BucketService; import capstone.facefriend.member.domain.member.Member; import capstone.facefriend.member.domain.member.MemberRepository; import capstone.facefriend.member.domain.member.QMember; import capstone.facefriend.member.exception.member.MemberException; +import capstone.facefriend.resume.domain.dto.QResumeHomeDetailResponse; import capstone.facefriend.resume.domain.dto.ResumeHomeDetailResponse; -import capstone.facefriend.resume.domain.dto.ResumeResponse; -import capstone.facefriend.resume.exception.ResumeException; import com.querydsl.core.BooleanBuilder; -import com.querydsl.core.types.Projections; import com.querydsl.jpa.impl.JPAQueryFactory; -import jakarta.persistence.EntityManager; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; @@ -22,18 +18,13 @@ import static capstone.facefriend.member.exception.member.MemberExceptionType.NOT_FOUND; import static capstone.facefriend.resume.domain.QResume.resume; -import static capstone.facefriend.resume.exception.ResumeExceptionType.NO_RESUME; @Repository @RequiredArgsConstructor public class ResumeRepositoryImpl implements ResumeRepositoryCustom { - private final EntityManager em; private final JPAQueryFactory queryFactory; - private final MemberRepository memberRepository; - private final ResumeRepository resumeRepository; - private final BucketService bucketService; private static final List GOOD_COMBI_IN_CASE_0 = List.of(2, 4); // 화 private static final List GOOD_COMBI_IN_CASE_1 = List.of(2, 4); // 수 @@ -41,12 +32,6 @@ public class ResumeRepositoryImpl implements ResumeRepositoryCustom { private static final List GOOD_COMBI_IN_CASE_3 = List.of(1, 4); // 금 private static final List GOOD_COMBI_IN_CASE_4 = List.of(0, 3); // 토 - private static final List BAD_COMBI_IN_CASE_0 = List.of(1, 3); // 화 - private static final List BAD_COMBI_IN_CASE_1 = List.of(0, 4); // 수 - private static final List BAD_COMBI_IN_CASE_2 = List.of(3, 4); // 목 - private static final List BAD_COMBI_IN_CASE_3 = List.of(0, 2); // 금 - private static final List BAD_COMBI_IN_CASE_4 = List.of(1, 2); // 토 - // 좋은 궁합 동적 쿼리 public Page getResumesByGoodCombi(Long memberId, Pageable pageable) { Member me = findMemberById(memberId); @@ -72,58 +57,9 @@ public Page getResumesByGoodCombi(Long memberId, Pagea } List content = queryFactory - .select(Projections.bean(ResumeHomeDetailResponse.class, - resume.id, - resume.member.faceInfo.generatedS3url - )) - .from(resume) - .leftJoin(resume.member, QMember.member) // left join - .where(builder) // boolean builder - .orderBy(resume.id.desc()) - .offset(pageable.getOffset()) - .limit(pageable.getPageSize()) - .fetch(); - - int total = queryFactory - .select(resume) - .from(resume) - .leftJoin(resume.member, QMember.member) // left join - .where(builder) // boolean builder - .fetch() - .size(); - - return new PageImpl<>(content, pageable, total); - } - - // 나쁜 궁합 동적 쿼리 - public Page getResumesByBadCombi(Long memberId, Pageable pageable) { - Member me = findMemberById(memberId); - Integer faceShapeIdNum = me.getAnalysisInfo().getFaceShapeIdNum(); - - BooleanBuilder builder = new BooleanBuilder(); - switch (faceShapeIdNum) { - case 0: // 화 - builder.and(resume.member.analysisInfo.faceShapeIdNum.in(BAD_COMBI_IN_CASE_0)); - break; - case 1: // 수 - builder.and(resume.member.analysisInfo.faceShapeIdNum.in(BAD_COMBI_IN_CASE_1)); - break; - case 2: // 목 - builder.and(resume.member.analysisInfo.faceShapeIdNum.in(BAD_COMBI_IN_CASE_2)); - break; - case 3: // 금 - builder.and(resume.member.analysisInfo.faceShapeIdNum.in(BAD_COMBI_IN_CASE_3)); - break; - case 4: // 토 - builder.and(resume.member.analysisInfo.faceShapeIdNum.in(BAD_COMBI_IN_CASE_4)); - break; - } - - List content = queryFactory - .select(Projections.bean(ResumeHomeDetailResponse.class, - resume.id, - resume.member.faceInfo.generatedS3url - )) + .select(new QResumeHomeDetailResponse( + resume.id.as("resumeId"), + resume.member.faceInfo.generatedS3url.as("thumbnailS3url"))) .from(resume) .leftJoin(resume.member, QMember.member) // left join .where(builder) // boolean builder @@ -146,10 +82,9 @@ public Page getResumesByBadCombi(Long memberId, Pageab // 카테고리별 동적 쿼리 public Page getResumesByCategory(String category, Pageable pageable) { List content = queryFactory - .select(Projections.bean(ResumeHomeDetailResponse.class, - resume.id, - resume.member.faceInfo.generatedS3url - )) + .select(new QResumeHomeDetailResponse( + resume.id.as("resumeId"), + resume.member.faceInfo.generatedS3url.as("thumbnailS3url"))) .from(resume) .leftJoin(resume.member, QMember.member) // left join .where(resume.category.eq(Resume.Category.valueOf(category))) @@ -173,9 +108,4 @@ private Member findMemberById(Long memberId) { return memberRepository.findById(memberId) .orElseThrow(() -> new MemberException(NOT_FOUND)); } - - private Resume findResumeByMember(Member member) { - return resumeRepository.findResumeByMember(member) - .orElseThrow(() -> new ResumeException(NO_RESUME)); - } } From e84efb593bc2a4e2d4baf2a89ed9aabdd2bae2b4 Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Sat, 4 May 2024 08:53:31 +0900 Subject: [PATCH 154/265] =?UTF-8?q?feat:=20resumeService=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/dto/ResumeHomeDetailResponse.java | 17 +++++-- .../resume/domain/dto/ResumePostRequest.java | 6 --- .../resume/domain/dto/ResumePutRequest.java | 1 - .../resume/service/ResumeService.java | 46 +++++++++---------- 4 files changed, 36 insertions(+), 34 deletions(-) diff --git a/src/main/java/capstone/facefriend/resume/domain/dto/ResumeHomeDetailResponse.java b/src/main/java/capstone/facefriend/resume/domain/dto/ResumeHomeDetailResponse.java index fc656ee352..aa0e465f19 100644 --- a/src/main/java/capstone/facefriend/resume/domain/dto/ResumeHomeDetailResponse.java +++ b/src/main/java/capstone/facefriend/resume/domain/dto/ResumeHomeDetailResponse.java @@ -1,7 +1,16 @@ package capstone.facefriend.resume.domain.dto; -public record ResumeHomeDetailResponse( - Long resumeId, - String thumbnailS3url -){ +import com.querydsl.core.annotations.QueryProjection; +import lombok.Data; + +@Data +public class ResumeHomeDetailResponse { + private Long resumeId; + private String thumbnailS3url; + + @QueryProjection + public ResumeHomeDetailResponse(Long resumeId, String thumbnailS3url) { + this.resumeId = resumeId; + this.thumbnailS3url = thumbnailS3url; + } } diff --git a/src/main/java/capstone/facefriend/resume/domain/dto/ResumePostRequest.java b/src/main/java/capstone/facefriend/resume/domain/dto/ResumePostRequest.java index 4fc229e238..5ff29b2fe2 100644 --- a/src/main/java/capstone/facefriend/resume/domain/dto/ResumePostRequest.java +++ b/src/main/java/capstone/facefriend/resume/domain/dto/ResumePostRequest.java @@ -1,12 +1,6 @@ package capstone.facefriend.resume.domain.dto; -import org.springframework.web.multipart.MultipartFile; - -import java.util.List; - public record ResumePostRequest( - Long resumeId, - List images, String category, String content ) { diff --git a/src/main/java/capstone/facefriend/resume/domain/dto/ResumePutRequest.java b/src/main/java/capstone/facefriend/resume/domain/dto/ResumePutRequest.java index 400736b5fe..e223a91ac4 100644 --- a/src/main/java/capstone/facefriend/resume/domain/dto/ResumePutRequest.java +++ b/src/main/java/capstone/facefriend/resume/domain/dto/ResumePutRequest.java @@ -1,7 +1,6 @@ package capstone.facefriend.resume.domain.dto; public record ResumePutRequest( - Long resumeId, String category, String content ) { diff --git a/src/main/java/capstone/facefriend/resume/service/ResumeService.java b/src/main/java/capstone/facefriend/resume/service/ResumeService.java index 5035ceef12..173073cd6b 100644 --- a/src/main/java/capstone/facefriend/resume/service/ResumeService.java +++ b/src/main/java/capstone/facefriend/resume/service/ResumeService.java @@ -41,12 +41,17 @@ public ResumeResponse postResume( ResumePostRequest request ) throws IOException { + if (images.size() == 0) { + throw new ResumeException(AT_LEAST_ONE_IMAGE); + } + Member member = findMemberById(memberId); - if (!resumeRepository.findResumeByMember(member).isPresent()) { + if (resumeRepository.findResumeByMember(member).isPresent()) { throw new ResumeException(ALREADY_HAS_RESUME); } - List resumeImagesS3url = bucketService.uploadResumeImages(images, memberId); + List resumeImagesS3url = bucketService.uploadResumeImages(images); + Resume resume = Resume.builder() .member(member) .resumeImageS3urls(resumeImagesS3url) @@ -67,7 +72,6 @@ public ResumeResponse postResume( } public ResumeResponse getResume( - Long memberId, Long resumeId ) { Resume resume = resumeRepository.findResumeById(resumeId) @@ -86,30 +90,31 @@ public ResumeResponse getResume( public ResumeResponse putResume( Long memberId, + Long resumeId, List images, ResumePutRequest request ) throws IOException { - Member member = findMemberById(memberId); // me - Resume resume = findResumeByMember(member); // mine + Member me = findMemberById(memberId); + Resume myResume = findResumeByMember(me); // dirty check possible - if (request.resumeId() != resume.getId()) { // if resumeId is not mine + if (resumeId != myResume.getId()) { throw new ResumeException(UNAUTHORIZED); } - List resumeImageS3urls = bucketService.updateResumeImages(images, memberId, images.size()); + List resumeImageS3urls = bucketService.updateResumeImages(images, myResume); - resume.setResumeImageS3urls(resumeImageS3urls); - resume.setCategory(Category.valueOf(request.category())); - resume.setContent(request.content()); + myResume.setResumeImageS3urls(resumeImageS3urls); // dirty check possible + myResume.setCategory(Category.valueOf(request.category())); + myResume.setContent(request.content()); return new ResumeResponse( - resume.getId(), - resume.getResumeImageS3urls(), - resume.getMember().getFaceInfo(), - resume.getMember().getBasicInfo(), - resume.getMember().getAnalysisInfo(), - resume.getCategory(), - resume.getContent() + myResume.getId(), + myResume.getResumeImageS3urls(), + myResume.getMember().getFaceInfo(), + myResume.getMember().getBasicInfo(), + myResume.getMember().getAnalysisInfo(), + myResume.getCategory(), + myResume.getContent() ); } @@ -124,8 +129,7 @@ public ResumeDeleteResponse deleteResume( throw new ResumeException(UNAUTHORIZED); } - int size = resume.getResumeImageS3urls().size(); - bucketService.deleteResumeImages(memberId, size); + bucketService.deleteResumeImages(resume); resumeRepository.deleteResumeById(resumeId); return new ResumeDeleteResponse(DELETE_SUCCESS_MESSAGE); @@ -136,10 +140,6 @@ public Page getResumesByGoodCombi(Long memberId, Pagea return resumeRepository.getResumesByGoodCombi(memberId, pageable); } - public Page getResumesByBadCombi(Long memberId, Pageable pageable) { - return resumeRepository.getResumesByBadCombi(memberId, pageable); - } - public Page getResumesByCategory(String category, Pageable pageable) { return resumeRepository.getResumesByCategory(category, pageable); } From a292fc2766f6390fe3f175f6c5a988fac5de952c Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Sat, 4 May 2024 08:54:24 +0900 Subject: [PATCH 155/265] =?UTF-8?q?feat:resume=20=EC=BB=A8=ED=8A=B8?= =?UTF-8?q?=EB=A1=A4=EB=9F=AC=20=EB=B0=8F=20=EC=BB=A4=EC=8A=A4=ED=85=80=20?= =?UTF-8?q?=EC=98=88=EC=99=B8=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../resume/controller/ResumeController.java | 15 +++++---------- .../resume/exception/ResumeExceptionType.java | 3 ++- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/src/main/java/capstone/facefriend/resume/controller/ResumeController.java b/src/main/java/capstone/facefriend/resume/controller/ResumeController.java index 2e9c2c9cc0..0f05beeaf7 100644 --- a/src/main/java/capstone/facefriend/resume/controller/ResumeController.java +++ b/src/main/java/capstone/facefriend/resume/controller/ResumeController.java @@ -37,16 +37,17 @@ public ResponseEntity getResume( @AuthMember Long memberId, @RequestParam("resumeId") Long resumeId ) { - return ResponseEntity.ok(resumeService.getResume(memberId, resumeId)); + return ResponseEntity.ok(resumeService.getResume(resumeId)); } @PutMapping("/resume") public ResponseEntity putResume( @AuthMember Long memberId, + @RequestParam("resumeId") Long resumeId, @RequestPart("images") List images, @RequestPart("request") ResumePutRequest request ) throws IOException { - return ResponseEntity.ok(resumeService.putResume(memberId, images, request)); + return ResponseEntity.ok(resumeService.putResume(memberId, resumeId, images, request)); } @DeleteMapping("/resume") @@ -57,6 +58,8 @@ public ResponseEntity deleteResume( return ResponseEntity.ok(resumeService.deleteResume(memberId, resumeId)); } + + // 동적 쿼리 @GetMapping("/resume-by-good-combi") public Page getResumesByGoodCombi( @@ -66,14 +69,6 @@ public Page getResumesByGoodCombi( return resumeService.getResumesByGoodCombi(memberId, pageable); } - @GetMapping("/resume-by-bad-combi") - public Page getResumesByBadCombi( - @AuthMember Long memberId, - Pageable pageable - ) { - return resumeService.getResumesByBadCombi(memberId, pageable); - } - @GetMapping("/resume-by-category") public Page getResumesByCategory( @AuthMember Long memberId, diff --git a/src/main/java/capstone/facefriend/resume/exception/ResumeExceptionType.java b/src/main/java/capstone/facefriend/resume/exception/ResumeExceptionType.java index f68d0159f5..50de768373 100644 --- a/src/main/java/capstone/facefriend/resume/exception/ResumeExceptionType.java +++ b/src/main/java/capstone/facefriend/resume/exception/ResumeExceptionType.java @@ -8,7 +8,8 @@ public enum ResumeExceptionType implements ExceptionType { NO_RESUME(Status.NOT_FOUND, 7001, "자기소개서가 없습니다!."), ALREADY_HAS_RESUME(Status.BAD_REQUEST, 7002, "자기소개서는 1인당 1개만 생성할 수 있습니다!"), UNAUTHORIZED(Status.UNAUTHORIZED, 7003, "나의 자기소개서가 아닙니다!"), - FAIL_TO_DELETE(Status.BAD_REQUEST, 7004, "자기소개서 삭제 실패") + FAIL_TO_DELETE(Status.BAD_REQUEST, 7004, "자기소개서 삭제 실패"), + AT_LEAST_ONE_IMAGE(Status.BAD_REQUEST, 7005, "최소한 1개 이상의 이미지를 업로드해야 합니다!") ; private final Status status; From a553f6eaa87e606078946717f1d896e4d1011963 Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Sat, 4 May 2024 08:56:59 +0900 Subject: [PATCH 156/265] =?UTF-8?q?feat:=20=EB=8D=94=EB=AF=B8=EB=8D=B0?= =?UTF-8?q?=EC=9D=B4=ED=84=B0=20=EC=B6=94=EA=B0=80=20=EB=B0=8F=20=EC=9D=B8?= =?UTF-8?q?=ED=84=B0=EC=85=89=ED=84=B0=20=EA=B2=BD=EB=A1=9C=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../capstone/facefriend/DummyInitializer.java | 66 +++++++++++++++++-- .../facefriend/auth/config/AuthConfig.java | 12 +++- 2 files changed, 73 insertions(+), 5 deletions(-) diff --git a/src/main/java/capstone/facefriend/DummyInitializer.java b/src/main/java/capstone/facefriend/DummyInitializer.java index 37f24f6c42..704b1c5029 100644 --- a/src/main/java/capstone/facefriend/DummyInitializer.java +++ b/src/main/java/capstone/facefriend/DummyInitializer.java @@ -1,29 +1,87 @@ package capstone.facefriend; +import capstone.facefriend.member.domain.analysisInfo.AnalysisInfo; +import capstone.facefriend.member.domain.analysisInfo.AnalysisInfoRepository; import capstone.facefriend.member.domain.member.Member; import capstone.facefriend.member.domain.member.MemberRepository; +import capstone.facefriend.member.service.AnalysisInfoService; +import capstone.facefriend.member.service.MemberService; +import capstone.facefriend.member.service.dto.member.SignUpRequest; import capstone.facefriend.resume.domain.Resume; import capstone.facefriend.resume.domain.ResumeRepository; +import capstone.facefriend.resume.service.ResumeService; import jakarta.annotation.PostConstruct; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; + +import java.util.ArrayList; +import java.util.List; +import java.util.Random; @Component +@Slf4j @RequiredArgsConstructor public class DummyInitializer { + private final MemberService memberService; private final MemberRepository memberRepository; + + private final AnalysisInfoRepository analysisInfoRepository; + private final AnalysisInfoService analysisInfoService; + + private final ResumeService resumeService; private final ResumeRepository resumeRepository; + @PostConstruct + @Transactional public void init() { + Random random = new Random(); + + List GOOD_COMBI = new ArrayList<>(); - for (long i = 0; i < 100; i++) { - Member member = Member.builder().id(i).build(); - memberRepository.save(member); + List CATEGORY = List.of("FOOD", "WORKOUT", "MOVIE", "FASHION", "DATING", "STUDY", "ETC"); + int size = CATEGORY.size(); - Resume resume = Resume.builder().id(i).member(member).build(); + for (int i = 1; i <= 50; i++) { + // 회원 가입 + memberService.signUp(new SignUpRequest(i + "@" + i + ".com", "123", "123")); + Member member = memberRepository.findByEmail(i + "@" + i + ".com").get(); + + // 관상 분석 + AnalysisInfo analysisInfo = member.getAnalysisInfo(); + analysisInfo.setFaceShapeIdNum(random.nextInt(5)); // 얼굴형 넘버 랜덤 + analysisInfoRepository.save(analysisInfo); + + // 자기소개서 + Resume resume = Resume.builder() + .category(Resume.Category.valueOf(CATEGORY.get(random.nextInt(size-1)))) // 카테고리 랜덤 + .member(member) + .build(); resumeRepository.save(resume); } + + +// @PostConstruct +// @Transactional +// public void init() { +// Random random = new Random(); +// +// for (int i = 1; i <= 50; i++) { +// // 회원 가입 +// memberService.signUp(new SignUpRequest(i + "@" + i + ".com", "123", "123")); +// Member member = memberService.findMemberByEmail(i + "@" + i + ".com"); +// +// // 회원의 분석 정보 업데이트 +// AnalysisInfo analysisInfo = analysisInfoService.findAnalysisInfoByMemberId(member.getId()); +// analysisInfo.setFaceShapeIdNum((i - 1) % 5); +// +// // 이력 생성 +// Resume resume = Resume.builder().category(Resume.Category.DATING).member(member).build(); +// resumeService.saveResume(resume); +// } +// } } } diff --git a/src/main/java/capstone/facefriend/auth/config/AuthConfig.java b/src/main/java/capstone/facefriend/auth/config/AuthConfig.java index f12feeb42e..77d3eef4ae 100644 --- a/src/main/java/capstone/facefriend/auth/config/AuthConfig.java +++ b/src/main/java/capstone/facefriend/auth/config/AuthConfig.java @@ -52,6 +52,9 @@ private HandlerInterceptor loginCheckInterceptor() { .addIncludePathPattern("/basic-info", ANY) .addIncludePathPattern("/face-info", ANY) .addIncludePathPattern("/analysis-info", ANY) + .addIncludePathPattern("/resume", ANY) + .addIncludePathPattern("/resume-by-good-combi", ANY) + .addIncludePathPattern("/resume-by-category", ANY) .addExcludePathPattern("/auth/reissue/**", POST); // 토큰 만료 시에는 해당 요청을 가로채지 않아야 합니다. } @@ -66,6 +69,9 @@ private HandlerInterceptor loginInterceptor() { .addIncludePathPattern("/basic-info", ANY) .addIncludePathPattern("/face-info", ANY) .addIncludePathPattern("/analysis-info/**", ANY) + .addIncludePathPattern("/resume", ANY) + .addIncludePathPattern("/resume-by-good-combi", ANY) + .addIncludePathPattern("/resume-by-category", ANY) .addExcludePathPattern("/auth/reissue", POST); // 토큰 만료 시에는 해당 요청을 가로채지 않아야 합니다. } @@ -86,7 +92,11 @@ private HandlerInterceptor tokenBlackListInterceptor() { .addIncludePathPattern("/auth/reset-password", POST) .addIncludePathPattern("/basic-info", ANY) .addIncludePathPattern("/face-info", ANY) - .addIncludePathPattern("/analysis-info/**", ANY); + .addIncludePathPattern("/analysis-info/**", ANY) + .addIncludePathPattern("/resume", ANY) + .addIncludePathPattern("/resume-by-good-combi", ANY) + .addIncludePathPattern("/resume-by-category", ANY) + ; } @Override From c0bcc87ed7501931bfda410b084201c34017db90 Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Sat, 4 May 2024 08:58:11 +0900 Subject: [PATCH 157/265] =?UTF-8?q?chore:=20JAXB=20warning=20=EA=B4=80?= =?UTF-8?q?=EB=A0=A8=20=EC=9D=98=EC=A1=B4=EC=84=B1=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/build.gradle b/build.gradle index 5aeb5a5c62..8ed7156733 100644 --- a/build.gradle +++ b/build.gradle @@ -65,8 +65,8 @@ dependencies { annotationProcessor "jakarta.annotation:jakarta.annotation-api" // java.lang.NoClassDefFoundError (javax.annotation.Generated) 에러 대응 코드 annotationProcessor "jakarta.persistence:jakarta.persistence-api" // java.lang.NoClassDefFoundError (javax.annotation.Entity) 에러 대응 코드 - // jasypt - implementation 'com.github.ulisesbocchio:jasypt-spring-boot-starter:3.0.5' + // JAXB deprecated warning + implementation 'javax.xml.bind:jaxb-api:2.3.0' } tasks.named('test') { @@ -89,9 +89,4 @@ sourceSets { ///// gradle clean 시에 QClass 디렉토리 삭제 clean { delete file(generated) -} - -test { - useJUnitPlatform() - systemProperty 'jasypt.encryptor.password', findProperty("jasypt.encryptor.password") } \ No newline at end of file From de98f16b783aa8b41a12e4cd51908b475b3c00d4 Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Sat, 4 May 2024 16:45:42 +0900 Subject: [PATCH 158/265] =?UTF-8?q?feat:=20=EC=98=81=EC=86=8D=20=EC=8B=9C?= =?UTF-8?q?=EC=A0=90=EC=97=90=20=EB=94=B0=EB=A5=B8=20@Transaction=20?= =?UTF-8?q?=EC=95=A0=EB=85=B8=ED=85=8C=EC=9D=B4=EC=85=98=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../member/service/AnalysisInfoService.java | 31 +++++++++---------- .../member/service/BasicInfoService.java | 18 +++++------ .../member/service/FaceInfoService.java | 21 ++++++------- .../resume/domain/dto/ResumeCondition.java | 6 ---- .../resume/domain/dto/ResumeGetResponse.java | 2 ++ ...meResponse.java => ResumePutResponse.java} | 0 6 files changed, 35 insertions(+), 43 deletions(-) delete mode 100644 src/main/java/capstone/facefriend/resume/domain/dto/ResumeCondition.java create mode 100644 src/main/java/capstone/facefriend/resume/domain/dto/ResumeGetResponse.java rename src/main/java/capstone/facefriend/resume/domain/dto/{ResumeResponse.java => ResumePutResponse.java} (100%) diff --git a/src/main/java/capstone/facefriend/member/service/AnalysisInfoService.java b/src/main/java/capstone/facefriend/member/service/AnalysisInfoService.java index 171272260d..ec13b764a6 100644 --- a/src/main/java/capstone/facefriend/member/service/AnalysisInfoService.java +++ b/src/main/java/capstone/facefriend/member/service/AnalysisInfoService.java @@ -1,7 +1,6 @@ package capstone.facefriend.member.service; -import capstone.facefriend.member.domain.analysisInfo.AnalysisInfoRepository; import capstone.facefriend.member.domain.member.Member; import capstone.facefriend.member.domain.member.MemberRepository; import capstone.facefriend.member.exception.analysis.AnalysisException; @@ -37,7 +36,6 @@ import static capstone.facefriend.member.exception.member.MemberExceptionType.NOT_FOUND; -@Transactional @Slf4j @Service @RequiredArgsConstructor @@ -49,8 +47,9 @@ public class AnalysisInfoService { private final RestTemplate restTemplate; private final MemberRepository memberRepository; - private final AnalysisInfoRepository analysisInfoRepository; + + @Transactional public AnalysisInfoFullResponse analyze(MultipartFile origin, Long memberId) throws IOException { // convert MultipartFile into ByteArrayResource ByteArrayResource resource = new ByteArrayResource(origin.getBytes()) { @@ -87,18 +86,16 @@ public String getFilename() { List analysisShort = extractAnalysisInfoShort(total); Integer faceShapeIdNum = extractFaceShapeIdNum(total); - Member member = findMemberById(memberId); - member.getAnalysisInfo().setAnalysisInfoFull(analysisFull); + Member member = findMemberById(memberId); // 영속 상태 + member.getAnalysisInfo().setAnalysisInfoFull(analysisFull); // dirty member.getAnalysisInfo().setAnalysisInfoShort(analysisShort); member.getAnalysisInfo().setFaceShapeIdNum(faceShapeIdNum); -// memberRepository.save(member); return new AnalysisInfoFullResponse(analysisFull); } private Map extractAnalysisInfoFull(AnalysisInfoTotal total) { Map analysisFull = new HashMap<>(); - analysisFull.put(total.getEye().getName(), total.getEye().getDescription()); analysisFull.put(total.getFaceShape().getName(), total.getFaceShape().getDescription()); analysisFull.put(total.getLips().getName(), total.getLips().getDescription()); @@ -119,11 +116,11 @@ private Integer extractFaceShapeIdNum(AnalysisInfoTotal total) { private List extractAnalysisInfoShort(AnalysisInfoTotal total) { return Stream.of( - total.getEye().getTag(), - total.getFaceShape().getTag(), - total.getLips().getTag(), - total.getNose().getTag(), - total.getEyebrow().getTag() + total.getEye().getTag(), + total.getFaceShape().getTag(), + total.getLips().getTag(), + total.getNose().getTag(), + total.getEyebrow().getTag() ).flatMap(List::stream) .collect(Collectors.toList()); } @@ -154,7 +151,7 @@ private Member findMemberById(Long memberId) { @Setter @AllArgsConstructor(access = AccessLevel.PRIVATE) @NoArgsConstructor(access = AccessLevel.PROTECTED) - public static class AnalysisInfoTotal { + public static class AnalysisInfoTotal { @JsonProperty("face_shape") private FaceShape faceShape; private Eye eye; @@ -167,7 +164,7 @@ public static class AnalysisInfoTotal { @Setter @AllArgsConstructor(access = AccessLevel.PRIVATE) @NoArgsConstructor(access = AccessLevel.PROTECTED) - public static class FaceShape { + public static class FaceShape { private String name; private String description; @JsonDeserialize(using = StringListDeserializer.class) @@ -180,7 +177,7 @@ public static class FaceShape { @Setter @AllArgsConstructor(access = AccessLevel.PRIVATE) @NoArgsConstructor(access = AccessLevel.PROTECTED) - public static class Eye { + public static class Eye { private String name; private String description; @JsonDeserialize(using = StringListDeserializer.class) @@ -193,7 +190,7 @@ public static class Eye { @Setter @AllArgsConstructor(access = AccessLevel.PRIVATE) @NoArgsConstructor(access = AccessLevel.PROTECTED) - public static class Lips { + public static class Lips { private String name; private String description; @JsonDeserialize(using = StringListDeserializer.class) @@ -206,7 +203,7 @@ public static class Lips { @Setter @AllArgsConstructor(access = AccessLevel.PRIVATE) @NoArgsConstructor(access = AccessLevel.PROTECTED) - public static class Nose { + public static class Nose { private String name; private String description; @JsonDeserialize(using = StringListDeserializer.class) diff --git a/src/main/java/capstone/facefriend/member/service/BasicInfoService.java b/src/main/java/capstone/facefriend/member/service/BasicInfoService.java index 8a613d7ad4..ab31250a39 100644 --- a/src/main/java/capstone/facefriend/member/service/BasicInfoService.java +++ b/src/main/java/capstone/facefriend/member/service/BasicInfoService.java @@ -33,18 +33,18 @@ public BasicInfoResponse getBasicInfo(Long memberId) { @Transactional public BasicInfoResponse putBasicInfo(Long memberId, BasicInfoRequest request) { - Member member = findMemberById(memberId); - BasicInfo oldBasicInfo = member.getBasicInfo(); + Member member = findMemberById(memberId); // 영속 + BasicInfo oldBasicInfo = member.getBasicInfo(); // 영속 - oldBasicInfo.setNickname(request.nickname()); - oldBasicInfo.setGender(Gender.valueOf(request.gender())); - oldBasicInfo.setAgeGroup(AgeGroup.valueOf(request.ageGroup())); - oldBasicInfo.setAgeDegree(AgeDegree.valueOf(request.ageDegree())); - oldBasicInfo.setHeightGroup(HeightGroup.valueOf(request.heightGroup())); - oldBasicInfo.setRegion(Region.valueOf(request.region())); + oldBasicInfo.setNickname(request.nickname()); // dirty check + oldBasicInfo.setGender(Gender.valueOf(request.gender())); // dirty check + oldBasicInfo.setAgeGroup(AgeGroup.valueOf(request.ageGroup())); // dirty check + oldBasicInfo.setAgeDegree(AgeDegree.valueOf(request.ageDegree())); // dirty check + oldBasicInfo.setHeightGroup(HeightGroup.valueOf(request.heightGroup())); // dirty check + oldBasicInfo.setRegion(Region.valueOf(request.region())); // dirty check member.setBasicInfo(oldBasicInfo); -// memberRepository.save(member); + return BasicInfoResponse.of(oldBasicInfo); } diff --git a/src/main/java/capstone/facefriend/member/service/FaceInfoService.java b/src/main/java/capstone/facefriend/member/service/FaceInfoService.java index 02c0e916d7..36a22246cc 100644 --- a/src/main/java/capstone/facefriend/member/service/FaceInfoService.java +++ b/src/main/java/capstone/facefriend/member/service/FaceInfoService.java @@ -34,7 +34,7 @@ import java.util.Map; -@Transactional + @Slf4j @Service @RequiredArgsConstructor @@ -47,24 +47,23 @@ public class FaceInfoService { private final MemberRepository memberRepository; private final FaceInfoRepository faceInfoRepository; - - // origin 삭제 & generated 삭제 -> origin 업로드 & generated 업로드 + @Transactional // origin 삭제 & generated 삭제 -> origin 업로드 & generated 업로드 public FaceInfoResponse updateOrigin(MultipartFile origin, Long styleId, Long memberId) throws IOException { // bucket update ByteArrayMultipartFile generated = generate(origin, styleId, memberId); List s3urls = bucketService.updateOriginAndGenerated(origin, generated, memberId); // entity update - Member member = findMemberById(memberId); - FaceInfo faceInfo = faceInfoRepository.findFaceInfoById(member.getFaceInfo().getId()); + Member member = findMemberById(memberId); // 영속 + FaceInfo faceInfo = faceInfoRepository.findFaceInfoById(member.getFaceInfo().getId()); // 영속 String originS3url = s3urls.get(0); String generatedS3url = s3urls.get(1); - faceInfo.setOriginS3url(originS3url); - faceInfo.setGeneratedS3url(generatedS3url); + faceInfo.setOriginS3url(originS3url); // dirty check + faceInfo.setGeneratedS3url(generatedS3url); // dirty check - member.setFaceInfo(faceInfo); + member.setFaceInfo(faceInfo); // dirty check return new FaceInfoResponse(originS3url, generatedS3url); } @@ -75,11 +74,11 @@ public FaceInfoResponse getOriginAndGenerated(Long memberId) { return new FaceInfoResponse(faceInfo.getOriginS3url(), faceInfo.getGeneratedS3url()); } - // origin 삭제 & generated 삭제 + @Transactional // origin 삭제 & generated 삭제 public FaceInfoResponse deleteOriginAndGenerated(Long memberId) { String defaultFaceInfoS3url = bucketService.deleteOriginAndGenerated(memberId); - Member member = findMemberById(memberId); + Member member = findMemberById(memberId); // 영속 faceInfoRepository.deleteFaceInfoById(member.getFaceInfo().getId()); FaceInfo faceInfo = FaceInfo.builder() @@ -90,7 +89,7 @@ public FaceInfoResponse deleteOriginAndGenerated(Long memberId) { faceInfo.setGeneratedS3url(defaultFaceInfoS3url); faceInfoRepository.save(faceInfo); - member.setFaceInfo(faceInfo); + member.setFaceInfo(faceInfo); // dirty check return new FaceInfoResponse(defaultFaceInfoS3url, defaultFaceInfoS3url); } diff --git a/src/main/java/capstone/facefriend/resume/domain/dto/ResumeCondition.java b/src/main/java/capstone/facefriend/resume/domain/dto/ResumeCondition.java deleted file mode 100644 index 937ec5ab09..0000000000 --- a/src/main/java/capstone/facefriend/resume/domain/dto/ResumeCondition.java +++ /dev/null @@ -1,6 +0,0 @@ -package capstone.facefriend.resume.domain.dto; - -public record ResumeCondition( - String category -) { -} diff --git a/src/main/java/capstone/facefriend/resume/domain/dto/ResumeGetResponse.java b/src/main/java/capstone/facefriend/resume/domain/dto/ResumeGetResponse.java new file mode 100644 index 0000000000..9c4f325198 --- /dev/null +++ b/src/main/java/capstone/facefriend/resume/domain/dto/ResumeGetResponse.java @@ -0,0 +1,2 @@ +package capstone.facefriend.resume.domain.dto;public record ResumePostResponse() { +} diff --git a/src/main/java/capstone/facefriend/resume/domain/dto/ResumeResponse.java b/src/main/java/capstone/facefriend/resume/domain/dto/ResumePutResponse.java similarity index 100% rename from src/main/java/capstone/facefriend/resume/domain/dto/ResumeResponse.java rename to src/main/java/capstone/facefriend/resume/domain/dto/ResumePutResponse.java From 346cfe3c9b636f3cd78280659226285e01c235c2 Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Sat, 4 May 2024 16:48:03 +0900 Subject: [PATCH 159/265] =?UTF-8?q?style:=20=EC=BD=94=EB=93=9C=20=ED=8F=AC?= =?UTF-8?q?=EB=A7=A4=ED=8C=85=20=EB=B0=8F=20=EC=A3=BC=EC=84=9D=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../capstone/facefriend/DummyInitializer.java | 21 ------- .../facefriend/FacefriendApplication.java | 5 ++ .../domain/member/MemberRepository.java | 6 -- .../resume/controller/ResumeController.java | 10 ++- .../resume/domain/dto/ResumeGetResponse.java | 20 +++++- .../resume/domain/dto/ResumePutResponse.java | 8 +-- .../resume/exception/ResumeExceptionType.java | 2 +- .../resume/service/ResumeService.java | 61 +++++++++++-------- 8 files changed, 70 insertions(+), 63 deletions(-) diff --git a/src/main/java/capstone/facefriend/DummyInitializer.java b/src/main/java/capstone/facefriend/DummyInitializer.java index 704b1c5029..74f586db53 100644 --- a/src/main/java/capstone/facefriend/DummyInitializer.java +++ b/src/main/java/capstone/facefriend/DummyInitializer.java @@ -62,26 +62,5 @@ public void init() { .build(); resumeRepository.save(resume); } - - -// @PostConstruct -// @Transactional -// public void init() { -// Random random = new Random(); -// -// for (int i = 1; i <= 50; i++) { -// // 회원 가입 -// memberService.signUp(new SignUpRequest(i + "@" + i + ".com", "123", "123")); -// Member member = memberService.findMemberByEmail(i + "@" + i + ".com"); -// -// // 회원의 분석 정보 업데이트 -// AnalysisInfo analysisInfo = analysisInfoService.findAnalysisInfoByMemberId(member.getId()); -// analysisInfo.setFaceShapeIdNum((i - 1) % 5); -// -// // 이력 생성 -// Resume resume = Resume.builder().category(Resume.Category.DATING).member(member).build(); -// resumeService.saveResume(resume); -// } -// } } } diff --git a/src/main/java/capstone/facefriend/FacefriendApplication.java b/src/main/java/capstone/facefriend/FacefriendApplication.java index 723fc9435d..cff671970b 100644 --- a/src/main/java/capstone/facefriend/FacefriendApplication.java +++ b/src/main/java/capstone/facefriend/FacefriendApplication.java @@ -1,5 +1,6 @@ package capstone.facefriend; +import capstone.facefriend.resume.domain.Resume; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.data.jpa.repository.config.EnableJpaAuditing; @@ -13,5 +14,9 @@ public class FacefriendApplication { public static void main(String[] args) { TimeZone.setDefault(TimeZone.getTimeZone("Asia/Seoul")); SpringApplication.run(FacefriendApplication.class, args); + + + + } } diff --git a/src/main/java/capstone/facefriend/member/domain/member/MemberRepository.java b/src/main/java/capstone/facefriend/member/domain/member/MemberRepository.java index f7b31fd2bb..67b85d6746 100644 --- a/src/main/java/capstone/facefriend/member/domain/member/MemberRepository.java +++ b/src/main/java/capstone/facefriend/member/domain/member/MemberRepository.java @@ -1,7 +1,5 @@ package capstone.facefriend.member.domain.member; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.Pageable; import org.springframework.data.repository.Repository; import java.util.Optional; @@ -12,11 +10,7 @@ public interface MemberRepository extends Repository { Member save(Member member); - boolean existsById(Long id); - Optional findById(Long id); - Page findAll(Pageable pageable); - void deleteById(Long id); } diff --git a/src/main/java/capstone/facefriend/resume/controller/ResumeController.java b/src/main/java/capstone/facefriend/resume/controller/ResumeController.java index 0f05beeaf7..58f5ac3acb 100644 --- a/src/main/java/capstone/facefriend/resume/controller/ResumeController.java +++ b/src/main/java/capstone/facefriend/resume/controller/ResumeController.java @@ -24,7 +24,7 @@ public class ResumeController { // 정적 쿼리 @PostMapping("/resume") - public ResponseEntity postResume( + public ResponseEntity postResume( @AuthMember Long memberId, @RequestPart("images") List images, @RequestPart("request") ResumePostRequest request @@ -33,15 +33,15 @@ public ResponseEntity postResume( } @GetMapping("/resume") - public ResponseEntity getResume( + public ResponseEntity getResume( @AuthMember Long memberId, @RequestParam("resumeId") Long resumeId ) { - return ResponseEntity.ok(resumeService.getResume(resumeId)); + return ResponseEntity.ok(resumeService.getResume(memberId, resumeId)); } @PutMapping("/resume") - public ResponseEntity putResume( + public ResponseEntity putResume( @AuthMember Long memberId, @RequestParam("resumeId") Long resumeId, @RequestPart("images") List images, @@ -58,8 +58,6 @@ public ResponseEntity deleteResume( return ResponseEntity.ok(resumeService.deleteResume(memberId, resumeId)); } - - // 동적 쿼리 @GetMapping("/resume-by-good-combi") public Page getResumesByGoodCombi( diff --git a/src/main/java/capstone/facefriend/resume/domain/dto/ResumeGetResponse.java b/src/main/java/capstone/facefriend/resume/domain/dto/ResumeGetResponse.java index 9c4f325198..c0b07e68b1 100644 --- a/src/main/java/capstone/facefriend/resume/domain/dto/ResumeGetResponse.java +++ b/src/main/java/capstone/facefriend/resume/domain/dto/ResumeGetResponse.java @@ -1,2 +1,20 @@ -package capstone.facefriend.resume.domain.dto;public record ResumePostResponse() { +package capstone.facefriend.resume.domain.dto; + +import capstone.facefriend.member.domain.analysisInfo.AnalysisInfo; +import capstone.facefriend.member.domain.basicInfo.BasicInfo; +import capstone.facefriend.member.domain.faceInfo.FaceInfo; +import capstone.facefriend.resume.domain.Resume; + +import java.util.List; + +public record ResumeGetResponse( + Long resumeId, + List resumeImageS3urls, + FaceInfo faceInfo, + BasicInfo basicInfo, + AnalysisInfo analysisInfo, + Resume.Category category, + String content, + Boolean isMine +) { } diff --git a/src/main/java/capstone/facefriend/resume/domain/dto/ResumePutResponse.java b/src/main/java/capstone/facefriend/resume/domain/dto/ResumePutResponse.java index 0506d6c5c2..58645309de 100644 --- a/src/main/java/capstone/facefriend/resume/domain/dto/ResumePutResponse.java +++ b/src/main/java/capstone/facefriend/resume/domain/dto/ResumePutResponse.java @@ -8,13 +8,13 @@ import static capstone.facefriend.resume.domain.Resume.Category; -public record ResumeResponse( +public record ResumePutResponse( Long resumeId, - List resumeImageS3urls, // resume + List resumeImageS3urls, FaceInfo faceInfo, BasicInfo basicInfo, AnalysisInfo analysisInfo, - Category category, // resume - String content // resume + Category category, + String content ) { } diff --git a/src/main/java/capstone/facefriend/resume/exception/ResumeExceptionType.java b/src/main/java/capstone/facefriend/resume/exception/ResumeExceptionType.java index 50de768373..67b2183d6c 100644 --- a/src/main/java/capstone/facefriend/resume/exception/ResumeExceptionType.java +++ b/src/main/java/capstone/facefriend/resume/exception/ResumeExceptionType.java @@ -5,7 +5,7 @@ public enum ResumeExceptionType implements ExceptionType { - NO_RESUME(Status.NOT_FOUND, 7001, "자기소개서가 없습니다!."), + NO_RESUME(Status.NOT_FOUND, 7001, "자기소개서가 없습니다!"), ALREADY_HAS_RESUME(Status.BAD_REQUEST, 7002, "자기소개서는 1인당 1개만 생성할 수 있습니다!"), UNAUTHORIZED(Status.UNAUTHORIZED, 7003, "나의 자기소개서가 아닙니다!"), FAIL_TO_DELETE(Status.BAD_REQUEST, 7004, "자기소개서 삭제 실패"), diff --git a/src/main/java/capstone/facefriend/resume/service/ResumeService.java b/src/main/java/capstone/facefriend/resume/service/ResumeService.java index 173073cd6b..9a6aac0ba1 100644 --- a/src/main/java/capstone/facefriend/resume/service/ResumeService.java +++ b/src/main/java/capstone/facefriend/resume/service/ResumeService.java @@ -14,6 +14,7 @@ import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import org.springframework.web.multipart.MultipartFile; import java.io.IOException; @@ -35,7 +36,7 @@ public class ResumeService { private static final String DELETE_SUCCESS_MESSAGE = "자기소개서 삭제 완료!"; // 정적 쿼리 - public ResumeResponse postResume( + public ResumePutResponse postResume( Long memberId, List images, ResumePostRequest request @@ -60,7 +61,7 @@ public ResumeResponse postResume( .build(); resumeRepository.save(resume); - return new ResumeResponse( + return new ResumePutResponse( resume.getId(), resume.getResumeImageS3urls(), resume.getMember().getFaceInfo(), @@ -71,65 +72,77 @@ public ResumeResponse postResume( ); } - public ResumeResponse getResume( + public ResumeGetResponse getResume( + Long memberId, Long resumeId ) { Resume resume = resumeRepository.findResumeById(resumeId) .orElseThrow(() -> new ResumeException(NO_RESUME)); - return new ResumeResponse( + Member member = findMemberById(memberId); + Resume mine = findResumeByMember(member); + + Boolean isMine; + if (mine.equals(resume)) isMine = Boolean.TRUE; + else isMine = Boolean.FALSE; + + return new ResumeGetResponse( resume.getId(), resume.getResumeImageS3urls(), resume.getMember().getFaceInfo(), resume.getMember().getBasicInfo(), resume.getMember().getAnalysisInfo(), resume.getCategory(), - resume.getContent() + resume.getContent(), + isMine ); } - public ResumeResponse putResume( + @Transactional + public ResumePutResponse putResume( Long memberId, Long resumeId, List images, ResumePutRequest request ) throws IOException { + Member me = findMemberById(memberId); - Resume myResume = findResumeByMember(me); // dirty check possible + Resume mine = findResumeByMember(me); // 영속 상태 - if (resumeId != myResume.getId()) { + if (resumeId != mine.getId()) { throw new ResumeException(UNAUTHORIZED); } - List resumeImageS3urls = bucketService.updateResumeImages(images, myResume); + List resumeImageS3urls = bucketService.updateResumeImages(images, mine); - myResume.setResumeImageS3urls(resumeImageS3urls); // dirty check possible - myResume.setCategory(Category.valueOf(request.category())); - myResume.setContent(request.content()); + mine.setResumeImageS3urls(resumeImageS3urls); // dirty check + mine.setCategory(Category.valueOf(request.category())); // // dirty check + mine.setContent(request.content()); // // dirty check - return new ResumeResponse( - myResume.getId(), - myResume.getResumeImageS3urls(), - myResume.getMember().getFaceInfo(), - myResume.getMember().getBasicInfo(), - myResume.getMember().getAnalysisInfo(), - myResume.getCategory(), - myResume.getContent() + return new ResumePutResponse( + mine.getId(), + mine.getResumeImageS3urls(), + mine.getMember().getFaceInfo(), + mine.getMember().getBasicInfo(), + mine.getMember().getAnalysisInfo(), + mine.getCategory(), + mine.getContent() ); } + @Transactional public ResumeDeleteResponse deleteResume( Long memberId, Long resumeId ) { - Member member = findMemberById(memberId); // me - Resume resume = findResumeByMember(member); // mine + Member me = findMemberById(memberId); + Resume mine = findResumeByMember(me); - if (resumeId != resume.getId()) { // if resumeId is not mine + if (resumeId != mine.getId()) { // if resumeId is not mine throw new ResumeException(UNAUTHORIZED); } - bucketService.deleteResumeImages(resume); + bucketService.deleteResumeImages(mine); resumeRepository.deleteResumeById(resumeId); return new ResumeDeleteResponse(DELETE_SUCCESS_MESSAGE); From 57060b9f91587a1427046ef5f2577d3cdaab4544 Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Sun, 5 May 2024 21:52:29 +0900 Subject: [PATCH 160/265] =?UTF-8?q?feat:=20=EC=9E=90=EC=8B=A0=EC=9D=98=20?= =?UTF-8?q?=EC=9E=90=EA=B8=B0=EC=86=8C=EA=B0=9C=EC=84=9C=EB=A5=BC=20?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C=ED=95=98=EB=8A=94=20API=20=EA=B5=AC=ED=98=84?= =?UTF-8?q?=20&=20Response=20DTO=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 3 + build.gradle | 6 +- .../capstone/facefriend/DummyInitializer.java | 130 +++++++++++++++++- .../facefriend/FacefriendApplication.java | 4 - .../facefriend/auth/config/AuthConfig.java | 3 + .../resume/controller/ResumeController.java | 29 ++-- .../facefriend/resume/domain/Resume.java | 10 +- .../resume/domain/ResumeRepositoryImpl.java | 4 +- .../resume/domain/dto/ResumeGetResponse.java | 6 +- ...sponse.java => ResumePostPutResponse.java} | 6 +- .../resume/domain/dto/ResumePostRequest.java | 4 +- .../resume/domain/dto/ResumePutRequest.java | 4 +- .../resume/service/ResumeService.java | 63 +++++---- 13 files changed, 213 insertions(+), 59 deletions(-) rename src/main/java/capstone/facefriend/resume/domain/dto/{ResumePutResponse.java => ResumePostPutResponse.java} (81%) diff --git a/.gitignore b/.gitignore index 05880e264c..106f7fc143 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,9 @@ # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 +# .yml +*.yml + #querydsl /src/main/generated diff --git a/build.gradle b/build.gradle index 8ed7156733..0d76432937 100644 --- a/build.gradle +++ b/build.gradle @@ -62,8 +62,10 @@ dependencies { annotationProcessor "com.querydsl:querydsl-apt:${dependencyManagement.importedProperties['querydsl.version']}:jakarta" implementation "com.querydsl:querydsl-core" implementation "com.querydsl:querydsl-collections" - annotationProcessor "jakarta.annotation:jakarta.annotation-api" // java.lang.NoClassDefFoundError (javax.annotation.Generated) 에러 대응 코드 - annotationProcessor "jakarta.persistence:jakarta.persistence-api" // java.lang.NoClassDefFoundError (javax.annotation.Entity) 에러 대응 코드 + annotationProcessor "jakarta.annotation:jakarta.annotation-api" + // java.lang.NoClassDefFoundError (javax.annotation.Generated) 에러 대응 코드 + annotationProcessor "jakarta.persistence:jakarta.persistence-api" + // java.lang.NoClassDefFoundError (javax.annotation.Entity) 에러 대응 코드 // JAXB deprecated warning implementation 'javax.xml.bind:jaxb-api:2.3.0' diff --git a/src/main/java/capstone/facefriend/DummyInitializer.java b/src/main/java/capstone/facefriend/DummyInitializer.java index 74f586db53..b3b034efb9 100644 --- a/src/main/java/capstone/facefriend/DummyInitializer.java +++ b/src/main/java/capstone/facefriend/DummyInitializer.java @@ -19,6 +19,8 @@ import java.util.ArrayList; import java.util.List; import java.util.Random; +import java.util.Set; +import java.util.stream.Collectors; @Component @Slf4j @@ -42,8 +44,128 @@ public void init() { List GOOD_COMBI = new ArrayList<>(); - List CATEGORY = List.of("FOOD", "WORKOUT", "MOVIE", "FASHION", "DATING", "STUDY", "ETC"); - int size = CATEGORY.size(); + + List> CATEGORY = List.of( + List.of("FOOD"), + List.of("WORKOUT"), + List.of("MOVIE"), + List.of("FASHION"), + List.of("DATING"), + List.of("STUDY"), + List.of("ETC"), + List.of("FOOD", "WORKOUT"), + List.of("FOOD", "MOVIE"), + List.of("FOOD", "FASHION"), + List.of("FOOD", "DATING"), + List.of("FOOD", "STUDY"), + List.of("FOOD", "ETC"), + List.of("WORKOUT", "MOVIE"), + List.of("WORKOUT", "FASHION"), + List.of("WORKOUT", "DATING"), + List.of("WORKOUT", "STUDY"), + List.of("WORKOUT", "ETC"), + List.of("MOVIE", "FASHION"), + List.of("MOVIE", "DATING"), + List.of("MOVIE", "STUDY"), + List.of("MOVIE", "ETC"), + List.of("FASHION", "DATING"), + List.of("FASHION", "STUDY"), + List.of("FASHION", "ETC"), + List.of("DATING", "STUDY"), + List.of("DATING", "ETC"), + List.of("STUDY", "ETC"), + List.of("FOOD", "WORKOUT", "MOVIE"), + List.of("FOOD", "WORKOUT", "FASHION"), + List.of("FOOD", "WORKOUT", "DATING"), + List.of("FOOD", "WORKOUT", "STUDY"), + List.of("FOOD", "WORKOUT", "ETC"), + List.of("FOOD", "MOVIE", "FASHION"), + List.of("FOOD", "MOVIE", "DATING"), + List.of("FOOD", "MOVIE", "STUDY"), + List.of("FOOD", "MOVIE", "ETC"), + List.of("FOOD", "FASHION", "DATING"), + List.of("FOOD", "FASHION", "STUDY"), + List.of("FOOD", "FASHION", "ETC"), + List.of("FOOD", "DATING", "STUDY"), + List.of("FOOD", "DATING", "ETC"), + List.of("FOOD", "STUDY", "ETC"), + List.of("WORKOUT", "MOVIE", "FASHION"), + List.of("WORKOUT", "MOVIE", "DATING"), + List.of("WORKOUT", "MOVIE", "STUDY"), + List.of("WORKOUT", "MOVIE", "ETC"), + List.of("WORKOUT", "FASHION", "DATING"), + List.of("WORKOUT", "FASHION", "STUDY"), + List.of("WORKOUT", "FASHION", "ETC"), + List.of("WORKOUT", "DATING", "STUDY"), + List.of("WORKOUT", "DATING", "ETC"), + List.of("WORKOUT", "STUDY", "ETC"), + List.of("MOVIE", "FASHION", "DATING"), + List.of("MOVIE", "FASHION", "STUDY"), + List.of("MOVIE", "FASHION", "ETC"), + List.of("MOVIE", "DATING", "STUDY"), + List.of("MOVIE", "DATING", "ETC"), + List.of("MOVIE", "STUDY", "ETC"), + List.of("FASHION", "DATING", "STUDY"), + List.of("FASHION", "DATING", "ETC"), + List.of("FASHION", "STUDY", "ETC"), + List.of("DATING", "STUDY", "ETC"), + List.of("FOOD", "WORKOUT", "MOVIE", "FASHION"), + List.of("FOOD", "WORKOUT", "MOVIE", "DATING"), + List.of("FOOD", "WORKOUT", "MOVIE", "STUDY"), + List.of("FOOD", "WORKOUT", "MOVIE", "ETC"), + List.of("FOOD", "WORKOUT", "FASHION", "DATING"), + List.of("FOOD", "WORKOUT", "FASHION", "STUDY"), + List.of("FOOD", "WORKOUT", "FASHION", "ETC"), + List.of("FOOD", "WORKOUT", "DATING", "STUDY"), + List.of("FOOD", "WORKOUT", "DATING", "ETC"), + List.of("FOOD", "WORKOUT", "STUDY", "ETC"), + List.of("FOOD", "MOVIE", "FASHION", "DATING"), + List.of("FOOD", "MOVIE", "FASHION", "STUDY"), + List.of("FOOD", "MOVIE", "FASHION", "ETC"), + List.of("FOOD", "MOVIE", "DATING", "STUDY"), + List.of("FOOD", "MOVIE", "DATING", "ETC"), + List.of("FOOD", "MOVIE", "STUDY", "ETC"), + List.of("FOOD", "FASHION", "DATING", "STUDY"), + List.of("FOOD", "FASHION", "DATING", "ETC"), + List.of("FOOD", "FASHION", "STUDY", "ETC"), + List.of("FOOD", "DATING", "STUDY", "ETC"), + List.of("WORKOUT", "MOVIE", "FASHION", "DATING"), + List.of("WORKOUT", "MOVIE", "FASHION", "STUDY"), + List.of("WORKOUT", "MOVIE", "FASHION", "ETC"), + List.of("WORKOUT", "MOVIE", "DATING", "STUDY"), + List.of("WORKOUT", "MOVIE", "DATING", "ETC"), + List.of("WORKOUT", "MOVIE", "STUDY", "ETC"), + List.of("WORKOUT", "FASHION", "DATING", "STUDY"), + List.of("WORKOUT", "FASHION", "DATING", "ETC"), + List.of("WORKOUT", "FASHION", "STUDY", "ETC"), + List.of("WORKOUT", "DATING", "STUDY", "ETC"), + List.of("MOVIE", "FASHION", "DATING", "STUDY"), + List.of("MOVIE", "FASHION", "DATING", "ETC"), + List.of("MOVIE", "FASHION", "STUDY", "ETC"), + List.of("MOVIE", "DATING", "STUDY", "ETC"), + List.of("FASHION", "DATING", "STUDY", "ETC"), + List.of("FOOD", "WORKOUT", "MOVIE", "FASHION", "DATING"), + List.of("FOOD", "WORKOUT", "MOVIE", "FASHION", "STUDY"), + List.of("FOOD", "WORKOUT", "MOVIE", "FASHION", "ETC"), + List.of("FOOD", "WORKOUT", "MOVIE", "DATING", "STUDY"), + List.of("FOOD", "WORKOUT", "MOVIE", "DATING", "ETC"), + List.of("FOOD", "WORKOUT", "MOVIE", "STUDY", "ETC"), + List.of("FOOD", "WORKOUT", "FASHION", "DATING", "STUDY"), + List.of("FOOD", "WORKOUT", "FASHION", "DATING", "ETC"), + List.of("FOOD", "WORKOUT", "FASHION", "STUDY", "ETC"), + List.of("FOOD", "WORKOUT", "DATING", "STUDY", "ETC"), + List.of("FOOD", "MOVIE", "FASHION", "DATING", "STUDY"), + List.of("FOOD", "MOVIE", "FASHION", "DATING", "ETC"), + List.of("FOOD", "MOVIE", "FASHION", "STUDY", "ETC"), + List.of("FOOD", "MOVIE", "DATING", "STUDY", "ETC"), + List.of("FOOD", "FASHION", "DATING", "STUDY", "ETC"), + List.of("WORKOUT", "MOVIE", "FASHION", "DATING", "STUDY"), + List.of("WORKOUT", "MOVIE", "FASHION", "DATING", "ETC"), + List.of("WORKOUT", "MOVIE", "FASHION", "STUDY", "ETC"), + List.of("WORKOUT", "MOVIE", "DATING", "STUDY", "ETC"), + List.of("WORKOUT", "FASHION", "DATING", "STUDY", "ETC"), + List.of("MOVIE", "FASHION", "DATING", "STUDY", "ETC") + ); for (int i = 1; i <= 50; i++) { // 회원 가입 @@ -57,10 +179,12 @@ public void init() { // 자기소개서 Resume resume = Resume.builder() - .category(Resume.Category.valueOf(CATEGORY.get(random.nextInt(size-1)))) // 카테고리 랜덤 + .categories(CATEGORY.get(random.nextInt(CATEGORY.size() - 1)).stream().map(str -> Resume.Category.valueOf(str)).collect(Collectors.toSet())) // 카테고리 랜던 .member(member) .build(); resumeRepository.save(resume); + + // Resume.Category.valueOf(CATEGORY.get(random.nextInt(size-1)) } } } diff --git a/src/main/java/capstone/facefriend/FacefriendApplication.java b/src/main/java/capstone/facefriend/FacefriendApplication.java index cff671970b..8982f0bbc1 100644 --- a/src/main/java/capstone/facefriend/FacefriendApplication.java +++ b/src/main/java/capstone/facefriend/FacefriendApplication.java @@ -14,9 +14,5 @@ public class FacefriendApplication { public static void main(String[] args) { TimeZone.setDefault(TimeZone.getTimeZone("Asia/Seoul")); SpringApplication.run(FacefriendApplication.class, args); - - - - } } diff --git a/src/main/java/capstone/facefriend/auth/config/AuthConfig.java b/src/main/java/capstone/facefriend/auth/config/AuthConfig.java index 77d3eef4ae..65871fc037 100644 --- a/src/main/java/capstone/facefriend/auth/config/AuthConfig.java +++ b/src/main/java/capstone/facefriend/auth/config/AuthConfig.java @@ -53,6 +53,7 @@ private HandlerInterceptor loginCheckInterceptor() { .addIncludePathPattern("/face-info", ANY) .addIncludePathPattern("/analysis-info", ANY) .addIncludePathPattern("/resume", ANY) + .addIncludePathPattern("/my-resume", ANY) .addIncludePathPattern("/resume-by-good-combi", ANY) .addIncludePathPattern("/resume-by-category", ANY) @@ -70,6 +71,7 @@ private HandlerInterceptor loginInterceptor() { .addIncludePathPattern("/face-info", ANY) .addIncludePathPattern("/analysis-info/**", ANY) .addIncludePathPattern("/resume", ANY) + .addIncludePathPattern("/my-resume", ANY) .addIncludePathPattern("/resume-by-good-combi", ANY) .addIncludePathPattern("/resume-by-category", ANY) @@ -94,6 +96,7 @@ private HandlerInterceptor tokenBlackListInterceptor() { .addIncludePathPattern("/face-info", ANY) .addIncludePathPattern("/analysis-info/**", ANY) .addIncludePathPattern("/resume", ANY) + .addIncludePathPattern("/my-resume", ANY) .addIncludePathPattern("/resume-by-good-combi", ANY) .addIncludePathPattern("/resume-by-category", ANY) ; diff --git a/src/main/java/capstone/facefriend/resume/controller/ResumeController.java b/src/main/java/capstone/facefriend/resume/controller/ResumeController.java index 58f5ac3acb..feb6ebd870 100644 --- a/src/main/java/capstone/facefriend/resume/controller/ResumeController.java +++ b/src/main/java/capstone/facefriend/resume/controller/ResumeController.java @@ -23,8 +23,8 @@ public class ResumeController { private final ResumeService resumeService; // 정적 쿼리 - @PostMapping("/resume") - public ResponseEntity postResume( + @PostMapping("/my-resume") + public ResponseEntity postResume( @AuthMember Long memberId, @RequestPart("images") List images, @RequestPart("request") ResumePostRequest request @@ -40,22 +40,27 @@ public ResponseEntity getResume( return ResponseEntity.ok(resumeService.getResume(memberId, resumeId)); } - @PutMapping("/resume") - public ResponseEntity putResume( + @GetMapping("/my-resume") + public ResponseEntity getMyResume( + @AuthMember Long memberId + ) { + return ResponseEntity.ok(resumeService.getMyResume(memberId)); + } + + @PutMapping("/my-resume") + public ResponseEntity putMyResume( @AuthMember Long memberId, - @RequestParam("resumeId") Long resumeId, @RequestPart("images") List images, @RequestPart("request") ResumePutRequest request ) throws IOException { - return ResponseEntity.ok(resumeService.putResume(memberId, resumeId, images, request)); + return ResponseEntity.ok(resumeService.putResume(memberId, images, request)); } - @DeleteMapping("/resume") - public ResponseEntity deleteResume( - @AuthMember Long memberId, - @RequestParam("resumeId") Long resumeId + @DeleteMapping("/my-resume") + public ResponseEntity deleteMyResume( + @AuthMember Long memberId ) { - return ResponseEntity.ok(resumeService.deleteResume(memberId, resumeId)); + return ResponseEntity.ok(resumeService.deleteResume(memberId)); } // 동적 쿼리 @@ -70,7 +75,7 @@ public Page getResumesByGoodCombi( @GetMapping("/resume-by-category") public Page getResumesByCategory( @AuthMember Long memberId, - @RequestParam String category, + @RequestParam("category") String category, Pageable pageable ) { return resumeService.getResumesByCategory(category, pageable); diff --git a/src/main/java/capstone/facefriend/resume/domain/Resume.java b/src/main/java/capstone/facefriend/resume/domain/Resume.java index 366db93f81..5b3cc11556 100644 --- a/src/main/java/capstone/facefriend/resume/domain/Resume.java +++ b/src/main/java/capstone/facefriend/resume/domain/Resume.java @@ -7,9 +7,7 @@ import org.hibernate.annotations.DynamicInsert; import org.hibernate.annotations.DynamicUpdate; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; @Getter @Setter @@ -36,9 +34,11 @@ public class Resume { @CollectionTable(name = "RESUME_IMAGE_S3_URLS", joinColumns = @JoinColumn(name = "RESUME_ID")) private List resumeImageS3urls; + @Builder.Default + @ElementCollection(fetch = FetchType.EAGER) + @CollectionTable(name = "CATEGORIES", joinColumns = @JoinColumn(name = "RESUME_ID")) @Enumerated(EnumType.STRING) - @Column(nullable = false) - private Category category; + private Set categories = new HashSet<>(); private String content; diff --git a/src/main/java/capstone/facefriend/resume/domain/ResumeRepositoryImpl.java b/src/main/java/capstone/facefriend/resume/domain/ResumeRepositoryImpl.java index b15eb45ad0..f009b6a1d7 100644 --- a/src/main/java/capstone/facefriend/resume/domain/ResumeRepositoryImpl.java +++ b/src/main/java/capstone/facefriend/resume/domain/ResumeRepositoryImpl.java @@ -87,7 +87,7 @@ public Page getResumesByCategory(String category, Page resume.member.faceInfo.generatedS3url.as("thumbnailS3url"))) .from(resume) .leftJoin(resume.member, QMember.member) // left join - .where(resume.category.eq(Resume.Category.valueOf(category))) + .where(resume.categories.contains(Resume.Category.valueOf(category))) .orderBy(resume.id.desc()) .offset(pageable.getOffset()) .limit(pageable.getPageSize()) @@ -97,7 +97,7 @@ public Page getResumesByCategory(String category, Page .select(resume) .from(resume) .leftJoin(resume.member, QMember.member) // left join - .where(resume.category.eq(Resume.Category.valueOf(category))) + .where(resume.categories.contains(Resume.Category.valueOf(category))) .fetch() .size(); diff --git a/src/main/java/capstone/facefriend/resume/domain/dto/ResumeGetResponse.java b/src/main/java/capstone/facefriend/resume/domain/dto/ResumeGetResponse.java index c0b07e68b1..54302b9438 100644 --- a/src/main/java/capstone/facefriend/resume/domain/dto/ResumeGetResponse.java +++ b/src/main/java/capstone/facefriend/resume/domain/dto/ResumeGetResponse.java @@ -6,14 +6,18 @@ import capstone.facefriend.resume.domain.Resume; import java.util.List; +import java.util.Set; + +import static capstone.facefriend.resume.domain.Resume.*; public record ResumeGetResponse( Long resumeId, + Long memberId, List resumeImageS3urls, FaceInfo faceInfo, BasicInfo basicInfo, AnalysisInfo analysisInfo, - Resume.Category category, + Set category, String content, Boolean isMine ) { diff --git a/src/main/java/capstone/facefriend/resume/domain/dto/ResumePutResponse.java b/src/main/java/capstone/facefriend/resume/domain/dto/ResumePostPutResponse.java similarity index 81% rename from src/main/java/capstone/facefriend/resume/domain/dto/ResumePutResponse.java rename to src/main/java/capstone/facefriend/resume/domain/dto/ResumePostPutResponse.java index 58645309de..4f51bc9b17 100644 --- a/src/main/java/capstone/facefriend/resume/domain/dto/ResumePutResponse.java +++ b/src/main/java/capstone/facefriend/resume/domain/dto/ResumePostPutResponse.java @@ -5,16 +5,18 @@ import capstone.facefriend.member.domain.faceInfo.FaceInfo; import java.util.List; +import java.util.Set; import static capstone.facefriend.resume.domain.Resume.Category; -public record ResumePutResponse( +public record ResumePostPutResponse( Long resumeId, + Long memberId, List resumeImageS3urls, FaceInfo faceInfo, BasicInfo basicInfo, AnalysisInfo analysisInfo, - Category category, + Set category, String content ) { } diff --git a/src/main/java/capstone/facefriend/resume/domain/dto/ResumePostRequest.java b/src/main/java/capstone/facefriend/resume/domain/dto/ResumePostRequest.java index 5ff29b2fe2..7a4eb7b9e1 100644 --- a/src/main/java/capstone/facefriend/resume/domain/dto/ResumePostRequest.java +++ b/src/main/java/capstone/facefriend/resume/domain/dto/ResumePostRequest.java @@ -1,7 +1,9 @@ package capstone.facefriend.resume.domain.dto; +import java.util.List; + public record ResumePostRequest( - String category, + List categories, String content ) { } diff --git a/src/main/java/capstone/facefriend/resume/domain/dto/ResumePutRequest.java b/src/main/java/capstone/facefriend/resume/domain/dto/ResumePutRequest.java index e223a91ac4..386e31edd2 100644 --- a/src/main/java/capstone/facefriend/resume/domain/dto/ResumePutRequest.java +++ b/src/main/java/capstone/facefriend/resume/domain/dto/ResumePutRequest.java @@ -1,7 +1,9 @@ package capstone.facefriend.resume.domain.dto; +import java.util.List; + public record ResumePutRequest( - String category, + List categories, String content ) { } diff --git a/src/main/java/capstone/facefriend/resume/service/ResumeService.java b/src/main/java/capstone/facefriend/resume/service/ResumeService.java index 9a6aac0ba1..f0f32f1f87 100644 --- a/src/main/java/capstone/facefriend/resume/service/ResumeService.java +++ b/src/main/java/capstone/facefriend/resume/service/ResumeService.java @@ -19,6 +19,7 @@ import java.io.IOException; import java.util.List; +import java.util.stream.Collectors; import static capstone.facefriend.member.exception.member.MemberExceptionType.NOT_FOUND; import static capstone.facefriend.resume.domain.Resume.Category; @@ -36,7 +37,7 @@ public class ResumeService { private static final String DELETE_SUCCESS_MESSAGE = "자기소개서 삭제 완료!"; // 정적 쿼리 - public ResumePutResponse postResume( + public ResumePostPutResponse postResume( Long memberId, List images, ResumePostRequest request @@ -56,18 +57,19 @@ public ResumePutResponse postResume( Resume resume = Resume.builder() .member(member) .resumeImageS3urls(resumeImagesS3url) - .category(Category.valueOf(request.category())) + .categories(request.categories().stream().map(str -> Category.valueOf(str)).collect(Collectors.toSet())) .content(request.content()) .build(); resumeRepository.save(resume); - return new ResumePutResponse( + return new ResumePostPutResponse( resume.getId(), + memberId, resume.getResumeImageS3urls(), resume.getMember().getFaceInfo(), resume.getMember().getBasicInfo(), resume.getMember().getAnalysisInfo(), - resume.getCategory(), + resume.getCategories(), resume.getContent() ); } @@ -79,29 +81,46 @@ public ResumeGetResponse getResume( Resume resume = resumeRepository.findResumeById(resumeId) .orElseThrow(() -> new ResumeException(NO_RESUME)); - Member member = findMemberById(memberId); - Resume mine = findResumeByMember(member); + Member me = findMemberById(memberId); + Long mine = findResumeByMember(me).getId(); - Boolean isMine; - if (mine.equals(resume)) isMine = Boolean.TRUE; - else isMine = Boolean.FALSE; + Boolean isMine = (resumeId == mine) ? Boolean.TRUE : Boolean.FALSE; return new ResumeGetResponse( resume.getId(), + resume.getMember().getId(), resume.getResumeImageS3urls(), resume.getMember().getFaceInfo(), resume.getMember().getBasicInfo(), resume.getMember().getAnalysisInfo(), - resume.getCategory(), + resume.getCategories(), resume.getContent(), isMine ); } + public ResumeGetResponse getMyResume( + Long memberId + ) { + Member member = findMemberById(memberId); + Resume mine = findResumeByMember(member); + + return new ResumeGetResponse( + mine.getId(), + mine.getMember().getId(), + mine.getResumeImageS3urls(), + mine.getMember().getFaceInfo(), + mine.getMember().getBasicInfo(), + mine.getMember().getAnalysisInfo(), + mine.getCategories(), + mine.getContent(), + Boolean.TRUE + ); + } + @Transactional - public ResumePutResponse putResume( + public ResumePostPutResponse putResume( Long memberId, - Long resumeId, List images, ResumePutRequest request ) throws IOException { @@ -109,41 +128,33 @@ public ResumePutResponse putResume( Member me = findMemberById(memberId); Resume mine = findResumeByMember(me); // 영속 상태 - if (resumeId != mine.getId()) { - throw new ResumeException(UNAUTHORIZED); - } - List resumeImageS3urls = bucketService.updateResumeImages(images, mine); mine.setResumeImageS3urls(resumeImageS3urls); // dirty check - mine.setCategory(Category.valueOf(request.category())); // // dirty check + mine.setCategories(request.categories().stream().map(str -> Category.valueOf(str)).collect(Collectors.toSet())); // // dirty check mine.setContent(request.content()); // // dirty check - return new ResumePutResponse( + return new ResumePostPutResponse( mine.getId(), + memberId, mine.getResumeImageS3urls(), mine.getMember().getFaceInfo(), mine.getMember().getBasicInfo(), mine.getMember().getAnalysisInfo(), - mine.getCategory(), + mine.getCategories(), mine.getContent() ); } @Transactional public ResumeDeleteResponse deleteResume( - Long memberId, - Long resumeId + Long memberId ) { Member me = findMemberById(memberId); Resume mine = findResumeByMember(me); - if (resumeId != mine.getId()) { // if resumeId is not mine - throw new ResumeException(UNAUTHORIZED); - } - bucketService.deleteResumeImages(mine); - resumeRepository.deleteResumeById(resumeId); + resumeRepository.deleteResumeById(mine.getId()); return new ResumeDeleteResponse(DELETE_SUCCESS_MESSAGE); } From 5c927dc6d9b9e35bc1def95e69bdb6d4a8be282c Mon Sep 17 00:00:00 2001 From: KimChanJin97 <102044895+KimChanJin97@users.noreply.github.com> Date: Sun, 5 May 2024 22:26:57 +0900 Subject: [PATCH 161/265] Delete src/main/resources/application.yml --- src/main/resources/application.yml | 11 ----------- 1 file changed, 11 deletions(-) delete mode 100644 src/main/resources/application.yml diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml deleted file mode 100644 index 92d6385fe5..0000000000 --- a/src/main/resources/application.yml +++ /dev/null @@ -1,11 +0,0 @@ -#production에서 실행할 때 -#jasypt: -# encryptor: -# bean: jasyptStringEncryptor -# password: ${ENCRYPT_KEY} - - -# local에서 실행할 때 -spring: - profiles: - active: dev \ No newline at end of file From cbb5c25d3e0a29f2037217db8a00a9f97e71ba06 Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Mon, 6 May 2024 02:36:31 +0900 Subject: [PATCH 162/265] =?UTF-8?q?fix:=20debug=20=EB=A5=BC=20=EC=9C=84?= =?UTF-8?q?=ED=95=9C=20=EB=A1=9C=EC=A7=81=20=EB=B0=8F=20DTO=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../facefriend/auth/controller/dto/TokenResponse.java | 3 ++- .../facefriend/auth/infrastructure/JwtProvider.java | 2 +- .../java/capstone/facefriend/auth/service/AuthService.java | 2 +- .../facefriend/member/controller/MemberController.java | 2 +- .../capstone/facefriend/member/service/MemberService.java | 5 +++-- .../member/service/dto/member/SignupResponse.java | 6 ++++++ 6 files changed, 14 insertions(+), 6 deletions(-) create mode 100644 src/main/java/capstone/facefriend/member/service/dto/member/SignupResponse.java diff --git a/src/main/java/capstone/facefriend/auth/controller/dto/TokenResponse.java b/src/main/java/capstone/facefriend/auth/controller/dto/TokenResponse.java index ff282370fc..acc35d696e 100644 --- a/src/main/java/capstone/facefriend/auth/controller/dto/TokenResponse.java +++ b/src/main/java/capstone/facefriend/auth/controller/dto/TokenResponse.java @@ -2,6 +2,7 @@ public record TokenResponse( String accessToken, - String refreshToken + String refreshToken, + Long memberId ) { } diff --git a/src/main/java/capstone/facefriend/auth/infrastructure/JwtProvider.java b/src/main/java/capstone/facefriend/auth/infrastructure/JwtProvider.java index b22c10a452..12c358963b 100644 --- a/src/main/java/capstone/facefriend/auth/infrastructure/JwtProvider.java +++ b/src/main/java/capstone/facefriend/auth/infrastructure/JwtProvider.java @@ -45,7 +45,7 @@ private void init() { public TokenResponse createTokens(Long memberId) { String accessToken = createAccessToken(memberId); String refreshToken = createRefreshToken(memberId); - return new TokenResponse(accessToken, refreshToken); + return new TokenResponse(accessToken, refreshToken, memberId); } @Override diff --git a/src/main/java/capstone/facefriend/auth/service/AuthService.java b/src/main/java/capstone/facefriend/auth/service/AuthService.java index 4f6b6d730f..6a73f97142 100644 --- a/src/main/java/capstone/facefriend/auth/service/AuthService.java +++ b/src/main/java/capstone/facefriend/auth/service/AuthService.java @@ -39,7 +39,7 @@ public TokenResponse generateTokens(OAuthMember oAuthMember) { .orElseGet(() -> memberRepository.save(newMember)); Long memberId = member.getId(); - return new TokenResponse(getAccessToken(memberId), getRefreshToken(memberId)); + return new TokenResponse(getAccessToken(memberId), getRefreshToken(memberId), memberId); } private String getAccessToken(Long memberId) { diff --git a/src/main/java/capstone/facefriend/member/controller/MemberController.java b/src/main/java/capstone/facefriend/member/controller/MemberController.java index 89bf4138cc..b1f867bc0f 100644 --- a/src/main/java/capstone/facefriend/member/controller/MemberController.java +++ b/src/main/java/capstone/facefriend/member/controller/MemberController.java @@ -94,7 +94,7 @@ public ResponseEntity verifyCode( } @PostMapping("/auth/signup") - public ResponseEntity signUp( + public ResponseEntity signUp( @RequestBody SignUpRequest request ) { return ResponseEntity.ok(memberService.signUp(request)); diff --git a/src/main/java/capstone/facefriend/member/service/MemberService.java b/src/main/java/capstone/facefriend/member/service/MemberService.java index 9d8b8b40fd..879ab0b62c 100644 --- a/src/main/java/capstone/facefriend/member/service/MemberService.java +++ b/src/main/java/capstone/facefriend/member/service/MemberService.java @@ -16,6 +16,7 @@ import capstone.facefriend.member.service.dto.member.FindEmailResponse; import capstone.facefriend.member.service.dto.member.SignInRequest; import capstone.facefriend.member.service.dto.member.SignUpRequest; +import capstone.facefriend.member.service.dto.member.SignupResponse; import capstone.facefriend.redis.RedisDao; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -94,7 +95,7 @@ public EmailVerificationResponse verifyCode(String email, String code) { } @Transactional - public String signUp(SignUpRequest request) { + public SignupResponse signUp(SignUpRequest request) { String encodedPassword = passwordEncoder.encode(request.password()); // 기본정보 초기값 @@ -139,7 +140,7 @@ public String signUp(SignUpRequest request) { .build(); memberRepository.save(member); - return SIGN_UP_SUCCESS_MESSAGE; + return new SignupResponse(member.getId()); } @Transactional diff --git a/src/main/java/capstone/facefriend/member/service/dto/member/SignupResponse.java b/src/main/java/capstone/facefriend/member/service/dto/member/SignupResponse.java new file mode 100644 index 0000000000..ba52662f76 --- /dev/null +++ b/src/main/java/capstone/facefriend/member/service/dto/member/SignupResponse.java @@ -0,0 +1,6 @@ +package capstone.facefriend.member.service.dto.member; + +public record SignupResponse( + Long memberId +) { +} From f4b036270714233c327ecd27f6d62d5c71f306e1 Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Mon, 6 May 2024 03:11:41 +0900 Subject: [PATCH 163/265] =?UTF-8?q?fix:=20deprecated=20=EB=90=9C=20Jwts.pa?= =?UTF-8?q?rser()=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../facefriend/auth/infrastructure/JwtProvider.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/java/capstone/facefriend/auth/infrastructure/JwtProvider.java b/src/main/java/capstone/facefriend/auth/infrastructure/JwtProvider.java index 12c358963b..6c0d2d2736 100644 --- a/src/main/java/capstone/facefriend/auth/infrastructure/JwtProvider.java +++ b/src/main/java/capstone/facefriend/auth/infrastructure/JwtProvider.java @@ -102,8 +102,9 @@ private Date refreshTokenExpiredAt() { @Override public Long extractId(String token) { try { - Claims claims = Jwts.parser() + Claims claims = Jwts.parserBuilder() .setSigningKey(secret.getBytes()) + .build() .parseClaimsJws(token) .getBody(); return claims.get("id", Long.class); @@ -124,8 +125,9 @@ public Long extractId(String token) { @Override public Long extractIdIgnoringExpiration(String token) { try { - Claims claims = Jwts.parser() + Claims claims = Jwts.parserBuilder() .setSigningKey(secret.getBytes()) + .build() .parseClaimsJws(token) .getBody(); return claims.get("id", Long.class); From 8b13a4abff61e615b3c7d0a092fb89867b9dd6cf Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Mon, 6 May 2024 15:19:19 +0900 Subject: [PATCH 164/265] =?UTF-8?q?feat:=20=EC=9E=90=EA=B8=B0=EC=86=8C?= =?UTF-8?q?=EA=B0=9C=EC=84=9C=20=EC=9D=B4=EB=AF=B8=EC=A7=80=20=EC=97=85?= =?UTF-8?q?=EB=A1=9C=EB=93=9C=ED=95=98=EC=A7=80=20=EC=95=8A=EC=9C=BC?= =?UTF-8?q?=EB=A9=B4=20=EB=B9=88=EB=A6=AC=EC=8A=A4=ED=8A=B8=20=EB=B0=98?= =?UTF-8?q?=ED=99=98=ED=86=A0=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../facefriend/bucket/BucketService.java | 11 +++-- .../member/domain/resume/Resume.java | 4 -- .../domain/resume/ResumeRepository.java | 4 -- .../resume/controller/ResumeController.java | 8 ++-- .../resume/exception/ResumeExceptionType.java | 12 +++--- .../resume/service/ResumeService.java | 40 ++++++++++++++----- 6 files changed, 47 insertions(+), 32 deletions(-) delete mode 100644 src/main/java/capstone/facefriend/member/domain/resume/Resume.java delete mode 100644 src/main/java/capstone/facefriend/member/domain/resume/ResumeRepository.java diff --git a/src/main/java/capstone/facefriend/bucket/BucketService.java b/src/main/java/capstone/facefriend/bucket/BucketService.java index 8e19088331..46a5d73303 100644 --- a/src/main/java/capstone/facefriend/bucket/BucketService.java +++ b/src/main/java/capstone/facefriend/bucket/BucketService.java @@ -1,14 +1,11 @@ package capstone.facefriend.bucket; -import capstone.facefriend.member.domain.faceInfo.FaceInfo; -import capstone.facefriend.member.domain.faceInfo.FaceInfoRepository; import capstone.facefriend.member.domain.member.Member; import capstone.facefriend.member.domain.member.MemberRepository; import capstone.facefriend.member.exception.member.MemberException; import capstone.facefriend.member.exception.member.MemberExceptionType; import capstone.facefriend.member.multipartFile.ByteArrayMultipartFile; -import capstone.facefriend.member.service.dto.faceInfo.FaceInfoResponse; import capstone.facefriend.resume.domain.Resume; import com.amazonaws.services.s3.AmazonS3; import com.amazonaws.services.s3.model.CannedAccessControlList; @@ -127,14 +124,20 @@ public String deleteOriginAndGenerated( } - // Resume : images 업로드 public List uploadResumeImages( List images ) throws IOException { + List resumeImageS3urls = new ArrayList<>(); for (MultipartFile image : images) { + + log.info("size = {}", image.getSize()); + if (image.isEmpty() || image.getSize() == 0) { + return List.of(); + } + ObjectMetadata metadata = new ObjectMetadata(); metadata.setContentLength(image.getInputStream().available()); metadata.setContentType(image.getContentType()); diff --git a/src/main/java/capstone/facefriend/member/domain/resume/Resume.java b/src/main/java/capstone/facefriend/member/domain/resume/Resume.java deleted file mode 100644 index 20a4487d75..0000000000 --- a/src/main/java/capstone/facefriend/member/domain/resume/Resume.java +++ /dev/null @@ -1,4 +0,0 @@ -package capstone.facefriend.member.domain.resume; - -public class Resume { -} diff --git a/src/main/java/capstone/facefriend/member/domain/resume/ResumeRepository.java b/src/main/java/capstone/facefriend/member/domain/resume/ResumeRepository.java deleted file mode 100644 index 74768626ee..0000000000 --- a/src/main/java/capstone/facefriend/member/domain/resume/ResumeRepository.java +++ /dev/null @@ -1,4 +0,0 @@ -package capstone.facefriend.member.domain.resume; - -public interface ResumeRepository { -} diff --git a/src/main/java/capstone/facefriend/resume/controller/ResumeController.java b/src/main/java/capstone/facefriend/resume/controller/ResumeController.java index feb6ebd870..d821e753bc 100644 --- a/src/main/java/capstone/facefriend/resume/controller/ResumeController.java +++ b/src/main/java/capstone/facefriend/resume/controller/ResumeController.java @@ -24,12 +24,12 @@ public class ResumeController { // 정적 쿼리 @PostMapping("/my-resume") - public ResponseEntity postResume( + public ResponseEntity postMyResume( @AuthMember Long memberId, @RequestPart("images") List images, @RequestPart("request") ResumePostRequest request ) throws IOException { - return ResponseEntity.ok(resumeService.postResume(memberId, images, request)); + return ResponseEntity.ok(resumeService.postMyResume(memberId, images, request)); } @GetMapping("/resume") @@ -53,14 +53,14 @@ public ResponseEntity putMyResume( @RequestPart("images") List images, @RequestPart("request") ResumePutRequest request ) throws IOException { - return ResponseEntity.ok(resumeService.putResume(memberId, images, request)); + return ResponseEntity.ok(resumeService.putMyResume(memberId, images, request)); } @DeleteMapping("/my-resume") public ResponseEntity deleteMyResume( @AuthMember Long memberId ) { - return ResponseEntity.ok(resumeService.deleteResume(memberId)); + return ResponseEntity.ok(resumeService.deleteMyResume(memberId)); } // 동적 쿼리 diff --git a/src/main/java/capstone/facefriend/resume/exception/ResumeExceptionType.java b/src/main/java/capstone/facefriend/resume/exception/ResumeExceptionType.java index 67b2183d6c..c5f538e094 100644 --- a/src/main/java/capstone/facefriend/resume/exception/ResumeExceptionType.java +++ b/src/main/java/capstone/facefriend/resume/exception/ResumeExceptionType.java @@ -5,11 +5,13 @@ public enum ResumeExceptionType implements ExceptionType { - NO_RESUME(Status.NOT_FOUND, 7001, "자기소개서가 없습니다!"), - ALREADY_HAS_RESUME(Status.BAD_REQUEST, 7002, "자기소개서는 1인당 1개만 생성할 수 있습니다!"), - UNAUTHORIZED(Status.UNAUTHORIZED, 7003, "나의 자기소개서가 아닙니다!"), - FAIL_TO_DELETE(Status.BAD_REQUEST, 7004, "자기소개서 삭제 실패"), - AT_LEAST_ONE_IMAGE(Status.BAD_REQUEST, 7005, "최소한 1개 이상의 이미지를 업로드해야 합니다!") + NO_RESUME(Status.NOT_FOUND, 7001, "자기소개서가 없습니다!"), // 404 + ALREADY_HAS_RESUME(Status.BAD_REQUEST, 7002, "자기소개서는 1인당 1개만 생성할 수 있습니다!"), // 400 + UNAUTHORIZED(Status.UNAUTHORIZED, 7003, "나의 자기소개서가 아닙니다!"), // 401 + FAIL_TO_DELETE(Status.BAD_REQUEST, 7004, "자기소개서 삭제 실패"), // 400 + MUST_UPLOAD_ONE_IMAGE(Status.BAD_REQUEST, 7005, "최소한 1개 이상의 이미지를 업로드해야 합니다!"), // 400 + MUST_SELECT_ONE_CATEGORY(Status.BAD_REQUEST, 7006, "최소한 1개 이상의 카테고리를 선택해야 합니다!"), + MUST_FILL_CONTENT(Status.BAD_REQUEST, 7007, "내용을 작성해야 합니다!") ; private final Status status; diff --git a/src/main/java/capstone/facefriend/resume/service/ResumeService.java b/src/main/java/capstone/facefriend/resume/service/ResumeService.java index f0f32f1f87..ecc2c5b4d6 100644 --- a/src/main/java/capstone/facefriend/resume/service/ResumeService.java +++ b/src/main/java/capstone/facefriend/resume/service/ResumeService.java @@ -37,20 +37,15 @@ public class ResumeService { private static final String DELETE_SUCCESS_MESSAGE = "자기소개서 삭제 완료!"; // 정적 쿼리 - public ResumePostPutResponse postResume( + public ResumePostPutResponse postMyResume( Long memberId, List images, ResumePostRequest request ) throws IOException { - if (images.size() == 0) { - throw new ResumeException(AT_LEAST_ONE_IMAGE); - } - - Member member = findMemberById(memberId); - if (resumeRepository.findResumeByMember(member).isPresent()) { - throw new ResumeException(ALREADY_HAS_RESUME); - } + validateCategories(request.categories()); + validateContent(request.content()); + Member member = validateMemberHasResume(memberId); List resumeImagesS3url = bucketService.uploadResumeImages(images); @@ -119,12 +114,15 @@ public ResumeGetResponse getMyResume( } @Transactional - public ResumePostPutResponse putResume( + public ResumePostPutResponse putMyResume( Long memberId, List images, ResumePutRequest request ) throws IOException { + validateCategories(request.categories()); + validateContent(request.content()); + Member me = findMemberById(memberId); Resume mine = findResumeByMember(me); // 영속 상태 @@ -147,7 +145,7 @@ public ResumePostPutResponse putResume( } @Transactional - public ResumeDeleteResponse deleteResume( + public ResumeDeleteResponse deleteMyResume( Long memberId ) { Member me = findMemberById(memberId); @@ -178,4 +176,24 @@ private Resume findResumeByMember(Member member) { return resumeRepository.findResumeByMember(member) .orElseThrow(() -> new ResumeException(NO_RESUME)); } + + private void validateContent(String content) { + if (content.trim().length() == 0 || content == null || content.isEmpty()) { + throw new ResumeException(MUST_FILL_CONTENT); + } + } + + private void validateCategories(List categories) { + if (categories.size() == 0 || categories == null || categories.isEmpty()) { + throw new ResumeException(MUST_SELECT_ONE_CATEGORY); + } + } + + private Member validateMemberHasResume(Long memberId){ + Member member = findMemberById(memberId); + if (resumeRepository.findResumeByMember(member).isPresent()) { + throw new ResumeException(ALREADY_HAS_RESUME); + } + return member; + } } From e01bb5815ef6b7ff9d9c34adce8806cdf9d2ec99 Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Mon, 6 May 2024 23:32:10 +0900 Subject: [PATCH 165/265] =?UTF-8?q?fix:=20DTO=20=ED=95=84=EB=93=9C?= =?UTF-8?q?=EB=AA=85=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../member/service/dto/faceInfo/FaceInfoResponse.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/capstone/facefriend/member/service/dto/faceInfo/FaceInfoResponse.java b/src/main/java/capstone/facefriend/member/service/dto/faceInfo/FaceInfoResponse.java index 6cc6660082..eb9fc623ae 100644 --- a/src/main/java/capstone/facefriend/member/service/dto/faceInfo/FaceInfoResponse.java +++ b/src/main/java/capstone/facefriend/member/service/dto/faceInfo/FaceInfoResponse.java @@ -1,7 +1,7 @@ package capstone.facefriend.member.service.dto.faceInfo; public record FaceInfoResponse( - String originS3Url, - String generatedS3Url + String originS3url, + String generatedS3url ) { } From 1eff943b2383d2c3c16f0aab3c0830da0f046e3b Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Tue, 7 May 2024 00:29:37 +0900 Subject: [PATCH 166/265] =?UTF-8?q?fix:=20Category=20=EC=97=B4=EA=B1=B0?= =?UTF-8?q?=ED=98=95=20=ED=95=84=EB=93=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/capstone/facefriend/resume/domain/Resume.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/capstone/facefriend/resume/domain/Resume.java b/src/main/java/capstone/facefriend/resume/domain/Resume.java index 5b3cc11556..5316b0ccb5 100644 --- a/src/main/java/capstone/facefriend/resume/domain/Resume.java +++ b/src/main/java/capstone/facefriend/resume/domain/Resume.java @@ -56,7 +56,7 @@ public enum Category { FASHION("패션"), DATING("연애"), MUSIC("음악"), - STUDY("영화"), + STUDY("공부"), ETC("기타"); private final String value; From 597db1670649c93d1604611c215f34b7f6d39202 Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Tue, 7 May 2024 16:01:26 +0900 Subject: [PATCH 167/265] =?UTF-8?q?fix:=20=EC=9E=90=EA=B8=B0=EC=86=8C?= =?UTF-8?q?=EA=B0=9C=EC=84=9C=20=EB=94=94=EB=B2=84=EA=B9=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../facefriend/resume/controller/ResumeController.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/capstone/facefriend/resume/controller/ResumeController.java b/src/main/java/capstone/facefriend/resume/controller/ResumeController.java index d821e753bc..ccf6a5b152 100644 --- a/src/main/java/capstone/facefriend/resume/controller/ResumeController.java +++ b/src/main/java/capstone/facefriend/resume/controller/ResumeController.java @@ -26,8 +26,8 @@ public class ResumeController { @PostMapping("/my-resume") public ResponseEntity postMyResume( @AuthMember Long memberId, - @RequestPart("images") List images, - @RequestPart("request") ResumePostRequest request + @RequestPart(value = "images", required = false) List images, + @RequestPart(value = "request") ResumePostRequest request ) throws IOException { return ResponseEntity.ok(resumeService.postMyResume(memberId, images, request)); } @@ -50,8 +50,8 @@ public ResponseEntity getMyResume( @PutMapping("/my-resume") public ResponseEntity putMyResume( @AuthMember Long memberId, - @RequestPart("images") List images, - @RequestPart("request") ResumePutRequest request + @RequestPart(value = "images", required = false) List images, + @RequestPart(value = "request") ResumePutRequest request ) throws IOException { return ResponseEntity.ok(resumeService.putMyResume(memberId, images, request)); } From 4631e53a68fc7dc4e872b1613e7377e372eb5372 Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Tue, 7 May 2024 17:51:19 +0900 Subject: [PATCH 168/265] =?UTF-8?q?fix:=20=EC=9E=90=EA=B8=B0=EC=86=8C?= =?UTF-8?q?=EA=B0=9C=EC=84=9C=20=EB=94=94=EB=B2=84=EA=B9=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../facefriend/resume/controller/ResumeController.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/java/capstone/facefriend/resume/controller/ResumeController.java b/src/main/java/capstone/facefriend/resume/controller/ResumeController.java index ccf6a5b152..ac33160603 100644 --- a/src/main/java/capstone/facefriend/resume/controller/ResumeController.java +++ b/src/main/java/capstone/facefriend/resume/controller/ResumeController.java @@ -7,6 +7,7 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; +import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; @@ -23,10 +24,10 @@ public class ResumeController { private final ResumeService resumeService; // 정적 쿼리 - @PostMapping("/my-resume") + @PostMapping(value = "/my-resume", consumes = {MediaType.APPLICATION_JSON_VALUE, MediaType.MULTIPART_FORM_DATA_VALUE}) public ResponseEntity postMyResume( @AuthMember Long memberId, - @RequestPart(value = "images", required = false) List images, + @RequestPart(value = "images") List images, @RequestPart(value = "request") ResumePostRequest request ) throws IOException { return ResponseEntity.ok(resumeService.postMyResume(memberId, images, request)); @@ -47,7 +48,7 @@ public ResponseEntity getMyResume( return ResponseEntity.ok(resumeService.getMyResume(memberId)); } - @PutMapping("/my-resume") + @PutMapping(value = "/my-resume", consumes = {MediaType.APPLICATION_JSON_VALUE, MediaType.MULTIPART_FORM_DATA_VALUE}) public ResponseEntity putMyResume( @AuthMember Long memberId, @RequestPart(value = "images", required = false) List images, From 1d1dae5937209b26619a8201a56ca5ced03980ea Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Tue, 7 May 2024 20:35:45 +0900 Subject: [PATCH 169/265] =?UTF-8?q?fix:=20=EC=9E=90=EA=B8=B0=EC=86=8C?= =?UTF-8?q?=EA=B0=9C=EC=84=9C=20=EB=94=94=EB=B2=84=EA=B9=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../capstone/facefriend/DummyInitializer.java | 9 ------- .../facefriend/bucket/BucketService.java | 1 - .../resume/controller/ResumeController.java | 24 ++++++++++++++++--- 3 files changed, 21 insertions(+), 13 deletions(-) diff --git a/src/main/java/capstone/facefriend/DummyInitializer.java b/src/main/java/capstone/facefriend/DummyInitializer.java index b3b034efb9..ccce12efdb 100644 --- a/src/main/java/capstone/facefriend/DummyInitializer.java +++ b/src/main/java/capstone/facefriend/DummyInitializer.java @@ -31,20 +31,13 @@ public class DummyInitializer { private final MemberRepository memberRepository; private final AnalysisInfoRepository analysisInfoRepository; - private final AnalysisInfoService analysisInfoService; - - private final ResumeService resumeService; private final ResumeRepository resumeRepository; - @PostConstruct @Transactional public void init() { Random random = new Random(); - List GOOD_COMBI = new ArrayList<>(); - - List> CATEGORY = List.of( List.of("FOOD"), List.of("WORKOUT"), @@ -183,8 +176,6 @@ public void init() { .member(member) .build(); resumeRepository.save(resume); - - // Resume.Category.valueOf(CATEGORY.get(random.nextInt(size-1)) } } } diff --git a/src/main/java/capstone/facefriend/bucket/BucketService.java b/src/main/java/capstone/facefriend/bucket/BucketService.java index 46a5d73303..7c5676b804 100644 --- a/src/main/java/capstone/facefriend/bucket/BucketService.java +++ b/src/main/java/capstone/facefriend/bucket/BucketService.java @@ -133,7 +133,6 @@ public List uploadResumeImages( for (MultipartFile image : images) { - log.info("size = {}", image.getSize()); if (image.isEmpty() || image.getSize() == 0) { return List.of(); } diff --git a/src/main/java/capstone/facefriend/resume/controller/ResumeController.java b/src/main/java/capstone/facefriend/resume/controller/ResumeController.java index ac33160603..2c1852d8f3 100644 --- a/src/main/java/capstone/facefriend/resume/controller/ResumeController.java +++ b/src/main/java/capstone/facefriend/resume/controller/ResumeController.java @@ -14,6 +14,9 @@ import java.io.IOException; import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; +import java.util.stream.Stream; @Slf4j @@ -24,12 +27,20 @@ public class ResumeController { private final ResumeService resumeService; // 정적 쿼리 - @PostMapping(value = "/my-resume", consumes = {MediaType.APPLICATION_JSON_VALUE, MediaType.MULTIPART_FORM_DATA_VALUE}) + @PostMapping(value = "/my-resume") public ResponseEntity postMyResume( @AuthMember Long memberId, - @RequestPart(value = "images") List images, + @RequestPart(value = "image1", required = false) MultipartFile image1, + @RequestPart(value = "image2", required = false) MultipartFile image2, + @RequestPart(value = "image3", required = false) MultipartFile image3, + @RequestPart(value = "image4", required = false) MultipartFile image4, + @RequestPart(value = "image5", required = false) MultipartFile image5, @RequestPart(value = "request") ResumePostRequest request ) throws IOException { + List images = Stream.of(image1, image2, image3, image4, image5) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + log.info("size = {}", images.size()); return ResponseEntity.ok(resumeService.postMyResume(memberId, images, request)); } @@ -51,9 +62,16 @@ public ResponseEntity getMyResume( @PutMapping(value = "/my-resume", consumes = {MediaType.APPLICATION_JSON_VALUE, MediaType.MULTIPART_FORM_DATA_VALUE}) public ResponseEntity putMyResume( @AuthMember Long memberId, - @RequestPart(value = "images", required = false) List images, + @RequestPart(value = "image1", required = false) MultipartFile image1, + @RequestPart(value = "image2", required = false) MultipartFile image2, + @RequestPart(value = "image3", required = false) MultipartFile image3, + @RequestPart(value = "image4", required = false) MultipartFile image4, + @RequestPart(value = "image5", required = false) MultipartFile image5, @RequestPart(value = "request") ResumePutRequest request ) throws IOException { + List images = Stream.of(image1, image2, image3, image4, image5) + .filter(Objects::nonNull) + .collect(Collectors.toList()); return ResponseEntity.ok(resumeService.putMyResume(memberId, images, request)); } From f3c0e1d4097c3c6d9b0d681ce65dff103e7034af Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Wed, 8 May 2024 02:01:06 +0900 Subject: [PATCH 170/265] =?UTF-8?q?fix:=20=EC=9E=90=EA=B8=B0=EC=86=8C?= =?UTF-8?q?=EA=B0=9C=EC=84=9C=20=EB=94=94=EB=B2=84=EA=B9=85=20=EC=9B=90?= =?UTF-8?q?=EB=B3=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../resume/controller/ResumeController.java | 25 ++++--------------- .../resume/domain/dto/ResumeGetResponse.java | 2 +- .../domain/dto/ResumePostPutResponse.java | 2 +- 3 files changed, 7 insertions(+), 22 deletions(-) diff --git a/src/main/java/capstone/facefriend/resume/controller/ResumeController.java b/src/main/java/capstone/facefriend/resume/controller/ResumeController.java index 2c1852d8f3..1c7d9da3ed 100644 --- a/src/main/java/capstone/facefriend/resume/controller/ResumeController.java +++ b/src/main/java/capstone/facefriend/resume/controller/ResumeController.java @@ -30,17 +30,9 @@ public class ResumeController { @PostMapping(value = "/my-resume") public ResponseEntity postMyResume( @AuthMember Long memberId, - @RequestPart(value = "image1", required = false) MultipartFile image1, - @RequestPart(value = "image2", required = false) MultipartFile image2, - @RequestPart(value = "image3", required = false) MultipartFile image3, - @RequestPart(value = "image4", required = false) MultipartFile image4, - @RequestPart(value = "image5", required = false) MultipartFile image5, - @RequestPart(value = "request") ResumePostRequest request + @RequestPart("images") List images, + @RequestPart("request") ResumePostRequest request ) throws IOException { - List images = Stream.of(image1, image2, image3, image4, image5) - .filter(Objects::nonNull) - .collect(Collectors.toList()); - log.info("size = {}", images.size()); return ResponseEntity.ok(resumeService.postMyResume(memberId, images, request)); } @@ -59,19 +51,12 @@ public ResponseEntity getMyResume( return ResponseEntity.ok(resumeService.getMyResume(memberId)); } - @PutMapping(value = "/my-resume", consumes = {MediaType.APPLICATION_JSON_VALUE, MediaType.MULTIPART_FORM_DATA_VALUE}) + @PutMapping(value = "/my-resume") public ResponseEntity putMyResume( @AuthMember Long memberId, - @RequestPart(value = "image1", required = false) MultipartFile image1, - @RequestPart(value = "image2", required = false) MultipartFile image2, - @RequestPart(value = "image3", required = false) MultipartFile image3, - @RequestPart(value = "image4", required = false) MultipartFile image4, - @RequestPart(value = "image5", required = false) MultipartFile image5, - @RequestPart(value = "request") ResumePutRequest request + @RequestPart("images") List images, + @RequestPart("request") ResumePutRequest request ) throws IOException { - List images = Stream.of(image1, image2, image3, image4, image5) - .filter(Objects::nonNull) - .collect(Collectors.toList()); return ResponseEntity.ok(resumeService.putMyResume(memberId, images, request)); } diff --git a/src/main/java/capstone/facefriend/resume/domain/dto/ResumeGetResponse.java b/src/main/java/capstone/facefriend/resume/domain/dto/ResumeGetResponse.java index 54302b9438..d8e35bb018 100644 --- a/src/main/java/capstone/facefriend/resume/domain/dto/ResumeGetResponse.java +++ b/src/main/java/capstone/facefriend/resume/domain/dto/ResumeGetResponse.java @@ -17,7 +17,7 @@ public record ResumeGetResponse( FaceInfo faceInfo, BasicInfo basicInfo, AnalysisInfo analysisInfo, - Set category, + Set categories, String content, Boolean isMine ) { diff --git a/src/main/java/capstone/facefriend/resume/domain/dto/ResumePostPutResponse.java b/src/main/java/capstone/facefriend/resume/domain/dto/ResumePostPutResponse.java index 4f51bc9b17..cdee2e726a 100644 --- a/src/main/java/capstone/facefriend/resume/domain/dto/ResumePostPutResponse.java +++ b/src/main/java/capstone/facefriend/resume/domain/dto/ResumePostPutResponse.java @@ -16,7 +16,7 @@ public record ResumePostPutResponse( FaceInfo faceInfo, BasicInfo basicInfo, AnalysisInfo analysisInfo, - Set category, + Set categories, String content ) { } From f92e4478154ab691a8b3e14e5a6674a2ccd13506 Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Thu, 9 May 2024 02:09:18 +0900 Subject: [PATCH 171/265] =?UTF-8?q?debug:=20=EC=9E=90=EA=B8=B0=EC=86=8C?= =?UTF-8?q?=EA=B0=9C=EC=84=9C=20=EB=94=94=EB=B2=84=EA=B9=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../resume/controller/ResumeController.java | 12 +++++----- .../resume/service/ResumeService.java | 24 +++++++++---------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/main/java/capstone/facefriend/resume/controller/ResumeController.java b/src/main/java/capstone/facefriend/resume/controller/ResumeController.java index 1c7d9da3ed..7da6eb8e89 100644 --- a/src/main/java/capstone/facefriend/resume/controller/ResumeController.java +++ b/src/main/java/capstone/facefriend/resume/controller/ResumeController.java @@ -30,10 +30,10 @@ public class ResumeController { @PostMapping(value = "/my-resume") public ResponseEntity postMyResume( @AuthMember Long memberId, - @RequestPart("images") List images, - @RequestPart("request") ResumePostRequest request + @RequestPart("images") List images +// @RequestPart("request") ResumePostRequest request ) throws IOException { - return ResponseEntity.ok(resumeService.postMyResume(memberId, images, request)); + return ResponseEntity.ok(resumeService.postMyResume(memberId, images)); } @GetMapping("/resume") @@ -54,10 +54,10 @@ public ResponseEntity getMyResume( @PutMapping(value = "/my-resume") public ResponseEntity putMyResume( @AuthMember Long memberId, - @RequestPart("images") List images, - @RequestPart("request") ResumePutRequest request + @RequestPart("images") List images +// @RequestPart("request") ResumePutRequest request ) throws IOException { - return ResponseEntity.ok(resumeService.putMyResume(memberId, images, request)); + return ResponseEntity.ok(resumeService.putMyResume(memberId, images)); } @DeleteMapping("/my-resume") diff --git a/src/main/java/capstone/facefriend/resume/service/ResumeService.java b/src/main/java/capstone/facefriend/resume/service/ResumeService.java index ecc2c5b4d6..c909d30754 100644 --- a/src/main/java/capstone/facefriend/resume/service/ResumeService.java +++ b/src/main/java/capstone/facefriend/resume/service/ResumeService.java @@ -39,12 +39,12 @@ public class ResumeService { // 정적 쿼리 public ResumePostPutResponse postMyResume( Long memberId, - List images, - ResumePostRequest request + List images +// ResumePostRequest request ) throws IOException { - validateCategories(request.categories()); - validateContent(request.content()); +// validateCategories(request.categories()); +// validateContent(request.content()); Member member = validateMemberHasResume(memberId); List resumeImagesS3url = bucketService.uploadResumeImages(images); @@ -52,8 +52,8 @@ public ResumePostPutResponse postMyResume( Resume resume = Resume.builder() .member(member) .resumeImageS3urls(resumeImagesS3url) - .categories(request.categories().stream().map(str -> Category.valueOf(str)).collect(Collectors.toSet())) - .content(request.content()) +// .categories(request.categories().stream().map(str -> Category.valueOf(str)).collect(Collectors.toSet())) +// .content(request.content()) .build(); resumeRepository.save(resume); @@ -116,12 +116,12 @@ public ResumeGetResponse getMyResume( @Transactional public ResumePostPutResponse putMyResume( Long memberId, - List images, - ResumePutRequest request + List images +// ResumePutRequest request ) throws IOException { - validateCategories(request.categories()); - validateContent(request.content()); +// validateCategories(request.categories()); +// validateContent(request.content()); Member me = findMemberById(memberId); Resume mine = findResumeByMember(me); // 영속 상태 @@ -129,8 +129,8 @@ public ResumePostPutResponse putMyResume( List resumeImageS3urls = bucketService.updateResumeImages(images, mine); mine.setResumeImageS3urls(resumeImageS3urls); // dirty check - mine.setCategories(request.categories().stream().map(str -> Category.valueOf(str)).collect(Collectors.toSet())); // // dirty check - mine.setContent(request.content()); // // dirty check +// mine.setCategories(request.categories().stream().map(str -> Category.valueOf(str)).collect(Collectors.toSet())); // // dirty check +// mine.setContent(request.content()); // // dirty check return new ResumePostPutResponse( mine.getId(), From b4d2625c3e409760a59b86df38f8446c6362464e Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Thu, 9 May 2024 02:46:06 +0900 Subject: [PATCH 172/265] =?UTF-8?q?debug:=20=EC=9E=90=EA=B8=B0=EC=86=8C?= =?UTF-8?q?=EA=B0=9C=EC=84=9C=20=EB=94=94=EB=B2=84=EA=B9=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../resume/controller/ResumeController.java | 16 +++-------- ...Request.java => ResumePostPutRequest.java} | 5 +++- .../resume/domain/dto/ResumePutRequest.java | 9 ------- .../resume/service/ResumeService.java | 27 +++++++++---------- 4 files changed, 20 insertions(+), 37 deletions(-) rename src/main/java/capstone/facefriend/resume/domain/dto/{ResumePostRequest.java => ResumePostPutRequest.java} (50%) delete mode 100644 src/main/java/capstone/facefriend/resume/domain/dto/ResumePutRequest.java diff --git a/src/main/java/capstone/facefriend/resume/controller/ResumeController.java b/src/main/java/capstone/facefriend/resume/controller/ResumeController.java index 7da6eb8e89..c5d19b7c77 100644 --- a/src/main/java/capstone/facefriend/resume/controller/ResumeController.java +++ b/src/main/java/capstone/facefriend/resume/controller/ResumeController.java @@ -7,16 +7,10 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; -import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; -import org.springframework.web.multipart.MultipartFile; import java.io.IOException; -import java.util.List; -import java.util.Objects; -import java.util.stream.Collectors; -import java.util.stream.Stream; @Slf4j @@ -30,10 +24,9 @@ public class ResumeController { @PostMapping(value = "/my-resume") public ResponseEntity postMyResume( @AuthMember Long memberId, - @RequestPart("images") List images -// @RequestPart("request") ResumePostRequest request + @ModelAttribute ResumePostPutRequest request ) throws IOException { - return ResponseEntity.ok(resumeService.postMyResume(memberId, images)); + return ResponseEntity.ok(resumeService.postMyResume(memberId, request)); } @GetMapping("/resume") @@ -54,10 +47,9 @@ public ResponseEntity getMyResume( @PutMapping(value = "/my-resume") public ResponseEntity putMyResume( @AuthMember Long memberId, - @RequestPart("images") List images -// @RequestPart("request") ResumePutRequest request + @ModelAttribute ResumePostPutRequest request ) throws IOException { - return ResponseEntity.ok(resumeService.putMyResume(memberId, images)); + return ResponseEntity.ok(resumeService.putMyResume(memberId, request)); } @DeleteMapping("/my-resume") diff --git a/src/main/java/capstone/facefriend/resume/domain/dto/ResumePostRequest.java b/src/main/java/capstone/facefriend/resume/domain/dto/ResumePostPutRequest.java similarity index 50% rename from src/main/java/capstone/facefriend/resume/domain/dto/ResumePostRequest.java rename to src/main/java/capstone/facefriend/resume/domain/dto/ResumePostPutRequest.java index 7a4eb7b9e1..086b2127b8 100644 --- a/src/main/java/capstone/facefriend/resume/domain/dto/ResumePostRequest.java +++ b/src/main/java/capstone/facefriend/resume/domain/dto/ResumePostPutRequest.java @@ -1,8 +1,11 @@ package capstone.facefriend.resume.domain.dto; +import org.springframework.web.multipart.MultipartFile; + import java.util.List; -public record ResumePostRequest( +public record ResumePostPutRequest( + List images, List categories, String content ) { diff --git a/src/main/java/capstone/facefriend/resume/domain/dto/ResumePutRequest.java b/src/main/java/capstone/facefriend/resume/domain/dto/ResumePutRequest.java deleted file mode 100644 index 386e31edd2..0000000000 --- a/src/main/java/capstone/facefriend/resume/domain/dto/ResumePutRequest.java +++ /dev/null @@ -1,9 +0,0 @@ -package capstone.facefriend.resume.domain.dto; - -import java.util.List; - -public record ResumePutRequest( - List categories, - String content -) { -} diff --git a/src/main/java/capstone/facefriend/resume/service/ResumeService.java b/src/main/java/capstone/facefriend/resume/service/ResumeService.java index c909d30754..e7b843c806 100644 --- a/src/main/java/capstone/facefriend/resume/service/ResumeService.java +++ b/src/main/java/capstone/facefriend/resume/service/ResumeService.java @@ -15,7 +15,6 @@ import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import org.springframework.web.multipart.MultipartFile; import java.io.IOException; import java.util.List; @@ -39,21 +38,20 @@ public class ResumeService { // 정적 쿼리 public ResumePostPutResponse postMyResume( Long memberId, - List images -// ResumePostRequest request + ResumePostPutRequest request ) throws IOException { -// validateCategories(request.categories()); -// validateContent(request.content()); + validateCategories(request.categories()); + validateContent(request.content()); Member member = validateMemberHasResume(memberId); - List resumeImagesS3url = bucketService.uploadResumeImages(images); + List resumeImagesS3url = bucketService.uploadResumeImages(request.images()); Resume resume = Resume.builder() .member(member) .resumeImageS3urls(resumeImagesS3url) -// .categories(request.categories().stream().map(str -> Category.valueOf(str)).collect(Collectors.toSet())) -// .content(request.content()) + .categories(request.categories().stream().map(str -> Category.valueOf(str)).collect(Collectors.toSet())) + .content(request.content()) .build(); resumeRepository.save(resume); @@ -116,21 +114,20 @@ public ResumeGetResponse getMyResume( @Transactional public ResumePostPutResponse putMyResume( Long memberId, - List images -// ResumePutRequest request + ResumePostPutRequest request ) throws IOException { -// validateCategories(request.categories()); -// validateContent(request.content()); + validateCategories(request.categories()); + validateContent(request.content()); Member me = findMemberById(memberId); Resume mine = findResumeByMember(me); // 영속 상태 - List resumeImageS3urls = bucketService.updateResumeImages(images, mine); + List resumeImageS3urls = bucketService.updateResumeImages(request.images(), mine); mine.setResumeImageS3urls(resumeImageS3urls); // dirty check -// mine.setCategories(request.categories().stream().map(str -> Category.valueOf(str)).collect(Collectors.toSet())); // // dirty check -// mine.setContent(request.content()); // // dirty check + mine.setCategories(request.categories().stream().map(str -> Category.valueOf(str)).collect(Collectors.toSet())); // dirty check + mine.setContent(request.content()); // dirty check return new ResumePostPutResponse( mine.getId(), From ce0bdd646d8e0ec752802ff19df6b95723c1c19b Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Thu, 9 May 2024 02:56:35 +0900 Subject: [PATCH 173/265] =?UTF-8?q?debug:=20=EC=9E=90=EA=B8=B0=EC=86=8C?= =?UTF-8?q?=EA=B0=9C=EC=84=9C=20=EB=94=94=EB=B2=84=EA=B9=852?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../resume/controller/ResumeController.java | 12 +++++----- .../resume/domain/dto/ResumePostRequest.java | 9 ------- ...sumePutRequest.java => ResumeRequest.java} | 2 +- .../resume/service/ResumeService.java | 24 +++++++++---------- 4 files changed, 19 insertions(+), 28 deletions(-) delete mode 100644 src/main/java/capstone/facefriend/resume/domain/dto/ResumePostRequest.java rename src/main/java/capstone/facefriend/resume/domain/dto/{ResumePutRequest.java => ResumeRequest.java} (80%) diff --git a/src/main/java/capstone/facefriend/resume/controller/ResumeController.java b/src/main/java/capstone/facefriend/resume/controller/ResumeController.java index 7da6eb8e89..406877be09 100644 --- a/src/main/java/capstone/facefriend/resume/controller/ResumeController.java +++ b/src/main/java/capstone/facefriend/resume/controller/ResumeController.java @@ -30,10 +30,10 @@ public class ResumeController { @PostMapping(value = "/my-resume") public ResponseEntity postMyResume( @AuthMember Long memberId, - @RequestPart("images") List images -// @RequestPart("request") ResumePostRequest request + @RequestPart("images") List images, + @RequestPart("resumeRequest") ResumeRequest request ) throws IOException { - return ResponseEntity.ok(resumeService.postMyResume(memberId, images)); + return ResponseEntity.ok(resumeService.postMyResume(memberId, images, request)); } @GetMapping("/resume") @@ -54,10 +54,10 @@ public ResponseEntity getMyResume( @PutMapping(value = "/my-resume") public ResponseEntity putMyResume( @AuthMember Long memberId, - @RequestPart("images") List images -// @RequestPart("request") ResumePutRequest request + @RequestPart("images") List images, + @RequestPart("resumeRequest") ResumeRequest request ) throws IOException { - return ResponseEntity.ok(resumeService.putMyResume(memberId, images)); + return ResponseEntity.ok(resumeService.putMyResume(memberId, images, request)); } @DeleteMapping("/my-resume") diff --git a/src/main/java/capstone/facefriend/resume/domain/dto/ResumePostRequest.java b/src/main/java/capstone/facefriend/resume/domain/dto/ResumePostRequest.java deleted file mode 100644 index 7a4eb7b9e1..0000000000 --- a/src/main/java/capstone/facefriend/resume/domain/dto/ResumePostRequest.java +++ /dev/null @@ -1,9 +0,0 @@ -package capstone.facefriend.resume.domain.dto; - -import java.util.List; - -public record ResumePostRequest( - List categories, - String content -) { -} diff --git a/src/main/java/capstone/facefriend/resume/domain/dto/ResumePutRequest.java b/src/main/java/capstone/facefriend/resume/domain/dto/ResumeRequest.java similarity index 80% rename from src/main/java/capstone/facefriend/resume/domain/dto/ResumePutRequest.java rename to src/main/java/capstone/facefriend/resume/domain/dto/ResumeRequest.java index 386e31edd2..1745f368a2 100644 --- a/src/main/java/capstone/facefriend/resume/domain/dto/ResumePutRequest.java +++ b/src/main/java/capstone/facefriend/resume/domain/dto/ResumeRequest.java @@ -2,7 +2,7 @@ import java.util.List; -public record ResumePutRequest( +public record ResumeRequest( List categories, String content ) { diff --git a/src/main/java/capstone/facefriend/resume/service/ResumeService.java b/src/main/java/capstone/facefriend/resume/service/ResumeService.java index c909d30754..6c8a58713f 100644 --- a/src/main/java/capstone/facefriend/resume/service/ResumeService.java +++ b/src/main/java/capstone/facefriend/resume/service/ResumeService.java @@ -39,12 +39,12 @@ public class ResumeService { // 정적 쿼리 public ResumePostPutResponse postMyResume( Long memberId, - List images -// ResumePostRequest request + List images, + ResumeRequest request ) throws IOException { -// validateCategories(request.categories()); -// validateContent(request.content()); + validateCategories(request.categories()); + validateContent(request.content()); Member member = validateMemberHasResume(memberId); List resumeImagesS3url = bucketService.uploadResumeImages(images); @@ -52,8 +52,8 @@ public ResumePostPutResponse postMyResume( Resume resume = Resume.builder() .member(member) .resumeImageS3urls(resumeImagesS3url) -// .categories(request.categories().stream().map(str -> Category.valueOf(str)).collect(Collectors.toSet())) -// .content(request.content()) + .categories(request.categories().stream().map(str -> Category.valueOf(str)).collect(Collectors.toSet())) + .content(request.content()) .build(); resumeRepository.save(resume); @@ -116,12 +116,12 @@ public ResumeGetResponse getMyResume( @Transactional public ResumePostPutResponse putMyResume( Long memberId, - List images -// ResumePutRequest request + List images, + ResumeRequest request ) throws IOException { -// validateCategories(request.categories()); -// validateContent(request.content()); + validateCategories(request.categories()); + validateContent(request.content()); Member me = findMemberById(memberId); Resume mine = findResumeByMember(me); // 영속 상태 @@ -129,8 +129,8 @@ public ResumePostPutResponse putMyResume( List resumeImageS3urls = bucketService.updateResumeImages(images, mine); mine.setResumeImageS3urls(resumeImageS3urls); // dirty check -// mine.setCategories(request.categories().stream().map(str -> Category.valueOf(str)).collect(Collectors.toSet())); // // dirty check -// mine.setContent(request.content()); // // dirty check + mine.setCategories(request.categories().stream().map(str -> Category.valueOf(str)).collect(Collectors.toSet())); // dirty check + mine.setContent(request.content()); // dirty check return new ResumePostPutResponse( mine.getId(), From 24ee7c48c3288bd1f2b501746e4c5f0d48d9272f Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Thu, 9 May 2024 03:36:44 +0900 Subject: [PATCH 174/265] =?UTF-8?q?feat:=20=EC=9E=90=EA=B8=B0=EC=86=8C?= =?UTF-8?q?=EA=B0=9C=EC=84=9C=20=EB=94=94=EB=B2=84=EA=B9=85=20=EC=99=84?= =?UTF-8?q?=EB=A3=8C,=20validation=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../facefriend/resume/domain/dto/ResumeRequest.java | 9 --------- .../facefriend/resume/service/ResumeService.java | 10 +--------- 2 files changed, 1 insertion(+), 18 deletions(-) delete mode 100644 src/main/java/capstone/facefriend/resume/domain/dto/ResumeRequest.java diff --git a/src/main/java/capstone/facefriend/resume/domain/dto/ResumeRequest.java b/src/main/java/capstone/facefriend/resume/domain/dto/ResumeRequest.java deleted file mode 100644 index 1745f368a2..0000000000 --- a/src/main/java/capstone/facefriend/resume/domain/dto/ResumeRequest.java +++ /dev/null @@ -1,9 +0,0 @@ -package capstone.facefriend.resume.domain.dto; - -import java.util.List; - -public record ResumeRequest( - List categories, - String content -) { -} diff --git a/src/main/java/capstone/facefriend/resume/service/ResumeService.java b/src/main/java/capstone/facefriend/resume/service/ResumeService.java index e7b843c806..4bc0802de4 100644 --- a/src/main/java/capstone/facefriend/resume/service/ResumeService.java +++ b/src/main/java/capstone/facefriend/resume/service/ResumeService.java @@ -42,7 +42,6 @@ public ResumePostPutResponse postMyResume( ) throws IOException { validateCategories(request.categories()); - validateContent(request.content()); Member member = validateMemberHasResume(memberId); List resumeImagesS3url = bucketService.uploadResumeImages(request.images()); @@ -118,7 +117,6 @@ public ResumePostPutResponse putMyResume( ) throws IOException { validateCategories(request.categories()); - validateContent(request.content()); Member me = findMemberById(memberId); Resume mine = findResumeByMember(me); // 영속 상태 @@ -174,14 +172,8 @@ private Resume findResumeByMember(Member member) { .orElseThrow(() -> new ResumeException(NO_RESUME)); } - private void validateContent(String content) { - if (content.trim().length() == 0 || content == null || content.isEmpty()) { - throw new ResumeException(MUST_FILL_CONTENT); - } - } - private void validateCategories(List categories) { - if (categories.size() == 0 || categories == null || categories.isEmpty()) { + if (categories == null || categories.size() == 0 || categories.isEmpty()) { // null validation 먼저 throw new ResumeException(MUST_SELECT_ONE_CATEGORY); } } From d56e02eb2011d555ec633e2cb3dddefd41ae35ac Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Thu, 9 May 2024 11:15:52 +0900 Subject: [PATCH 175/265] =?UTF-8?q?style:=20resume=20dto=20=EB=94=94?= =?UTF-8?q?=EB=A0=89=ED=86=A0=EB=A6=AC=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/capstone/facefriend/chat/domain/ChatRoom.java | 1 + .../capstone/facefriend/member/service/MemberService.java | 1 - .../facefriend/resume/controller/ResumeController.java | 2 +- .../facefriend/resume/domain/ResumeRepositoryCustom.java | 2 +- .../facefriend/resume/domain/ResumeRepositoryImpl.java | 4 ++-- .../capstone/facefriend/resume/service/ResumeService.java | 2 +- .../resume/{domain => service}/dto/ResumeDeleteResponse.java | 2 +- .../resume/{domain => service}/dto/ResumeGetResponse.java | 2 +- .../{domain => service}/dto/ResumeHomeDetailResponse.java | 2 +- .../resume/{domain => service}/dto/ResumePostPutRequest.java | 2 +- .../resume/{domain => service}/dto/ResumePostPutResponse.java | 2 +- 11 files changed, 11 insertions(+), 11 deletions(-) rename src/main/java/capstone/facefriend/resume/{domain => service}/dto/ResumeDeleteResponse.java (57%) rename src/main/java/capstone/facefriend/resume/{domain => service}/dto/ResumeGetResponse.java (93%) rename src/main/java/capstone/facefriend/resume/{domain => service}/dto/ResumeHomeDetailResponse.java (88%) rename src/main/java/capstone/facefriend/resume/{domain => service}/dto/ResumePostPutRequest.java (81%) rename src/main/java/capstone/facefriend/resume/{domain => service}/dto/ResumePostPutResponse.java (92%) diff --git a/src/main/java/capstone/facefriend/chat/domain/ChatRoom.java b/src/main/java/capstone/facefriend/chat/domain/ChatRoom.java index e2daef07ed..6aa10eeef3 100644 --- a/src/main/java/capstone/facefriend/chat/domain/ChatRoom.java +++ b/src/main/java/capstone/facefriend/chat/domain/ChatRoom.java @@ -21,6 +21,7 @@ public class ChatRoom extends BaseEntity { @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; + @Builder.Default @Enumerated(EnumType.STRING) @Column private Status status = Status.set; diff --git a/src/main/java/capstone/facefriend/member/service/MemberService.java b/src/main/java/capstone/facefriend/member/service/MemberService.java index 879ab0b62c..811da86c30 100644 --- a/src/main/java/capstone/facefriend/member/service/MemberService.java +++ b/src/main/java/capstone/facefriend/member/service/MemberService.java @@ -49,7 +49,6 @@ public class MemberService { private final RedisDao redisDao; private static final String SIGN_UP_VALID_EMAIL = "사용 가능한 이메일입니다."; - private static final String SIGN_UP_SUCCESS_MESSAGE = "회원가입 성공"; private static final String SIGN_OUT_SUCCESS_MESSAGE = "로그아웃 성공"; private static final String RESET_PASSWORD_SUCCESS_MESSAGE = "비밀번호 재설정 성공"; private static final String EXIT_SUCCESS_MESSAGE = "회원탈퇴 성공"; diff --git a/src/main/java/capstone/facefriend/resume/controller/ResumeController.java b/src/main/java/capstone/facefriend/resume/controller/ResumeController.java index c5d19b7c77..63c4e27abf 100644 --- a/src/main/java/capstone/facefriend/resume/controller/ResumeController.java +++ b/src/main/java/capstone/facefriend/resume/controller/ResumeController.java @@ -1,8 +1,8 @@ package capstone.facefriend.resume.controller; import capstone.facefriend.auth.controller.support.AuthMember; -import capstone.facefriend.resume.domain.dto.*; import capstone.facefriend.resume.service.ResumeService; +import capstone.facefriend.resume.service.dto.*; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.data.domain.Page; diff --git a/src/main/java/capstone/facefriend/resume/domain/ResumeRepositoryCustom.java b/src/main/java/capstone/facefriend/resume/domain/ResumeRepositoryCustom.java index 61509ceffb..618615f5c2 100644 --- a/src/main/java/capstone/facefriend/resume/domain/ResumeRepositoryCustom.java +++ b/src/main/java/capstone/facefriend/resume/domain/ResumeRepositoryCustom.java @@ -1,6 +1,6 @@ package capstone.facefriend.resume.domain; -import capstone.facefriend.resume.domain.dto.ResumeHomeDetailResponse; +import capstone.facefriend.resume.service.dto.ResumeHomeDetailResponse; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; diff --git a/src/main/java/capstone/facefriend/resume/domain/ResumeRepositoryImpl.java b/src/main/java/capstone/facefriend/resume/domain/ResumeRepositoryImpl.java index f009b6a1d7..25bddc86a1 100644 --- a/src/main/java/capstone/facefriend/resume/domain/ResumeRepositoryImpl.java +++ b/src/main/java/capstone/facefriend/resume/domain/ResumeRepositoryImpl.java @@ -4,8 +4,8 @@ import capstone.facefriend.member.domain.member.MemberRepository; import capstone.facefriend.member.domain.member.QMember; import capstone.facefriend.member.exception.member.MemberException; -import capstone.facefriend.resume.domain.dto.QResumeHomeDetailResponse; -import capstone.facefriend.resume.domain.dto.ResumeHomeDetailResponse; +import capstone.facefriend.resume.service.dto.QResumeHomeDetailResponse; +import capstone.facefriend.resume.service.dto.ResumeHomeDetailResponse; import com.querydsl.core.BooleanBuilder; import com.querydsl.jpa.impl.JPAQueryFactory; import lombok.RequiredArgsConstructor; diff --git a/src/main/java/capstone/facefriend/resume/service/ResumeService.java b/src/main/java/capstone/facefriend/resume/service/ResumeService.java index 4bc0802de4..0991394a41 100644 --- a/src/main/java/capstone/facefriend/resume/service/ResumeService.java +++ b/src/main/java/capstone/facefriend/resume/service/ResumeService.java @@ -7,8 +7,8 @@ import capstone.facefriend.member.exception.member.MemberException; import capstone.facefriend.resume.domain.Resume; import capstone.facefriend.resume.domain.ResumeRepository; -import capstone.facefriend.resume.domain.dto.*; import capstone.facefriend.resume.exception.ResumeException; +import capstone.facefriend.resume.service.dto.*; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.data.domain.Page; diff --git a/src/main/java/capstone/facefriend/resume/domain/dto/ResumeDeleteResponse.java b/src/main/java/capstone/facefriend/resume/service/dto/ResumeDeleteResponse.java similarity index 57% rename from src/main/java/capstone/facefriend/resume/domain/dto/ResumeDeleteResponse.java rename to src/main/java/capstone/facefriend/resume/service/dto/ResumeDeleteResponse.java index 871e0ee430..c2ab6265ff 100644 --- a/src/main/java/capstone/facefriend/resume/domain/dto/ResumeDeleteResponse.java +++ b/src/main/java/capstone/facefriend/resume/service/dto/ResumeDeleteResponse.java @@ -1,4 +1,4 @@ -package capstone.facefriend.resume.domain.dto; +package capstone.facefriend.resume.service.dto; public record ResumeDeleteResponse( String message diff --git a/src/main/java/capstone/facefriend/resume/domain/dto/ResumeGetResponse.java b/src/main/java/capstone/facefriend/resume/service/dto/ResumeGetResponse.java similarity index 93% rename from src/main/java/capstone/facefriend/resume/domain/dto/ResumeGetResponse.java rename to src/main/java/capstone/facefriend/resume/service/dto/ResumeGetResponse.java index d8e35bb018..95bc849e70 100644 --- a/src/main/java/capstone/facefriend/resume/domain/dto/ResumeGetResponse.java +++ b/src/main/java/capstone/facefriend/resume/service/dto/ResumeGetResponse.java @@ -1,4 +1,4 @@ -package capstone.facefriend.resume.domain.dto; +package capstone.facefriend.resume.service.dto; import capstone.facefriend.member.domain.analysisInfo.AnalysisInfo; import capstone.facefriend.member.domain.basicInfo.BasicInfo; diff --git a/src/main/java/capstone/facefriend/resume/domain/dto/ResumeHomeDetailResponse.java b/src/main/java/capstone/facefriend/resume/service/dto/ResumeHomeDetailResponse.java similarity index 88% rename from src/main/java/capstone/facefriend/resume/domain/dto/ResumeHomeDetailResponse.java rename to src/main/java/capstone/facefriend/resume/service/dto/ResumeHomeDetailResponse.java index aa0e465f19..9e6e22f65e 100644 --- a/src/main/java/capstone/facefriend/resume/domain/dto/ResumeHomeDetailResponse.java +++ b/src/main/java/capstone/facefriend/resume/service/dto/ResumeHomeDetailResponse.java @@ -1,4 +1,4 @@ -package capstone.facefriend.resume.domain.dto; +package capstone.facefriend.resume.service.dto; import com.querydsl.core.annotations.QueryProjection; import lombok.Data; diff --git a/src/main/java/capstone/facefriend/resume/domain/dto/ResumePostPutRequest.java b/src/main/java/capstone/facefriend/resume/service/dto/ResumePostPutRequest.java similarity index 81% rename from src/main/java/capstone/facefriend/resume/domain/dto/ResumePostPutRequest.java rename to src/main/java/capstone/facefriend/resume/service/dto/ResumePostPutRequest.java index 086b2127b8..61a3c66fac 100644 --- a/src/main/java/capstone/facefriend/resume/domain/dto/ResumePostPutRequest.java +++ b/src/main/java/capstone/facefriend/resume/service/dto/ResumePostPutRequest.java @@ -1,4 +1,4 @@ -package capstone.facefriend.resume.domain.dto; +package capstone.facefriend.resume.service.dto; import org.springframework.web.multipart.MultipartFile; diff --git a/src/main/java/capstone/facefriend/resume/domain/dto/ResumePostPutResponse.java b/src/main/java/capstone/facefriend/resume/service/dto/ResumePostPutResponse.java similarity index 92% rename from src/main/java/capstone/facefriend/resume/domain/dto/ResumePostPutResponse.java rename to src/main/java/capstone/facefriend/resume/service/dto/ResumePostPutResponse.java index cdee2e726a..1813bccfb7 100644 --- a/src/main/java/capstone/facefriend/resume/domain/dto/ResumePostPutResponse.java +++ b/src/main/java/capstone/facefriend/resume/service/dto/ResumePostPutResponse.java @@ -1,4 +1,4 @@ -package capstone.facefriend.resume.domain.dto; +package capstone.facefriend.resume.service.dto; import capstone.facefriend.member.domain.analysisInfo.AnalysisInfo; import capstone.facefriend.member.domain.basicInfo.BasicInfo; From 4aad62919bafd18e32aa3ab9ccd109e97894e442 Mon Sep 17 00:00:00 2001 From: imjanghyeok Date: Fri, 10 May 2024 13:22:37 +0900 Subject: [PATCH 176/265] =?UTF-8?q?Feat:=20=EC=97=B0=EB=8F=99=EC=8B=9C=20D?= =?UTF-8?q?ebug=EB=A5=BC=20=EC=9C=84=ED=95=9C=20interceptor=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../facefriend/chat/config/ChatConfig.java | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/main/java/capstone/facefriend/chat/config/ChatConfig.java b/src/main/java/capstone/facefriend/chat/config/ChatConfig.java index d54818d4ba..b9cde28468 100644 --- a/src/main/java/capstone/facefriend/chat/config/ChatConfig.java +++ b/src/main/java/capstone/facefriend/chat/config/ChatConfig.java @@ -1,6 +1,9 @@ package capstone.facefriend.chat.config; +import capstone.facefriend.chat.controller.interceptor.FilterChannelInterceptor; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; +import org.springframework.messaging.simp.config.ChannelRegistration; import org.springframework.messaging.simp.config.MessageBrokerRegistry; import org.springframework.web.socket.config.annotation.EnableWebSocket; import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker; @@ -12,6 +15,13 @@ @EnableWebSocketMessageBroker public class ChatConfig implements WebSocketMessageBrokerConfigurer { + @Autowired + private FilterChannelInterceptor filterChannelInterceptor; + + public ChatConfig(FilterChannelInterceptor filterChannelInterceptor) { + this.filterChannelInterceptor = filterChannelInterceptor; + } + // sockJS Fallback을 이용해 노출할 endpoint 설정 @Override public void registerStompEndpoints(StompEndpointRegistry registry) { @@ -31,5 +41,9 @@ public void configureMessageBroker(MessageBrokerRegistry registry) { // 클라이언트->서버로 발행하는 메세지에 대한 endpoint 설정 : 구독에 대한 메세지 registry.setApplicationDestinationPrefixes("/pub"); } -} + @Override + public void configureClientInboundChannel(ChannelRegistration registration){ + registration.interceptors(filterChannelInterceptor); + } +} \ No newline at end of file From 7fcf26b13438ae7fff51b8b4ce5516fefbb2e848 Mon Sep 17 00:00:00 2001 From: imjanghyeok Date: Fri, 10 May 2024 13:22:56 +0900 Subject: [PATCH 177/265] =?UTF-8?q?Feat:=20=EC=97=B0=EB=8F=99=EC=8B=9C=20D?= =?UTF-8?q?ebug=EB=A5=BC=20=EC=9C=84=ED=95=9C=20Channel=20interceptor=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../interceptor/FilterChannelInterceptor.java | 96 +++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 src/main/java/capstone/facefriend/chat/controller/interceptor/FilterChannelInterceptor.java diff --git a/src/main/java/capstone/facefriend/chat/controller/interceptor/FilterChannelInterceptor.java b/src/main/java/capstone/facefriend/chat/controller/interceptor/FilterChannelInterceptor.java new file mode 100644 index 0000000000..bf1be7aad9 --- /dev/null +++ b/src/main/java/capstone/facefriend/chat/controller/interceptor/FilterChannelInterceptor.java @@ -0,0 +1,96 @@ +package capstone.facefriend.chat.controller.interceptor; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.messaging.Message; +import org.springframework.messaging.MessageChannel; +import org.springframework.messaging.simp.stomp.StompCommand; +import org.springframework.messaging.simp.stomp.StompHeaderAccessor; +import org.springframework.messaging.support.ChannelInterceptor; +import org.springframework.messaging.support.MessageHeaderAccessor; +import org.springframework.stereotype.Component; + +@Component +@RequiredArgsConstructor +@Slf4j +public class FilterChannelInterceptor implements ChannelInterceptor { + + private static final String BEARER_PREFIX = "Bearer "; + + @Override + public Message preSend(Message message, MessageChannel channel) { + + log.info("Stomp Handler 실행"); + StompHeaderAccessor accessor = MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class); + StompHeaderAccessor headerAccessor = MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class); + + StompCommand command = headerAccessor.getCommand(); + String destination = headerAccessor.getDestination(); + String subscribeUrl = headerAccessor.getSubscriptionId(); + String sessionId = headerAccessor.getSessionId(); + Object payload = message.getPayload(); + String messageHeaders = String.valueOf(accessor.getMessageHeaders()); + String messageType = String.valueOf(accessor.getMessageType()); + + log.info("Command: {}", command); + log.info("Destination: {}", destination); + log.info("Subscription ID: {}", subscribeUrl); + log.info("Session ID: {}", sessionId); + log.info("Payload: {}", payload.toString()); + log.info("Message Type: {}", messageType); + log.info("Message Headers: {}", messageHeaders.toString()); + + + String authorizationHeader = headerAccessor.getFirstNativeHeader("Authorization"); + if (headerAccessor.getCommand() == StompCommand.CONNECT) { + log.info("Command: {}", command); + log.info("Destination: {}", destination); + log.info("Subscription ID: {}", subscribeUrl); + log.info("Session ID: {}", sessionId); + log.info("Payload: {}", payload.toString()); + log.info("Message Type: {}", messageType); + log.info("Message Headers: {}", messageHeaders.toString()); +// String token = authorizationHeader.substring(BEARER_PREFIX.length()); +// log.info("token: {}", token); +// if (token == null) try { +// throw new AccessDeniedException(""); +// } catch (AccessDeniedException e) { +// throw new RuntimeException(e); +// } + } + + if (headerAccessor.getCommand() == StompCommand.SUBSCRIBE) { + log.info("Command: {}", command); + log.info("Destination: {}", destination); + log.info("Subscription ID: {}", subscribeUrl); + log.info("Session ID: {}", sessionId); + log.info("Payload: {}", payload.toString()); + log.info("Message Type: {}", messageType); + log.info("Message Headers: {}", messageHeaders.toString()); + } + + if (headerAccessor.getCommand() == StompCommand.DISCONNECT) { + log.info("Command: {}", command); + log.info("Destination: {}", destination); + log.info("Subscription ID: {}", subscribeUrl); + log.info("Session ID: {}", sessionId); + log.info("Payload: {}", payload.toString()); + log.info("Message Type: {}", messageType); + log.info("Message Headers: {}", messageHeaders.toString()); + } + + + if (headerAccessor.getCommand() == StompCommand.SEND) { + log.info("Command: {}", command); + log.info("Destination: {}", destination); + log.info("Subscription ID: {}", subscribeUrl); + log.info("Session ID: {}", sessionId); + log.info("Payload: {}", payload.toString()); + log.info("Message Type: {}", messageType); + log.info("Message Headers: {}", messageHeaders.toString()); + } + + + return message; + } +} \ No newline at end of file From 4093fb52717686416ecfb7829c5a249a437bcad7 Mon Sep 17 00:00:00 2001 From: imjanghyeok Date: Fri, 10 May 2024 13:24:05 +0900 Subject: [PATCH 178/265] =?UTF-8?q?Feat:=20=EC=B1=84=ED=8C=85=EB=B0=A9=20?= =?UTF-8?q?=EC=9E=85=EC=9E=A5=20=EC=A0=95=EB=B3=B4=20=EC=A0=80=EC=9E=A5?= =?UTF-8?q?=EC=9D=84=20=EC=9C=84=ED=95=9C=20redis=20domain=20=EC=9E=91?= =?UTF-8?q?=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../facefriend/chat/domain/ChatRoomInfo.java | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 src/main/java/capstone/facefriend/chat/domain/ChatRoomInfo.java diff --git a/src/main/java/capstone/facefriend/chat/domain/ChatRoomInfo.java b/src/main/java/capstone/facefriend/chat/domain/ChatRoomInfo.java new file mode 100644 index 0000000000..4b65746935 --- /dev/null +++ b/src/main/java/capstone/facefriend/chat/domain/ChatRoomInfo.java @@ -0,0 +1,25 @@ +package capstone.facefriend.chat.domain; + +import jakarta.persistence.Column; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; +import org.springframework.data.annotation.Id; +import org.springframework.data.redis.core.RedisHash; + +import java.time.LocalDateTime; + +@Getter +@Setter +@NoArgsConstructor +@RedisHash("ChatRoom") +@Slf4j +public class ChatRoomInfo { + @Id + private String chatRoomInfoId; + + @Column(name = "enterTime", nullable = false) + private LocalDateTime enterTime; + +} \ No newline at end of file From 75fc88ec304bcbc67d77c21708d96f01af510bfb Mon Sep 17 00:00:00 2001 From: imjanghyeok Date: Fri, 10 May 2024 13:24:19 +0900 Subject: [PATCH 179/265] =?UTF-8?q?Feat:=20=EC=95=A0=ED=94=8C=EB=A6=AC?= =?UTF-8?q?=EC=BC=80=EC=9D=B4=EC=85=98=20=EC=9E=85=EC=9E=A5=20=EC=A0=95?= =?UTF-8?q?=EB=B3=B4=20=EC=A0=80=EC=9E=A5=EC=9D=84=20=EC=9C=84=ED=95=9C=20?= =?UTF-8?q?redis=20domain=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../facefriend/chat/domain/SocketInfo.java | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 src/main/java/capstone/facefriend/chat/domain/SocketInfo.java diff --git a/src/main/java/capstone/facefriend/chat/domain/SocketInfo.java b/src/main/java/capstone/facefriend/chat/domain/SocketInfo.java new file mode 100644 index 0000000000..50674ddfab --- /dev/null +++ b/src/main/java/capstone/facefriend/chat/domain/SocketInfo.java @@ -0,0 +1,19 @@ +package capstone.facefriend.chat.domain; + +import jakarta.persistence.Column; +import lombok.*; +import org.springframework.data.annotation.Id; +import org.springframework.data.redis.core.RedisHash; + +import java.time.LocalDateTime; + +@Data +@NoArgsConstructor +@RedisHash("SocketInfo") +public class SocketInfo { + @Id + private Long memberId; + + @Column(name = "connectTime", nullable = false) + private LocalDateTime connectTime; +} \ No newline at end of file From 768cc2b62ae3d0456c892ccf74182fbb6aff56b2 Mon Sep 17 00:00:00 2001 From: imjanghyeok Date: Fri, 10 May 2024 13:25:01 +0900 Subject: [PATCH 180/265] =?UTF-8?q?Feat:=20Redis=20Repository=EB=A5=BC=20?= =?UTF-8?q?=EC=82=AC=EC=9A=A9=ED=95=98=EA=B8=B0=20=EC=9C=84=ED=95=9C=20?= =?UTF-8?q?=EC=96=B4=EB=85=B8=ED=85=8C=EC=9D=B4=EC=85=98=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20=EB=B0=8F=20=EC=BD=94=EB=93=9C=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../facefriend/chat/config/RedisConfig.java | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/main/java/capstone/facefriend/chat/config/RedisConfig.java b/src/main/java/capstone/facefriend/chat/config/RedisConfig.java index dd8851deef..07af53c8f7 100644 --- a/src/main/java/capstone/facefriend/chat/config/RedisConfig.java +++ b/src/main/java/capstone/facefriend/chat/config/RedisConfig.java @@ -1,11 +1,9 @@ package capstone.facefriend.chat.config; - import capstone.facefriend.chat.service.RedisSubscriber; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; -import lombok.RequiredArgsConstructor; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -13,14 +11,16 @@ import org.springframework.data.redis.connection.RedisStandaloneConfiguration; import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.data.redis.listener.ChannelTopic; import org.springframework.data.redis.listener.RedisMessageListenerContainer; import org.springframework.data.redis.listener.adapter.MessageListenerAdapter; +import org.springframework.data.redis.repository.configuration.EnableRedisRepositories; import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; import org.springframework.data.redis.serializer.StringRedisSerializer; @Configuration -@RequiredArgsConstructor +@EnableRedisRepositories public class RedisConfig { @Value("${spring.data.redis.host}") private String redisHost; @@ -68,6 +68,13 @@ public RedisTemplate redisTemplate(RedisConnectionFactory redisC return redisTemplate; } + @Bean + public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) { + StringRedisTemplate redisTemplate = new StringRedisTemplate(); + redisTemplate.setConnectionFactory(redisConnectionFactory); + return redisTemplate; + } + @Bean public MessageListenerAdapter listenerAdapter(RedisSubscriber subscriber) { return new MessageListenerAdapter(subscriber, "sendMessage"); From bad6b4fd8e4a1f486bfdf7b1f392bd2211ca6546 Mon Sep 17 00:00:00 2001 From: imjanghyeok Date: Fri, 10 May 2024 13:27:41 +0900 Subject: [PATCH 181/265] =?UTF-8?q?Feat:=20=EC=B1=84=ED=8C=85=EB=B0=A9=20?= =?UTF-8?q?=EC=9E=85=EC=9E=A5=20=EC=A0=95=EB=B3=B4=20=EA=B4=80=EB=A0=A8?= =?UTF-8?q?=EB=90=9C=20Repository=20=EC=9D=B8=ED=84=B0=ED=8E=98=EC=9D=B4?= =?UTF-8?q?=EC=8A=A4=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../chat/repository/ChatRoomInfoRedisRepository.java | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 src/main/java/capstone/facefriend/chat/repository/ChatRoomInfoRedisRepository.java diff --git a/src/main/java/capstone/facefriend/chat/repository/ChatRoomInfoRedisRepository.java b/src/main/java/capstone/facefriend/chat/repository/ChatRoomInfoRedisRepository.java new file mode 100644 index 0000000000..c8262c8ad0 --- /dev/null +++ b/src/main/java/capstone/facefriend/chat/repository/ChatRoomInfoRedisRepository.java @@ -0,0 +1,10 @@ +package capstone.facefriend.chat.repository; + +import capstone.facefriend.chat.domain.ChatRoomInfo; +import org.springframework.data.repository.CrudRepository; + +import java.util.Optional; + +public interface ChatRoomInfoRedisRepository extends CrudRepository { + Optional findById(String chatRoomInfoId); +} \ No newline at end of file From f7aabe84ababa106942a69d62960f9b903e869d3 Mon Sep 17 00:00:00 2001 From: imjanghyeok Date: Fri, 10 May 2024 13:27:52 +0900 Subject: [PATCH 182/265] =?UTF-8?q?Feat:=20=EC=95=A0=ED=94=8C=EB=A6=AC?= =?UTF-8?q?=EC=BC=80=EC=9D=B4=EC=85=98=20=EC=9E=85=EC=9E=A5=20=EC=A0=95?= =?UTF-8?q?=EB=B3=B4=20=EA=B4=80=EB=A0=A8=EB=90=9C=20Repository=20?= =?UTF-8?q?=EC=9D=B8=ED=84=B0=ED=8E=98=EC=9D=B4=EC=8A=A4=20=EC=9E=91?= =?UTF-8?q?=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../chat/repository/SocketInfoRedisRepository.java | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 src/main/java/capstone/facefriend/chat/repository/SocketInfoRedisRepository.java diff --git a/src/main/java/capstone/facefriend/chat/repository/SocketInfoRedisRepository.java b/src/main/java/capstone/facefriend/chat/repository/SocketInfoRedisRepository.java new file mode 100644 index 0000000000..4d5591d1ed --- /dev/null +++ b/src/main/java/capstone/facefriend/chat/repository/SocketInfoRedisRepository.java @@ -0,0 +1,8 @@ +package capstone.facefriend.chat.repository; + +import capstone.facefriend.chat.domain.SocketInfo; +import org.springframework.data.repository.CrudRepository; + +public interface SocketInfoRedisRepository extends CrudRepository { + +} \ No newline at end of file From c38f6c6a412dfbe7c06a0a3677f04f79c10c255c Mon Sep 17 00:00:00 2001 From: imjanghyeok Date: Fri, 10 May 2024 13:32:58 +0900 Subject: [PATCH 183/265] =?UTF-8?q?Refactor:=20=EC=B1=84=ED=8C=85=EB=B0=A9?= =?UTF-8?q?=20=EB=A6=AC=EC=8A=A4=ED=8A=B8=20service=20=EC=B1=84=ED=8C=85?= =?UTF-8?q?=EB=B0=A9=20=EC=83=81=ED=83=9C=EB=B3=84=20=EB=B0=8F=20=EB=8C=80?= =?UTF-8?q?=ED=99=94=ED=95=98=EA=B3=A0=20=EC=9E=88=EB=8A=94=20=EC=83=81?= =?UTF-8?q?=EB=8C=80=EA=B0=80=20sender=EA=B0=80=20=EB=90=98=EB=8F=84?= =?UTF-8?q?=EB=A1=9D=20refactor?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../chat/service/ChatRoomService.java | 89 +++++++++++++++---- 1 file changed, 73 insertions(+), 16 deletions(-) diff --git a/src/main/java/capstone/facefriend/chat/service/ChatRoomService.java b/src/main/java/capstone/facefriend/chat/service/ChatRoomService.java index 2bc79438c0..218683d6c8 100644 --- a/src/main/java/capstone/facefriend/chat/service/ChatRoomService.java +++ b/src/main/java/capstone/facefriend/chat/service/ChatRoomService.java @@ -2,20 +2,25 @@ import capstone.facefriend.chat.domain.ChatMessage; import capstone.facefriend.chat.domain.ChatRoom; +import capstone.facefriend.chat.domain.ChatRoomInfo; import capstone.facefriend.chat.domain.ChatRoomMember; +import capstone.facefriend.chat.exception.ChatException; +import capstone.facefriend.chat.exception.ChatExceptionType; import capstone.facefriend.chat.repository.ChatMessageRepository; +import capstone.facefriend.chat.repository.ChatRoomInfoRedisRepository; import capstone.facefriend.chat.repository.ChatRoomMemberRepository; -import capstone.facefriend.chat.service.dto.chatroom.ChatRoomEmptyResponse; -import capstone.facefriend.chat.service.dto.chatroom.ChatRoomHeartResponse; -import capstone.facefriend.chat.service.dto.chatroom.ChatRoomMessageResponse; -import capstone.facefriend.chat.service.dto.chatroom.ChatRoomOpenResponse; -import capstone.facefriend.chat.service.dto.heart.GetSendHeartResponse; +import capstone.facefriend.chat.repository.ChatRoomRepository; +import capstone.facefriend.chat.service.dto.chatroom.*; import capstone.facefriend.member.domain.member.Member; +import capstone.facefriend.member.domain.member.MemberRepository; +import capstone.facefriend.member.exception.member.MemberException; +import capstone.facefriend.member.exception.member.MemberExceptionType; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.time.LocalDateTime; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -25,11 +30,20 @@ @Slf4j @RequiredArgsConstructor public class ChatRoomService { - + private final ChatRoomRepository chatRoomRepository; private final ChatRoomMemberRepository chatRoomMemberRepository; private final ChatMessageRepository chatMessageRepository; + private final ChatRoomInfoRedisRepository chatRoomInfoRedisRepository; + private final MemberRepository memberRepository; private static final String EMPTY_MESSAGE = "채팅을 시작하지 않았습니다."; private static final String OPEN_MESSAGE = "채팅을 시작해보세요!"; + private static final String CLOSE_MESSAGE = "상대방이 떠났습니다."; + + private Member findMemberById(Long memberId) { + Member member = memberRepository.findById(memberId) + .orElseThrow(() -> new MemberException(MemberExceptionType.NOT_FOUND)); + return member; + } private List findAllChatRoomMemberBySenderId(Long memberId) { return chatRoomMemberRepository.findAllBySenderId(memberId).orElse(new ArrayList<>()); @@ -57,36 +71,79 @@ public Map getChatRoomList(Long memberId) { List chatRoomsMessage = new ArrayList<>(); List chatRoomsHeart = new ArrayList<>(); List chatRoomsOpen = new ArrayList<>(); + List chatRoomClose = new ArrayList<>(); + + Member member = findMemberById(memberId); for (ChatRoomMember chatRoomMember : chatRoomMemberList) { ChatRoom.Status status = chatRoomMember.getChatRoom().getStatus(); if (status == ChatRoom.Status.set) { - GetSendHeartResponse sendHeartResponse = new GetSendHeartResponse(); - sendHeartResponse.setType("Heart"); - sendHeartResponse.setRoomId(chatRoomMember.getChatRoom().getId()); - sendHeartResponse.setSenderId(chatRoomMember.getSender().getId()); - sendHeartResponse.setReceiveId(chatRoomMember.getReceiver().getId()); - sendHeartResponse.setSenderName(chatRoomMember.getSender().getBasicInfo().getNickname()); - ChatRoomHeartResponse chatRoomHeartResponse = ChatRoomHeartResponse.of(chatRoomMember, sendHeartResponse); + Member sender = identifySender(chatRoomMember, memberId); + Boolean isSender = isSender(chatRoomMember, memberId); + ChatRoomHeartResponse chatRoomHeartResponse = ChatRoomHeartResponse.of(member, sender, chatRoomMember.getChatRoom(), isSender); chatRoomsHeart.add(chatRoomHeartResponse); } else if (status == ChatRoom.Status.progress) { + Member sender = identifySender(chatRoomMember, memberId); ChatMessage chatMessage = chatMessageRepository.findFirstByChatRoomIdOrderBySendTimeDesc(chatRoomMember.getChatRoom().getId()); - ChatRoomMessageResponse chatRoomResponse = ChatRoomMessageResponse.of(chatRoomMember, chatMessage); + ChatRoomMessageResponse chatRoomResponse = ChatRoomMessageResponse.of(member, sender, chatRoomMember.getChatRoom(), chatMessage); chatRoomsMessage.add(chatRoomResponse); } else if (status == ChatRoom.Status.open) { - Member Sender = chatRoomMember.getSender(); - ChatRoomOpenResponse chatRoomOpenResponse = ChatRoomOpenResponse.of(Sender.getId(), Sender.getBasicInfo().getNickname(), OPEN_MESSAGE); + Member sender = identifySender(chatRoomMember, memberId); + ChatRoomOpenResponse chatRoomOpenResponse = ChatRoomOpenResponse.of(member, sender, chatRoomMember.getChatRoom(), OPEN_MESSAGE); chatRoomsOpen.add(chatRoomOpenResponse); + } else if (status == ChatRoom.Status.close) { + Member leftMember = identifyLeftMember(memberId, chatRoomMember); + if (member != leftMember) { + ChatRoomCloseResponse chatRoomCloseResponse = ChatRoomCloseResponse.of(member, chatRoomMember.getChatRoom(), CLOSE_MESSAGE); + chatRoomClose.add(chatRoomCloseResponse); + } } } chatRooms.put("chatRoomHeartList", chatRoomsHeart); chatRooms.put("chatRoomMessageList", chatRoomsMessage); chatRooms.put("chatRoomOpenList", chatRoomsOpen); + chatRooms.put("chatRoomCloseList", chatRoomClose); return chatRooms; } + + + private Member identifySender(ChatRoomMember chatRoomMember, Long memberId) { + Member member = findMemberById(memberId); + if (member.getId().equals(chatRoomMember.getSender().getId())) { + return chatRoomMember.getReceiver(); + } else { + return chatRoomMember.getSender(); + } + } + + private Member identifyLeftMember(Long memberId, ChatRoomMember chatRoomMember) { + Member member = findMemberById(memberId); + if (chatRoomMember.getSender() == member) { + if (chatRoomMember.isSenderExist()){ + return chatRoomMember.getReceiver(); + } else { + return member; + } + } else { + if (chatRoomMember.isReceiverExist()) { + return chatRoomMember.getSender(); + } else { + return member; + } + } + } + + private Boolean isSender(ChatRoomMember chatRoomMember, Long memberId) { + Member member = findMemberById(memberId); + if (member.getId().equals(chatRoomMember.getSender().getId())) { + return true; + } else { + return false; + } + } } \ No newline at end of file From 47c2d650fdeb85e7ca49de5832ffd5a98a2fbd0c Mon Sep 17 00:00:00 2001 From: imjanghyeok Date: Fri, 10 May 2024 13:34:14 +0900 Subject: [PATCH 184/265] =?UTF-8?q?Feat:=20=EC=B1=84=ED=8C=85=EB=B0=A9=20?= =?UTF-8?q?=EC=9E=85=EC=9E=A5=20=EC=A0=95=EB=B3=B4=20=EC=A0=80=EC=9E=A5=20?= =?UTF-8?q?Api=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../facefriend/chat/service/ChatRoomService.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/main/java/capstone/facefriend/chat/service/ChatRoomService.java b/src/main/java/capstone/facefriend/chat/service/ChatRoomService.java index 218683d6c8..a476c4ced1 100644 --- a/src/main/java/capstone/facefriend/chat/service/ChatRoomService.java +++ b/src/main/java/capstone/facefriend/chat/service/ChatRoomService.java @@ -111,6 +111,18 @@ public Map getChatRoomList(Long memberId) { return chatRooms; } + public ChatRoomEnterResponse enterRoom(Long roomId, Long memberId) { + String chatRoomInfoId = roomId + "/member/" + memberId; + ChatRoomInfo chatRoomInfo = new ChatRoomInfo(); + chatRoomInfo.setChatRoomInfoId(chatRoomInfoId); + chatRoomInfo.setEnterTime(LocalDateTime.now()); + chatRoomInfoRedisRepository.save(chatRoomInfo); + return ChatRoomEnterResponse.of(roomId, memberId, chatRoomInfo); + } + + + + private Member identifySender(ChatRoomMember chatRoomMember, Long memberId) { Member member = findMemberById(memberId); From b2528ceab10f7e9cf7dbd147e68d81a0b5b129f1 Mon Sep 17 00:00:00 2001 From: imjanghyeok Date: Fri, 10 May 2024 13:36:12 +0900 Subject: [PATCH 185/265] =?UTF-8?q?Feat:=20=EC=B1=84=ED=8C=85=EB=B0=A9=20?= =?UTF-8?q?=EC=9E=85=EC=9E=A5=20=EC=A0=95=EB=B3=B4=20=EC=82=AD=EC=A0=9C=20?= =?UTF-8?q?=EC=84=9C=EB=B9=84=EC=8A=A4=20=EC=BD=94=EB=93=9C=20=EC=9E=91?= =?UTF-8?q?=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../facefriend/chat/service/ChatRoomService.java | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/main/java/capstone/facefriend/chat/service/ChatRoomService.java b/src/main/java/capstone/facefriend/chat/service/ChatRoomService.java index a476c4ced1..d7fea98bce 100644 --- a/src/main/java/capstone/facefriend/chat/service/ChatRoomService.java +++ b/src/main/java/capstone/facefriend/chat/service/ChatRoomService.java @@ -53,6 +53,14 @@ private List findAllChatRoomMemberByReceiverId(Long memberId) { return chatRoomMemberRepository.findAllByReceiverId(memberId).orElse(new ArrayList<>()); } + private ChatRoomInfo findChatRoomInfo(String chatRoomInfoId) { + ChatRoomInfo chatRoomInfo = chatRoomInfoRedisRepository.findById(chatRoomInfoId) + .orElseThrow(()-> new ChatException(ChatExceptionType.NOT_FOUND)); + return chatRoomInfo; + } + + + @Transactional public Map getChatRoomList(Long memberId) { @@ -120,7 +128,13 @@ public ChatRoomEnterResponse enterRoom(Long roomId, Long memberId) { return ChatRoomEnterResponse.of(roomId, memberId, chatRoomInfo); } - + public ChatRoomExitResponse exitRoom(Long roomId, Long memberId) { + String chatRoomInfoId = roomId + "/member/" + memberId; + ChatRoomInfo chatRoomInfo = findChatRoomInfo(chatRoomInfoId); + chatRoomInfoRedisRepository.delete(chatRoomInfo); + LocalDateTime exitChatRoomTime = LocalDateTime.now(); + return ChatRoomExitResponse.of(roomId, memberId, exitChatRoomTime); + } From 26c4f2c0d65601903f42b1bf4d91543f0dc17f1c Mon Sep 17 00:00:00 2001 From: imjanghyeok Date: Fri, 10 May 2024 13:37:11 +0900 Subject: [PATCH 186/265] =?UTF-8?q?Feat:=20=EC=B1=84=ED=8C=85=EB=B0=A9=20?= =?UTF-8?q?=EB=82=98=EA=B0=80=EA=B8=B0=20service=20=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../chat/service/ChatRoomService.java | 50 ++++++++++++++++++- 1 file changed, 49 insertions(+), 1 deletion(-) diff --git a/src/main/java/capstone/facefriend/chat/service/ChatRoomService.java b/src/main/java/capstone/facefriend/chat/service/ChatRoomService.java index d7fea98bce..90293597bd 100644 --- a/src/main/java/capstone/facefriend/chat/service/ChatRoomService.java +++ b/src/main/java/capstone/facefriend/chat/service/ChatRoomService.java @@ -59,6 +59,22 @@ private ChatRoomInfo findChatRoomInfo(String chatRoomInfoId) { return chatRoomInfo; } + private ChatRoom findRoomById(Long roomId) { + ChatRoom chatRoom = chatRoomRepository.findById(roomId) + .orElseThrow(()-> new ChatException(ChatExceptionType.NOT_FOUND)); + return chatRoom; + } + + private ChatRoomMember findChatRoomMemberByChatRoomId(Long roomId) { + ChatRoomMember chatRoomMember = chatRoomMemberRepository.findByChatRoomId(roomId) + .orElseThrow(()-> new ChatException(ChatExceptionType.NOT_FOUND)); + return chatRoomMember; + } + + private List findChatRoomMessageByChatRoomId(Long roomId) { + return chatMessageRepository.findChatMessagesByChatRoomId(roomId); + } + @Transactional @@ -136,7 +152,39 @@ public ChatRoomExitResponse exitRoom(Long roomId, Long memberId) { return ChatRoomExitResponse.of(roomId, memberId, exitChatRoomTime); } - + @Transactional + public String leftRoom(Long roomId, Long memberId) { + ChatRoom chatRoom = findRoomById(roomId); + ChatRoom.Status status = chatRoom.getStatus(); + ChatRoomMember chatRoomMember = findChatRoomMemberByChatRoomId(roomId); + Member member = findMemberById(memberId); + Member leftMember = identifyLeftMember(memberId, chatRoomMember); + if (status== ChatRoom.Status.close) { + if (member != leftMember) { + chatRoomMemberRepository.delete(chatRoomMember); + chatRoomRepository.delete(chatRoom); + return "채팅방을 떠났습니다."; + } else { + return "이미 떠난 채팅방입니다."; + } + } + List chatMessages = findChatRoomMessageByChatRoomId(roomId); + log.info("FindChatRoomIdChatMessage:{}", chatMessages.toString()); + if(!chatMessages.isEmpty()){ + chatMessageRepository.deleteAll(chatMessages); + } + if (chatRoomMember.getSender() == member) { + chatRoomMember.setSenderExist(false); + } else if (chatRoomMember.getReceiver() == member){ + chatRoomMember.setReceiverExist(false); + } else { + return "속해있지 않은 채팅방입니디."; + } + chatRoom.setStatus(ChatRoom.Status.close); + chatRoomRepository.save(chatRoom); + chatRoomMemberRepository.save(chatRoomMember); + return "채팅방을 떠났습니다"; + } private Member identifySender(ChatRoomMember chatRoomMember, Long memberId) { Member member = findMemberById(memberId); From c84b2ff1d39b10be71addc038415e15c9cebe556 Mon Sep 17 00:00:00 2001 From: imjanghyeok Date: Fri, 10 May 2024 13:38:09 +0900 Subject: [PATCH 187/265] =?UTF-8?q?Refactor:=20Open=20=EC=83=81=ED=83=9C?= =?UTF-8?q?=EC=9D=98=20=EC=B1=84=ED=8C=85=EB=B0=A9=20Dto=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/chatroom/ChatRoomOpenResponse.java | 23 ++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/src/main/java/capstone/facefriend/chat/service/dto/chatroom/ChatRoomOpenResponse.java b/src/main/java/capstone/facefriend/chat/service/dto/chatroom/ChatRoomOpenResponse.java index 393f9a8880..a968a98ac5 100644 --- a/src/main/java/capstone/facefriend/chat/service/dto/chatroom/ChatRoomOpenResponse.java +++ b/src/main/java/capstone/facefriend/chat/service/dto/chatroom/ChatRoomOpenResponse.java @@ -1,14 +1,31 @@ package capstone.facefriend.chat.service.dto.chatroom; +import capstone.facefriend.chat.domain.ChatRoom; +import capstone.facefriend.member.domain.member.Member; + public record ChatRoomOpenResponse ( + Long memberId, + String memberNickname, + String memberGeneratedS3url, + String memberOriginS3url, Long senderId, String senderNickname, + String senderGeneratedS3url, + String senderOriginS3url, + ChatRoom chatRoom, String message ) { - public static ChatRoomOpenResponse of(Long senderId, String senderNickname, String message) { + public static ChatRoomOpenResponse of(Member member, Member sender, ChatRoom chatRoom, String message) { return new ChatRoomOpenResponse( - senderId, - senderNickname, + member.getId(), + member.getBasicInfo().getNickname(), + member.getFaceInfo().getGeneratedS3url(), + member.getFaceInfo().getOriginS3url(), + sender.getId(), + sender.getBasicInfo().getNickname(), + sender.getFaceInfo().getGeneratedS3url(), + sender.getFaceInfo().getOriginS3url(), + chatRoom, message ); } From c065f35cf9f936c51cbd9e5ebdc67aa39e603048 Mon Sep 17 00:00:00 2001 From: imjanghyeok Date: Fri, 10 May 2024 13:38:53 +0900 Subject: [PATCH 188/265] =?UTF-8?q?Refactor:=20set=20=EC=83=81=ED=83=9C?= =?UTF-8?q?=EC=9D=98=20=EC=B1=84=ED=8C=85=EB=B0=A9=20=EB=A6=AC=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20Dto=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/chatroom/ChatRoomHeartResponse.java | 31 +++++++++++++------ 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/src/main/java/capstone/facefriend/chat/service/dto/chatroom/ChatRoomHeartResponse.java b/src/main/java/capstone/facefriend/chat/service/dto/chatroom/ChatRoomHeartResponse.java index d3a9457690..341fc1f584 100644 --- a/src/main/java/capstone/facefriend/chat/service/dto/chatroom/ChatRoomHeartResponse.java +++ b/src/main/java/capstone/facefriend/chat/service/dto/chatroom/ChatRoomHeartResponse.java @@ -1,22 +1,33 @@ package capstone.facefriend.chat.service.dto.chatroom; import capstone.facefriend.chat.domain.ChatRoom; -import capstone.facefriend.chat.domain.ChatRoomMember; -import capstone.facefriend.chat.service.dto.heart.GetSendHeartResponse; +import capstone.facefriend.member.domain.member.Member; public record ChatRoomHeartResponse ( - Long sender, - Long receiver, + Long memberId, + String memberNickname, + String memberGeneratedS3url, + String memberOriginS3url, + Long senderId, + String senderNickname, + String senderGeneratedS3url, + String senderOriginS3url, ChatRoom chatRoom, - GetSendHeartResponse sendHeart + Boolean isSender ) { - public static ChatRoomHeartResponse of(ChatRoomMember chatRoomMember, GetSendHeartResponse sendHeartResponse) { + public static ChatRoomHeartResponse of(Member member, Member sender, ChatRoom chatRoom, Boolean isSender) { return new ChatRoomHeartResponse( - chatRoomMember.getSender().getId(), - chatRoomMember.getReceiver().getId(), - chatRoomMember.getChatRoom(), - sendHeartResponse + member.getId(), + member.getBasicInfo().getNickname(), + member.getFaceInfo().getGeneratedS3url(), + member.getFaceInfo().getOriginS3url(), + sender.getId(), + sender.getBasicInfo().getNickname(), + sender.getFaceInfo().getGeneratedS3url(), + sender.getFaceInfo().getOriginS3url(), + chatRoom, + isSender ); } } \ No newline at end of file From 7c86edfb81b4b1441a7453d771cb519b7286a2b8 Mon Sep 17 00:00:00 2001 From: imjanghyeok Date: Fri, 10 May 2024 13:39:04 +0900 Subject: [PATCH 189/265] =?UTF-8?q?Refactor:=20progress=20=EC=83=81?= =?UTF-8?q?=ED=83=9C=EC=9D=98=20=EC=B1=84=ED=8C=85=EB=B0=A9=20=EB=A6=AC?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20Dto=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/chatroom/ChatRoomMessageResponse.java | 40 ++++++++++--------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/src/main/java/capstone/facefriend/chat/service/dto/chatroom/ChatRoomMessageResponse.java b/src/main/java/capstone/facefriend/chat/service/dto/chatroom/ChatRoomMessageResponse.java index 9d037bd3b4..cd4287dd5d 100644 --- a/src/main/java/capstone/facefriend/chat/service/dto/chatroom/ChatRoomMessageResponse.java +++ b/src/main/java/capstone/facefriend/chat/service/dto/chatroom/ChatRoomMessageResponse.java @@ -2,31 +2,33 @@ import capstone.facefriend.chat.domain.ChatMessage; import capstone.facefriend.chat.domain.ChatRoom; -import capstone.facefriend.chat.domain.ChatRoomMember; - -import java.time.LocalDateTime; +import capstone.facefriend.member.domain.member.Member; public record ChatRoomMessageResponse( - Long sender, - Long receiver, - ChatRoom chatRoom, - String content, - LocalDateTime sendTime, + Long memberId, + String memberNickname, + String memberGeneratedFaceS3url, + String memberOriginFaceS3url, + Long senderId, String senderNickname, - String senderGeneratedFaceInfo, - String senderOriginalFaceInfo + String senderGeneratedFaceS3url, + String senderOriginFaceS3url, + ChatRoom chatRoom, + String content ){ - public static ChatRoomMessageResponse of(ChatRoomMember chatRoomMember, ChatMessage message) { + public static ChatRoomMessageResponse of(Member member, Member sender, ChatRoom chatRoom, ChatMessage message) { return new ChatRoomMessageResponse( - chatRoomMember.getSender().getId(), - chatRoomMember.getReceiver().getId(), - chatRoomMember.getChatRoom(), - message.getContent(), - message.getSendTime(), - message.getSender().getBasicInfo().getNickname(), - message.getSender().getFaceInfo().getGeneratedS3url(), - message.getSender().getFaceInfo().getOriginS3url() + member.getId(), + member.getBasicInfo().getNickname(), + member.getFaceInfo().getGeneratedS3url(), + member.getFaceInfo().getOriginS3url(), + sender.getId(), + sender.getBasicInfo().getNickname(), + sender.getFaceInfo().getGeneratedS3url(), + sender.getFaceInfo().getOriginS3url(), + chatRoom, + message.getContent() ); } } \ No newline at end of file From d745e9bcc95a078c67661df9daf88949037e7a94 Mon Sep 17 00:00:00 2001 From: imjanghyeok Date: Fri, 10 May 2024 13:39:37 +0900 Subject: [PATCH 190/265] =?UTF-8?q?Feat:=20Close=20=EC=83=81=ED=83=9C?= =?UTF-8?q?=EC=9D=98=20=EC=B1=84=ED=8C=85=EB=B0=A9=20=EB=A6=AC=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20Dto=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../chat/service/ChatRoomService.java | 2 -- .../dto/chatroom/ChatRoomCloseResponse.java | 24 +++++++++++++++++++ 2 files changed, 24 insertions(+), 2 deletions(-) create mode 100644 src/main/java/capstone/facefriend/chat/service/dto/chatroom/ChatRoomCloseResponse.java diff --git a/src/main/java/capstone/facefriend/chat/service/ChatRoomService.java b/src/main/java/capstone/facefriend/chat/service/ChatRoomService.java index 90293597bd..43f7626e21 100644 --- a/src/main/java/capstone/facefriend/chat/service/ChatRoomService.java +++ b/src/main/java/capstone/facefriend/chat/service/ChatRoomService.java @@ -75,8 +75,6 @@ private List findChatRoomMessageByChatRoomId(Long roomId) { return chatMessageRepository.findChatMessagesByChatRoomId(roomId); } - - @Transactional public Map getChatRoomList(Long memberId) { diff --git a/src/main/java/capstone/facefriend/chat/service/dto/chatroom/ChatRoomCloseResponse.java b/src/main/java/capstone/facefriend/chat/service/dto/chatroom/ChatRoomCloseResponse.java new file mode 100644 index 0000000000..3051a5d98e --- /dev/null +++ b/src/main/java/capstone/facefriend/chat/service/dto/chatroom/ChatRoomCloseResponse.java @@ -0,0 +1,24 @@ +package capstone.facefriend.chat.service.dto.chatroom; + +import capstone.facefriend.chat.domain.ChatRoom; +import capstone.facefriend.member.domain.member.Member; + +public record ChatRoomCloseResponse( + Long memberId, + String memberNickname, + String memberGeneratedS3url, + String memberOriginS3url, + ChatRoom chatRoom, + String message +) { + public static ChatRoomCloseResponse of(Member member, ChatRoom chatRoom, String message) { + return new ChatRoomCloseResponse( + member.getId(), + member.getBasicInfo().getNickname(), + member.getFaceInfo().getGeneratedS3url(), + member.getFaceInfo().getOriginS3url(), + chatRoom, + message + ); + } +} \ No newline at end of file From 554c78acfffae7bcf0bc509d82c204e62cd427bc Mon Sep 17 00:00:00 2001 From: imjanghyeok Date: Fri, 10 May 2024 13:39:49 +0900 Subject: [PATCH 191/265] =?UTF-8?q?Feat:=20=EC=B1=84=ED=8C=85=EB=B0=A9=20?= =?UTF-8?q?=EC=9E=85=EC=9E=A5=20=EC=A0=95=EB=B3=B4=20=EC=A0=80=EC=9E=A5=20?= =?UTF-8?q?Dto=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/chatroom/ChatRoomEnterResponse.java | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 src/main/java/capstone/facefriend/chat/service/dto/chatroom/ChatRoomEnterResponse.java diff --git a/src/main/java/capstone/facefriend/chat/service/dto/chatroom/ChatRoomEnterResponse.java b/src/main/java/capstone/facefriend/chat/service/dto/chatroom/ChatRoomEnterResponse.java new file mode 100644 index 0000000000..8b64273412 --- /dev/null +++ b/src/main/java/capstone/facefriend/chat/service/dto/chatroom/ChatRoomEnterResponse.java @@ -0,0 +1,19 @@ +package capstone.facefriend.chat.service.dto.chatroom; + +import capstone.facefriend.chat.domain.ChatRoomInfo; + +import java.time.LocalDateTime; + +public record ChatRoomEnterResponse( + Long roomId, + Long memberId, + LocalDateTime enterTime +) { + public static ChatRoomEnterResponse of(Long roomId, Long memberId, ChatRoomInfo chatRoomInfo) { + return new ChatRoomEnterResponse( + roomId, + memberId, + chatRoomInfo.getEnterTime() + ); + } +} \ No newline at end of file From d5c6b75c9fc97ae0d8f297b1f280b0506f235b5a Mon Sep 17 00:00:00 2001 From: imjanghyeok Date: Fri, 10 May 2024 13:39:57 +0900 Subject: [PATCH 192/265] =?UTF-8?q?Feat:=20=EC=B1=84=ED=8C=85=EB=B0=A9=20?= =?UTF-8?q?=ED=87=B4=EC=9E=A5=20=EC=A0=95=EB=B3=B4=20Dto=20=EC=9E=91?= =?UTF-8?q?=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/chatroom/ChatRoomExitResponse.java | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 src/main/java/capstone/facefriend/chat/service/dto/chatroom/ChatRoomExitResponse.java diff --git a/src/main/java/capstone/facefriend/chat/service/dto/chatroom/ChatRoomExitResponse.java b/src/main/java/capstone/facefriend/chat/service/dto/chatroom/ChatRoomExitResponse.java new file mode 100644 index 0000000000..6884b1da1d --- /dev/null +++ b/src/main/java/capstone/facefriend/chat/service/dto/chatroom/ChatRoomExitResponse.java @@ -0,0 +1,19 @@ +package capstone.facefriend.chat.service.dto.chatroom; + +import capstone.facefriend.chat.domain.ChatRoomInfo; + +import java.time.LocalDateTime; + +public record ChatRoomExitResponse( + Long roomId, + Long memberId, + LocalDateTime enterTime +) { + public static ChatRoomExitResponse of(Long roomId, Long memberId, LocalDateTime exitTime) { + return new ChatRoomExitResponse( + roomId, + memberId, + exitTime + ); + } +} \ No newline at end of file From 36898b415569d1ab4349da45020e1820a527ec2e Mon Sep 17 00:00:00 2001 From: imjanghyeok Date: Fri, 10 May 2024 13:41:01 +0900 Subject: [PATCH 193/265] =?UTF-8?q?Feat:=20=EC=B1=84=ED=8C=85=EB=B0=A9=20?= =?UTF-8?q?=EC=9E=85=EC=9E=A5=20=EC=A0=95=EB=B3=B4,=20=EC=B1=84=ED=8C=85?= =?UTF-8?q?=EB=B0=A9=20=ED=87=B4=EC=9E=A5=20=EC=A0=95=EB=B3=B4,=20?= =?UTF-8?q?=EC=B1=84=ED=8C=85=EB=B0=A9=20=EB=82=98=EA=B0=80=EA=B8=B0=20api?= =?UTF-8?q?=20Mapping?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../chat/controller/ChatRoomController.java | 31 +++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/src/main/java/capstone/facefriend/chat/controller/ChatRoomController.java b/src/main/java/capstone/facefriend/chat/controller/ChatRoomController.java index 3ccfb34806..6f9d82155a 100644 --- a/src/main/java/capstone/facefriend/chat/controller/ChatRoomController.java +++ b/src/main/java/capstone/facefriend/chat/controller/ChatRoomController.java @@ -2,11 +2,12 @@ import capstone.facefriend.auth.controller.support.AuthMember; import capstone.facefriend.chat.service.ChatRoomService; +import capstone.facefriend.chat.service.dto.chatroom.ChatRoomEnterResponse; +import capstone.facefriend.chat.service.dto.chatroom.ChatRoomExitResponse; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; import java.util.Map; @@ -22,4 +23,30 @@ ResponseEntity> getChatRoomList( ) { return ResponseEntity.ok(chatRoomService.getChatRoomList(memberId)); } + + @PostMapping("/room/{roomId}/enter") + public ResponseEntity enterChatRoom( + @PathVariable("roomId") Long roomId, + @AuthMember Long memberId, + @RequestParam(required = false, defaultValue = "0", value = "page") int pageNo + ){ + return ResponseEntity.ok(chatRoomService.enterRoom(roomId, memberId)); + } + + @PostMapping("/room/{roomId}/exit") + public ResponseEntity exitChatRoom( + @PathVariable("roomId") Long roomId, + @AuthMember Long memberId + ){ + return ResponseEntity.ok(chatRoomService.exitRoom(roomId, memberId)); + } + + @PostMapping("/room/{roomId}/left") + public ResponseEntity leftChatRoom( + @PathVariable("roomId") Long roomId, + @AuthMember Long memberId + ){ + return ResponseEntity.ok(chatRoomService.leftRoom(roomId, memberId)); + } + } \ No newline at end of file From 9090dcbbdb27b9acfb4610121bcb09d69f3d8f30 Mon Sep 17 00:00:00 2001 From: imjanghyeok Date: Fri, 10 May 2024 13:42:57 +0900 Subject: [PATCH 194/265] =?UTF-8?q?Refactor:=20=EC=9E=91=EC=84=B1=ED=95=9C?= =?UTF-8?q?=20=EC=84=9C=EB=B9=84=EC=8A=A4=20=EC=BD=94=EB=93=9C=EC=97=90=20?= =?UTF-8?q?=EB=A7=9E=EA=B2=8C=20repository=20=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../chat/repository/ChatRoomMemberRepository.java | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/main/java/capstone/facefriend/chat/repository/ChatRoomMemberRepository.java b/src/main/java/capstone/facefriend/chat/repository/ChatRoomMemberRepository.java index 7cb178a2ad..a1a738303a 100644 --- a/src/main/java/capstone/facefriend/chat/repository/ChatRoomMemberRepository.java +++ b/src/main/java/capstone/facefriend/chat/repository/ChatRoomMemberRepository.java @@ -1,23 +1,18 @@ package capstone.facefriend.chat.repository; -import capstone.facefriend.chat.domain.ChatRoom; import capstone.facefriend.chat.domain.ChatRoomMember; -import io.lettuce.core.dynamic.annotation.Param; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; import java.util.List; import java.util.Optional; public interface ChatRoomMemberRepository extends JpaRepository { - Optional findByChatRoom(ChatRoom chatRoom); - + Optional findByChatRoomId(Long roomId); @Query("SELECT c FROM ChatRoomMember c WHERE c.sender.id = :senderId AND c.receiver.id = :receiverId") Optional findBySenderAndReceiver(@Param("senderId") Long senderId, @Param("receiverId") Long receiverId); - - // Optional findChatRoomMemberBySenderAAndReceiver(Member sender, Member receiver); ChatRoomMember save(ChatRoomMember chatRoomMember); - Optional> findAllBySenderId(Long senderId); Optional> findAllByReceiverId(Long senderId); } \ No newline at end of file From d2fd15bbd2304d5b5a5f83943fcac6ddead157f0 Mon Sep 17 00:00:00 2001 From: imjanghyeok Date: Fri, 10 May 2024 13:44:15 +0900 Subject: [PATCH 195/265] =?UTF-8?q?Refactor:=20=EC=9E=91=EC=84=B1=ED=95=9C?= =?UTF-8?q?=20=EC=84=9C=EB=B9=84=EC=8A=A4=20=EC=BD=94=EB=93=9C=EC=97=90=20?= =?UTF-8?q?=EB=A7=9E=EA=B2=8C=20ChatMessageRepository=20=EC=BD=94=EB=93=9C?= =?UTF-8?q?=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../facefriend/chat/repository/ChatMessageRepository.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/main/java/capstone/facefriend/chat/repository/ChatMessageRepository.java b/src/main/java/capstone/facefriend/chat/repository/ChatMessageRepository.java index f6a20281f5..2cc6c36a37 100644 --- a/src/main/java/capstone/facefriend/chat/repository/ChatMessageRepository.java +++ b/src/main/java/capstone/facefriend/chat/repository/ChatMessageRepository.java @@ -1,11 +1,15 @@ package capstone.facefriend.chat.repository; import capstone.facefriend.chat.domain.ChatMessage; +import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; +import java.time.LocalDateTime; +import java.util.List; + public interface ChatMessageRepository extends JpaRepository { - ChatMessage findFirstByChatRoomIdOrderBySendTimeDesc(Long roomId); ChatMessage save(ChatMessage chatMessage); - + List findChatMessagesByChatRoom_IdAndSendTimeBefore(Long roomId, LocalDateTime time, Pageable pageable); + List findChatMessagesByChatRoomId(Long roomId); } \ No newline at end of file From 30da64a8a50a065b486413012d10583fb38e0a11 Mon Sep 17 00:00:00 2001 From: imjanghyeok Date: Fri, 10 May 2024 13:50:15 +0900 Subject: [PATCH 196/265] =?UTF-8?q?Feat:=20=EC=BD=94=EB=93=9C=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=20=EB=B0=8F=20=EC=95=A0=ED=94=8C=EB=A6=AC=EC=BC=80?= =?UTF-8?q?=EC=9D=B4=EC=85=98=20=EC=9E=84=EC=9E=A5=20=EC=A0=95=EB=B3=B4=20?= =?UTF-8?q?=EC=A0=80=EC=9E=A5=20=EB=B0=8F=20=EC=95=B1=EC=9D=B4=20=EA=BA=BC?= =?UTF-8?q?=EC=A1=8C=EC=9D=84=20=EB=95=8C=20=EC=A0=84=EB=8B=AC=EB=90=9C=20?= =?UTF-8?q?=EB=A9=94=EC=8B=9C=EC=A7=80=20=EC=A0=84=EB=8B=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../chat/service/MessageService.java | 101 +++++++++++++++--- 1 file changed, 85 insertions(+), 16 deletions(-) diff --git a/src/main/java/capstone/facefriend/chat/service/MessageService.java b/src/main/java/capstone/facefriend/chat/service/MessageService.java index 831f589008..03d7d6a745 100644 --- a/src/main/java/capstone/facefriend/chat/service/MessageService.java +++ b/src/main/java/capstone/facefriend/chat/service/MessageService.java @@ -1,15 +1,12 @@ package capstone.facefriend.chat.service; -import capstone.facefriend.chat.domain.ChatMessage; -import capstone.facefriend.chat.domain.ChatRoom; -import capstone.facefriend.chat.domain.ChatRoomMember; +import capstone.facefriend.chat.domain.*; import capstone.facefriend.chat.exception.ChatException; import capstone.facefriend.chat.exception.ChatExceptionType; -import capstone.facefriend.chat.repository.ChatMessageRepository; -import capstone.facefriend.chat.repository.ChatRoomMemberRepository; -import capstone.facefriend.chat.repository.ChatRoomRepository; +import capstone.facefriend.chat.repository.*; import capstone.facefriend.chat.service.dto.heart.HeartReplyRequest; import capstone.facefriend.chat.service.dto.heart.SendHeartResponse; +import capstone.facefriend.chat.service.dto.message.MessageListResponse; import capstone.facefriend.chat.service.dto.message.MessageRequest; import capstone.facefriend.chat.service.dto.message.MessageResponse; import capstone.facefriend.member.domain.member.Member; @@ -18,6 +15,9 @@ import capstone.facefriend.member.exception.member.MemberExceptionType; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.listener.ChannelTopic; import org.springframework.messaging.simp.SimpMessagingTemplate; @@ -25,19 +25,23 @@ import org.springframework.transaction.annotation.Transactional; import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; @Service @Slf4j @RequiredArgsConstructor public class MessageService { - private final ChatMessageRepository chatMessageRepository; private final ChatRoomMemberRepository chatRoomMemberRepository; private final MemberRepository memberRepository; private final ChatRoomRepository chatRoomRepository; - private final RedisTemplate redisTemplate; + private final RedisTemplate redisTemplate; private final ChannelTopic channelTopic; - private final SimpMessagingTemplate messagingTemplate; + private final SocketInfoRedisRepository socketInfoRedisRepository; + private final SimpMessagingTemplate simpMessagingTemplate; + private final ChatRoomInfoRedisRepository chatRoomInfoRedisRepository; private Member findMemberById(Long memberId) { Member member = memberRepository.findById(memberId) @@ -51,22 +55,27 @@ private ChatRoom findRoomById(Long roomId) { return chatRoom; } - private ChatRoomMember findSenderReceiver(Long senderId, Long receiveId) { + private ChatRoomMember findSenderReceiver(Long senderId, Long receiveId) { ChatRoomMember chatRoomMember = chatRoomMemberRepository.findBySenderAndReceiver(senderId, receiveId) .orElseThrow(()-> new ChatException(ChatExceptionType.NOT_FOUND)); return chatRoomMember; } + private ChatRoomMember findChatRoomMemberByChatRoomId(Long roomId) { + ChatRoomMember chatRoomMember = chatRoomMemberRepository.findByChatRoomId(roomId) + .orElseThrow(()-> new ChatException(ChatExceptionType.NOT_FOUND)); + return chatRoomMember; + } @Transactional public void sendMessage(MessageRequest messageRequest, Long senderId) { Member sender = findMemberById(senderId); Member receiver = findMemberById(messageRequest.getReceiveId()); - ChatRoomMember chatRoomMember = findSenderReceiver(sender.getId(), receiver.getId()); ChatRoom chatRoom = findRoomById(messageRequest.getRoomId()); if (chatRoom.getStatus() == ChatRoom.Status.open) { chatRoom.setStatus(ChatRoom.Status.progress); + ChatRoomMember chatRoomMember = findChatRoomMemberByChatRoomId(chatRoom.getId()); chatRoomRepository.save(chatRoom); chatRoomMemberRepository.save(chatRoomMember); } else if ((chatRoom.getStatus() == ChatRoom.Status.close)) { @@ -93,6 +102,7 @@ public void sendMessage(MessageRequest messageRequest, Long senderId) { messageResponse.setSenderId(senderId); messageResponse.setReceiveId(receiver.getId()); messageResponse.setSenderNickname(sender.getBasicInfo().getNickname()); + messageResponse.setSenderFaceInfoS3Url(sender.getFaceInfo().getOriginS3url()); messageResponse.setContent(chatMessage.getContent()); messageResponse.setType("message"); messageResponse.setCreatedAt(chatMessage.getSendTime()); @@ -105,8 +115,6 @@ public void sendMessage(MessageRequest messageRequest, Long senderId) { } @Transactional public void sendHeart(Long senderId, Long receiveId) { - - ChatRoom chatRoom = ChatRoom.builder() .status(ChatRoom.Status.set) .isPublic(false) @@ -125,8 +133,8 @@ public void sendHeart(Long senderId, Long receiveId) { .chatRoom(chatRoom) .sender(sender) .receiver(receiver) - .isSenderExist(false) - .isReceiverExist(false) + .isSenderExist(true) + .isReceiverExist(true) .isSenderPublic(false) .isReceiverPublic(false) .build(); @@ -172,6 +180,67 @@ public void heartReply(HeartReplyRequest heartReplyRequest, Long receiveId) { String destination = "/sub/chat/" + sender.getId(); // 메시지 전송 - messagingTemplate.convertAndSend(destination, message); + simpMessagingTemplate.convertAndSend(destination, message); + } + + + @Transactional + public void enterApplication(Long memberId) { + SocketInfo socketInfo = new SocketInfo(); + socketInfo.setMemberId(memberId); + socketInfo.setConnectTime(LocalDateTime.now()); + socketInfoRedisRepository.save(socketInfo); + if (isExistUnReadMessage(memberId)) { + sendSentMessage(memberId); + } + + if(isExistUnSendHeart(memberId)) { + sendSentHeart(memberId); + } + } + + private Boolean isExistUnReadMessage(Long memberId) { + Boolean isUnRead = redisTemplate.hasKey("/sub/chat/" + memberId + "message"); + log.info(isUnRead.toString()); + return !isUnRead; + } + + private Boolean isExistUnSendHeart(Long memberId) { + Boolean isUnRead = redisTemplate.hasKey("/sub/chat/" + memberId + "SendHeart"); + log.info(isUnRead.toString()); + return !isUnRead; + } + + private void sendSentMessage(Long receiveId) { + String topic = channelTopic.getTopic(); + String destination = "/sub/chat" + receiveId + "message"; + Long messagesListSize = redisTemplate.opsForList().size(destination); + log.info(messagesListSize.toString()); + log.info("messageList: {}", redisTemplate.opsForList().range(destination, 0, -1)); + + if (messagesListSize > 0) { + for (Long i = messagesListSize; i > 0; i--) { + // 맵으로 받음 + LinkedHashMap map = (LinkedHashMap) redisTemplate.opsForList().rightPop(destination); + MessageResponse messageResponse = (MessageResponse) map.get(destination); + log.info("messageResponse: {}", messageResponse.toString()); + redisTemplate.convertAndSend(topic, messageResponse); + } + } + } + + private void sendSentHeart(Long receiveId) { + String topic = channelTopic.getTopic(); + String destination = "/sub/chat" + receiveId + "heart"; + Long messagesListSize = redisTemplate.opsForList().size(destination); + log.info("SendHeartListSize: {}", messagesListSize.toString()); + if (messagesListSize > 0) { + for (Long i = messagesListSize; i > 0; i--) { + LinkedHashMap map = (LinkedHashMap) redisTemplate.opsForList().rightPop(destination); + SendHeartResponse sendHeartResponse = (SendHeartResponse) map.get(destination); + log.info("messageResponse: {}", sendHeartResponse.toString()); + redisTemplate.convertAndSend(topic, sendHeartResponse); + } + } } } \ No newline at end of file From fec0d7897732c07e10a2497f6134e5b9315ceab2 Mon Sep 17 00:00:00 2001 From: imjanghyeok Date: Fri, 10 May 2024 13:51:30 +0900 Subject: [PATCH 197/265] =?UTF-8?q?Feat:=20=EB=A9=94=EC=8B=9C=EC=A7=80=20?= =?UTF-8?q?=EC=A0=84=EB=8B=AC=EC=8B=9C,=20=EC=95=B1=EC=9D=B4=20=EC=BC=9C?= =?UTF-8?q?=EC=A0=B8=EC=9E=88=EB=8A=94=20=EC=A7=80=20=ED=99=95=EC=9D=B8?= =?UTF-8?q?=ED=95=98=EA=B3=A0=20=EC=83=81=EB=8C=80=EB=B0=A9=EC=9D=98=20?= =?UTF-8?q?=EC=86=8C=EC=BC=93=EC=9D=B4=20=EC=97=B0=EA=B2=B0=EB=90=98?= =?UTF-8?q?=EC=96=B4=20=EC=9E=88=EC=A7=80=20=EC=95=8A=EC=9C=BC=EB=A9=B4=20?= =?UTF-8?q?redis=EC=97=90=20=EC=A0=80=EC=9E=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../chat/service/RedisSubscriber.java | 56 +++++++++++++++++-- 1 file changed, 50 insertions(+), 6 deletions(-) diff --git a/src/main/java/capstone/facefriend/chat/service/RedisSubscriber.java b/src/main/java/capstone/facefriend/chat/service/RedisSubscriber.java index 6b9de1fcb4..af3b1cf2aa 100644 --- a/src/main/java/capstone/facefriend/chat/service/RedisSubscriber.java +++ b/src/main/java/capstone/facefriend/chat/service/RedisSubscriber.java @@ -1,19 +1,23 @@ package capstone.facefriend.chat.service; -import capstone.facefriend.chat.service.dto.message.GetMessageResponse; +import capstone.facefriend.chat.domain.SocketInfo; import capstone.facefriend.chat.service.dto.heart.GetSendHeartResponse; -import capstone.facefriend.chat.service.dto.message.MessageResponse; import capstone.facefriend.chat.service.dto.heart.SendHeartResponse; +import capstone.facefriend.chat.service.dto.message.GetMessageResponse; +import capstone.facefriend.chat.service.dto.message.MessageResponse; import com.fasterxml.jackson.databind.ObjectMapper; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.data.redis.connection.Message; import org.springframework.data.redis.connection.MessageListener; +import org.springframework.data.redis.core.ListOperations; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.messaging.simp.SimpMessageSendingOperations; import org.springframework.stereotype.Service; import java.io.IOException; +import java.util.List; +import java.util.Objects; @Slf4j @RequiredArgsConstructor @@ -21,7 +25,7 @@ public class RedisSubscriber implements MessageListener { private final ObjectMapper objectMapper; - private final RedisTemplate redisTemplate; + private final RedisTemplate redisTemplate; private final SimpMessageSendingOperations messagingTemplate; @Override @@ -32,21 +36,61 @@ public void onMessage(Message message, byte[] pattern) { String publishMessage = (String) redisTemplate.getStringSerializer().deserialize(message.getBody()); if (publishMessage.contains("message")) { + log.info(publishMessage); MessageResponse messageResponse = objectMapper.readValue(publishMessage, MessageResponse.class); - + log.info(messageResponse.toString()); GetMessageResponse chatMessageResponse = new GetMessageResponse(messageResponse); + log.info(chatMessageResponse.toString()); + if (isExistSubscriber(messageResponse.getReceiveId())) { + messagingTemplate.convertAndSend("/sub/chat/" + messageResponse.getReceiveId(), chatMessageResponse); + } else { + saveUnReadMessage("/sub/chat" + messageResponse.getReceiveId() + "message", messageResponse); + } - messagingTemplate.convertAndSend("/sub/chat/" + messageResponse.getReceiveId(), chatMessageResponse); } else if (publishMessage.contains("Heart")) { SendHeartResponse sendHeartResponse = objectMapper.readValue(publishMessage, SendHeartResponse.class); GetSendHeartResponse chatSendHeartResponse = new GetSendHeartResponse(sendHeartResponse); + if (isExistSubscriber(chatSendHeartResponse.getReceiveId())) { + messagingTemplate.convertAndSend("/sub/chat/" + sendHeartResponse.getReceiveId(), chatSendHeartResponse); + } else { + saveUnReadHeart("/sub/chat" + sendHeartResponse.getReceiveId() + "heart", sendHeartResponse); + } - messagingTemplate.convertAndSend("/sub/chat/" + sendHeartResponse.getReceiveId(), chatSendHeartResponse); } } catch (IOException e) { throw new RuntimeException("Failed to process message", e); } } + + private Boolean isExistSubscriber(Long memberId) { + log.info("isExistSubscriber 호출"); + Boolean isMember = redisTemplate.opsForSet().isMember("SocketInfo", memberId); + log.info("Socket: " + memberId); + log.info("SocketInfo: " + isMember); + + return isMember; + } + + + + private void saveUnReadMessage(String destination, MessageResponse messageResponse) { + Boolean isUnRead = redisTemplate.hasKey(destination); + log.info(isUnRead.toString()); + if (isUnRead) { + redisTemplate.opsForList().rightPush(destination, messageResponse); + } else { + redisTemplate.opsForList().rightPush(destination, messageResponse); + } + } + + private void saveUnReadHeart(String destination, SendHeartResponse sendHeartResponse) { + Boolean isUnRead = redisTemplate.hasKey(destination); + if (isUnRead) { + redisTemplate.opsForList().rightPush(destination, sendHeartResponse); + } else { + redisTemplate.opsForList().rightPush(destination, sendHeartResponse); + } + } } \ No newline at end of file From c9d74e09e6b6a02d6c386ff5f384511f03736b09 Mon Sep 17 00:00:00 2001 From: imjanghyeok Date: Fri, 10 May 2024 13:53:09 +0900 Subject: [PATCH 198/265] =?UTF-8?q?Feat:=20=EC=95=B1=EC=9D=84=20=EA=BB=90?= =?UTF-8?q?=EC=9D=84=20=EB=95=8C=20redis=EC=97=90=20=EC=A0=80=EC=9E=A5?= =?UTF-8?q?=EB=90=9C=20=EC=A0=95=EB=B3=B4=20=EC=A0=9C=EA=B1=B0=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5=20service=20=EC=BD=94=EB=93=9C=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../facefriend/chat/service/MessageService.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/main/java/capstone/facefriend/chat/service/MessageService.java b/src/main/java/capstone/facefriend/chat/service/MessageService.java index 03d7d6a745..b2b5c58788 100644 --- a/src/main/java/capstone/facefriend/chat/service/MessageService.java +++ b/src/main/java/capstone/facefriend/chat/service/MessageService.java @@ -67,6 +67,12 @@ private ChatRoomMember findChatRoomMemberByChatRoomId(Long roomId) { return chatRoomMember; } + private SocketInfo findSocketInfo(Long memberId) { + SocketInfo socketInfo = socketInfoRedisRepository.findById(memberId) + .orElseThrow(()-> new ChatException(ChatExceptionType.NOT_FOUND)); + return socketInfo; + } + @Transactional public void sendMessage(MessageRequest messageRequest, Long senderId) { Member sender = findMemberById(senderId); @@ -199,6 +205,13 @@ public void enterApplication(Long memberId) { } } + @Transactional + public String exitApplication(Long memberId) { + SocketInfo socketInfo = findSocketInfo(memberId); + socketInfoRedisRepository.delete(socketInfo); + return "성공"; + } + private Boolean isExistUnReadMessage(Long memberId) { Boolean isUnRead = redisTemplate.hasKey("/sub/chat/" + memberId + "message"); log.info(isUnRead.toString()); From 518e903eecad265edbf8de6c842b361838a1f738 Mon Sep 17 00:00:00 2001 From: imjanghyeok Date: Fri, 10 May 2024 13:55:31 +0900 Subject: [PATCH 199/265] =?UTF-8?q?Feat:=20=EC=B1=84=ED=8C=85=EB=B0=A9?= =?UTF-8?q?=EC=97=90=EC=84=9C=20=EB=82=98=EB=88=88=20=EB=A9=94=EC=8B=9C?= =?UTF-8?q?=EC=A7=80=20=EA=B8=B0=EB=A1=9D=EC=9D=84=20=EB=B3=B4=EB=8A=94=20?= =?UTF-8?q?service=20=EC=BD=94=EB=93=9C=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../chat/service/MessageService.java | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/main/java/capstone/facefriend/chat/service/MessageService.java b/src/main/java/capstone/facefriend/chat/service/MessageService.java index b2b5c58788..25abd5de20 100644 --- a/src/main/java/capstone/facefriend/chat/service/MessageService.java +++ b/src/main/java/capstone/facefriend/chat/service/MessageService.java @@ -73,6 +73,12 @@ private SocketInfo findSocketInfo(Long memberId) { return socketInfo; } + private ChatRoomInfo findChatRoomInfo(String chatRoomInfoId) { + ChatRoomInfo chatRoomInfo = chatRoomInfoRedisRepository.findById(chatRoomInfoId) + .orElseThrow(()-> new ChatException(ChatExceptionType.NOT_FOUND)); + return chatRoomInfo; + } + @Transactional public void sendMessage(MessageRequest messageRequest, Long senderId) { Member sender = findMemberById(senderId); @@ -212,6 +218,23 @@ public String exitApplication(Long memberId) { return "성공"; } + public List getMessagePage(Long roomId, Long memberId, int pageNo) { + pageNo = pageNo - 1; + int pageSize = 20; + Sort sort = Sort.by(Sort.Direction.DESC, "sendTime"); + Pageable pageable = PageRequest.of(pageNo, pageSize, sort); + String chatRoomInfoId = roomId + "/member/" + memberId; + ChatRoomInfo chatRoomInfo = findChatRoomInfo(chatRoomInfoId); + LocalDateTime time = chatRoomInfo.getEnterTime(); + List chatMessagePage = chatMessageRepository.findChatMessagesByChatRoom_IdAndSendTimeBefore(roomId, time, pageable); + List messageListResponses = new ArrayList<>(); + for (ChatMessage chatMessage : chatMessagePage){ + MessageListResponse messageListResponse = MessageListResponse.of(chatMessage); + messageListResponses.add(messageListResponse); + } + return messageListResponses; + } + private Boolean isExistUnReadMessage(Long memberId) { Boolean isUnRead = redisTemplate.hasKey("/sub/chat/" + memberId + "message"); log.info(isUnRead.toString()); From 4d4a3f9735107c34ee87c0588a8d4ad89e9006e5 Mon Sep 17 00:00:00 2001 From: imjanghyeok Date: Fri, 10 May 2024 13:56:13 +0900 Subject: [PATCH 200/265] =?UTF-8?q?Feat:=20=EC=B1=84=ED=8C=85=EB=B0=A9?= =?UTF-8?q?=EC=97=90=EC=84=9C=20=EB=82=98=EB=88=88=20=EB=A9=94=EC=8B=9C?= =?UTF-8?q?=EC=A7=80=20=EA=B8=B0=EB=A1=9D=EC=9D=B4=20=EC=A4=91=EB=B3=B5?= =?UTF-8?q?=EB=90=98=EC=A7=80=20=EC=95=8A=EB=8F=84=EB=A1=9D=20repository?= =?UTF-8?q?=20=EC=BD=94=EB=93=9C=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../facefriend/chat/repository/ChatMessageRepository.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/capstone/facefriend/chat/repository/ChatMessageRepository.java b/src/main/java/capstone/facefriend/chat/repository/ChatMessageRepository.java index 2cc6c36a37..ea4b3b3ddf 100644 --- a/src/main/java/capstone/facefriend/chat/repository/ChatMessageRepository.java +++ b/src/main/java/capstone/facefriend/chat/repository/ChatMessageRepository.java @@ -9,6 +9,7 @@ public interface ChatMessageRepository extends JpaRepository { + ChatMessage findFirstByChatRoomIdOrderBySendTimeDesc(Long roomId); ChatMessage save(ChatMessage chatMessage); List findChatMessagesByChatRoom_IdAndSendTimeBefore(Long roomId, LocalDateTime time, Pageable pageable); List findChatMessagesByChatRoomId(Long roomId); From 55bfed2412a2da49a27b8ef0ff993db31f8dba7a Mon Sep 17 00:00:00 2001 From: imjanghyeok Date: Fri, 10 May 2024 13:57:45 +0900 Subject: [PATCH 201/265] =?UTF-8?q?Refactor:=20created=5Fat=EC=9D=84=20sen?= =?UTF-8?q?dtime=EC=9C=BC=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../chat/service/dto/heart/GetSendHeartResponse.java | 5 ++--- .../chat/service/dto/heart/SendHeartResponse.java | 1 - .../chat/service/dto/message/GetMessageResponse.java | 6 ++++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/java/capstone/facefriend/chat/service/dto/heart/GetSendHeartResponse.java b/src/main/java/capstone/facefriend/chat/service/dto/heart/GetSendHeartResponse.java index 69645c8461..28b2de256d 100644 --- a/src/main/java/capstone/facefriend/chat/service/dto/heart/GetSendHeartResponse.java +++ b/src/main/java/capstone/facefriend/chat/service/dto/heart/GetSendHeartResponse.java @@ -3,7 +3,6 @@ import com.fasterxml.jackson.annotation.JsonFormat; import lombok.Data; import lombok.NoArgsConstructor; -import lombok.Setter; import java.time.LocalDateTime; @@ -17,7 +16,7 @@ public class GetSendHeartResponse { private String type; private Long roomId; @JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss", timezone = "Asia/Seoul") - private LocalDateTime createdAt; + private LocalDateTime sendTime; public GetSendHeartResponse(SendHeartResponse sendHeartResponse) { @@ -26,6 +25,6 @@ public GetSendHeartResponse(SendHeartResponse sendHeartResponse) { this.receiveId = sendHeartResponse.getReceiveId(); this.type = sendHeartResponse.getType(); this.roomId = sendHeartResponse.getRoomId(); - this.createdAt = sendHeartResponse.getCreatedAt(); + this.sendTime = sendHeartResponse.getCreatedAt(); } } \ No newline at end of file diff --git a/src/main/java/capstone/facefriend/chat/service/dto/heart/SendHeartResponse.java b/src/main/java/capstone/facefriend/chat/service/dto/heart/SendHeartResponse.java index 438e842d61..205e578156 100644 --- a/src/main/java/capstone/facefriend/chat/service/dto/heart/SendHeartResponse.java +++ b/src/main/java/capstone/facefriend/chat/service/dto/heart/SendHeartResponse.java @@ -18,5 +18,4 @@ public class SendHeartResponse implements Serializable { private String sessionId; @JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss", timezone = "Asia/Seoul") private LocalDateTime createdAt; - } \ No newline at end of file diff --git a/src/main/java/capstone/facefriend/chat/service/dto/message/GetMessageResponse.java b/src/main/java/capstone/facefriend/chat/service/dto/message/GetMessageResponse.java index 2b80749085..e096705661 100644 --- a/src/main/java/capstone/facefriend/chat/service/dto/message/GetMessageResponse.java +++ b/src/main/java/capstone/facefriend/chat/service/dto/message/GetMessageResponse.java @@ -14,10 +14,11 @@ public class GetMessageResponse { private Long senderId; private Long receiveId; private String senderNickname; + private String senderFaceInfoS3Url; private String type; private String content; @JsonFormat(pattern = "yyyy-MM-dd hh:mm", timezone = "Asia/Seoul") - private LocalDateTime createdAt; + private LocalDateTime sendTime; private Boolean isRead; public GetMessageResponse(MessageResponse messageResponse) { @@ -25,9 +26,10 @@ public GetMessageResponse(MessageResponse messageResponse) { this.senderId = messageResponse.getSenderId(); this.receiveId = messageResponse.getReceiveId(); this.senderNickname = messageResponse.getSenderNickname(); + this.senderFaceInfoS3Url = messageResponse.getSenderFaceInfoS3Url(); this.type = messageResponse.getType(); this.content = messageResponse.getContent(); - this.createdAt = LocalDateTime.now(); + this.sendTime = messageResponse.getCreatedAt(); this.isRead = messageResponse.getIsRead(); } } \ No newline at end of file From f815fb147c1717c18d6bb5da02c6a523784c4248 Mon Sep 17 00:00:00 2001 From: imjanghyeok Date: Fri, 10 May 2024 13:59:44 +0900 Subject: [PATCH 202/265] =?UTF-8?q?Refactor:=20api=20=EC=97=B0=EB=8F=99=20?= =?UTF-8?q?=EC=A4=91=EC=97=90=20=EC=97=90=EB=9F=AC=20=EC=88=98=EC=A0=95=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../facefriend/chat/service/dto/heart/SendHeartRequest.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/capstone/facefriend/chat/service/dto/heart/SendHeartRequest.java b/src/main/java/capstone/facefriend/chat/service/dto/heart/SendHeartRequest.java index d659c4bb23..4176169283 100644 --- a/src/main/java/capstone/facefriend/chat/service/dto/heart/SendHeartRequest.java +++ b/src/main/java/capstone/facefriend/chat/service/dto/heart/SendHeartRequest.java @@ -2,9 +2,13 @@ import lombok.Data; import lombok.Getter; +import lombok.NoArgsConstructor; import lombok.Setter; @Data +@Getter +@Setter +@NoArgsConstructor public class SendHeartRequest { private Long receiveId; } \ No newline at end of file From d3e1c5179d5c595ca627ffe070a57e32454b5913 Mon Sep 17 00:00:00 2001 From: imjanghyeok Date: Fri, 10 May 2024 14:00:18 +0900 Subject: [PATCH 203/265] =?UTF-8?q?Refactor:=20MessageResponse=EC=97=90=20?= =?UTF-8?q?faceInfo=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../facefriend/chat/service/dto/message/MessageResponse.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/capstone/facefriend/chat/service/dto/message/MessageResponse.java b/src/main/java/capstone/facefriend/chat/service/dto/message/MessageResponse.java index cb55138300..9236902bff 100644 --- a/src/main/java/capstone/facefriend/chat/service/dto/message/MessageResponse.java +++ b/src/main/java/capstone/facefriend/chat/service/dto/message/MessageResponse.java @@ -19,6 +19,7 @@ public class MessageResponse implements Serializable { private String content; private String type; private String senderNickname; + private String senderFaceInfoS3Url; @JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss", timezone = "Asia/Seoul") private LocalDateTime createdAt; private Boolean isRead; From 2b1413812f90b4d7731f7594848140f814756bbe Mon Sep 17 00:00:00 2001 From: imjanghyeok Date: Fri, 10 May 2024 14:00:40 +0900 Subject: [PATCH 204/265] =?UTF-8?q?Feat:=20MessageList=EC=97=90=20?= =?UTF-8?q?=EB=8C=80=ED=95=9C=20Dto=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/message/MessageListResponse.java | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 src/main/java/capstone/facefriend/chat/service/dto/message/MessageListResponse.java diff --git a/src/main/java/capstone/facefriend/chat/service/dto/message/MessageListResponse.java b/src/main/java/capstone/facefriend/chat/service/dto/message/MessageListResponse.java new file mode 100644 index 0000000000..f5e03c28ac --- /dev/null +++ b/src/main/java/capstone/facefriend/chat/service/dto/message/MessageListResponse.java @@ -0,0 +1,25 @@ +package capstone.facefriend.chat.service.dto.message; + +import capstone.facefriend.chat.domain.ChatMessage; + +import java.time.LocalDateTime; + +public record MessageListResponse( + Long senderId, + String senderNickname, + String senderOriginS3Url, + String senderGeneratedS3Url, + String content, + LocalDateTime sendTime +) { + public static MessageListResponse of (ChatMessage message) { + return new MessageListResponse( + message.getSender().getId(), + message.getSender().getBasicInfo().getNickname(), + message.getSender().getFaceInfo().getOriginS3url(), + message.getSender().getFaceInfo().getGeneratedS3url(), + message.getContent(), + message.getSendTime() + ); + } +} \ No newline at end of file From 96412eec31b5f82e354997633b3804b12b69ebae Mon Sep 17 00:00:00 2001 From: imjanghyeok Date: Fri, 10 May 2024 14:02:55 +0900 Subject: [PATCH 205/265] =?UTF-8?q?Feat:=20application=20run=20info=20?= =?UTF-8?q?=EC=A0=80=EC=9E=A5=20=EB=B0=8F=20application=20run=20info=20?= =?UTF-8?q?=EC=82=AD=EC=A0=9C=20=EA=B8=B0=EB=8A=A5,=20=EB=A9=94=EC=8B=9C?= =?UTF-8?q?=EC=A7=80=20=EA=B8=B0=EB=A1=9D=20=ED=8E=98=EC=9D=B4=EC=A7=80?= =?UTF-8?q?=EB=84=A4=EC=9D=B4=EC=85=98=20api=20mapping?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../chat/controller/MessageController.java | 45 ++++++++++++++----- 1 file changed, 34 insertions(+), 11 deletions(-) diff --git a/src/main/java/capstone/facefriend/chat/controller/MessageController.java b/src/main/java/capstone/facefriend/chat/controller/MessageController.java index 6ea040fa1a..6eaa0715be 100644 --- a/src/main/java/capstone/facefriend/chat/controller/MessageController.java +++ b/src/main/java/capstone/facefriend/chat/controller/MessageController.java @@ -1,18 +1,20 @@ package capstone.facefriend.chat.controller; +import capstone.facefriend.auth.controller.support.AuthMember; import capstone.facefriend.auth.infrastructure.JwtProvider; +import capstone.facefriend.chat.service.MessageService; import capstone.facefriend.chat.service.dto.heart.HeartReplyRequest; -import capstone.facefriend.chat.service.dto.message.MessageRequest; import capstone.facefriend.chat.service.dto.heart.SendHeartRequest; -import capstone.facefriend.chat.service.MessageService; +import capstone.facefriend.chat.service.dto.message.MessageListResponse; +import capstone.facefriend.chat.service.dto.message.MessageRequest; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; +import org.springframework.http.ResponseEntity; import org.springframework.messaging.handler.annotation.MessageMapping; -import org.springframework.messaging.handler.annotation.SendTo; import org.springframework.messaging.simp.stomp.StompHeaderAccessor; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; + +import java.util.List; @RestController @Slf4j @@ -21,15 +23,36 @@ public class MessageController { private static final String BEARER_PREFIX = "Bearer "; private final MessageService messageService; - private final MappingJackson2HttpMessageConverter converter; private final JwtProvider jwtProvider; - @MessageMapping("/test") - @SendTo("/sub/test") - public String test() { - return "테스트 메시지"; + @MessageMapping("/stomp/connect") + public void enterApp( + StompHeaderAccessor headerAccessor + ){ + String authorizationHeader = headerAccessor.getFirstNativeHeader("Authorization"); + String token = authorizationHeader.substring(BEARER_PREFIX.length()); + Long memberId = jwtProvider.extractId(token); + messageService.enterApplication(memberId); } + @PostMapping("/stomp/disconnect") + public String exitApp( + @AuthMember Long memberId + ){ + String msg = messageService.exitApplication(memberId); + return msg; + } + + @GetMapping("/chat/{roomId}/messages") + public ResponseEntity> getMessagesPage( + @PathVariable("roomId") Long roomId, + @AuthMember Long memberId, + @RequestParam(required = false, defaultValue = "1", value = "page") int pageNo + ){ + return ResponseEntity.ok(messageService.getMessagePage(roomId, memberId,pageNo)); + } + + @MessageMapping("/chat/messages") public void message( StompHeaderAccessor headerAccessor, From 151919c9779382ef392359ccbb99ba1ccb47f6b9 Mon Sep 17 00:00:00 2001 From: imjanghyeok Date: Fri, 10 May 2024 14:03:13 +0900 Subject: [PATCH 206/265] =?UTF-8?q?Refactor:=20=EC=95=88=EC=93=B0=EB=8A=94?= =?UTF-8?q?=20Exception=20type=20=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../facefriend/chat/exception/ChatExceptionType.java | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/src/main/java/capstone/facefriend/chat/exception/ChatExceptionType.java b/src/main/java/capstone/facefriend/chat/exception/ChatExceptionType.java index afea94e745..0606db047d 100644 --- a/src/main/java/capstone/facefriend/chat/exception/ChatExceptionType.java +++ b/src/main/java/capstone/facefriend/chat/exception/ChatExceptionType.java @@ -9,15 +9,7 @@ public enum ChatExceptionType implements ExceptionType { INVALID_ACCESS(Status.FORBIDDEN, 5003, "본인의 계정이 아닙니다."), UNAUTHORIZED(Status.UNAUTHORIZED, 5005, "접근 정보가 잘못되었습니다."), ALREADY_CHATROOM(Status.BAD_REQUEST, 5006, "이미 존재하는 채팅방입니다."), - WRONG_PASSWORD(Status.BAD_REQUEST, 5007, "잘못된 비밀번호입니다."), - EXPIRED_ACCESS_TOKEN(Status.BAD_REQUEST, 5008, "만료된 액세스 토큰이므로 재발급해야 합니다."), - INVALID_ACCESS_TOKEN(Status.BAD_REQUEST, 5009, "유효하지 않은 액세스 토큰이므로 재발급해야 합니다."), - INVALID_REFRESH_TOKEN(Status.BAD_REQUEST, 5010, "유효하지 않은 리프레시 토큰입니다. 토큰 재발급이 불가능합니다."), - ACCESS_TOKEN_IS_IN_BLACKLIST(Status.BAD_REQUEST, 5011, "액세스 토큰이 로그아웃 처리되었습니다. 재로그인하시기 바랍니다."), - NOT_VERIFIED(Status.BAD_REQUEST, 5012, "본인 인증을 먼저 완료해야 합니다."), - PASSWORDS_NOT_EQUAL(Status.BAD_REQUEST, 5013, "재설정하는 비밀번호들이 동일하지 않습니다."), - WRONG_TEMPORARY_PASSWORD(Status.BAD_REQUEST, 5014, "임시 비밀번호가 올바르지 않습니다."), - NOT_FOUND_GENDER(Status.NOT_FOUND, 5015, "일치하는 성별이 없습니다.") + ; private final Status status; private final int exceptionCode; From e8d8bd59d20515d1b5b7d3863f0e2537bac7de56 Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Fri, 10 May 2024 16:18:29 +0900 Subject: [PATCH 207/265] =?UTF-8?q?fix:=20=ED=95=84=EB=93=9C=EB=AA=85=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../facefriend/member/domain/analysisInfo/AnalysisInfo.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/capstone/facefriend/member/domain/analysisInfo/AnalysisInfo.java b/src/main/java/capstone/facefriend/member/domain/analysisInfo/AnalysisInfo.java index 2bf08b2352..c5fbd1da6b 100644 --- a/src/main/java/capstone/facefriend/member/domain/analysisInfo/AnalysisInfo.java +++ b/src/main/java/capstone/facefriend/member/domain/analysisInfo/AnalysisInfo.java @@ -28,11 +28,11 @@ public class AnalysisInfo { @Builder.Default @ElementCollection @CollectionTable(name = "ANALYSIS_INFO_FULL", joinColumns = @JoinColumn(name = "ANALYSIS_INFO_ID")) - private Map analysisInfoFull = new HashMap<>(); + private Map analysisFull = new HashMap<>(); @Builder.Default @ElementCollection @CollectionTable(name = "ANALYSIS_INFO_SHORT", joinColumns = @JoinColumn(name = "ANALYSIS_INFO_ID")) - private List analysisInfoShort = new ArrayList<>(); + private List analysisShort = new ArrayList<>(); } From 38b43d7790ef87902562c0220ad6bd491fad5a36 Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Fri, 10 May 2024 16:34:24 +0900 Subject: [PATCH 208/265] =?UTF-8?q?fix:=20getter=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../member/service/AnalysisInfoService.java | 12 ++++++------ .../facefriend/member/service/MemberService.java | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/main/java/capstone/facefriend/member/service/AnalysisInfoService.java b/src/main/java/capstone/facefriend/member/service/AnalysisInfoService.java index ec13b764a6..1095305afc 100644 --- a/src/main/java/capstone/facefriend/member/service/AnalysisInfoService.java +++ b/src/main/java/capstone/facefriend/member/service/AnalysisInfoService.java @@ -87,8 +87,8 @@ public String getFilename() { Integer faceShapeIdNum = extractFaceShapeIdNum(total); Member member = findMemberById(memberId); // 영속 상태 - member.getAnalysisInfo().setAnalysisInfoFull(analysisFull); // dirty - member.getAnalysisInfo().setAnalysisInfoShort(analysisShort); + member.getAnalysisInfo().setAnalysisFull(analysisFull); // dirty + member.getAnalysisInfo().setAnalysisShort(analysisShort); member.getAnalysisInfo().setFaceShapeIdNum(faceShapeIdNum); return new AnalysisInfoFullResponse(analysisFull); @@ -127,19 +127,19 @@ private List extractAnalysisInfoShort(AnalysisInfoTotal total) { public AnalysisInfoFullShortResponse getAnalysisInfoFullShort(Long memberId) { Member member = findMemberById(memberId); - Map analysisInfoFull = member.getAnalysisInfo().getAnalysisInfoFull(); - List analysisInfoShort = member.getAnalysisInfo().getAnalysisInfoShort(); + Map analysisInfoFull = member.getAnalysisInfo().getAnalysisFull(); + List analysisInfoShort = member.getAnalysisInfo().getAnalysisShort(); return new AnalysisInfoFullShortResponse(analysisInfoFull, analysisInfoShort); } public AnalysisInfoFullResponse getAnalysisInfoFull(Long memberId) { Member member = findMemberById(memberId); - return new AnalysisInfoFullResponse(member.getAnalysisInfo().getAnalysisInfoFull()); + return new AnalysisInfoFullResponse(member.getAnalysisInfo().getAnalysisFull()); } public AnalysisInfoShortResponse getAnalysisInfoShort(Long memberId) { Member member = findMemberById(memberId); - return new AnalysisInfoShortResponse(member.getAnalysisInfo().getAnalysisInfoShort()); + return new AnalysisInfoShortResponse(member.getAnalysisInfo().getAnalysisShort()); } private Member findMemberById(Long memberId) { diff --git a/src/main/java/capstone/facefriend/member/service/MemberService.java b/src/main/java/capstone/facefriend/member/service/MemberService.java index 811da86c30..7dfbc017a0 100644 --- a/src/main/java/capstone/facefriend/member/service/MemberService.java +++ b/src/main/java/capstone/facefriend/member/service/MemberService.java @@ -117,13 +117,13 @@ public SignupResponse signUp(SignUpRequest request) { // 관상 분석 초기값 AnalysisInfo analysisInfo = AnalysisInfo.builder() - .analysisInfoFull(Map.of( + .analysisFull(Map.of( INITIAL_ANALYSIS_NAME_EYE, INITIAL_ANALYSIS_DESCRIPTION, INITIAL_ANALYSIS_NAME_FACE_SHAPE, INITIAL_ANALYSIS_DESCRIPTION, INITIAL_ANALYSIS_NAME_LIPS, INITIAL_ANALYSIS_DESCRIPTION, INITIAL_ANALYSIS_NAME_NOSE, INITIAL_ANALYSIS_DESCRIPTION, INITIAL_ANALYSIS_NAME_EYEBROW, INITIAL_ANALYSIS_DESCRIPTION)) - .analysisInfoShort(List.of(INITIAL_ANALYSIS_TAG)) + .analysisShort(List.of(INITIAL_ANALYSIS_TAG)) .faceShapeIdNum(INITIAL_ANALYSIS_FACE_SHAPE_NUM) .build(); analysisInfoRepository.save(analysisInfo); From 7ec4dc661f319cd0aa259818b3c21dc6a5692f33 Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Fri, 10 May 2024 16:58:53 +0900 Subject: [PATCH 209/265] feat: include path pattern --- .../capstone/facefriend/auth/config/AuthConfig.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/main/java/capstone/facefriend/auth/config/AuthConfig.java b/src/main/java/capstone/facefriend/auth/config/AuthConfig.java index 65871fc037..1f58b1f7c6 100644 --- a/src/main/java/capstone/facefriend/auth/config/AuthConfig.java +++ b/src/main/java/capstone/facefriend/auth/config/AuthConfig.java @@ -56,6 +56,10 @@ private HandlerInterceptor loginCheckInterceptor() { .addIncludePathPattern("/my-resume", ANY) .addIncludePathPattern("/resume-by-good-combi", ANY) .addIncludePathPattern("/resume-by-category", ANY) + .addIncludePathPattern("/room/list", GET) + .addIncludePathPattern("/stomp/disconnect", POST) + .addIncludePathPattern("/chat/**", GET) + .addIncludePathPattern("/room/**", POST) .addExcludePathPattern("/auth/reissue/**", POST); // 토큰 만료 시에는 해당 요청을 가로채지 않아야 합니다. } @@ -74,6 +78,10 @@ private HandlerInterceptor loginInterceptor() { .addIncludePathPattern("/my-resume", ANY) .addIncludePathPattern("/resume-by-good-combi", ANY) .addIncludePathPattern("/resume-by-category", ANY) + .addIncludePathPattern("/room/list", GET) + .addIncludePathPattern("/stomp/disconnect", POST) + .addIncludePathPattern("/chat/**", GET) + .addIncludePathPattern("/room/**", POST) .addExcludePathPattern("/auth/reissue", POST); // 토큰 만료 시에는 해당 요청을 가로채지 않아야 합니다. } @@ -99,6 +107,10 @@ private HandlerInterceptor tokenBlackListInterceptor() { .addIncludePathPattern("/my-resume", ANY) .addIncludePathPattern("/resume-by-good-combi", ANY) .addIncludePathPattern("/resume-by-category", ANY) + .addIncludePathPattern("/room/list", GET) + .addIncludePathPattern("/stomp/disconnect", POST) + .addIncludePathPattern("/chat/**", GET) + .addIncludePathPattern("/room/**", POST) ; } From 6dc4495f5dbe1d6daf9570fab9b00a7c5e4cd400 Mon Sep 17 00:00:00 2001 From: imjanghyeok Date: Fri, 10 May 2024 20:40:14 +0900 Subject: [PATCH 210/265] =?UTF-8?q?Refactor:=20Exception=20handling=20?= =?UTF-8?q?=EC=84=9C=EB=B2=84=EC=97=90=EC=84=9C=EB=A7=8C=20=EC=98=88?= =?UTF-8?q?=EC=99=B8=EB=A5=BC=20=EC=B2=98=EB=A6=AC=ED=95=98=EB=8A=94=20?= =?UTF-8?q?=EA=B2=8C=20=EC=95=84=EB=8B=88=EB=9D=BC=20stomp=EB=A1=9C=20?= =?UTF-8?q?=EC=9D=B4=EB=A5=BC=20=EC=A0=84=EC=86=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../chat/service/MessageService.java | 72 +++++++++++++------ 1 file changed, 49 insertions(+), 23 deletions(-) diff --git a/src/main/java/capstone/facefriend/chat/service/MessageService.java b/src/main/java/capstone/facefriend/chat/service/MessageService.java index 25abd5de20..025d33e2ac 100644 --- a/src/main/java/capstone/facefriend/chat/service/MessageService.java +++ b/src/main/java/capstone/facefriend/chat/service/MessageService.java @@ -43,27 +43,44 @@ public class MessageService { private final SimpMessagingTemplate simpMessagingTemplate; private final ChatRoomInfoRedisRepository chatRoomInfoRedisRepository; - private Member findMemberById(Long memberId) { + private Member findMemberById(String destination, Long memberId) { Member member = memberRepository.findById(memberId) - .orElseThrow(() -> new MemberException(MemberExceptionType.NOT_FOUND)); + .orElse(null); + if (member == null) { + simpMessagingTemplate.convertAndSend(destination, MemberExceptionType.NOT_FOUND.message()); + throw new MemberException(MemberExceptionType.NOT_FOUND); + } + return member; } - private ChatRoom findRoomById(Long roomId) { + private ChatRoom findRoomById(String destination, Long roomId) { ChatRoom chatRoom = chatRoomRepository.findById(roomId) - .orElseThrow(()-> new ChatException(ChatExceptionType.NOT_FOUND)); + .orElse(null); + if (chatRoom == null) { + simpMessagingTemplate.convertAndSend(destination, MemberExceptionType.NOT_FOUND.message()); + throw new MemberException(MemberExceptionType.NOT_FOUND); + } return chatRoom; } - private ChatRoomMember findSenderReceiver(Long senderId, Long receiveId) { + private ChatRoomMember findSenderReceiver(String destination, Long senderId, Long receiveId) { ChatRoomMember chatRoomMember = chatRoomMemberRepository.findBySenderAndReceiver(senderId, receiveId) - .orElseThrow(()-> new ChatException(ChatExceptionType.NOT_FOUND)); + .orElse(null); + if (chatRoomMember == null) { + simpMessagingTemplate.convertAndSend(destination, MemberExceptionType.NOT_FOUND.message()); + throw new MemberException(MemberExceptionType.NOT_FOUND); + } return chatRoomMember; } - private ChatRoomMember findChatRoomMemberByChatRoomId(Long roomId) { + private ChatRoomMember findChatRoomMemberByChatRoomId(String destination, Long roomId) { ChatRoomMember chatRoomMember = chatRoomMemberRepository.findByChatRoomId(roomId) - .orElseThrow(()-> new ChatException(ChatExceptionType.NOT_FOUND)); + .orElse(null); + if (chatRoomMember == null) { + simpMessagingTemplate.convertAndSend(destination, MemberExceptionType.NOT_FOUND.message()); + throw new MemberException(MemberExceptionType.NOT_FOUND); + } return chatRoomMember; } @@ -81,20 +98,24 @@ private ChatRoomInfo findChatRoomInfo(String chatRoomInfoId) { @Transactional public void sendMessage(MessageRequest messageRequest, Long senderId) { - Member sender = findMemberById(senderId); - Member receiver = findMemberById(messageRequest.getReceiveId()); - ChatRoom chatRoom = findRoomById(messageRequest.getRoomId()); + String exceptionDestination = "/sub/chat/" + senderId; + Member sender = findMemberById(exceptionDestination, senderId); + Member receiver = findMemberById(exceptionDestination, messageRequest.getReceiveId()); + ChatRoom chatRoom = findRoomById(exceptionDestination, messageRequest.getRoomId()); if (chatRoom.getStatus() == ChatRoom.Status.open) { chatRoom.setStatus(ChatRoom.Status.progress); - ChatRoomMember chatRoomMember = findChatRoomMemberByChatRoomId(chatRoom.getId()); + ChatRoomMember chatRoomMember = findChatRoomMemberByChatRoomId(exceptionDestination, chatRoom.getId()); chatRoomRepository.save(chatRoom); chatRoomMemberRepository.save(chatRoomMember); } else if ((chatRoom.getStatus() == ChatRoom.Status.close)) { + simpMessagingTemplate.convertAndSend(exceptionDestination, ChatExceptionType.INVALIDED_CHATROOM.message()); throw new ChatException(ChatExceptionType.INVALIDED_CHATROOM); } else if ((chatRoom.getStatus() == ChatRoom.Status.set)) { + simpMessagingTemplate.convertAndSend(exceptionDestination, ChatExceptionType.INVALIDED_CHATROOM.message()); throw new ChatException(ChatExceptionType.INVALIDED_CHATROOM); } else if ((chatRoom.getStatus() == ChatRoom.Status.delete)) { + simpMessagingTemplate.convertAndSend(exceptionDestination, ChatExceptionType.INVALIDED_CHATROOM.message()); throw new ChatException(ChatExceptionType.INVALIDED_CHATROOM); } @@ -127,19 +148,22 @@ public void sendMessage(MessageRequest messageRequest, Long senderId) { } @Transactional public void sendHeart(Long senderId, Long receiveId) { + String exceptionDestination = "/sub/chat/" + senderId; + +// if (chatRoomMemberRepository.findBySenderAndReceiver(senderId, receiveId).isPresent()){ +// String exceptionMessage = ChatExceptionType.ALREADY_CHATROOM.message(); +// simpMessagingTemplate.convertAndSend(exceptionDestination, exceptionMessage); +// throw new ChatException(ChatExceptionType.ALREADY_CHATROOM); +// } + ChatRoom chatRoom = ChatRoom.builder() .status(ChatRoom.Status.set) .isPublic(false) .build(); chatRoomRepository.save(chatRoom); - Member sender = findMemberById(senderId); - Member receiver = findMemberById(receiveId); - - - if (chatRoomMemberRepository.findBySenderAndReceiver(senderId, receiveId).isPresent()) - throw new ChatException(ChatExceptionType.ALREADY_CHATROOM); - + Member sender = findMemberById(exceptionDestination, senderId); + Member receiver = findMemberById(exceptionDestination, receiveId); ChatRoomMember chatRoomMember = ChatRoomMember.builder() .chatRoom(chatRoom) @@ -166,13 +190,14 @@ public void sendHeart(Long senderId, Long receiveId) { } @Transactional public void heartReply(HeartReplyRequest heartReplyRequest, Long receiveId) { + String exceptionDestination = "/sub/chat/" + receiveId; String message = null; - Member receiver = findMemberById(receiveId); - Member sender = findMemberById(heartReplyRequest.getSenderId()); + Member receiver = findMemberById(exceptionDestination, receiveId); + Member sender = findMemberById(exceptionDestination, heartReplyRequest.getSenderId()); - ChatRoomMember chatRoomMember = findSenderReceiver(sender.getId(), receiver.getId()); - ChatRoom chatRoom = findRoomById(chatRoomMember.getChatRoom().getId()); + ChatRoomMember chatRoomMember = findSenderReceiver(exceptionDestination, sender.getId(), receiver.getId()); + ChatRoom chatRoom = findRoomById(exceptionDestination, chatRoomMember.getChatRoom().getId()); if (heartReplyRequest.getIntention().equals("positive")) { chatRoom.setStatus(ChatRoom.Status.open); @@ -186,6 +211,7 @@ public void heartReply(HeartReplyRequest heartReplyRequest, Long receiveId) { message = receiver.getBasicInfo().getNickname() + "님이 거절했습니다."; } else { + simpMessagingTemplate.convertAndSend(exceptionDestination, ChatExceptionType.ALREADY_CHATROOM); throw new ChatException(ChatExceptionType.ALREADY_CHATROOM); } // 동적으로 목적지 설정 From 4169a89dee2f74b0bebf953e6e425e11ff0e0a34 Mon Sep 17 00:00:00 2001 From: imjanghyeok Date: Fri, 10 May 2024 22:56:18 +0900 Subject: [PATCH 211/265] =?UTF-8?q?Refactor:=20=EC=84=B1=EA=B3=B5=EC=97=90?= =?UTF-8?q?=20=EB=8C=80=ED=95=9C=20response=20=ED=95=84=EC=9A=94=EB=A1=9C?= =?UTF-8?q?=20=EC=BD=94=EB=93=9C=20=EB=A6=AC=ED=8C=A9=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../chat/controller/MessageController.java | 1 - .../chat/service/MessageService.java | 19 +++++++++++++------ 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/src/main/java/capstone/facefriend/chat/controller/MessageController.java b/src/main/java/capstone/facefriend/chat/controller/MessageController.java index 6eaa0715be..54001ef6fb 100644 --- a/src/main/java/capstone/facefriend/chat/controller/MessageController.java +++ b/src/main/java/capstone/facefriend/chat/controller/MessageController.java @@ -86,5 +86,4 @@ public void heartreply( messageService.heartReply(heartReplyRequest, receiveId); } - } \ No newline at end of file diff --git a/src/main/java/capstone/facefriend/chat/service/MessageService.java b/src/main/java/capstone/facefriend/chat/service/MessageService.java index 025d33e2ac..03f7dc5f10 100644 --- a/src/main/java/capstone/facefriend/chat/service/MessageService.java +++ b/src/main/java/capstone/facefriend/chat/service/MessageService.java @@ -150,11 +150,11 @@ public void sendMessage(MessageRequest messageRequest, Long senderId) { public void sendHeart(Long senderId, Long receiveId) { String exceptionDestination = "/sub/chat/" + senderId; -// if (chatRoomMemberRepository.findBySenderAndReceiver(senderId, receiveId).isPresent()){ -// String exceptionMessage = ChatExceptionType.ALREADY_CHATROOM.message(); -// simpMessagingTemplate.convertAndSend(exceptionDestination, exceptionMessage); -// throw new ChatException(ChatExceptionType.ALREADY_CHATROOM); -// } + if (chatRoomMemberRepository.findBySenderAndReceiver(senderId, receiveId).isPresent()){ + String exceptionMessage = ChatExceptionType.ALREADY_CHATROOM.message(); + simpMessagingTemplate.convertAndSend(exceptionDestination, exceptionMessage); + throw new ChatException(ChatExceptionType.ALREADY_CHATROOM); + } ChatRoom chatRoom = ChatRoom.builder() .status(ChatRoom.Status.set) @@ -186,6 +186,7 @@ public void sendHeart(Long senderId, Long receiveId) { sendHeartResponse.setType("Heart"); String topic = channelTopic.getTopic(); + simpMessagingTemplate.convertAndSend(exceptionDestination, "대화 요청 성공"); redisTemplate.convertAndSend(topic, sendHeartResponse); } @Transactional @@ -216,7 +217,10 @@ public void heartReply(HeartReplyRequest heartReplyRequest, Long receiveId) { } // 동적으로 목적지 설정 String destination = "/sub/chat/" + sender.getId(); - + + // 대화 수락 + simpMessagingTemplate.convertAndSend(exceptionDestination, "대화 수락 성공"); + // 메시지 전송 simpMessagingTemplate.convertAndSend(destination, message); } @@ -224,6 +228,7 @@ public void heartReply(HeartReplyRequest heartReplyRequest, Long receiveId) { @Transactional public void enterApplication(Long memberId) { + String exceptionDestination = "/sub/chat/" + memberId; SocketInfo socketInfo = new SocketInfo(); socketInfo.setMemberId(memberId); socketInfo.setConnectTime(LocalDateTime.now()); @@ -235,6 +240,8 @@ public void enterApplication(Long memberId) { if(isExistUnSendHeart(memberId)) { sendSentHeart(memberId); } + + simpMessagingTemplate.convertAndSend(exceptionDestination, "저장 성공"); } @Transactional From fa947eee7334068ef394b711f845735dd51af7b4 Mon Sep 17 00:00:00 2001 From: imjanghyeok Date: Sat, 11 May 2024 20:01:18 +0900 Subject: [PATCH 212/265] =?UTF-8?q?Refactor:=20=EC=86=8C=EC=BC=93=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=EB=B3=B4=EB=82=B4=EB=8A=94=20response=20=EB=A9=94?= =?UTF-8?q?=EC=8B=9C=EC=A7=80=EB=A5=BC=20json=ED=98=95=ED=83=9C=EB=A1=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../facefriend/chat/service/MessageService.java | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/main/java/capstone/facefriend/chat/service/MessageService.java b/src/main/java/capstone/facefriend/chat/service/MessageService.java index 03f7dc5f10..e8018c923e 100644 --- a/src/main/java/capstone/facefriend/chat/service/MessageService.java +++ b/src/main/java/capstone/facefriend/chat/service/MessageService.java @@ -192,7 +192,6 @@ public void sendHeart(Long senderId, Long receiveId) { @Transactional public void heartReply(HeartReplyRequest heartReplyRequest, Long receiveId) { String exceptionDestination = "/sub/chat/" + receiveId; - String message = null; Member receiver = findMemberById(exceptionDestination, receiveId); Member sender = findMemberById(exceptionDestination, heartReplyRequest.getSenderId()); @@ -205,24 +204,24 @@ public void heartReply(HeartReplyRequest heartReplyRequest, Long receiveId) { chatRoomRepository.save(chatRoom); chatRoomMemberRepository.save(chatRoomMember); - message = receiver.getBasicInfo().getNickname() + "님이 수락했습니다."; + // 대화 수락 + simpMessagingTemplate.convertAndSend(exceptionDestination, "대화 수락"); + } else if (heartReplyRequest.getIntention().equals("negative")) { chatRoomMemberRepository.delete(chatRoomMember); chatRoomRepository.delete(chatRoom); + // 대화 거절 + simpMessagingTemplate.convertAndSend(exceptionDestination, "대화 거절"); - message = receiver.getBasicInfo().getNickname() + "님이 거절했습니다."; } else { simpMessagingTemplate.convertAndSend(exceptionDestination, ChatExceptionType.ALREADY_CHATROOM); throw new ChatException(ChatExceptionType.ALREADY_CHATROOM); } // 동적으로 목적지 설정 String destination = "/sub/chat/" + sender.getId(); - - // 대화 수락 - simpMessagingTemplate.convertAndSend(exceptionDestination, "대화 수락 성공"); - + // 메시지 전송 - simpMessagingTemplate.convertAndSend(destination, message); + simpMessagingTemplate.convertAndSend(destination, heartReplyRequest); } From a0ba69e243eb4c61bb267e4028e0877674e1c418 Mon Sep 17 00:00:00 2001 From: imjanghyeok Date: Sat, 11 May 2024 20:13:33 +0900 Subject: [PATCH 213/265] =?UTF-8?q?Refactor:=20=EC=B1=84=ED=8C=85=EB=B0=A9?= =?UTF-8?q?=EC=9D=84=20=EB=96=A0=EB=82=A0=20=EB=95=8C,=20=EC=83=81?= =?UTF-8?q?=EB=8C=80=EB=B0=A9=EC=9D=B4=20=EC=95=8C=20=EC=88=98=20=EC=9E=88?= =?UTF-8?q?=EB=8F=84=EB=A1=9Dresponse=20=EB=A9=94=EC=8B=9C=EC=A7=80?= =?UTF-8?q?=EB=A5=BC=20json=ED=98=95=ED=83=9C=EB=A1=9C=20ws=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=EC=A0=84=EB=8B=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../capstone/facefriend/chat/service/ChatRoomService.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/main/java/capstone/facefriend/chat/service/ChatRoomService.java b/src/main/java/capstone/facefriend/chat/service/ChatRoomService.java index 43f7626e21..da8ac6e2f9 100644 --- a/src/main/java/capstone/facefriend/chat/service/ChatRoomService.java +++ b/src/main/java/capstone/facefriend/chat/service/ChatRoomService.java @@ -17,6 +17,7 @@ import capstone.facefriend.member.exception.member.MemberExceptionType; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.messaging.simp.SimpMessagingTemplate; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -35,6 +36,7 @@ public class ChatRoomService { private final ChatMessageRepository chatMessageRepository; private final ChatRoomInfoRedisRepository chatRoomInfoRedisRepository; private final MemberRepository memberRepository; + private final SimpMessagingTemplate simpMessagingTemplate; private static final String EMPTY_MESSAGE = "채팅을 시작하지 않았습니다."; private static final String OPEN_MESSAGE = "채팅을 시작해보세요!"; private static final String CLOSE_MESSAGE = "상대방이 떠났습니다."; @@ -156,6 +158,7 @@ public String leftRoom(Long roomId, Long memberId) { ChatRoom.Status status = chatRoom.getStatus(); ChatRoomMember chatRoomMember = findChatRoomMemberByChatRoomId(roomId); Member member = findMemberById(memberId); + Member sender = identifySender(chatRoomMember, memberId); Member leftMember = identifyLeftMember(memberId, chatRoomMember); if (status== ChatRoom.Status.close) { if (member != leftMember) { @@ -176,11 +179,13 @@ public String leftRoom(Long roomId, Long memberId) { } else if (chatRoomMember.getReceiver() == member){ chatRoomMember.setReceiverExist(false); } else { - return "속해있지 않은 채팅방입니디."; + return "속해있지 않은 채팅방입니다."; } chatRoom.setStatus(ChatRoom.Status.close); + simpMessagingTemplate.convertAndSend("/sub/chat/" + sender.getId(), ChatRoomLeftResponse.of(sender, "상대방이 떠났습니다.")); chatRoomRepository.save(chatRoom); chatRoomMemberRepository.save(chatRoomMember); + return "채팅방을 떠났습니다"; } From 45c9a74d44c4a025b0fcec7a48f096cf63adeea8 Mon Sep 17 00:00:00 2001 From: imjanghyeok Date: Sat, 11 May 2024 20:14:15 +0900 Subject: [PATCH 214/265] =?UTF-8?q?Refactor:=20=EC=9A=94=EC=B2=AD=20?= =?UTF-8?q?=EC=88=98=EB=9D=BD=EC=8B=9C,=20response=20=EB=A9=94=EC=8B=9C?= =?UTF-8?q?=EC=A7=80=EB=A5=BC=20json=ED=98=95=ED=83=9C=EB=A1=9C=20ws?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20=EC=A0=84=EB=8B=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../facefriend/chat/service/MessageService.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/java/capstone/facefriend/chat/service/MessageService.java b/src/main/java/capstone/facefriend/chat/service/MessageService.java index e8018c923e..41b35e11c1 100644 --- a/src/main/java/capstone/facefriend/chat/service/MessageService.java +++ b/src/main/java/capstone/facefriend/chat/service/MessageService.java @@ -4,7 +4,7 @@ import capstone.facefriend.chat.exception.ChatException; import capstone.facefriend.chat.exception.ChatExceptionType; import capstone.facefriend.chat.repository.*; -import capstone.facefriend.chat.service.dto.heart.HeartReplyRequest; +import capstone.facefriend.chat.service.dto.heart.HeartReplyRequestResponse; import capstone.facefriend.chat.service.dto.heart.SendHeartResponse; import capstone.facefriend.chat.service.dto.message.MessageListResponse; import capstone.facefriend.chat.service.dto.message.MessageRequest; @@ -190,16 +190,16 @@ public void sendHeart(Long senderId, Long receiveId) { redisTemplate.convertAndSend(topic, sendHeartResponse); } @Transactional - public void heartReply(HeartReplyRequest heartReplyRequest, Long receiveId) { + public void heartReply(HeartReplyRequestResponse heartReplyRequestResponse, Long receiveId) { String exceptionDestination = "/sub/chat/" + receiveId; Member receiver = findMemberById(exceptionDestination, receiveId); - Member sender = findMemberById(exceptionDestination, heartReplyRequest.getSenderId()); + Member sender = findMemberById(exceptionDestination, heartReplyRequestResponse.senderId()); ChatRoomMember chatRoomMember = findSenderReceiver(exceptionDestination, sender.getId(), receiver.getId()); ChatRoom chatRoom = findRoomById(exceptionDestination, chatRoomMember.getChatRoom().getId()); - if (heartReplyRequest.getIntention().equals("positive")) { + if (heartReplyRequestResponse.intention().equals("positive")) { chatRoom.setStatus(ChatRoom.Status.open); chatRoomRepository.save(chatRoom); chatRoomMemberRepository.save(chatRoomMember); @@ -207,7 +207,7 @@ public void heartReply(HeartReplyRequest heartReplyRequest, Long receiveId) { // 대화 수락 simpMessagingTemplate.convertAndSend(exceptionDestination, "대화 수락"); - } else if (heartReplyRequest.getIntention().equals("negative")) { + } else if (heartReplyRequestResponse.intention().equals("negative")) { chatRoomMemberRepository.delete(chatRoomMember); chatRoomRepository.delete(chatRoom); // 대화 거절 @@ -221,7 +221,7 @@ public void heartReply(HeartReplyRequest heartReplyRequest, Long receiveId) { String destination = "/sub/chat/" + sender.getId(); // 메시지 전송 - simpMessagingTemplate.convertAndSend(destination, heartReplyRequest); + simpMessagingTemplate.convertAndSend(destination, heartReplyRequestResponse.of(receiveId, heartReplyRequestResponse)); } From bb5f5aac0b25af0a96f465bacf3f8cb5f66324c1 Mon Sep 17 00:00:00 2001 From: imjanghyeok Date: Sat, 11 May 2024 20:24:36 +0900 Subject: [PATCH 215/265] =?UTF-8?q?Refactor:=20=EC=9A=94=EC=B2=AD=20?= =?UTF-8?q?=EC=88=98=EB=9D=BD=EC=8B=9C,=20response=20=EB=A9=94=EC=8B=9C?= =?UTF-8?q?=EC=A7=80=EB=A5=BC=20json=ED=98=95=ED=83=9C=EB=A1=9C=20ws?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20=EC=A0=84=EB=8B=AC=ED=95=98=EA=B8=B0=20?= =?UTF-8?q?=EC=9C=84=ED=95=9C=20dto=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/chatroom/ChatRoomLeftResponse.java | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 src/main/java/capstone/facefriend/chat/service/dto/chatroom/ChatRoomLeftResponse.java diff --git a/src/main/java/capstone/facefriend/chat/service/dto/chatroom/ChatRoomLeftResponse.java b/src/main/java/capstone/facefriend/chat/service/dto/chatroom/ChatRoomLeftResponse.java new file mode 100644 index 0000000000..4ad11d7e9b --- /dev/null +++ b/src/main/java/capstone/facefriend/chat/service/dto/chatroom/ChatRoomLeftResponse.java @@ -0,0 +1,17 @@ +package capstone.facefriend.chat.service.dto.chatroom; + +import capstone.facefriend.member.domain.member.Member; + +public record ChatRoomLeftResponse ( + Long senderId, + String senderNickname, + String content +){ + public static ChatRoomLeftResponse of (Member sender, String content) { + return new ChatRoomLeftResponse( + sender.getId(), + sender.getBasicInfo().getNickname(), + content + ); + } +} From f038d24c69375cb369543ee8b68f2bbaba055d38 Mon Sep 17 00:00:00 2001 From: imjanghyeok Date: Sat, 11 May 2024 20:26:44 +0900 Subject: [PATCH 216/265] =?UTF-8?q?Refactor:=20=EC=B1=84=ED=8C=85=20?= =?UTF-8?q?=EC=9A=94=EC=B2=AD=EC=8B=9C,=20reply=20=EB=A9=94=EC=8B=9C?= =?UTF-8?q?=EC=A7=80=EB=A1=9C=20json=EC=9C=BC=EB=A1=9C=20=EC=A0=84?= =?UTF-8?q?=EB=8B=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../facefriend/chat/service/MessageService.java | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/main/java/capstone/facefriend/chat/service/MessageService.java b/src/main/java/capstone/facefriend/chat/service/MessageService.java index 41b35e11c1..6d46b8867e 100644 --- a/src/main/java/capstone/facefriend/chat/service/MessageService.java +++ b/src/main/java/capstone/facefriend/chat/service/MessageService.java @@ -4,7 +4,8 @@ import capstone.facefriend.chat.exception.ChatException; import capstone.facefriend.chat.exception.ChatExceptionType; import capstone.facefriend.chat.repository.*; -import capstone.facefriend.chat.service.dto.heart.HeartReplyRequestResponse; +import capstone.facefriend.chat.service.dto.heart.HeartReplyRequest; +import capstone.facefriend.chat.service.dto.heart.HeartReplyResponse; import capstone.facefriend.chat.service.dto.heart.SendHeartResponse; import capstone.facefriend.chat.service.dto.message.MessageListResponse; import capstone.facefriend.chat.service.dto.message.MessageRequest; @@ -190,16 +191,16 @@ public void sendHeart(Long senderId, Long receiveId) { redisTemplate.convertAndSend(topic, sendHeartResponse); } @Transactional - public void heartReply(HeartReplyRequestResponse heartReplyRequestResponse, Long receiveId) { + public void heartReply(HeartReplyRequest heartReplyRequest, Long receiveId) { String exceptionDestination = "/sub/chat/" + receiveId; Member receiver = findMemberById(exceptionDestination, receiveId); - Member sender = findMemberById(exceptionDestination, heartReplyRequestResponse.senderId()); + Member sender = findMemberById(exceptionDestination, heartReplyRequest.senderId()); ChatRoomMember chatRoomMember = findSenderReceiver(exceptionDestination, sender.getId(), receiver.getId()); ChatRoom chatRoom = findRoomById(exceptionDestination, chatRoomMember.getChatRoom().getId()); - if (heartReplyRequestResponse.intention().equals("positive")) { + if (heartReplyRequest.intention().equals("positive")) { chatRoom.setStatus(ChatRoom.Status.open); chatRoomRepository.save(chatRoom); chatRoomMemberRepository.save(chatRoomMember); @@ -207,7 +208,7 @@ public void heartReply(HeartReplyRequestResponse heartReplyRequestResponse, Long // 대화 수락 simpMessagingTemplate.convertAndSend(exceptionDestination, "대화 수락"); - } else if (heartReplyRequestResponse.intention().equals("negative")) { + } else if (heartReplyRequest.intention().equals("negative")) { chatRoomMemberRepository.delete(chatRoomMember); chatRoomRepository.delete(chatRoom); // 대화 거절 @@ -220,8 +221,10 @@ public void heartReply(HeartReplyRequestResponse heartReplyRequestResponse, Long // 동적으로 목적지 설정 String destination = "/sub/chat/" + sender.getId(); + HeartReplyResponse heartReplyResponse = HeartReplyResponse.of(receiveId, heartReplyRequest); + // 메시지 전송 - simpMessagingTemplate.convertAndSend(destination, heartReplyRequestResponse.of(receiveId, heartReplyRequestResponse)); + simpMessagingTemplate.convertAndSend(destination, heartReplyResponse); } From e0339fd1c6fe72caa3d89ada78e01261f1205757 Mon Sep 17 00:00:00 2001 From: imjanghyeok Date: Sat, 11 May 2024 20:27:09 +0900 Subject: [PATCH 217/265] =?UTF-8?q?Refactor:=20reply=20request=20record?= =?UTF-8?q?=EB=A1=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../chat/service/dto/heart/HeartReplyRequest.java | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/src/main/java/capstone/facefriend/chat/service/dto/heart/HeartReplyRequest.java b/src/main/java/capstone/facefriend/chat/service/dto/heart/HeartReplyRequest.java index 22b42fdfa4..c680454d96 100644 --- a/src/main/java/capstone/facefriend/chat/service/dto/heart/HeartReplyRequest.java +++ b/src/main/java/capstone/facefriend/chat/service/dto/heart/HeartReplyRequest.java @@ -1,11 +1,6 @@ package capstone.facefriend.chat.service.dto.heart; -import lombok.Getter; -import lombok.Setter; - -@Getter -@Setter -public class HeartReplyRequest { - Long senderId; - String intention; -} \ No newline at end of file +public record HeartReplyRequest( + Long senderId, + String intention +) {} \ No newline at end of file From 172d489456b196c81b39b36eb03e2e19e9720af0 Mon Sep 17 00:00:00 2001 From: imjanghyeok Date: Sat, 11 May 2024 20:27:57 +0900 Subject: [PATCH 218/265] =?UTF-8?q?Refactor:=20HeartReplyResponse=20dto=20?= =?UTF-8?q?record=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../chat/service/dto/heart/HeartReplyResponse.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 src/main/java/capstone/facefriend/chat/service/dto/heart/HeartReplyResponse.java diff --git a/src/main/java/capstone/facefriend/chat/service/dto/heart/HeartReplyResponse.java b/src/main/java/capstone/facefriend/chat/service/dto/heart/HeartReplyResponse.java new file mode 100644 index 0000000000..99bd8c3468 --- /dev/null +++ b/src/main/java/capstone/facefriend/chat/service/dto/heart/HeartReplyResponse.java @@ -0,0 +1,13 @@ +package capstone.facefriend.chat.service.dto.heart; + +public record HeartReplyResponse( + Long senderId, + String intention +) { + public static HeartReplyResponse of (Long senderId, HeartReplyRequest heartReplyRequest) { + return new HeartReplyResponse( + senderId, + heartReplyRequest.intention() + ); + } +} \ No newline at end of file From effaa33e7a187bf51641489f4c44d2393202a639 Mon Sep 17 00:00:00 2001 From: imjanghyeok Date: Sat, 11 May 2024 21:21:05 +0900 Subject: [PATCH 219/265] =?UTF-8?q?Refactor:=20MessageSize=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/capstone/facefriend/chat/service/MessageService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/capstone/facefriend/chat/service/MessageService.java b/src/main/java/capstone/facefriend/chat/service/MessageService.java index 6d46b8867e..a005775878 100644 --- a/src/main/java/capstone/facefriend/chat/service/MessageService.java +++ b/src/main/java/capstone/facefriend/chat/service/MessageService.java @@ -255,7 +255,7 @@ public String exitApplication(Long memberId) { public List getMessagePage(Long roomId, Long memberId, int pageNo) { pageNo = pageNo - 1; - int pageSize = 20; + int pageSize = 40; Sort sort = Sort.by(Sort.Direction.DESC, "sendTime"); Pageable pageable = PageRequest.of(pageNo, pageSize, sort); String chatRoomInfoId = roomId + "/member/" + memberId; From d85c6fcc7802149c8feac753792c81f65c483eef Mon Sep 17 00:00:00 2001 From: imjanghyeok Date: Sun, 12 May 2024 14:56:19 +0900 Subject: [PATCH 220/265] =?UTF-8?q?Refactor:=20=EC=95=88=20=EC=9D=BD?= =?UTF-8?q?=EC=9D=80=20=EB=A9=94=EC=8B=9C=EC=A7=80=20=EB=B0=8F=20=EB=8C=80?= =?UTF-8?q?=ED=99=94=20=EC=9A=94=EC=B2=AD=20=EB=B3=B4=EA=B4=80=20=EB=B6=88?= =?UTF-8?q?=ED=95=84=EC=9A=94=EB=A1=9C=20=EC=BD=94=EB=93=9C=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../chat/service/RedisSubscriber.java | 50 +------------------ 1 file changed, 2 insertions(+), 48 deletions(-) diff --git a/src/main/java/capstone/facefriend/chat/service/RedisSubscriber.java b/src/main/java/capstone/facefriend/chat/service/RedisSubscriber.java index af3b1cf2aa..22db6c6d0b 100644 --- a/src/main/java/capstone/facefriend/chat/service/RedisSubscriber.java +++ b/src/main/java/capstone/facefriend/chat/service/RedisSubscriber.java @@ -31,66 +31,20 @@ public class RedisSubscriber implements MessageListener { @Override public void onMessage(Message message, byte[] pattern) { try { - // redis에서 발행된 데이터를 받아 역직렬화 String publishMessage = (String) redisTemplate.getStringSerializer().deserialize(message.getBody()); if (publishMessage.contains("message")) { - log.info(publishMessage); MessageResponse messageResponse = objectMapper.readValue(publishMessage, MessageResponse.class); - log.info(messageResponse.toString()); GetMessageResponse chatMessageResponse = new GetMessageResponse(messageResponse); - log.info(chatMessageResponse.toString()); - if (isExistSubscriber(messageResponse.getReceiveId())) { - messagingTemplate.convertAndSend("/sub/chat/" + messageResponse.getReceiveId(), chatMessageResponse); - } else { - saveUnReadMessage("/sub/chat" + messageResponse.getReceiveId() + "message", messageResponse); - } - - + messagingTemplate.convertAndSend("/sub/chat/" + messageResponse.getReceiveId(), chatMessageResponse); } else if (publishMessage.contains("Heart")) { SendHeartResponse sendHeartResponse = objectMapper.readValue(publishMessage, SendHeartResponse.class); - GetSendHeartResponse chatSendHeartResponse = new GetSendHeartResponse(sendHeartResponse); - if (isExistSubscriber(chatSendHeartResponse.getReceiveId())) { - messagingTemplate.convertAndSend("/sub/chat/" + sendHeartResponse.getReceiveId(), chatSendHeartResponse); - } else { - saveUnReadHeart("/sub/chat" + sendHeartResponse.getReceiveId() + "heart", sendHeartResponse); - } - + messagingTemplate.convertAndSend("/sub/chat/" + sendHeartResponse.getReceiveId(), chatSendHeartResponse); } } catch (IOException e) { throw new RuntimeException("Failed to process message", e); } } - - private Boolean isExistSubscriber(Long memberId) { - log.info("isExistSubscriber 호출"); - Boolean isMember = redisTemplate.opsForSet().isMember("SocketInfo", memberId); - log.info("Socket: " + memberId); - log.info("SocketInfo: " + isMember); - - return isMember; - } - - - - private void saveUnReadMessage(String destination, MessageResponse messageResponse) { - Boolean isUnRead = redisTemplate.hasKey(destination); - log.info(isUnRead.toString()); - if (isUnRead) { - redisTemplate.opsForList().rightPush(destination, messageResponse); - } else { - redisTemplate.opsForList().rightPush(destination, messageResponse); - } - } - - private void saveUnReadHeart(String destination, SendHeartResponse sendHeartResponse) { - Boolean isUnRead = redisTemplate.hasKey(destination); - if (isUnRead) { - redisTemplate.opsForList().rightPush(destination, sendHeartResponse); - } else { - redisTemplate.opsForList().rightPush(destination, sendHeartResponse); - } - } } \ No newline at end of file From 2a9047f483483e42b0dc024c56a525756ba57c81 Mon Sep 17 00:00:00 2001 From: imjanghyeok Date: Sun, 12 May 2024 14:56:41 +0900 Subject: [PATCH 221/265] =?UTF-8?q?Refactor:=20=EC=95=88=20=EC=9D=BD?= =?UTF-8?q?=EC=9D=80=20=EB=A9=94=EC=8B=9C=EC=A7=80=20=EB=B0=8F=20=EB=8C=80?= =?UTF-8?q?=ED=99=94=20=EC=9A=94=EC=B2=AD=20=EB=B3=B4=EA=B4=80=20=EB=B6=88?= =?UTF-8?q?=ED=95=84=EC=9A=94=EB=A1=9C=20=EC=BD=94=EB=93=9C=20service=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20=EC=88=98=EC=A0=95=20=EB=B0=8F=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../chat/service/MessageService.java | 71 ------------------- 1 file changed, 71 deletions(-) diff --git a/src/main/java/capstone/facefriend/chat/service/MessageService.java b/src/main/java/capstone/facefriend/chat/service/MessageService.java index a005775878..84721398c6 100644 --- a/src/main/java/capstone/facefriend/chat/service/MessageService.java +++ b/src/main/java/capstone/facefriend/chat/service/MessageService.java @@ -227,32 +227,6 @@ public void heartReply(HeartReplyRequest heartReplyRequest, Long receiveId) { simpMessagingTemplate.convertAndSend(destination, heartReplyResponse); } - - @Transactional - public void enterApplication(Long memberId) { - String exceptionDestination = "/sub/chat/" + memberId; - SocketInfo socketInfo = new SocketInfo(); - socketInfo.setMemberId(memberId); - socketInfo.setConnectTime(LocalDateTime.now()); - socketInfoRedisRepository.save(socketInfo); - if (isExistUnReadMessage(memberId)) { - sendSentMessage(memberId); - } - - if(isExistUnSendHeart(memberId)) { - sendSentHeart(memberId); - } - - simpMessagingTemplate.convertAndSend(exceptionDestination, "저장 성공"); - } - - @Transactional - public String exitApplication(Long memberId) { - SocketInfo socketInfo = findSocketInfo(memberId); - socketInfoRedisRepository.delete(socketInfo); - return "성공"; - } - public List getMessagePage(Long roomId, Long memberId, int pageNo) { pageNo = pageNo - 1; int pageSize = 40; @@ -269,49 +243,4 @@ public List getMessagePage(Long roomId, Long memberId, int } return messageListResponses; } - - private Boolean isExistUnReadMessage(Long memberId) { - Boolean isUnRead = redisTemplate.hasKey("/sub/chat/" + memberId + "message"); - log.info(isUnRead.toString()); - return !isUnRead; - } - - private Boolean isExistUnSendHeart(Long memberId) { - Boolean isUnRead = redisTemplate.hasKey("/sub/chat/" + memberId + "SendHeart"); - log.info(isUnRead.toString()); - return !isUnRead; - } - - private void sendSentMessage(Long receiveId) { - String topic = channelTopic.getTopic(); - String destination = "/sub/chat" + receiveId + "message"; - Long messagesListSize = redisTemplate.opsForList().size(destination); - log.info(messagesListSize.toString()); - log.info("messageList: {}", redisTemplate.opsForList().range(destination, 0, -1)); - - if (messagesListSize > 0) { - for (Long i = messagesListSize; i > 0; i--) { - // 맵으로 받음 - LinkedHashMap map = (LinkedHashMap) redisTemplate.opsForList().rightPop(destination); - MessageResponse messageResponse = (MessageResponse) map.get(destination); - log.info("messageResponse: {}", messageResponse.toString()); - redisTemplate.convertAndSend(topic, messageResponse); - } - } - } - - private void sendSentHeart(Long receiveId) { - String topic = channelTopic.getTopic(); - String destination = "/sub/chat" + receiveId + "heart"; - Long messagesListSize = redisTemplate.opsForList().size(destination); - log.info("SendHeartListSize: {}", messagesListSize.toString()); - if (messagesListSize > 0) { - for (Long i = messagesListSize; i > 0; i--) { - LinkedHashMap map = (LinkedHashMap) redisTemplate.opsForList().rightPop(destination); - SendHeartResponse sendHeartResponse = (SendHeartResponse) map.get(destination); - log.info("messageResponse: {}", sendHeartResponse.toString()); - redisTemplate.convertAndSend(topic, sendHeartResponse); - } - } - } } \ No newline at end of file From 77d80de7980f8330acaf170265665b14b30f0b68 Mon Sep 17 00:00:00 2001 From: imjanghyeok Date: Sun, 12 May 2024 14:57:14 +0900 Subject: [PATCH 222/265] =?UTF-8?q?Refactor:=20=EC=95=88=20=EC=9D=BD?= =?UTF-8?q?=EC=9D=80=20=EB=A9=94=EC=8B=9C=EC=A7=80=20=EB=B0=8F=20=EB=8C=80?= =?UTF-8?q?=ED=99=94=20=EC=9A=94=EC=B2=AD=20=EB=B3=B4=EA=B4=80=20=EB=B6=88?= =?UTF-8?q?=ED=95=84=EC=9A=94=EB=A1=9C=20controller=EC=97=90=EC=84=9C=20ap?= =?UTF-8?q?i=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../chat/controller/MessageController.java | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/src/main/java/capstone/facefriend/chat/controller/MessageController.java b/src/main/java/capstone/facefriend/chat/controller/MessageController.java index 54001ef6fb..5ce6840308 100644 --- a/src/main/java/capstone/facefriend/chat/controller/MessageController.java +++ b/src/main/java/capstone/facefriend/chat/controller/MessageController.java @@ -25,24 +25,6 @@ public class MessageController { private final MessageService messageService; private final JwtProvider jwtProvider; - @MessageMapping("/stomp/connect") - public void enterApp( - StompHeaderAccessor headerAccessor - ){ - String authorizationHeader = headerAccessor.getFirstNativeHeader("Authorization"); - String token = authorizationHeader.substring(BEARER_PREFIX.length()); - Long memberId = jwtProvider.extractId(token); - messageService.enterApplication(memberId); - } - - @PostMapping("/stomp/disconnect") - public String exitApp( - @AuthMember Long memberId - ){ - String msg = messageService.exitApplication(memberId); - return msg; - } - @GetMapping("/chat/{roomId}/messages") public ResponseEntity> getMessagesPage( @PathVariable("roomId") Long roomId, From a5dd9e33fe48e76b5ff0cf695f0a2f220a138d96 Mon Sep 17 00:00:00 2001 From: imjanghyeok Date: Sun, 12 May 2024 16:09:50 +0900 Subject: [PATCH 223/265] =?UTF-8?q?Refactor:=20getMessagePage=20api=20?= =?UTF-8?q?=EB=A6=AC=ED=8C=A9=ED=86=A0=EB=A7=81=20=EB=B0=8F=20=EC=9D=B4?= =?UTF-8?q?=EC=A0=84=20=EB=A9=94=EC=84=9C=EB=93=9C=20=EB=8B=A4=EC=8B=9C=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../chat/service/MessageService.java | 79 +++++++++++++++++-- 1 file changed, 74 insertions(+), 5 deletions(-) diff --git a/src/main/java/capstone/facefriend/chat/service/MessageService.java b/src/main/java/capstone/facefriend/chat/service/MessageService.java index 84721398c6..628ac12647 100644 --- a/src/main/java/capstone/facefriend/chat/service/MessageService.java +++ b/src/main/java/capstone/facefriend/chat/service/MessageService.java @@ -7,6 +7,7 @@ import capstone.facefriend.chat.service.dto.heart.HeartReplyRequest; import capstone.facefriend.chat.service.dto.heart.HeartReplyResponse; import capstone.facefriend.chat.service.dto.heart.SendHeartResponse; +import capstone.facefriend.chat.service.dto.message.MessageListRequest; import capstone.facefriend.chat.service.dto.message.MessageListResponse; import capstone.facefriend.chat.service.dto.message.MessageRequest; import capstone.facefriend.chat.service.dto.message.MessageResponse; @@ -227,15 +228,13 @@ public void heartReply(HeartReplyRequest heartReplyRequest, Long receiveId) { simpMessagingTemplate.convertAndSend(destination, heartReplyResponse); } - public List getMessagePage(Long roomId, Long memberId, int pageNo) { + public List getMessagePage(Long roomId, int pageNo, MessageListRequest messageListRequest) { pageNo = pageNo - 1; int pageSize = 40; Sort sort = Sort.by(Sort.Direction.DESC, "sendTime"); Pageable pageable = PageRequest.of(pageNo, pageSize, sort); - String chatRoomInfoId = roomId + "/member/" + memberId; - ChatRoomInfo chatRoomInfo = findChatRoomInfo(chatRoomInfoId); - LocalDateTime time = chatRoomInfo.getEnterTime(); - List chatMessagePage = chatMessageRepository.findChatMessagesByChatRoom_IdAndSendTimeBefore(roomId, time, pageable); + LocalDateTime time2 = messageListRequest.sendTime(); + List chatMessagePage = chatMessageRepository.findChatMessagesByChatRoom_IdAndSendTimeBefore(roomId, time2, pageable); List messageListResponses = new ArrayList<>(); for (ChatMessage chatMessage : chatMessagePage){ MessageListResponse messageListResponse = MessageListResponse.of(chatMessage); @@ -243,4 +242,74 @@ public List getMessagePage(Long roomId, Long memberId, int } return messageListResponses; } + + @Transactional + public void enterApplication(Long memberId) { + String exceptionDestination = "/sub/chat/" + memberId; + SocketInfo socketInfo = new SocketInfo(); + socketInfo.setMemberId(memberId); + socketInfo.setConnectTime(LocalDateTime.now()); + socketInfoRedisRepository.save(socketInfo); + if (isExistUnReadMessage(memberId)) { + sendSentMessage(memberId); + } + + if(isExistUnSendHeart(memberId)) { + sendSentHeart(memberId); + } + + simpMessagingTemplate.convertAndSend(exceptionDestination, "저장 성공"); + } + + @Transactional + public String exitApplication(Long memberId) { + SocketInfo socketInfo = findSocketInfo(memberId); + socketInfoRedisRepository.delete(socketInfo); + return "성공"; + } + + private Boolean isExistUnReadMessage(Long memberId) { + Boolean isUnRead = redisTemplate.hasKey("/sub/chat/" + memberId + "message"); + log.info(isUnRead.toString()); + return !isUnRead; + } + + private Boolean isExistUnSendHeart(Long memberId) { + Boolean isUnRead = redisTemplate.hasKey("/sub/chat/" + memberId + "SendHeart"); + log.info(isUnRead.toString()); + return !isUnRead; + } + + private void sendSentMessage(Long receiveId) { + String topic = channelTopic.getTopic(); + String destination = "/sub/chat" + receiveId + "message"; + Long messagesListSize = redisTemplate.opsForList().size(destination); + log.info(messagesListSize.toString()); + log.info("messageList: {}", redisTemplate.opsForList().range(destination, 0, -1)); + + if (messagesListSize > 0) { + for (Long i = messagesListSize; i > 0; i--) { + // 맵으로 받음 + LinkedHashMap map = (LinkedHashMap) redisTemplate.opsForList().rightPop(destination); + MessageResponse messageResponse = (MessageResponse) map.get(destination); + log.info("messageResponse: {}", messageResponse.toString()); + redisTemplate.convertAndSend(topic, messageResponse); + } + } + } + + private void sendSentHeart(Long receiveId) { + String topic = channelTopic.getTopic(); + String destination = "/sub/chat" + receiveId + "heart"; + Long messagesListSize = redisTemplate.opsForList().size(destination); + log.info("SendHeartListSize: {}", messagesListSize.toString()); + if (messagesListSize > 0) { + for (Long i = messagesListSize; i > 0; i--) { + LinkedHashMap map = (LinkedHashMap) redisTemplate.opsForList().rightPop(destination); + SendHeartResponse sendHeartResponse = (SendHeartResponse) map.get(destination); + log.info("messageResponse: {}", sendHeartResponse.toString()); + redisTemplate.convertAndSend(topic, sendHeartResponse); + } + } + } } \ No newline at end of file From 1d63f962650b922e7b8b99019e1b7ecde8d6be91 Mon Sep 17 00:00:00 2001 From: imjanghyeok Date: Sun, 12 May 2024 16:10:25 +0900 Subject: [PATCH 224/265] =?UTF-8?q?Refactor:=20=EC=9D=B4=EC=A0=84=20?= =?UTF-8?q?=EB=A9=94=EC=84=9C=EB=93=9C=20=EB=8B=A4=EC=8B=9C=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=EC=97=90=20=EB=94=B0=EB=A5=B8=20RedisSubscriber=20?= =?UTF-8?q?=EC=9D=B4=EC=A0=84=20=EB=B2=84=EC=A0=84=EC=9C=BC=EB=A1=9C=20?= =?UTF-8?q?=EB=B3=B5=EA=B5=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../chat/service/RedisSubscriber.java | 48 ++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/src/main/java/capstone/facefriend/chat/service/RedisSubscriber.java b/src/main/java/capstone/facefriend/chat/service/RedisSubscriber.java index 22db6c6d0b..8d96edf7a7 100644 --- a/src/main/java/capstone/facefriend/chat/service/RedisSubscriber.java +++ b/src/main/java/capstone/facefriend/chat/service/RedisSubscriber.java @@ -33,18 +33,64 @@ public void onMessage(Message message, byte[] pattern) { try { // redis에서 발행된 데이터를 받아 역직렬화 String publishMessage = (String) redisTemplate.getStringSerializer().deserialize(message.getBody()); - if (publishMessage.contains("message")) { + log.info(publishMessage); MessageResponse messageResponse = objectMapper.readValue(publishMessage, MessageResponse.class); + log.info(messageResponse.toString()); GetMessageResponse chatMessageResponse = new GetMessageResponse(messageResponse); + log.info(chatMessageResponse.toString()); + if (isExistSubscriber(messageResponse.getReceiveId())) { + messagingTemplate.convertAndSend("/sub/chat/" + messageResponse.getReceiveId(), chatMessageResponse); + } else { + saveUnReadMessage("/sub/chat" + messageResponse.getReceiveId() + "message", messageResponse); + } + + messagingTemplate.convertAndSend("/sub/chat/" + messageResponse.getReceiveId(), chatMessageResponse); } else if (publishMessage.contains("Heart")) { SendHeartResponse sendHeartResponse = objectMapper.readValue(publishMessage, SendHeartResponse.class); + GetSendHeartResponse chatSendHeartResponse = new GetSendHeartResponse(sendHeartResponse); + if (isExistSubscriber(chatSendHeartResponse.getReceiveId())) { + messagingTemplate.convertAndSend("/sub/chat/" + sendHeartResponse.getReceiveId(), chatSendHeartResponse); + } else { + saveUnReadHeart("/sub/chat" + sendHeartResponse.getReceiveId() + "heart", sendHeartResponse); + } + messagingTemplate.convertAndSend("/sub/chat/" + sendHeartResponse.getReceiveId(), chatSendHeartResponse); } } catch (IOException e) { throw new RuntimeException("Failed to process message", e); } } + + private Boolean isExistSubscriber(Long memberId) { + log.info("isExistSubscriber 호출"); + Boolean isMember = redisTemplate.opsForSet().isMember("SocketInfo", memberId); + log.info("Socket: " + memberId); + log.info("SocketInfo: " + isMember); + + return isMember; + } + + + + private void saveUnReadMessage(String destination, MessageResponse messageResponse) { + Boolean isUnRead = redisTemplate.hasKey(destination); + log.info(isUnRead.toString()); + if (isUnRead) { + redisTemplate.opsForList().rightPush(destination, messageResponse); + } else { + redisTemplate.opsForList().rightPush(destination, messageResponse); + } + } + + private void saveUnReadHeart(String destination, SendHeartResponse sendHeartResponse) { + Boolean isUnRead = redisTemplate.hasKey(destination); + if (isUnRead) { + redisTemplate.opsForList().rightPush(destination, sendHeartResponse); + } else { + redisTemplate.opsForList().rightPush(destination, sendHeartResponse); + } + } } \ No newline at end of file From a75efc86ae7bca78536c11499f1be8318de55461 Mon Sep 17 00:00:00 2001 From: imjanghyeok Date: Sun, 12 May 2024 16:10:53 +0900 Subject: [PATCH 225/265] =?UTF-8?q?Refactor:=20getMessagePage=20api=20requ?= =?UTF-8?q?est=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../chat/service/dto/message/MessageListRequest.java | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 src/main/java/capstone/facefriend/chat/service/dto/message/MessageListRequest.java diff --git a/src/main/java/capstone/facefriend/chat/service/dto/message/MessageListRequest.java b/src/main/java/capstone/facefriend/chat/service/dto/message/MessageListRequest.java new file mode 100644 index 0000000000..c8c19b6791 --- /dev/null +++ b/src/main/java/capstone/facefriend/chat/service/dto/message/MessageListRequest.java @@ -0,0 +1,11 @@ +package capstone.facefriend.chat.service.dto.message; + +import org.springframework.format.annotation.DateTimeFormat; + +import java.time.LocalDateTime; + +public record MessageListRequest( + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm") + LocalDateTime sendTime +) { +} From fb2f8ed4d8c98144a491080ffa2f43e8d8a61a66 Mon Sep 17 00:00:00 2001 From: imjanghyeok Date: Sun, 12 May 2024 16:10:58 +0900 Subject: [PATCH 226/265] =?UTF-8?q?Refactor:=20getMessagePage=20api=20requ?= =?UTF-8?q?est=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../chat/controller/MessageController.java | 26 ++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/src/main/java/capstone/facefriend/chat/controller/MessageController.java b/src/main/java/capstone/facefriend/chat/controller/MessageController.java index 5ce6840308..bba738abda 100644 --- a/src/main/java/capstone/facefriend/chat/controller/MessageController.java +++ b/src/main/java/capstone/facefriend/chat/controller/MessageController.java @@ -5,6 +5,7 @@ import capstone.facefriend.chat.service.MessageService; import capstone.facefriend.chat.service.dto.heart.HeartReplyRequest; import capstone.facefriend.chat.service.dto.heart.SendHeartRequest; +import capstone.facefriend.chat.service.dto.message.MessageListRequest; import capstone.facefriend.chat.service.dto.message.MessageListResponse; import capstone.facefriend.chat.service.dto.message.MessageRequest; import lombok.RequiredArgsConstructor; @@ -25,13 +26,32 @@ public class MessageController { private final MessageService messageService; private final JwtProvider jwtProvider; + @MessageMapping("/stomp/connect") + public void enterApp( + StompHeaderAccessor headerAccessor + ){ + String authorizationHeader = headerAccessor.getFirstNativeHeader("Authorization"); + String token = authorizationHeader.substring(BEARER_PREFIX.length()); + Long memberId = jwtProvider.extractId(token); + messageService.enterApplication(memberId); + } + + @PostMapping("/stomp/disconnect") + public String exitApp( + @AuthMember Long memberId + ){ + String msg = messageService.exitApplication(memberId); + return msg; + } + @GetMapping("/chat/{roomId}/messages") public ResponseEntity> getMessagesPage( @PathVariable("roomId") Long roomId, - @AuthMember Long memberId, - @RequestParam(required = false, defaultValue = "1", value = "page") int pageNo +// @AuthMember Long memberId, + @RequestParam(required = false, defaultValue = "1", value = "page") int pageNo, + @RequestBody MessageListRequest messageListRequest ){ - return ResponseEntity.ok(messageService.getMessagePage(roomId, memberId,pageNo)); + return ResponseEntity.ok(messageService.getMessagePage(roomId, pageNo, messageListRequest)); } From 4d316ce94b91a7c292eabd1030fb4762809260fc Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Mon, 13 May 2024 17:08:47 +0900 Subject: [PATCH 227/265] =?UTF-8?q?fix:=20resume=20images=20null=20?= =?UTF-8?q?=EC=9D=BC=20=EB=95=8C=20=EB=B0=98=EB=B3=B5=EB=AC=B8=20=EB=8F=8C?= =?UTF-8?q?=EC=A7=80=20=EC=95=8A=EA=B3=A0=20=EB=B9=88=20=EB=A6=AC=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EB=B0=98=ED=99=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../facefriend/bucket/BucketService.java | 37 +++++++++---------- 1 file changed, 17 insertions(+), 20 deletions(-) diff --git a/src/main/java/capstone/facefriend/bucket/BucketService.java b/src/main/java/capstone/facefriend/bucket/BucketService.java index 7c5676b804..e0464225a9 100644 --- a/src/main/java/capstone/facefriend/bucket/BucketService.java +++ b/src/main/java/capstone/facefriend/bucket/BucketService.java @@ -131,27 +131,24 @@ public List uploadResumeImages( List resumeImageS3urls = new ArrayList<>(); - for (MultipartFile image : images) { - - if (image.isEmpty() || image.getSize() == 0) { - return List.of(); + if (images != null) { + for (MultipartFile image : images) { + ObjectMetadata metadata = new ObjectMetadata(); + metadata.setContentLength(image.getInputStream().available()); + metadata.setContentType(image.getContentType()); + + String imageObjectName = UUID.randomUUID() + resumePostfix; + + amazonS3.putObject( + new PutObjectRequest( + bucketName, + imageObjectName, + image.getInputStream(), + metadata + ).withCannedAcl(CannedAccessControlList.PublicRead) + ); + resumeImageS3urls.add(amazonS3.getUrl(bucketName, imageObjectName).toString()); } - - ObjectMetadata metadata = new ObjectMetadata(); - metadata.setContentLength(image.getInputStream().available()); - metadata.setContentType(image.getContentType()); - - String imageObjectName = UUID.randomUUID() + resumePostfix; - - amazonS3.putObject( - new PutObjectRequest( - bucketName, - imageObjectName, - image.getInputStream(), - metadata - ).withCannedAcl(CannedAccessControlList.PublicRead) - ); - resumeImageS3urls.add(amazonS3.getUrl(bucketName, imageObjectName).toString()); } return resumeImageS3urls; } From 61b6f4aa13d0a0acde7e69fec3ead06e50ca7c17 Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Mon, 13 May 2024 17:34:40 +0900 Subject: [PATCH 228/265] =?UTF-8?q?fix:=20resume=20images=20put=20size=200?= =?UTF-8?q?=20=EB=94=94=EB=B2=84=EA=B7=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../capstone/facefriend/resume/service/ResumeService.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/capstone/facefriend/resume/service/ResumeService.java b/src/main/java/capstone/facefriend/resume/service/ResumeService.java index 0991394a41..cb4975ca85 100644 --- a/src/main/java/capstone/facefriend/resume/service/ResumeService.java +++ b/src/main/java/capstone/facefriend/resume/service/ResumeService.java @@ -121,6 +121,10 @@ public ResumePostPutResponse putMyResume( Member me = findMemberById(memberId); Resume mine = findResumeByMember(me); // 영속 상태 + if (request.images().size() == 0 || request.images() == null) { + throw new ResumeException(MUST_UPLOAD_ONE_IMAGE); + } + List resumeImageS3urls = bucketService.updateResumeImages(request.images(), mine); mine.setResumeImageS3urls(resumeImageS3urls); // dirty check From e1ae3d83f3402d7d4fb638c0e59d10fd176cb27c Mon Sep 17 00:00:00 2001 From: imjanghyeok Date: Mon, 13 May 2024 19:19:26 +0900 Subject: [PATCH 229/265] =?UTF-8?q?Refactor:=20message=20list=20=EC=9A=94?= =?UTF-8?q?=EC=B2=AD=20http=20method=20get=EC=97=90=EC=84=9C=20post?= =?UTF-8?q?=EB=A1=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../capstone/facefriend/chat/controller/MessageController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/capstone/facefriend/chat/controller/MessageController.java b/src/main/java/capstone/facefriend/chat/controller/MessageController.java index bba738abda..a96131170e 100644 --- a/src/main/java/capstone/facefriend/chat/controller/MessageController.java +++ b/src/main/java/capstone/facefriend/chat/controller/MessageController.java @@ -44,7 +44,7 @@ public String exitApp( return msg; } - @GetMapping("/chat/{roomId}/messages") + @PostMapping("/chat/{roomId}/messages") public ResponseEntity> getMessagesPage( @PathVariable("roomId") Long roomId, // @AuthMember Long memberId, From 9dbc89ec8008e6374fe3bbb65d59d9b17651db0c Mon Sep 17 00:00:00 2001 From: imjanghyeok Date: Wed, 15 May 2024 11:03:31 +0900 Subject: [PATCH 230/265] =?UTF-8?q?Refactor:=20=ED=94=84=EB=A1=A0=ED=8A=B8?= =?UTF-8?q?=EC=97=90=EC=84=9C=20=EA=B5=AC=EB=B6=84=ED=95=A0=20=EC=88=98=20?= =?UTF-8?q?=EC=9E=88=EA=B2=8C=20=EB=A9=94=EC=84=9C=EB=93=9C=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=ED=95=98=EC=97=AC=20stomp=20response=20=EB=B3=B4?= =?UTF-8?q?=EB=82=B4=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../chat/service/MessageService.java | 23 +++++++++++++++---- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/src/main/java/capstone/facefriend/chat/service/MessageService.java b/src/main/java/capstone/facefriend/chat/service/MessageService.java index 628ac12647..b810337eb9 100644 --- a/src/main/java/capstone/facefriend/chat/service/MessageService.java +++ b/src/main/java/capstone/facefriend/chat/service/MessageService.java @@ -133,6 +133,7 @@ public void sendMessage(MessageRequest messageRequest, Long senderId) { chatMessageRepository.save(chatMessage); MessageResponse messageResponse = new MessageResponse(); + messageResponse.setMethod("receiveChat"); messageResponse.setRoomId(chatMessage.getChatRoom().getId()); messageResponse.setSenderId(senderId); messageResponse.setReceiveId(receiver.getId()); @@ -180,12 +181,16 @@ public void sendHeart(Long senderId, Long receiveId) { chatRoomMemberRepository.save(chatRoomMember); SendHeartResponse sendHeartResponse = new SendHeartResponse(); - sendHeartResponse.setRoomId(chatRoom.getId()); - sendHeartResponse.setSenderId(sender.getId()); - sendHeartResponse.setReceiveId(receiveId); + sendHeartResponse.setMethod("receiveHeart"); + sendHeartResponse.setMemberId(receiveId); sendHeartResponse.setSenderName(sender.getBasicInfo().getNickname()); - sendHeartResponse.setCreatedAt(LocalDateTime.now()); + sendHeartResponse.setSenderId(sender.getId()); sendHeartResponse.setType("Heart"); + sendHeartResponse.setSenderOriginS3url(sender.getFaceInfo().getOriginS3url()); + sendHeartResponse.setSenderGeneratedS3url(sender.getFaceInfo().getGeneratedS3url()); + sendHeartResponse.setChatRoom(chatRoom); + sendHeartResponse.setCreatedAt(LocalDateTime.now()); + sendHeartResponse.setSender(false); String topic = channelTopic.getTopic(); simpMessagingTemplate.convertAndSend(exceptionDestination, "대화 요청 성공"); @@ -222,7 +227,9 @@ public void heartReply(HeartReplyRequest heartReplyRequest, Long receiveId) { // 동적으로 목적지 설정 String destination = "/sub/chat/" + sender.getId(); - HeartReplyResponse heartReplyResponse = HeartReplyResponse.of(receiveId, heartReplyRequest); + String method = "receiveHeartResponse"; + + HeartReplyResponse heartReplyResponse = HeartReplyResponse.of(receiveId, heartReplyRequest, method); // 메시지 전송 simpMessagingTemplate.convertAndSend(destination, heartReplyResponse); @@ -252,10 +259,14 @@ public void enterApplication(Long memberId) { socketInfoRedisRepository.save(socketInfo); if (isExistUnReadMessage(memberId)) { sendSentMessage(memberId); + } else { + simpMessagingTemplate.convertAndSend(exceptionDestination, "큐잉된 메시지가 없습니다."); } if(isExistUnSendHeart(memberId)) { sendSentHeart(memberId); + } else { + simpMessagingTemplate.convertAndSend(exceptionDestination, "큐잉된 대화요청이 없습니다."); } simpMessagingTemplate.convertAndSend(exceptionDestination, "저장 성공"); @@ -293,6 +304,7 @@ private void sendSentMessage(Long receiveId) { LinkedHashMap map = (LinkedHashMap) redisTemplate.opsForList().rightPop(destination); MessageResponse messageResponse = (MessageResponse) map.get(destination); log.info("messageResponse: {}", messageResponse.toString()); + messageResponse.setMethod("connectChat"); redisTemplate.convertAndSend(topic, messageResponse); } } @@ -308,6 +320,7 @@ private void sendSentHeart(Long receiveId) { LinkedHashMap map = (LinkedHashMap) redisTemplate.opsForList().rightPop(destination); SendHeartResponse sendHeartResponse = (SendHeartResponse) map.get(destination); log.info("messageResponse: {}", sendHeartResponse.toString()); + sendHeartResponse.setMethod("connectHeart"); redisTemplate.convertAndSend(topic, sendHeartResponse); } } From 64d406c66d289a534400e578fe58ee9899b17357 Mon Sep 17 00:00:00 2001 From: imjanghyeok Date: Wed, 15 May 2024 11:04:00 +0900 Subject: [PATCH 231/265] =?UTF-8?q?Refactor:=20=ED=94=84=EB=A1=A0=ED=8A=B8?= =?UTF-8?q?=EC=97=90=EC=84=9C=20=EA=B5=AC=EB=B6=84=ED=95=A0=20=EC=88=98=20?= =?UTF-8?q?=EC=9E=88=EA=B2=8C=20=EB=A9=94=EC=84=9C=EB=93=9C=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=ED=95=98=EC=97=AC=20stomp=20message=20response=20dto?= =?UTF-8?q?=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../facefriend/chat/service/dto/message/MessageResponse.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/capstone/facefriend/chat/service/dto/message/MessageResponse.java b/src/main/java/capstone/facefriend/chat/service/dto/message/MessageResponse.java index 9236902bff..827d06a8ac 100644 --- a/src/main/java/capstone/facefriend/chat/service/dto/message/MessageResponse.java +++ b/src/main/java/capstone/facefriend/chat/service/dto/message/MessageResponse.java @@ -13,6 +13,7 @@ @NoArgsConstructor @AllArgsConstructor public class MessageResponse implements Serializable { + private String method; private Long roomId; private Long receiveId; private Long senderId; From 4f4e4a40b02d54dbdfff6b04e1e4d64a418ff229 Mon Sep 17 00:00:00 2001 From: imjanghyeok Date: Wed, 15 May 2024 11:04:24 +0900 Subject: [PATCH 232/265] =?UTF-8?q?Refactor:=20=ED=94=84=EB=A1=A0=ED=8A=B8?= =?UTF-8?q?=EC=97=90=EC=84=9C=20=EA=B5=AC=EB=B6=84=ED=95=A0=20=EC=88=98=20?= =?UTF-8?q?=EC=9E=88=EA=B2=8C=20=EB=A9=94=EC=84=9C=EB=93=9C=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=ED=95=98=EC=97=AC=20stomp=20response=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD=EC=97=90=20=EC=9D=98=ED=95=9C=20=EB=A1=9C=EC=A7=81=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../capstone/facefriend/chat/service/RedisSubscriber.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/capstone/facefriend/chat/service/RedisSubscriber.java b/src/main/java/capstone/facefriend/chat/service/RedisSubscriber.java index 8d96edf7a7..53209dbe3f 100644 --- a/src/main/java/capstone/facefriend/chat/service/RedisSubscriber.java +++ b/src/main/java/capstone/facefriend/chat/service/RedisSubscriber.java @@ -51,13 +51,13 @@ public void onMessage(Message message, byte[] pattern) { SendHeartResponse sendHeartResponse = objectMapper.readValue(publishMessage, SendHeartResponse.class); GetSendHeartResponse chatSendHeartResponse = new GetSendHeartResponse(sendHeartResponse); - if (isExistSubscriber(chatSendHeartResponse.getReceiveId())) { - messagingTemplate.convertAndSend("/sub/chat/" + sendHeartResponse.getReceiveId(), chatSendHeartResponse); + if (isExistSubscriber(chatSendHeartResponse.getMemberId())) { + messagingTemplate.convertAndSend("/sub/chat/" + sendHeartResponse.getMemberId(), chatSendHeartResponse); } else { - saveUnReadHeart("/sub/chat" + sendHeartResponse.getReceiveId() + "heart", sendHeartResponse); + saveUnReadHeart("/sub/chat" + sendHeartResponse.getMemberId() + "heart", sendHeartResponse); } - messagingTemplate.convertAndSend("/sub/chat/" + sendHeartResponse.getReceiveId(), chatSendHeartResponse); + messagingTemplate.convertAndSend("/sub/chat/" + sendHeartResponse.getMemberId(), chatSendHeartResponse); } } catch (IOException e) { throw new RuntimeException("Failed to process message", e); From 703d1bcab03e7aa93acb1e3110336a4f3a4ce5e0 Mon Sep 17 00:00:00 2001 From: imjanghyeok Date: Wed, 15 May 2024 11:04:44 +0900 Subject: [PATCH 233/265] =?UTF-8?q?Refactor:=20=ED=94=84=EB=A1=A0=ED=8A=B8?= =?UTF-8?q?=EC=97=90=EC=84=9C=20=EA=B5=AC=EB=B6=84=ED=95=A0=20=EC=88=98=20?= =?UTF-8?q?=EC=9E=88=EA=B2=8C=20=EB=A9=94=EC=84=9C=EB=93=9C=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=ED=95=98=EC=97=AC=20=EC=B1=84=ED=8C=85=EB=B0=A9=20?= =?UTF-8?q?=EB=82=98=EA=B0=80=EA=B8=B0=20dto=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/dto/chatroom/ChatRoomLeftResponse.java | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/main/java/capstone/facefriend/chat/service/dto/chatroom/ChatRoomLeftResponse.java b/src/main/java/capstone/facefriend/chat/service/dto/chatroom/ChatRoomLeftResponse.java index 4ad11d7e9b..cc8618c57b 100644 --- a/src/main/java/capstone/facefriend/chat/service/dto/chatroom/ChatRoomLeftResponse.java +++ b/src/main/java/capstone/facefriend/chat/service/dto/chatroom/ChatRoomLeftResponse.java @@ -2,15 +2,23 @@ import capstone.facefriend.member.domain.member.Member; +import java.time.LocalDateTime; + public record ChatRoomLeftResponse ( + String method, + Long roomId, Long senderId, String senderNickname, + LocalDateTime sendTime, String content ){ - public static ChatRoomLeftResponse of (Member sender, String content) { + public static ChatRoomLeftResponse of (String method, Long roomId, Member sender, LocalDateTime sendTime, String content) { return new ChatRoomLeftResponse( + method, + roomId, sender.getId(), sender.getBasicInfo().getNickname(), + sendTime, content ); } From 04a966d5fc60f6f31d5df53a32c99bc2f669da27 Mon Sep 17 00:00:00 2001 From: imjanghyeok Date: Wed, 15 May 2024 11:04:55 +0900 Subject: [PATCH 234/265] =?UTF-8?q?Refactor:=20=ED=94=84=EB=A1=A0=ED=8A=B8?= =?UTF-8?q?=EC=97=90=EC=84=9C=20=EA=B5=AC=EB=B6=84=ED=95=A0=20=EC=88=98=20?= =?UTF-8?q?=EC=9E=88=EA=B2=8C=20=EB=A9=94=EC=84=9C=EB=93=9C=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=ED=95=98=EC=97=AC=20=EC=B1=84=ED=8C=85=EB=B0=A9=20?= =?UTF-8?q?=EB=82=98=EA=B0=80=EA=B8=B0=20service=20code=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../capstone/facefriend/chat/service/ChatRoomService.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/java/capstone/facefriend/chat/service/ChatRoomService.java b/src/main/java/capstone/facefriend/chat/service/ChatRoomService.java index da8ac6e2f9..0e3f03604e 100644 --- a/src/main/java/capstone/facefriend/chat/service/ChatRoomService.java +++ b/src/main/java/capstone/facefriend/chat/service/ChatRoomService.java @@ -182,7 +182,11 @@ public String leftRoom(Long roomId, Long memberId) { return "속해있지 않은 채팅방입니다."; } chatRoom.setStatus(ChatRoom.Status.close); - simpMessagingTemplate.convertAndSend("/sub/chat/" + sender.getId(), ChatRoomLeftResponse.of(sender, "상대방이 떠났습니다.")); + String content = "상대방이 떠났습니다."; + String method = "receiveLeftRoom"; + LocalDateTime sendTime = LocalDateTime.now(); + ChatRoomLeftResponse chatRoomLeftResponse =ChatRoomLeftResponse.of(method, roomId, leftMember, sendTime, content); + simpMessagingTemplate.convertAndSend("/sub/chat/" + sender.getId(),chatRoomLeftResponse); chatRoomRepository.save(chatRoom); chatRoomMemberRepository.save(chatRoomMember); From 3db34e0cf609e0988df32af56e1b5647d4b50ea7 Mon Sep 17 00:00:00 2001 From: imjanghyeok Date: Wed, 15 May 2024 11:06:15 +0900 Subject: [PATCH 235/265] =?UTF-8?q?Refactor:=20=ED=94=84=EB=A1=A0=ED=8A=B8?= =?UTF-8?q?=EC=97=90=EC=84=9C=20=EA=B5=AC=EB=B6=84=ED=95=A0=20=EC=88=98=20?= =?UTF-8?q?=EC=9E=88=EA=B2=8C=20=EB=A9=94=EC=84=9C=EB=93=9C=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=ED=95=98=EC=97=AC=20=EC=B1=84=ED=8C=85=20=EB=B3=B4?= =?UTF-8?q?=EB=82=B4=EA=B8=B0=20dto=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../facefriend/chat/service/dto/message/GetMessageResponse.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/capstone/facefriend/chat/service/dto/message/GetMessageResponse.java b/src/main/java/capstone/facefriend/chat/service/dto/message/GetMessageResponse.java index e096705661..043d3ee5f0 100644 --- a/src/main/java/capstone/facefriend/chat/service/dto/message/GetMessageResponse.java +++ b/src/main/java/capstone/facefriend/chat/service/dto/message/GetMessageResponse.java @@ -10,6 +10,7 @@ @Data @NoArgsConstructor public class GetMessageResponse { + private String method; private Long roomId; private Long senderId; private Long receiveId; From 6503055146ec3c16ffbebe9b9688a507913a85b0 Mon Sep 17 00:00:00 2001 From: imjanghyeok Date: Wed, 15 May 2024 11:06:25 +0900 Subject: [PATCH 236/265] =?UTF-8?q?Refactor:=20=ED=94=84=EB=A1=A0=ED=8A=B8?= =?UTF-8?q?=EC=97=90=EC=84=9C=20=EA=B5=AC=EB=B6=84=ED=95=A0=20=EC=88=98=20?= =?UTF-8?q?=EC=9E=88=EA=B2=8C=20=EB=A9=94=EC=84=9C=EB=93=9C=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=ED=95=98=EC=97=AC=20=EC=B1=84=ED=8C=85=20=EC=9A=94?= =?UTF-8?q?=EC=B2=AD=20=EB=B3=B4=EB=82=B4=EA=B8=B0=20dto=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../chat/service/dto/heart/SendHeartResponse.java | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/main/java/capstone/facefriend/chat/service/dto/heart/SendHeartResponse.java b/src/main/java/capstone/facefriend/chat/service/dto/heart/SendHeartResponse.java index 205e578156..621288305d 100644 --- a/src/main/java/capstone/facefriend/chat/service/dto/heart/SendHeartResponse.java +++ b/src/main/java/capstone/facefriend/chat/service/dto/heart/SendHeartResponse.java @@ -1,5 +1,6 @@ package capstone.facefriend.chat.service.dto.heart; +import capstone.facefriend.chat.domain.ChatRoom; import com.fasterxml.jackson.annotation.JsonFormat; import lombok.*; @@ -10,12 +11,16 @@ @NoArgsConstructor @AllArgsConstructor public class SendHeartResponse implements Serializable { - private Long roomId; - private Long senderId; - private Long receiveId; + private String method; + private Long memberId; private String senderName; + private Long senderId; private String type; + private String senderGeneratedS3url; + private String senderOriginS3url; + private ChatRoom chatRoom; private String sessionId; @JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss", timezone = "Asia/Seoul") private LocalDateTime createdAt; + private boolean isSender; } \ No newline at end of file From 3816ad6843f272a377ba17a4b2fbf70054e55817 Mon Sep 17 00:00:00 2001 From: imjanghyeok Date: Wed, 15 May 2024 11:06:28 +0900 Subject: [PATCH 237/265] =?UTF-8?q?Refactor:=20=ED=94=84=EB=A1=A0=ED=8A=B8?= =?UTF-8?q?=EC=97=90=EC=84=9C=20=EA=B5=AC=EB=B6=84=ED=95=A0=20=EC=88=98=20?= =?UTF-8?q?=EC=9E=88=EA=B2=8C=20=EB=A9=94=EC=84=9C=EB=93=9C=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=ED=95=98=EC=97=AC=20=EC=B1=84=ED=8C=85=20=EC=9A=94?= =?UTF-8?q?=EC=B2=AD=20=EB=B3=B4=EB=82=B4=EA=B8=B0=20dto=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/dto/heart/GetSendHeartResponse.java | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/main/java/capstone/facefriend/chat/service/dto/heart/GetSendHeartResponse.java b/src/main/java/capstone/facefriend/chat/service/dto/heart/GetSendHeartResponse.java index 28b2de256d..cee9f54c5e 100644 --- a/src/main/java/capstone/facefriend/chat/service/dto/heart/GetSendHeartResponse.java +++ b/src/main/java/capstone/facefriend/chat/service/dto/heart/GetSendHeartResponse.java @@ -1,5 +1,6 @@ package capstone.facefriend.chat.service.dto.heart; +import capstone.facefriend.chat.domain.ChatRoom; import com.fasterxml.jackson.annotation.JsonFormat; import lombok.Data; import lombok.NoArgsConstructor; @@ -10,21 +11,29 @@ @Data @NoArgsConstructor public class GetSendHeartResponse { + private String method; private String senderName; private Long senderId; - private Long receiveId; + private Long memberId; private String type; - private Long roomId; + private String senderGeneratedS3url; + private String senderOriginS3url; + private ChatRoom chatRoom; @JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss", timezone = "Asia/Seoul") private LocalDateTime sendTime; + private Boolean isSender; public GetSendHeartResponse(SendHeartResponse sendHeartResponse) { + this.method = sendHeartResponse.getMethod(); this.senderName = sendHeartResponse.getSenderName(); this.senderId = sendHeartResponse.getSenderId(); - this.receiveId = sendHeartResponse.getReceiveId(); + this.memberId = sendHeartResponse.getMemberId(); this.type = sendHeartResponse.getType(); - this.roomId = sendHeartResponse.getRoomId(); this.sendTime = sendHeartResponse.getCreatedAt(); + this.isSender = sendHeartResponse.isSender(); + this.chatRoom = sendHeartResponse.getChatRoom(); + this.senderGeneratedS3url = sendHeartResponse.getSenderGeneratedS3url(); + this.senderOriginS3url = sendHeartResponse.getSenderOriginS3url(); } } \ No newline at end of file From da04866907e34640334eab20a921256f9b6b72d2 Mon Sep 17 00:00:00 2001 From: imjanghyeok Date: Wed, 15 May 2024 11:06:35 +0900 Subject: [PATCH 238/265] =?UTF-8?q?Refactor:=20=ED=94=84=EB=A1=A0=ED=8A=B8?= =?UTF-8?q?=EC=97=90=EC=84=9C=20=EA=B5=AC=EB=B6=84=ED=95=A0=20=EC=88=98=20?= =?UTF-8?q?=EC=9E=88=EA=B2=8C=20=EB=A9=94=EC=84=9C=EB=93=9C=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=ED=95=98=EC=97=AC=20=EC=B1=84=ED=8C=85=20=EC=9A=94?= =?UTF-8?q?=EC=B2=AD=20=EC=9D=91=EB=8B=B5=20dto=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../chat/service/dto/heart/HeartReplyResponse.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/java/capstone/facefriend/chat/service/dto/heart/HeartReplyResponse.java b/src/main/java/capstone/facefriend/chat/service/dto/heart/HeartReplyResponse.java index 99bd8c3468..aef980425a 100644 --- a/src/main/java/capstone/facefriend/chat/service/dto/heart/HeartReplyResponse.java +++ b/src/main/java/capstone/facefriend/chat/service/dto/heart/HeartReplyResponse.java @@ -1,11 +1,14 @@ package capstone.facefriend.chat.service.dto.heart; public record HeartReplyResponse( + String method, Long senderId, String intention + ) { - public static HeartReplyResponse of (Long senderId, HeartReplyRequest heartReplyRequest) { + public static HeartReplyResponse of (Long senderId, HeartReplyRequest heartReplyRequest, String method) { return new HeartReplyResponse( + method, senderId, heartReplyRequest.intention() ); From beb8d88aead2ce2d940d692da19ea99024924077 Mon Sep 17 00:00:00 2001 From: imjanghyeok Date: Wed, 15 May 2024 11:09:10 +0900 Subject: [PATCH 239/265] =?UTF-8?q?Refactor:=20=EC=8B=9C=EA=B0=84=EC=9D=84?= =?UTF-8?q?=20json=EC=9C=BC=EB=A1=9C=20=EB=B3=80=ED=99=98=ED=95=98?= =?UTF-8?q?=EA=B8=B0=20=EC=9C=84=ED=95=B4=20=EC=B1=84=ED=8C=85=EB=B0=A9=20?= =?UTF-8?q?=EB=96=A0=EB=82=98=EA=B8=B0=20dto=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../chat/service/dto/chatroom/ChatRoomLeftResponse.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/capstone/facefriend/chat/service/dto/chatroom/ChatRoomLeftResponse.java b/src/main/java/capstone/facefriend/chat/service/dto/chatroom/ChatRoomLeftResponse.java index cc8618c57b..678ad688e2 100644 --- a/src/main/java/capstone/facefriend/chat/service/dto/chatroom/ChatRoomLeftResponse.java +++ b/src/main/java/capstone/facefriend/chat/service/dto/chatroom/ChatRoomLeftResponse.java @@ -1,6 +1,7 @@ package capstone.facefriend.chat.service.dto.chatroom; import capstone.facefriend.member.domain.member.Member; +import com.fasterxml.jackson.annotation.JsonFormat; import java.time.LocalDateTime; @@ -9,7 +10,7 @@ public record ChatRoomLeftResponse ( Long roomId, Long senderId, String senderNickname, - LocalDateTime sendTime, + @JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss", timezone = "Asia/Seoul") LocalDateTime sendTime, String content ){ public static ChatRoomLeftResponse of (String method, Long roomId, Member sender, LocalDateTime sendTime, String content) { From 51a7d2ac2bbceecc0cb951976f7f96ee92207add Mon Sep 17 00:00:00 2001 From: imjanghyeok Date: Wed, 15 May 2024 12:02:58 +0900 Subject: [PATCH 240/265] =?UTF-8?q?Refactor:=20=EC=B1=84=ED=8C=85=EB=B3=B4?= =?UTF-8?q?=EB=82=B4=EA=B8=B0=20dto=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../facefriend/chat/service/dto/message/GetMessageResponse.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/capstone/facefriend/chat/service/dto/message/GetMessageResponse.java b/src/main/java/capstone/facefriend/chat/service/dto/message/GetMessageResponse.java index 043d3ee5f0..412fcd419f 100644 --- a/src/main/java/capstone/facefriend/chat/service/dto/message/GetMessageResponse.java +++ b/src/main/java/capstone/facefriend/chat/service/dto/message/GetMessageResponse.java @@ -23,6 +23,7 @@ public class GetMessageResponse { private Boolean isRead; public GetMessageResponse(MessageResponse messageResponse) { + this.method = messageResponse.getMethod(); this.roomId = messageResponse.getRoomId(); this.senderId = messageResponse.getSenderId(); this.receiveId = messageResponse.getReceiveId(); From 6797dc407de318ffc60f367d60040217b52ad202 Mon Sep 17 00:00:00 2001 From: imjanghyeok Date: Wed, 15 May 2024 13:24:29 +0900 Subject: [PATCH 241/265] =?UTF-8?q?Refactor:=20fix=20=EC=95=88=EC=9D=BD?= =?UTF-8?q?=EC=9D=80=20=EB=A9=94=EC=8B=9C=EC=A7=80=20=EB=A1=9C=EC=A7=81=20?= =?UTF-8?q?=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../chat/service/MessageService.java | 65 ++++++++++++------- 1 file changed, 41 insertions(+), 24 deletions(-) diff --git a/src/main/java/capstone/facefriend/chat/service/MessageService.java b/src/main/java/capstone/facefriend/chat/service/MessageService.java index b810337eb9..4e1ec0c789 100644 --- a/src/main/java/capstone/facefriend/chat/service/MessageService.java +++ b/src/main/java/capstone/facefriend/chat/service/MessageService.java @@ -60,8 +60,8 @@ private ChatRoom findRoomById(String destination, Long roomId) { ChatRoom chatRoom = chatRoomRepository.findById(roomId) .orElse(null); if (chatRoom == null) { - simpMessagingTemplate.convertAndSend(destination, MemberExceptionType.NOT_FOUND.message()); - throw new MemberException(MemberExceptionType.NOT_FOUND); + simpMessagingTemplate.convertAndSend(destination, ChatExceptionType.NOT_FOUND.message()); + throw new ChatException(ChatExceptionType.NOT_FOUND); } return chatRoom; } @@ -70,8 +70,8 @@ private ChatRoomMember findSenderReceiver(String destination, Long senderId, Lon ChatRoomMember chatRoomMember = chatRoomMemberRepository.findBySenderAndReceiver(senderId, receiveId) .orElse(null); if (chatRoomMember == null) { - simpMessagingTemplate.convertAndSend(destination, MemberExceptionType.NOT_FOUND.message()); - throw new MemberException(MemberExceptionType.NOT_FOUND); + simpMessagingTemplate.convertAndSend(destination, ChatExceptionType.NOT_FOUND.message()); + throw new ChatException(ChatExceptionType.NOT_FOUND); } return chatRoomMember; } @@ -80,8 +80,8 @@ private ChatRoomMember findChatRoomMemberByChatRoomId(String destination, Long r ChatRoomMember chatRoomMember = chatRoomMemberRepository.findByChatRoomId(roomId) .orElse(null); if (chatRoomMember == null) { - simpMessagingTemplate.convertAndSend(destination, MemberExceptionType.NOT_FOUND.message()); - throw new MemberException(MemberExceptionType.NOT_FOUND); + simpMessagingTemplate.convertAndSend(destination, ChatExceptionType.NOT_FOUND.message()); + throw new ChatException(ChatExceptionType.NOT_FOUND); } return chatRoomMember; } @@ -264,7 +264,7 @@ public void enterApplication(Long memberId) { } if(isExistUnSendHeart(memberId)) { - sendSentHeart(memberId); + sendSentHeart(exceptionDestination, memberId); } else { simpMessagingTemplate.convertAndSend(exceptionDestination, "큐잉된 대화요청이 없습니다."); } @@ -294,35 +294,52 @@ private Boolean isExistUnSendHeart(Long memberId) { private void sendSentMessage(Long receiveId) { String topic = channelTopic.getTopic(); String destination = "/sub/chat" + receiveId + "message"; - Long messagesListSize = redisTemplate.opsForList().size(destination); - log.info(messagesListSize.toString()); - log.info("messageList: {}", redisTemplate.opsForList().range(destination, 0, -1)); + List messages = redisTemplate.opsForList().range(destination, 0, -1); + Long messagesListSize = (messages != null) ? (long) messages.size() : 0; + log.info("Message list size: {}", messagesListSize); if (messagesListSize > 0) { - for (Long i = messagesListSize; i > 0; i--) { - // 맵으로 받음 - LinkedHashMap map = (LinkedHashMap) redisTemplate.opsForList().rightPop(destination); - MessageResponse messageResponse = (MessageResponse) map.get(destination); - log.info("messageResponse: {}", messageResponse.toString()); - messageResponse.setMethod("connectChat"); + for (Object messageObj : messages) { + LinkedHashMap map = (LinkedHashMap) messageObj; + MessageResponse messageResponse = new MessageResponse(map); + log.info("UnReadMessageResponse: {}", messageResponse.toString()); redisTemplate.convertAndSend(topic, messageResponse); } + // 리스트 비우기 + redisTemplate.delete(destination); + log.info("Message list cleared."); + } else { + log.warn("Message list is empty."); } } - private void sendSentHeart(Long receiveId) { + + + + + + private void sendSentHeart(String exceptionDestination, Long receiveId) { String topic = channelTopic.getTopic(); String destination = "/sub/chat" + receiveId + "heart"; - Long messagesListSize = redisTemplate.opsForList().size(destination); - log.info("SendHeartListSize: {}", messagesListSize.toString()); + List sendHearts = redisTemplate.opsForList().range(destination, 0, -1); + Long messagesListSize = (sendHearts != null) ? (long) sendHearts.size() : 0; + log.info("Message list size: {}", messagesListSize); + if (messagesListSize > 0) { - for (Long i = messagesListSize; i > 0; i--) { - LinkedHashMap map = (LinkedHashMap) redisTemplate.opsForList().rightPop(destination); - SendHeartResponse sendHeartResponse = (SendHeartResponse) map.get(destination); - log.info("messageResponse: {}", sendHeartResponse.toString()); - sendHeartResponse.setMethod("connectHeart"); + for (Object sendHeartObj : sendHearts) { + LinkedHashMap map = (LinkedHashMap) sendHeartObj; + LinkedHashMap chatRoomMap = (LinkedHashMap) map.get("chatRoom"); + Long roomId = ((Number) chatRoomMap.get("id")).longValue(); + ChatRoom chatRoom = findRoomById(exceptionDestination, roomId); + SendHeartResponse sendHeartResponse = new SendHeartResponse(map, chatRoom); + log.info("UnReadSendHeartResponse: {}", sendHeartResponse.toString()); redisTemplate.convertAndSend(topic, sendHeartResponse); } + // 리스트 비우기 + redisTemplate.delete(destination); + log.info("Sendheart list cleared."); + } else { + log.warn("Sendheart list is empty."); } } } \ No newline at end of file From bfdb605e1f9654405b114abd876da41d37281c27 Mon Sep 17 00:00:00 2001 From: imjanghyeok Date: Wed, 15 May 2024 13:24:51 +0900 Subject: [PATCH 242/265] =?UTF-8?q?Refactor:=20=EC=95=88=20=EC=9D=BD?= =?UTF-8?q?=EC=9D=80=20=EB=A9=94=EC=8B=9C=EC=A7=80=20=EB=A1=9C=EC=A7=81=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=EC=97=90=20=EB=94=B0=EB=A5=B8=20dto=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../chat/service/dto/message/MessageResponse.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/main/java/capstone/facefriend/chat/service/dto/message/MessageResponse.java b/src/main/java/capstone/facefriend/chat/service/dto/message/MessageResponse.java index 827d06a8ac..bc083b3323 100644 --- a/src/main/java/capstone/facefriend/chat/service/dto/message/MessageResponse.java +++ b/src/main/java/capstone/facefriend/chat/service/dto/message/MessageResponse.java @@ -7,6 +7,8 @@ import java.io.Serializable; import java.time.LocalDateTime; +import java.util.LinkedHashMap; +import java.util.LinkedList; @Data @@ -24,4 +26,17 @@ public class MessageResponse implements Serializable { @JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss", timezone = "Asia/Seoul") private LocalDateTime createdAt; private Boolean isRead; + + public MessageResponse(LinkedHashMap map) { + this.method = (String) map.get("method"); + this.roomId = ((Number) map.get("roomId")).longValue(); + this.receiveId = ((Number) map.get("receiveId")).longValue(); + this.senderId = ((Number) map.get("senderId")).longValue(); + this.content = (String) map.get("content"); + this.type = (String) map.get("type"); + this.senderNickname = (String) map.get("senderNickname"); + this.senderFaceInfoS3Url = (String) map.get("senderFaceInfoS3Url"); + this.createdAt = LocalDateTime.parse((String) map.get("createdAt")); + this.isRead = (Boolean) map.get("isRead"); + } } \ No newline at end of file From 59d9ca2d6376f8b1730709d5471f5681b605f79d Mon Sep 17 00:00:00 2001 From: imjanghyeok Date: Wed, 15 May 2024 13:24:59 +0900 Subject: [PATCH 243/265] =?UTF-8?q?Refactor:=20=EC=95=88=20=EC=9D=BD?= =?UTF-8?q?=EC=9D=80=20=EB=A9=94=EC=8B=9C=EC=A7=80=20=EB=A1=9C=EC=A7=81=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=EC=97=90=20=EB=94=B0=EB=A5=B8=20=EB=8C=80?= =?UTF-8?q?=ED=99=94=20=EC=9A=94=EC=B2=AD=20dto=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../chat/service/dto/heart/SendHeartResponse.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/main/java/capstone/facefriend/chat/service/dto/heart/SendHeartResponse.java b/src/main/java/capstone/facefriend/chat/service/dto/heart/SendHeartResponse.java index 621288305d..36fd16baf1 100644 --- a/src/main/java/capstone/facefriend/chat/service/dto/heart/SendHeartResponse.java +++ b/src/main/java/capstone/facefriend/chat/service/dto/heart/SendHeartResponse.java @@ -6,6 +6,7 @@ import java.io.Serializable; import java.time.LocalDateTime; +import java.util.LinkedHashMap; @Data @NoArgsConstructor @@ -23,4 +24,18 @@ public class SendHeartResponse implements Serializable { @JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss", timezone = "Asia/Seoul") private LocalDateTime createdAt; private boolean isSender; + + public SendHeartResponse(LinkedHashMap map, ChatRoom chatRoom) { + this.method = (String) map.get("method"); + this.memberId = ((Number) map.get("memberId")).longValue(); + this.senderName = (String) map.get("senderName"); + this.senderId = ((Number) map.get("senderId")).longValue(); // Fix this line + this.type = (String) map.get("type"); + this.senderGeneratedS3url = (String) map.get("senderGeneratedS3url"); + this.senderOriginS3url = (String) map.get("senderOriginS3url"); + this.sessionId = (String) map.get("sessionId"); + this.createdAt = LocalDateTime.parse((String) map.get("createdAt")); + this.isSender = (boolean) map.get("sender"); + } + } \ No newline at end of file From 0214f846a23b89b269fe97f40b091d52a9323065 Mon Sep 17 00:00:00 2001 From: imjanghyeok Date: Wed, 15 May 2024 13:25:16 +0900 Subject: [PATCH 244/265] =?UTF-8?q?Refactor:=20=EC=95=88=20=EC=9D=BD?= =?UTF-8?q?=EC=9D=80=20=EB=A9=94=EC=8B=9C=EC=A7=80=20=EB=A1=9C=EC=A7=81=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../capstone/facefriend/chat/service/RedisSubscriber.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/capstone/facefriend/chat/service/RedisSubscriber.java b/src/main/java/capstone/facefriend/chat/service/RedisSubscriber.java index 53209dbe3f..48df5291d3 100644 --- a/src/main/java/capstone/facefriend/chat/service/RedisSubscriber.java +++ b/src/main/java/capstone/facefriend/chat/service/RedisSubscriber.java @@ -79,8 +79,10 @@ private void saveUnReadMessage(String destination, MessageResponse messageRespon Boolean isUnRead = redisTemplate.hasKey(destination); log.info(isUnRead.toString()); if (isUnRead) { + messageResponse.setMethod("connectChat"); redisTemplate.opsForList().rightPush(destination, messageResponse); } else { + messageResponse.setMethod("connectChat"); redisTemplate.opsForList().rightPush(destination, messageResponse); } } @@ -88,8 +90,10 @@ private void saveUnReadMessage(String destination, MessageResponse messageRespon private void saveUnReadHeart(String destination, SendHeartResponse sendHeartResponse) { Boolean isUnRead = redisTemplate.hasKey(destination); if (isUnRead) { + sendHeartResponse.setMethod("connectHeart"); redisTemplate.opsForList().rightPush(destination, sendHeartResponse); } else { + sendHeartResponse.setMethod("connectHeart"); redisTemplate.opsForList().rightPush(destination, sendHeartResponse); } } From ed7d9461b3be24b95e25d7af3642fb49815ca7bf Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Fri, 17 May 2024 06:58:14 +0900 Subject: [PATCH 245/265] =?UTF-8?q?feat:=20=EC=B9=9C=EB=B0=80=EB=8F=84?= =?UTF-8?q?=EC=97=90=20=EB=94=B0=EB=A5=B8=20=EC=9D=B4=EB=AF=B8=EC=A7=80=20?= =?UTF-8?q?=EB=B3=80=ED=99=94=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 3 + .../facefriend/FacefriendApplication.java | 1 - .../facefriend/bucket/BucketService.java | 109 +++++-- .../capstone/facefriend/chat/aop/ChatAop.java | 270 ++++++++++++++++++ .../chat/config/ChatAsyncConfig.java | 23 ++ .../chat/domain/ChatRoomMember.java | 21 +- .../chat/exception/ChatExceptionType.java | 13 +- .../repository/ChatMessageRepository.java | 2 + .../repository/ChatRoomMemberRepository.java | 4 + .../chat/service/ChatRoomService.java | 10 +- .../chat/service/MessageService.java | 75 +++-- .../member/controller/FaceInfoController.java | 2 +- .../member/domain/faceInfo/FaceInfo.java | 3 + .../domain/faceInfo/FaceInfoByLevel.java | 21 ++ .../faceInfo/FaceInfoByLevelRepository.java | 8 + .../member/domain/member/Member.java | 11 + .../member/service/FaceInfoService.java | 48 +++- .../member/service/MemberService.java | 8 + 18 files changed, 564 insertions(+), 68 deletions(-) create mode 100644 src/main/java/capstone/facefriend/chat/aop/ChatAop.java create mode 100644 src/main/java/capstone/facefriend/chat/config/ChatAsyncConfig.java create mode 100644 src/main/java/capstone/facefriend/member/domain/faceInfo/FaceInfoByLevel.java create mode 100644 src/main/java/capstone/facefriend/member/domain/faceInfo/FaceInfoByLevelRepository.java diff --git a/build.gradle b/build.gradle index 67cbe92b61..b2cb05bea3 100644 --- a/build.gradle +++ b/build.gradle @@ -75,6 +75,9 @@ dependencies { // JAXB deprecated warning implementation 'javax.xml.bind:jaxb-api:2.3.0' + + // aop + implementation 'org.springframework.boot:spring-boot-starter-aop' } tasks.named('test') { diff --git a/src/main/java/capstone/facefriend/FacefriendApplication.java b/src/main/java/capstone/facefriend/FacefriendApplication.java index 8982f0bbc1..723fc9435d 100644 --- a/src/main/java/capstone/facefriend/FacefriendApplication.java +++ b/src/main/java/capstone/facefriend/FacefriendApplication.java @@ -1,6 +1,5 @@ package capstone.facefriend; -import capstone.facefriend.resume.domain.Resume; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.data.jpa.repository.config.EnableJpaAuditing; diff --git a/src/main/java/capstone/facefriend/bucket/BucketService.java b/src/main/java/capstone/facefriend/bucket/BucketService.java index e0464225a9..70c3c39e8c 100644 --- a/src/main/java/capstone/facefriend/bucket/BucketService.java +++ b/src/main/java/capstone/facefriend/bucket/BucketService.java @@ -1,6 +1,13 @@ package capstone.facefriend.bucket; +import capstone.facefriend.chat.domain.ChatRoom; +import capstone.facefriend.chat.domain.ChatRoomMember; +import capstone.facefriend.chat.exception.ChatException; +import capstone.facefriend.chat.exception.ChatExceptionType; +import capstone.facefriend.chat.repository.ChatMessageRepository; +import capstone.facefriend.chat.repository.ChatRoomMemberRepository; +import capstone.facefriend.chat.repository.ChatRoomRepository; import capstone.facefriend.member.domain.member.Member; import capstone.facefriend.member.domain.member.MemberRepository; import capstone.facefriend.member.exception.member.MemberException; @@ -24,6 +31,8 @@ import java.util.List; import java.util.UUID; +import static capstone.facefriend.chat.exception.ChatExceptionType.*; + @Transactional @Slf4j @RequiredArgsConstructor @@ -31,19 +40,24 @@ public class BucketService { @Value("${spring.cloud.aws.s3.bucket}") - private String bucketName; + private String BUCKET_NAME; @Value("${spring.cloud.aws.s3.default-faceInfo-s3url}") - private String defaultFaceInfoS3url; + private String DEFAULT_FACE_INFO_S3_URL; @Value("${spring.cloud.aws.s3.origin-postfix}") - private String originPostfix; + private String ORIGIN_POSTFIX; @Value("${spring.cloud.aws.s3.generated-postfix}") - private String generatedPostfix; + private String GENERATED_POSTFIX; @Value("${spring.cloud.aws.s3.resume-postfix}") - private String resumePostfix; + private String RESUME_POSTFIX; + @Value("${spring.cloud.aws.s3.generated-by-level-postfix}") + private String GENERATED_BY_LEVEL_POSTFIX; private final AmazonS3 amazonS3; private final MemberRepository memberRepository; + private final ChatMessageRepository chatMessageRepository; + private final ChatRoomRepository chatRoomRepository; + private final ChatRoomMemberRepository chatRoomMemberRepository; // FaceInfo : origin 업로드 & generated 업로드 public List uploadOriginAndGenerated( @@ -56,16 +70,16 @@ public List uploadOriginAndGenerated( originMetadata.setContentLength(origin.getInputStream().available()); originMetadata.setContentType(origin.getContentType()); - String originObjectName = UUID.randomUUID() + originPostfix; + String originObjectName = UUID.randomUUID() + ORIGIN_POSTFIX; amazonS3.putObject( new PutObjectRequest( - bucketName, + BUCKET_NAME, originObjectName, origin.getInputStream(), // origin originMetadata ).withCannedAcl(CannedAccessControlList.PublicRead) ); - String originS3url = amazonS3.getUrl(bucketName, originObjectName).toString(); + String originS3url = amazonS3.getUrl(BUCKET_NAME, originObjectName).toString(); /** upload generated to s3 */ // set metadata @@ -73,20 +87,21 @@ public List uploadOriginAndGenerated( generatedMetadata.setContentLength(generated.getInputStream().available()); generatedMetadata.setContentType(generatedMetadata.getContentType()); - String generatedObjectName = UUID.randomUUID() + generatedPostfix; + String generatedObjectName = UUID.randomUUID() + GENERATED_POSTFIX; amazonS3.putObject( new PutObjectRequest( - bucketName, + BUCKET_NAME, generatedObjectName, generated.getInputStream(), // generated generatedMetadata ).withCannedAcl(CannedAccessControlList.PublicRead) ); - String generatedS3url = amazonS3.getUrl(bucketName, generatedObjectName).toString(); + String generatedS3url = amazonS3.getUrl(BUCKET_NAME, generatedObjectName).toString(); return List.of(originS3url, generatedS3url); } + // FaceInfo : origin 수정 -> generated 수정 public List updateOriginAndGenerated( MultipartFile origin, @@ -98,7 +113,7 @@ public List updateOriginAndGenerated( String originS3url = member.getFaceInfo().getOriginS3url(); String generatedS3url = member.getFaceInfo().getGeneratedS3url(); - if (originS3url.equals(defaultFaceInfoS3url) || generatedS3url.equals(defaultFaceInfoS3url)) { + if (originS3url.equals(DEFAULT_FACE_INFO_S3_URL) || generatedS3url.equals(DEFAULT_FACE_INFO_S3_URL)) { return uploadOriginAndGenerated(origin, generated); } @@ -114,13 +129,13 @@ public String deleteOriginAndGenerated( String originS3url = member.getFaceInfo().getOriginS3url(); String originObjectName = originS3url.substring(originS3url.lastIndexOf("/") + 1); - amazonS3.deleteObject(new DeleteObjectRequest(bucketName, originObjectName)); + amazonS3.deleteObject(new DeleteObjectRequest(BUCKET_NAME, originObjectName)); String generatedS3url = member.getFaceInfo().getGeneratedS3url(); String generatedObjectName = generatedS3url.substring(generatedS3url.lastIndexOf("/") + 1); - amazonS3.deleteObject(new DeleteObjectRequest(bucketName, generatedObjectName)); + amazonS3.deleteObject(new DeleteObjectRequest(BUCKET_NAME, generatedObjectName)); - return defaultFaceInfoS3url; + return DEFAULT_FACE_INFO_S3_URL; } @@ -137,17 +152,17 @@ public List uploadResumeImages( metadata.setContentLength(image.getInputStream().available()); metadata.setContentType(image.getContentType()); - String imageObjectName = UUID.randomUUID() + resumePostfix; + String imageObjectName = UUID.randomUUID() + RESUME_POSTFIX; amazonS3.putObject( new PutObjectRequest( - bucketName, + BUCKET_NAME, imageObjectName, image.getInputStream(), metadata ).withCannedAcl(CannedAccessControlList.PublicRead) ); - resumeImageS3urls.add(amazonS3.getUrl(bucketName, imageObjectName).toString()); + resumeImageS3urls.add(amazonS3.getUrl(BUCKET_NAME, imageObjectName).toString()); } } return resumeImageS3urls; @@ -170,10 +185,66 @@ public void deleteResumeImages( for (String resumeImageS3url : resumeImageS3urls) { String resumeImageObjectName = resumeImageS3url.substring(resumeImageS3url.lastIndexOf("/") + 1); - amazonS3.deleteObject(new DeleteObjectRequest(bucketName, resumeImageObjectName)); + amazonS3.deleteObject(new DeleteObjectRequest(BUCKET_NAME, resumeImageObjectName)); } } + + // FaceInfoByLevel : generatedByLevel 업로드 + public String uploadGeneratedByLevel( + MultipartFile generatedByLevel + ) throws IOException { + // set metadata + ObjectMetadata generatedByLevelMetaData = new ObjectMetadata(); + generatedByLevelMetaData.setContentLength(generatedByLevel.getInputStream().available()); + generatedByLevelMetaData.setContentType(generatedByLevel.getContentType()); + + String generatedByLevelObjectName = UUID.randomUUID() + GENERATED_BY_LEVEL_POSTFIX; + amazonS3.putObject( + new PutObjectRequest( + BUCKET_NAME, + generatedByLevelObjectName, + generatedByLevel.getInputStream(), // origin + generatedByLevelMetaData + ).withCannedAcl(CannedAccessControlList.PublicRead) + ); + return amazonS3.getUrl(BUCKET_NAME, generatedByLevelObjectName).toString(); + } + + // FaceInfoByLevel : generatedByLevel 수정 = generatedByLevel 삭제 후 업로드 + public String updateGeneratedByLevel( + ByteArrayMultipartFile generatedByLevel, + Long roomId + ) throws IOException { + deleteGeneratedByLevel(roomId); + return uploadGeneratedByLevel(generatedByLevel); + } + + // FaceInfo : generatedByLevel 삭제 + public void deleteGeneratedByLevel( + Long roomId // 삭제 요청된 방 + ) { + ChatRoom chatRoom = findChatRoomByRoomId(roomId); + ChatRoomMember chatRoomMember = findChatRoomMemberByChatRoom(chatRoom); + + String senderGeneratedByLevelS3url = chatRoomMember.getSenderFaceInfoByLevel().getGeneratedByLevelS3url(); + String receiverGeneratedByLevelS3url = chatRoomMember.getReceiverFaceInfoByLevel().getGeneratedByLevelS3url(); + + String senderGeneratedByLevelObjectName = senderGeneratedByLevelS3url.substring(senderGeneratedByLevelS3url.lastIndexOf("/") + 1); + String receiverGeneratedByLevelObjectName = receiverGeneratedByLevelS3url.substring(receiverGeneratedByLevelS3url.lastIndexOf("/") + 1); + + amazonS3.deleteObject(new DeleteObjectRequest(BUCKET_NAME, senderGeneratedByLevelObjectName)); + amazonS3.deleteObject(new DeleteObjectRequest(BUCKET_NAME, receiverGeneratedByLevelObjectName)); + } + + private ChatRoomMember findChatRoomMemberByChatRoom(ChatRoom chatRoom) { + return chatRoomMemberRepository.findChatRoomMemberByChatRoom(chatRoom).orElseThrow(() -> new ChatException(NOT_FOUND_CHAT_ROOM_MEMBER)); + } + + private ChatRoom findChatRoomByRoomId(Long roomId) { + return chatRoomRepository.findById(roomId).orElseThrow(() -> new ChatException(NOT_FOUND_CHAT_ROOM)); + } + private Member findMemberById(Long memberId) { return memberRepository.findById(memberId) .orElseThrow(() -> new MemberException(MemberExceptionType.NOT_FOUND)); diff --git a/src/main/java/capstone/facefriend/chat/aop/ChatAop.java b/src/main/java/capstone/facefriend/chat/aop/ChatAop.java new file mode 100644 index 0000000000..81d43d75a8 --- /dev/null +++ b/src/main/java/capstone/facefriend/chat/aop/ChatAop.java @@ -0,0 +1,270 @@ +package capstone.facefriend.chat.aop; + +import capstone.facefriend.bucket.BucketService; +import capstone.facefriend.chat.domain.ChatMessage; +import capstone.facefriend.chat.domain.ChatRoomMember; +import capstone.facefriend.chat.exception.ChatException; +import capstone.facefriend.chat.repository.ChatMessageRepository; +import capstone.facefriend.chat.repository.ChatRoomMemberRepository; +import capstone.facefriend.member.domain.faceInfo.FaceInfoByLevel; +import capstone.facefriend.member.domain.member.Member; +import capstone.facefriend.member.domain.member.MemberRepository; +import capstone.facefriend.member.exception.member.MemberException; +import capstone.facefriend.member.multipartFile.ByteArrayMultipartFile; +import capstone.facefriend.member.service.FaceInfoService; +import com.amazonaws.services.s3.AmazonS3; +import com.amazonaws.services.s3.model.GetObjectRequest; +import com.amazonaws.services.s3.model.S3Object; +import com.amazonaws.services.s3.model.S3ObjectInputStream; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.annotation.After; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Before; +import org.aspectj.lang.annotation.Pointcut; +import org.aspectj.lang.reflect.MethodSignature; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.lang.reflect.Method; + +import static capstone.facefriend.chat.exception.ChatExceptionType.NOT_FOUND_CHAT_ROOM_MEMBER; +import static capstone.facefriend.member.exception.member.MemberExceptionType.NOT_FOUND; + +@Slf4j +@Aspect +@Component +@Transactional +@RequiredArgsConstructor +public class ChatAop { + + private final ChatRoomMemberRepository chatRoomMemberRepository; + private final FaceInfoService faceInfoService; + private final MemberRepository memberRepository; + private final ChatMessageRepository chatMessageRepository; + private final AmazonS3 amazonS3; + private final BucketService bucketService; + + @Value("${spring.cloud.aws.s3.bucket}") + private String BUCKET_NAME; + + public static final int LEVEL_TWO = 5; + public static final int LEVEL_THREE = 10; + public static final int LEVEL_FOUR = 15; + + @Pointcut("execution(* capstone.facefriend.chat.service.MessageService.sendHeart(..))") + private void sendHeart() { + } + + // 하트를 보낼 때 senderFaceInfoByLevel, receiverFaceInfoByLevel 의 generatedByLevel 필드를 generated 로 초기화하므로 after 이어야만 한다. + // 하트 거절하면 findChatRoomMemberBySenderAndReceiver 에서 예외 터트릴거임 + @Transactional + @After("sendHeart()") + public void afterSendHeart(JoinPoint joinPoint) throws IOException { + + Object[] params = joinPoint.getArgs(); + MethodSignature signature = (MethodSignature) joinPoint.getSignature(); + Method method = signature.getMethod(); + + Long senderId = -1L; + Long receiveId = -1L; + for (int i = 0; i < method.getParameters().length; i++) { + String paramName = method.getParameters()[i].getName(); + if (paramName.equals("senderId")) { + senderId = (Long) params[i]; + } + if (paramName.equals("receiveId")) { + receiveId = (Long) params[i]; + } + } + + Member sender = findMemberById(senderId); // 영속 + Member receiver = findMemberById(receiveId); // 영속 + + ChatRoomMember chatRoomMember = findChatRoomMemberBySenderAndReceiver(sender, receiver); // 영속 + + // senderGeneratedByLevel 은 generated 로 DB 저장되어있음 + String senderGeneratedByLevelS3url = chatRoomMember.getSenderFaceInfoByLevel().getGeneratedByLevelS3url(); + // generated 를 MultipartFile 형태로 얻어내고 이를 level 1 로 generatedByLevel 를 S3 업로드 (generated 자체가 level 1) + ByteArrayMultipartFile senderGeneratedByLevel = getGeneratedByLevel(senderGeneratedByLevelS3url); + String newSenderGeneratedByLevelS3url = bucketService.uploadGeneratedByLevel(senderGeneratedByLevel); + // senderGeneratedByLevelS3url 수정 + chatRoomMember.getSenderFaceInfoByLevel().setGeneratedByLevelS3url(newSenderGeneratedByLevelS3url); // dirty check + + // receiverGeneratedByLevel 은 generated 로 DB 저장되어있음 + String receiverGeneratedByLevelS3url = chatRoomMember.getReceiverFaceInfoByLevel().getGeneratedByLevelS3url(); + // generated 를 MultipartFile 형태로 얻어내고 이를 level 1 로 generatedByLevel 를 S3 업로드 (generated 자체가 level 1) + ByteArrayMultipartFile receiverGenerated = getGeneratedByLevel(receiverGeneratedByLevelS3url); + String newReceiverGeneratedByLevelS3url = bucketService.uploadGeneratedByLevel(receiverGenerated); + // receiverGeneratedByLevelS3url 수정 + chatRoomMember.getReceiverFaceInfoByLevel().setGeneratedByLevelS3url(newReceiverGeneratedByLevelS3url); // dirty check + } + + private ChatRoomMember findChatRoomMemberBySenderAndReceiver(Member sender, Member receiver) { + return chatRoomMemberRepository.findChatRoomMemberBySenderAndReceiver(sender, receiver) + .orElseThrow(() -> new ChatException(NOT_FOUND_CHAT_ROOM_MEMBER)); + } + + @Pointcut("execution(* capstone.facefriend.chat.repository.ChatMessageRepository.save(..))") + private void saveChatMessage() { + } + + // 본인이 보낸 chatMessage 를 저장하기 전마다 chatMessage 가 속한 chatRoom 의 모든 chatMessage 갯수를 count 하고 특정 수준을 넘는지를 확인한다. + // 수준에 따라 가중치를 조절해 generate_face_by_level() 호출하여 인공지능 서버에 이미지 생성을 요청한다. + // 또한 가중치 이미지를 s3에 업데이트하고 그 url을 db에 저장한다. + @Transactional + @Before("saveChatMessage()") + public void beforeSaveChatMessage(JoinPoint joinPoint) throws IOException { + + Object[] params = joinPoint.getArgs(); + MethodSignature signature = (MethodSignature) joinPoint.getSignature(); + Method method = signature.getMethod(); + + ChatMessage chatMessage = null; + for (int i = 0; i < method.getParameters().length; i++) { + String paramName = method.getParameters()[i].getName(); + if (paramName.equals("chatMessage")) { + chatMessage = (ChatMessage) params[i]; + } + } + + Long roomId = chatMessage.getChatRoom().getId(); + Member me = chatMessage.getSender(); + + ChatRoomMember chatRoomMember = findChatRoomMemberByRoomId(roomId); // 영속 + FaceInfoByLevel senderFaceInfoByLevel = chatRoomMember.getSenderFaceInfoByLevel(); // 영속 + FaceInfoByLevel receiverFaceInfoByLevel = chatRoomMember.getReceiverFaceInfoByLevel(); // 영속 + + // 나는 최초의 sender 일 수도, receiver 일 수도 있다. + Member sender = chatRoomMember.getSender(); + Member receiver = chatRoomMember.getReceiver(); + + // chatRoom 의 chatMessage 객체의 갯수 count + Integer chatMessageCount = chatMessageRepository.countChatMessagesByChatRoom(chatMessage.getChatRoom()); + + /** param 1 : convert originS3url into MultipartFile **/ + ByteArrayMultipartFile senderOrigin = getOrigin(sender); + ByteArrayMultipartFile receiverOrigin = getOrigin(receiver); + /** param 2 : styleId **/ + Integer senderStyleId = sender.getFaceInfo().getStyleId(); + Integer receiverStyleId = receiver.getFaceInfo().getStyleId(); + /** param 3 : memberId **/ + Long senderId = sender.getId(); + Long receiverId = receiver.getId(); + + switch (chatMessageCount) { + case LEVEL_TWO: + ByteArrayMultipartFile senderGeneratedByLevelTwo = faceInfoService.generateByLevel(senderOrigin, senderId, senderStyleId, 2); + ByteArrayMultipartFile receiverGeneratedByLevelTwo = faceInfoService.generateByLevel(receiverOrigin, receiverId, receiverStyleId, 2); + + String senderGeneratedByLevelTwoS3url = bucketService.updateGeneratedByLevel(senderGeneratedByLevelTwo, roomId); + String receiverGeneratedByLevelTwoS3url = bucketService.updateGeneratedByLevel(receiverGeneratedByLevelTwo, roomId); + + senderFaceInfoByLevel.setGeneratedByLevelS3url(senderGeneratedByLevelTwoS3url); // dirty check + receiverFaceInfoByLevel.setGeneratedByLevelS3url(receiverGeneratedByLevelTwoS3url); // dirty check + + case LEVEL_THREE: + ByteArrayMultipartFile senderGeneratedByLevelThree = faceInfoService.generateByLevel(senderOrigin, senderId, senderStyleId, 3); + ByteArrayMultipartFile receiverGeneratedByLevelThree = faceInfoService.generateByLevel(receiverOrigin, receiverId, receiverStyleId, 3); + + String senderGeneratedByLevelThreeS3url = bucketService.updateGeneratedByLevel(senderGeneratedByLevelThree, roomId); + String receiverGeneratedByLevelThreeS3url = bucketService.updateGeneratedByLevel(receiverGeneratedByLevelThree, roomId); + + senderFaceInfoByLevel.setGeneratedByLevelS3url(senderGeneratedByLevelThreeS3url); // dirty check + receiverFaceInfoByLevel.setGeneratedByLevelS3url(receiverGeneratedByLevelThreeS3url); // dirty check + + case LEVEL_FOUR: + ByteArrayMultipartFile senderGeneratedByLevelFour = faceInfoService.generateByLevel(senderOrigin, senderId, senderStyleId, 4); + ByteArrayMultipartFile receiverGeneratedByLevelFour = faceInfoService.generateByLevel(receiverOrigin, receiverId, receiverStyleId, 4); + + String senderGeneratedByLevelFourS3url = bucketService.updateGeneratedByLevel(senderGeneratedByLevelFour, roomId); + String receiverGeneratedByLevelFourS3url = bucketService.updateGeneratedByLevel(receiverGeneratedByLevelFour, roomId); + + senderFaceInfoByLevel.setGeneratedByLevelS3url(senderGeneratedByLevelFourS3url); // dirty check + receiverFaceInfoByLevel.setGeneratedByLevelS3url(receiverGeneratedByLevelFourS3url); // dirty check + } + } + + @Pointcut("execution(* capstone.facefriend.chat.service.ChatRoomService.leftRoom(..))") + private void leftRoom() { + } + + // 채팅방을 나가면 sender, receiver 의 generatedByLevel 을 모두 삭제한다. + @Transactional + @Before("leftRoom()") + public void beforeLeftRoom(JoinPoint joinPoint) { + Object[] params = joinPoint.getArgs(); + MethodSignature signature = (MethodSignature) joinPoint.getSignature(); + Method method = signature.getMethod(); + + Long roomId = null; + Long memberId = null; + for (int i = 0; i < method.getParameters().length; i++) { + String paramName = method.getParameters()[i].getName(); + if (paramName.equals("roomId")) { + roomId = (Long) params[i]; + } + if (paramName.equals("memberId")) { + memberId = (Long) params[i]; + } + } + bucketService.deleteGeneratedByLevel(roomId); + } + + private ByteArrayMultipartFile getGeneratedByLevel(String generatedByLevelS3url) throws IOException { + String generatedByLevelObjectName = generatedByLevelS3url.substring(generatedByLevelS3url.lastIndexOf("/") + 1); + S3Object generatedByLevelS3Object = amazonS3.getObject(new GetObjectRequest(BUCKET_NAME, generatedByLevelObjectName)); + + S3ObjectInputStream generatedByLevelS3inputStream = generatedByLevelS3Object.getObjectContent(); + ByteArrayOutputStream generatedByLevelS3outputStream = new ByteArrayOutputStream(); + + byte[] buffer = new byte[4096]; + int bytesRead; + + while ((bytesRead = generatedByLevelS3inputStream.read(buffer)) != -1) { + generatedByLevelS3outputStream.write(buffer, 0, bytesRead); + } + generatedByLevelS3outputStream.close(); + byte[] generatedByLevelS3byteArray = generatedByLevelS3outputStream.toByteArray(); + + // convert byte[] into MultipartFile + ByteArrayMultipartFile generatedByLevel = new ByteArrayMultipartFile(generatedByLevelS3byteArray, generatedByLevelObjectName); + return generatedByLevel; + } + + private ByteArrayMultipartFile getOrigin(Member me) throws IOException { + String originS3url = me.getFaceInfo().getOriginS3url(); + String originObjectName = originS3url.substring(originS3url.lastIndexOf("/") + 1); + S3Object originS3Object = amazonS3.getObject(new GetObjectRequest(BUCKET_NAME, originObjectName)); + + S3ObjectInputStream originS3inputStream = originS3Object.getObjectContent(); + ByteArrayOutputStream originS3outputStream = new ByteArrayOutputStream(); + + byte[] buffer = new byte[4096]; + int bytesRead; + + while ((bytesRead = originS3inputStream.read(buffer)) != -1) { + originS3outputStream.write(buffer, 0, bytesRead); + } + originS3outputStream.close(); + byte[] originS3byteArray = originS3outputStream.toByteArray(); + + // convert byte[] into MultipartFile + ByteArrayMultipartFile origin = new ByteArrayMultipartFile(originS3byteArray, originObjectName); + return origin; + } + + private Member findMemberById(Long memberId) { + return memberRepository.findById(memberId) + .orElseThrow(() -> new MemberException(NOT_FOUND)); // 영속 + } + + private ChatRoomMember findChatRoomMemberByRoomId(Long roomId) { + return chatRoomMemberRepository.findByChatRoomId(roomId) + .orElseThrow(() -> new ChatException(NOT_FOUND_CHAT_ROOM_MEMBER)); // 영속 + } +} diff --git a/src/main/java/capstone/facefriend/chat/config/ChatAsyncConfig.java b/src/main/java/capstone/facefriend/chat/config/ChatAsyncConfig.java new file mode 100644 index 0000000000..070d66bfc6 --- /dev/null +++ b/src/main/java/capstone/facefriend/chat/config/ChatAsyncConfig.java @@ -0,0 +1,23 @@ +package capstone.facefriend.chat.config; + + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.annotation.EnableAsync; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; + +import java.util.concurrent.Executor; + +@EnableAsync +@Configuration +public class ChatAsyncConfig { + + @Bean("chatAsyncExecutor") + public Executor chatAsyncExecutor() { + ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); + executor.setCorePoolSize(10); + executor.setThreadNamePrefix("chat-"); + executor.initialize(); + return executor; + } +} diff --git a/src/main/java/capstone/facefriend/chat/domain/ChatRoomMember.java b/src/main/java/capstone/facefriend/chat/domain/ChatRoomMember.java index 5a3e250c92..5e1bdb4f8d 100644 --- a/src/main/java/capstone/facefriend/chat/domain/ChatRoomMember.java +++ b/src/main/java/capstone/facefriend/chat/domain/ChatRoomMember.java @@ -1,6 +1,7 @@ package capstone.facefriend.chat.domain; import capstone.facefriend.common.domain.BaseEntity; +import capstone.facefriend.member.domain.faceInfo.FaceInfoByLevel; import capstone.facefriend.member.domain.member.Member; import jakarta.persistence.*; import lombok.*; @@ -23,17 +24,25 @@ public class ChatRoomMember extends BaseEntity { private Long id; @ManyToOne - @JoinColumn(name = "Room_ID") + @JoinColumn(name = "ROOM_ID") private ChatRoom chatRoom; @ManyToOne @JoinColumn(name = "SENDER_ID") private Member sender; + @ManyToOne(cascade = CascadeType.ALL) + @JoinColumn(name = "SENDER_FACE_INFO_BY_LEVEL") + private FaceInfoByLevel senderFaceInfoByLevel; + @ManyToOne @JoinColumn(name = "RECEIVER_ID") private Member receiver; + @ManyToOne(cascade = CascadeType.ALL) + @JoinColumn(name = "RECEIVER_FACE_INFO_BY_LEVEL") + private FaceInfoByLevel receiverFaceInfoByLevel; + @Column private boolean isSenderExist; @@ -46,16 +55,6 @@ public class ChatRoomMember extends BaseEntity { @Column private boolean isReceiverPublic; - public void setChatRoom(ChatRoom chatRoom) { - this.chatRoom = chatRoom; - } - public void setSender(Member Sender) { - this.sender = sender; - } - public void setReceiver(Member Sender) { - this.receiver = receiver; - } - public boolean isSenderExist() {return this.isSenderExist == true;} public boolean isReceiverExist() {return this.isReceiverExist == true;} public boolean isSenderPublic() {return this.isSenderPublic == true;} diff --git a/src/main/java/capstone/facefriend/chat/exception/ChatExceptionType.java b/src/main/java/capstone/facefriend/chat/exception/ChatExceptionType.java index 0606db047d..9536de2017 100644 --- a/src/main/java/capstone/facefriend/chat/exception/ChatExceptionType.java +++ b/src/main/java/capstone/facefriend/chat/exception/ChatExceptionType.java @@ -4,11 +4,14 @@ import capstone.facefriend.common.exception.Status; public enum ChatExceptionType implements ExceptionType { - NOT_FOUND(Status.NOT_FOUND, 5001, "일치하는 채팅방이 없습니다."), - INVALIDED_CHATROOM(Status.BAD_REQUEST, 5002, "유효한 채팅방이 아닙니다"), - INVALID_ACCESS(Status.FORBIDDEN, 5003, "본인의 계정이 아닙니다."), - UNAUTHORIZED(Status.UNAUTHORIZED, 5005, "접근 정보가 잘못되었습니다."), - ALREADY_CHATROOM(Status.BAD_REQUEST, 5006, "이미 존재하는 채팅방입니다."), + NOT_FOUND_CHAT_ROOM(Status.NOT_FOUND, 5001, "일치하는 채팅방이 없습니다."), + NOT_FOUND_CHAT_ROOM_MEMBER(Status.NOT_FOUND, 5002, "채팅방에 채팅방 멤버가 존재하지 않습니다."), + FAIL_TO_SOCKET_INFO(Status.BAD_REQUEST, 5003, "소켓 연결 실패했습니다!"), + INVALIDED_CHATROOM(Status.BAD_REQUEST, 5004, "유효한 채팅방이 아닙니다"), + INVALID_ACCESS(Status.FORBIDDEN, 5005, "본인의 계정이 아닙니다."), + UNAUTHORIZED(Status.UNAUTHORIZED, 5006, "접근 정보가 잘못되었습니다."), + ALREADY_CHATROOM(Status.BAD_REQUEST, 5007, "이미 존재하는 채팅방입니다."), + FAIL_AOP_AFTER_UPDATE_ORIGIN(Status.SERVER_ERROR, 5008, "원본 사진을 업로드했지만 가중치 사진을 업로드 실패했습니다."), ; private final Status status; diff --git a/src/main/java/capstone/facefriend/chat/repository/ChatMessageRepository.java b/src/main/java/capstone/facefriend/chat/repository/ChatMessageRepository.java index ea4b3b3ddf..d596b67001 100644 --- a/src/main/java/capstone/facefriend/chat/repository/ChatMessageRepository.java +++ b/src/main/java/capstone/facefriend/chat/repository/ChatMessageRepository.java @@ -1,6 +1,7 @@ package capstone.facefriend.chat.repository; import capstone.facefriend.chat.domain.ChatMessage; +import capstone.facefriend.chat.domain.ChatRoom; import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; @@ -13,4 +14,5 @@ public interface ChatMessageRepository extends JpaRepository ChatMessage save(ChatMessage chatMessage); List findChatMessagesByChatRoom_IdAndSendTimeBefore(Long roomId, LocalDateTime time, Pageable pageable); List findChatMessagesByChatRoomId(Long roomId); + Integer countChatMessagesByChatRoom(ChatRoom chatRoom); } \ No newline at end of file diff --git a/src/main/java/capstone/facefriend/chat/repository/ChatRoomMemberRepository.java b/src/main/java/capstone/facefriend/chat/repository/ChatRoomMemberRepository.java index a1a738303a..787ae16e8b 100644 --- a/src/main/java/capstone/facefriend/chat/repository/ChatRoomMemberRepository.java +++ b/src/main/java/capstone/facefriend/chat/repository/ChatRoomMemberRepository.java @@ -1,6 +1,8 @@ package capstone.facefriend.chat.repository; +import capstone.facefriend.chat.domain.ChatRoom; import capstone.facefriend.chat.domain.ChatRoomMember; +import capstone.facefriend.member.domain.member.Member; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; @@ -15,4 +17,6 @@ public interface ChatRoomMemberRepository extends JpaRepository> findAllBySenderId(Long senderId); Optional> findAllByReceiverId(Long senderId); + Optional findChatRoomMemberByChatRoom(ChatRoom chatRoom); + Optional findChatRoomMemberBySenderAndReceiver(Member sender, Member receiver); } \ No newline at end of file diff --git a/src/main/java/capstone/facefriend/chat/service/ChatRoomService.java b/src/main/java/capstone/facefriend/chat/service/ChatRoomService.java index 0e3f03604e..035a3c1af5 100644 --- a/src/main/java/capstone/facefriend/chat/service/ChatRoomService.java +++ b/src/main/java/capstone/facefriend/chat/service/ChatRoomService.java @@ -27,6 +27,8 @@ import java.util.List; import java.util.Map; +import static capstone.facefriend.chat.exception.ChatExceptionType.*; + @Service @Slf4j @RequiredArgsConstructor @@ -57,19 +59,19 @@ private List findAllChatRoomMemberByReceiverId(Long memberId) { private ChatRoomInfo findChatRoomInfo(String chatRoomInfoId) { ChatRoomInfo chatRoomInfo = chatRoomInfoRedisRepository.findById(chatRoomInfoId) - .orElseThrow(()-> new ChatException(ChatExceptionType.NOT_FOUND)); + .orElseThrow(()-> new ChatException(NOT_FOUND_CHAT_ROOM)); return chatRoomInfo; } private ChatRoom findRoomById(Long roomId) { ChatRoom chatRoom = chatRoomRepository.findById(roomId) - .orElseThrow(()-> new ChatException(ChatExceptionType.NOT_FOUND)); + .orElseThrow(()-> new ChatException(NOT_FOUND_CHAT_ROOM)); return chatRoom; } private ChatRoomMember findChatRoomMemberByChatRoomId(Long roomId) { ChatRoomMember chatRoomMember = chatRoomMemberRepository.findByChatRoomId(roomId) - .orElseThrow(()-> new ChatException(ChatExceptionType.NOT_FOUND)); + .orElseThrow(()-> new ChatException(NOT_FOUND_CHAT_ROOM_MEMBER)); return chatRoomMember; } @@ -185,7 +187,7 @@ public String leftRoom(Long roomId, Long memberId) { String content = "상대방이 떠났습니다."; String method = "receiveLeftRoom"; LocalDateTime sendTime = LocalDateTime.now(); - ChatRoomLeftResponse chatRoomLeftResponse =ChatRoomLeftResponse.of(method, roomId, leftMember, sendTime, content); + ChatRoomLeftResponse chatRoomLeftResponse = ChatRoomLeftResponse.of(method, roomId, leftMember, sendTime, content); simpMessagingTemplate.convertAndSend("/sub/chat/" + sender.getId(),chatRoomLeftResponse); chatRoomRepository.save(chatRoom); chatRoomMemberRepository.save(chatRoomMember); diff --git a/src/main/java/capstone/facefriend/chat/service/MessageService.java b/src/main/java/capstone/facefriend/chat/service/MessageService.java index 4e1ec0c789..94d4b52927 100644 --- a/src/main/java/capstone/facefriend/chat/service/MessageService.java +++ b/src/main/java/capstone/facefriend/chat/service/MessageService.java @@ -11,6 +11,7 @@ import capstone.facefriend.chat.service.dto.message.MessageListResponse; import capstone.facefriend.chat.service.dto.message.MessageRequest; import capstone.facefriend.chat.service.dto.message.MessageResponse; +import capstone.facefriend.member.domain.faceInfo.FaceInfoByLevel; import capstone.facefriend.member.domain.member.Member; import capstone.facefriend.member.domain.member.MemberRepository; import capstone.facefriend.member.exception.member.MemberException; @@ -31,6 +32,9 @@ import java.util.LinkedHashMap; import java.util.List; +import static capstone.facefriend.chat.exception.ChatExceptionType.*; +import static capstone.facefriend.member.exception.member.MemberExceptionType.*; + @Service @Slf4j @RequiredArgsConstructor @@ -49,8 +53,8 @@ private Member findMemberById(String destination, Long memberId) { Member member = memberRepository.findById(memberId) .orElse(null); if (member == null) { - simpMessagingTemplate.convertAndSend(destination, MemberExceptionType.NOT_FOUND.message()); - throw new MemberException(MemberExceptionType.NOT_FOUND); + simpMessagingTemplate.convertAndSend(destination, NOT_FOUND.message()); + throw new MemberException(NOT_FOUND); } return member; @@ -60,8 +64,8 @@ private ChatRoom findRoomById(String destination, Long roomId) { ChatRoom chatRoom = chatRoomRepository.findById(roomId) .orElse(null); if (chatRoom == null) { - simpMessagingTemplate.convertAndSend(destination, ChatExceptionType.NOT_FOUND.message()); - throw new ChatException(ChatExceptionType.NOT_FOUND); + simpMessagingTemplate.convertAndSend(destination, NOT_FOUND_CHAT_ROOM.message()); + throw new ChatException(NOT_FOUND_CHAT_ROOM); } return chatRoom; } @@ -70,8 +74,8 @@ private ChatRoomMember findSenderReceiver(String destination, Long senderId, Lon ChatRoomMember chatRoomMember = chatRoomMemberRepository.findBySenderAndReceiver(senderId, receiveId) .orElse(null); if (chatRoomMember == null) { - simpMessagingTemplate.convertAndSend(destination, ChatExceptionType.NOT_FOUND.message()); - throw new ChatException(ChatExceptionType.NOT_FOUND); + simpMessagingTemplate.convertAndSend(destination, NOT_FOUND_CHAT_ROOM_MEMBER.message()); + throw new ChatException(NOT_FOUND_CHAT_ROOM_MEMBER); } return chatRoomMember; } @@ -80,21 +84,21 @@ private ChatRoomMember findChatRoomMemberByChatRoomId(String destination, Long r ChatRoomMember chatRoomMember = chatRoomMemberRepository.findByChatRoomId(roomId) .orElse(null); if (chatRoomMember == null) { - simpMessagingTemplate.convertAndSend(destination, ChatExceptionType.NOT_FOUND.message()); - throw new ChatException(ChatExceptionType.NOT_FOUND); + simpMessagingTemplate.convertAndSend(destination, NOT_FOUND_CHAT_ROOM_MEMBER.message()); + throw new ChatException(NOT_FOUND_CHAT_ROOM_MEMBER); } return chatRoomMember; } private SocketInfo findSocketInfo(Long memberId) { SocketInfo socketInfo = socketInfoRedisRepository.findById(memberId) - .orElseThrow(()-> new ChatException(ChatExceptionType.NOT_FOUND)); + .orElseThrow(()-> new ChatException(FAIL_TO_SOCKET_INFO)); return socketInfo; } private ChatRoomInfo findChatRoomInfo(String chatRoomInfoId) { ChatRoomInfo chatRoomInfo = chatRoomInfoRedisRepository.findById(chatRoomInfoId) - .orElseThrow(()-> new ChatException(ChatExceptionType.NOT_FOUND)); + .orElseThrow(()-> new ChatException(NOT_FOUND_CHAT_ROOM)); return chatRoomInfo; } @@ -111,17 +115,16 @@ public void sendMessage(MessageRequest messageRequest, Long senderId) { chatRoomRepository.save(chatRoom); chatRoomMemberRepository.save(chatRoomMember); } else if ((chatRoom.getStatus() == ChatRoom.Status.close)) { - simpMessagingTemplate.convertAndSend(exceptionDestination, ChatExceptionType.INVALIDED_CHATROOM.message()); - throw new ChatException(ChatExceptionType.INVALIDED_CHATROOM); + simpMessagingTemplate.convertAndSend(exceptionDestination, INVALIDED_CHATROOM.message()); + throw new ChatException(INVALIDED_CHATROOM); } else if ((chatRoom.getStatus() == ChatRoom.Status.set)) { - simpMessagingTemplate.convertAndSend(exceptionDestination, ChatExceptionType.INVALIDED_CHATROOM.message()); - throw new ChatException(ChatExceptionType.INVALIDED_CHATROOM); + simpMessagingTemplate.convertAndSend(exceptionDestination, INVALIDED_CHATROOM.message()); + throw new ChatException(INVALIDED_CHATROOM); } else if ((chatRoom.getStatus() == ChatRoom.Status.delete)) { - simpMessagingTemplate.convertAndSend(exceptionDestination, ChatExceptionType.INVALIDED_CHATROOM.message()); - throw new ChatException(ChatExceptionType.INVALIDED_CHATROOM); + simpMessagingTemplate.convertAndSend(exceptionDestination, INVALIDED_CHATROOM.message()); + throw new ChatException(INVALIDED_CHATROOM); } - ChatMessage chatMessage = ChatMessage.builder() .content(messageRequest.getContent()) .sender(sender) @@ -129,16 +132,17 @@ public void sendMessage(MessageRequest messageRequest, Long senderId) { .sendTime(LocalDateTime.now()) .isRead(false) .build(); - chatMessageRepository.save(chatMessage); + ChatRoomMember chatRoomMember = findChatRoomMember(chatRoom); // 영속 + MessageResponse messageResponse = new MessageResponse(); messageResponse.setMethod("receiveChat"); messageResponse.setRoomId(chatMessage.getChatRoom().getId()); messageResponse.setSenderId(senderId); messageResponse.setReceiveId(receiver.getId()); messageResponse.setSenderNickname(sender.getBasicInfo().getNickname()); - messageResponse.setSenderFaceInfoS3Url(sender.getFaceInfo().getOriginS3url()); + messageResponse.setSenderFaceInfoS3Url(chatRoomMember.getSenderFaceInfoByLevel().getGeneratedByLevelS3url()); // 수정한 부분 messageResponse.setContent(chatMessage.getContent()); messageResponse.setType("message"); messageResponse.setCreatedAt(chatMessage.getSendTime()); @@ -149,14 +153,19 @@ public void sendMessage(MessageRequest messageRequest, Long senderId) { redisTemplate.convertAndSend(topic, messageResponse); } + + private ChatRoomMember findChatRoomMember(ChatRoom chatRoom) { + return chatRoomMemberRepository.findByChatRoomId(chatRoom.getId()).orElseThrow(() -> new ChatException(NOT_FOUND_CHAT_ROOM_MEMBER)); + } + @Transactional public void sendHeart(Long senderId, Long receiveId) { String exceptionDestination = "/sub/chat/" + senderId; if (chatRoomMemberRepository.findBySenderAndReceiver(senderId, receiveId).isPresent()){ - String exceptionMessage = ChatExceptionType.ALREADY_CHATROOM.message(); + String exceptionMessage = ALREADY_CHATROOM.message(); simpMessagingTemplate.convertAndSend(exceptionDestination, exceptionMessage); - throw new ChatException(ChatExceptionType.ALREADY_CHATROOM); + throw new ChatException(ALREADY_CHATROOM); } ChatRoom chatRoom = ChatRoom.builder() @@ -171,7 +180,17 @@ public void sendHeart(Long senderId, Long receiveId) { ChatRoomMember chatRoomMember = ChatRoomMember.builder() .chatRoom(chatRoom) .sender(sender) + .senderFaceInfoByLevel( // faceInfoByLevel is initialized by generated + FaceInfoByLevel.builder() + .generatedByLevelS3url(sender.getFaceInfo().getGeneratedS3url()) // since generated is level 1 + .build() + ) .receiver(receiver) + .receiverFaceInfoByLevel( // faceInfoByLevel is initialized by generated + FaceInfoByLevel.builder() + .generatedByLevelS3url(receiver.getFaceInfo().getGeneratedS3url()) // since generated is level 1 + .build() + ) .isSenderExist(true) .isReceiverExist(true) .isSenderPublic(false) @@ -196,6 +215,11 @@ public void sendHeart(Long senderId, Long receiveId) { simpMessagingTemplate.convertAndSend(exceptionDestination, "대화 요청 성공"); redisTemplate.convertAndSend(topic, sendHeartResponse); } + + private ChatRoomMember findChatRoomMemberBySenderAndReceiver(Member sender, Member receiver) { + return chatRoomMemberRepository.findChatRoomMemberBySenderAndReceiver(sender, receiver).orElseThrow(() -> new ChatException(NOT_FOUND_CHAT_ROOM_MEMBER)); + } + @Transactional public void heartReply(HeartReplyRequest heartReplyRequest, Long receiveId) { String exceptionDestination = "/sub/chat/" + receiveId; @@ -221,8 +245,8 @@ public void heartReply(HeartReplyRequest heartReplyRequest, Long receiveId) { simpMessagingTemplate.convertAndSend(exceptionDestination, "대화 거절"); } else { - simpMessagingTemplate.convertAndSend(exceptionDestination, ChatExceptionType.ALREADY_CHATROOM); - throw new ChatException(ChatExceptionType.ALREADY_CHATROOM); + simpMessagingTemplate.convertAndSend(exceptionDestination, ALREADY_CHATROOM); + throw new ChatException(ALREADY_CHATROOM); } // 동적으로 목적지 설정 String destination = "/sub/chat/" + sender.getId(); @@ -342,4 +366,9 @@ private void sendSentHeart(String exceptionDestination, Long receiveId) { log.warn("Sendheart list is empty."); } } + + private Member findMemberByid(Long memberId) { + return memberRepository.findById(memberId) + .orElseThrow(() -> new MemberException(NOT_FOUND)); + } } \ No newline at end of file diff --git a/src/main/java/capstone/facefriend/member/controller/FaceInfoController.java b/src/main/java/capstone/facefriend/member/controller/FaceInfoController.java index ca4af52cad..4f2a56c8ae 100644 --- a/src/main/java/capstone/facefriend/member/controller/FaceInfoController.java +++ b/src/main/java/capstone/facefriend/member/controller/FaceInfoController.java @@ -28,7 +28,7 @@ public ResponseEntity get( @PutMapping("/face-info") public ResponseEntity update( @RequestPart("origin")MultipartFile origin, - @RequestParam("styleId") Long styleId, + @RequestParam("styleId") Integer styleId, @AuthMember Long memberId ) throws IOException { return ResponseEntity.ok(faceInfoService.updateOrigin(origin, styleId, memberId)); diff --git a/src/main/java/capstone/facefriend/member/domain/faceInfo/FaceInfo.java b/src/main/java/capstone/facefriend/member/domain/faceInfo/FaceInfo.java index 5c2a5a8526..0dd7a30111 100644 --- a/src/main/java/capstone/facefriend/member/domain/faceInfo/FaceInfo.java +++ b/src/main/java/capstone/facefriend/member/domain/faceInfo/FaceInfo.java @@ -16,6 +16,9 @@ public class FaceInfo { @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; + @Column + private Integer styleId; + @Column private String originS3url; diff --git a/src/main/java/capstone/facefriend/member/domain/faceInfo/FaceInfoByLevel.java b/src/main/java/capstone/facefriend/member/domain/faceInfo/FaceInfoByLevel.java new file mode 100644 index 0000000000..9158d4e047 --- /dev/null +++ b/src/main/java/capstone/facefriend/member/domain/faceInfo/FaceInfoByLevel.java @@ -0,0 +1,21 @@ +package capstone.facefriend.member.domain.faceInfo; + +import jakarta.persistence.*; +import lombok.*; + +@Getter +@Setter +@Builder +@AllArgsConstructor(access = AccessLevel.PRIVATE) +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@EqualsAndHashCode(of = {"id"}, callSuper = false) +@Entity +public class FaceInfoByLevel { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column + private String generatedByLevelS3url; +} diff --git a/src/main/java/capstone/facefriend/member/domain/faceInfo/FaceInfoByLevelRepository.java b/src/main/java/capstone/facefriend/member/domain/faceInfo/FaceInfoByLevelRepository.java new file mode 100644 index 0000000000..93ad93fe52 --- /dev/null +++ b/src/main/java/capstone/facefriend/member/domain/faceInfo/FaceInfoByLevelRepository.java @@ -0,0 +1,8 @@ +package capstone.facefriend.member.domain.faceInfo; + +import org.springframework.data.repository.Repository; + +public interface FaceInfoByLevelRepository extends Repository { + + FaceInfoByLevel save(FaceInfoByLevel faceInfoByLevel); +} diff --git a/src/main/java/capstone/facefriend/member/domain/member/Member.java b/src/main/java/capstone/facefriend/member/domain/member/Member.java index 347d49bfc8..991de3d686 100644 --- a/src/main/java/capstone/facefriend/member/domain/member/Member.java +++ b/src/main/java/capstone/facefriend/member/domain/member/Member.java @@ -4,12 +4,16 @@ import capstone.facefriend.member.domain.analysisInfo.AnalysisInfo; import capstone.facefriend.member.domain.basicInfo.BasicInfo; import capstone.facefriend.member.domain.faceInfo.FaceInfo; +import capstone.facefriend.member.domain.faceInfo.FaceInfoByLevel; import jakarta.persistence.*; import lombok.*; import lombok.extern.slf4j.Slf4j; import org.hibernate.annotations.DynamicInsert; import org.hibernate.annotations.DynamicUpdate; +import java.util.HashMap; +import java.util.Map; + @Getter @Setter @Builder @@ -48,6 +52,13 @@ public class Member extends BaseEntity { @JoinColumn(name = "ANALYSIS_INFO_ID", nullable = false) private AnalysisInfo analysisInfo; + @Builder.Default + @ElementCollection + @CollectionTable(name = "MEMBER_IS_PUBLIC_MAP", joinColumns = @JoinColumn(name = "MEMBER_ID")) + @MapKeyColumn(name = "MEM_ID") // key = other member + @Column(name = "FAMILIARITY") // value = open or close + private Map friends = new HashMap<>(); + public Member(String email) { this.email = email; this.role = Role.USER; diff --git a/src/main/java/capstone/facefriend/member/service/FaceInfoService.java b/src/main/java/capstone/facefriend/member/service/FaceInfoService.java index 36a22246cc..644c29bfe1 100644 --- a/src/main/java/capstone/facefriend/member/service/FaceInfoService.java +++ b/src/main/java/capstone/facefriend/member/service/FaceInfoService.java @@ -41,14 +41,17 @@ public class FaceInfoService { @Value("${flask.generate-url}") - private String requestUrl; + private String GENERATE_IMAGE_REQUEST_URL; + @Value("${flask.generate-by-level-url}") + private String GENERATE_IMAGE_BY_LEVEL_REQUEST_URL; + private final RestTemplate restTemplate; private final BucketService bucketService; private final MemberRepository memberRepository; private final FaceInfoRepository faceInfoRepository; @Transactional // origin 삭제 & generated 삭제 -> origin 업로드 & generated 업로드 - public FaceInfoResponse updateOrigin(MultipartFile origin, Long styleId, Long memberId) throws IOException { + public FaceInfoResponse updateOrigin(MultipartFile origin, Integer styleId, Long memberId) throws IOException { // bucket update ByteArrayMultipartFile generated = generate(origin, styleId, memberId); List s3urls = bucketService.updateOriginAndGenerated(origin, generated, memberId); @@ -62,6 +65,7 @@ public FaceInfoResponse updateOrigin(MultipartFile origin, Long styleId, Long me faceInfo.setOriginS3url(originS3url); // dirty check faceInfo.setGeneratedS3url(generatedS3url); // dirty check + faceInfo.setStyleId(styleId); member.setFaceInfo(faceInfo); // dirty check @@ -84,6 +88,7 @@ public FaceInfoResponse deleteOriginAndGenerated(Long memberId) { FaceInfo faceInfo = FaceInfo.builder() .originS3url(defaultFaceInfoS3url) .generatedS3url(defaultFaceInfoS3url) + .styleId(-1) .build(); faceInfo.setOriginS3url(defaultFaceInfoS3url); faceInfo.setGeneratedS3url(defaultFaceInfoS3url); @@ -94,7 +99,7 @@ public FaceInfoResponse deleteOriginAndGenerated(Long memberId) { return new FaceInfoResponse(defaultFaceInfoS3url, defaultFaceInfoS3url); } - private ByteArrayMultipartFile generate(MultipartFile origin, Long styleId, Long memberId) throws IOException { + private ByteArrayMultipartFile generate(MultipartFile origin, Integer styleId, Long memberId) throws IOException { // convert MultipartFile into ByteArrayResource ByteArrayResource resource = new ByteArrayResource(origin.getBytes()) { @Override @@ -117,7 +122,7 @@ public String getFilename() { HttpEntity> requestEntity = new HttpEntity<>(body, headers); // response entity - ResponseEntity responseEntity = restTemplate.postForEntity(requestUrl, requestEntity, JsonNode.class); // 문제 + ResponseEntity responseEntity = restTemplate.postForEntity(GENERATE_IMAGE_REQUEST_URL, requestEntity, JsonNode.class); // 문제 // convert JSON into Map ObjectMapper objectMapper = new ObjectMapper(); @@ -129,6 +134,41 @@ public String getFilename() { return new ByteArrayMultipartFile(imageBinary, origin.getOriginalFilename()); } + public ByteArrayMultipartFile generateByLevel(MultipartFile origin, Long memberId, int styleId, int level) throws IOException { + // convert MultipartFile into ByteArrayResource + ByteArrayResource resource = new ByteArrayResource(origin.getBytes()) { + @Override + public String getFilename() { + return URLEncoder.encode(origin.getOriginalFilename(), StandardCharsets.UTF_8); + } + }; + + // body + LinkedMultiValueMap body = new LinkedMultiValueMap<>(); + body.add("image", resource); + body.add("user_id", memberId); + body.add("style_id", styleId); + body.add("level", level); + + // header + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.MULTIPART_FORM_DATA); + + // request entity + HttpEntity> requestEntity = new HttpEntity<>(body, headers); + + // response entity + ResponseEntity responseEntity = restTemplate.postForEntity(GENERATE_IMAGE_BY_LEVEL_REQUEST_URL, requestEntity, JsonNode.class); + + // convert JSON into Map + ObjectMapper objectMapper = new ObjectMapper(); + Map result = objectMapper.convertValue(responseEntity.getBody(), new TypeReference<>() {}); + + byte[] imageBinary = Base64.getDecoder().decode((String) result.get("image_binary")); + + return new ByteArrayMultipartFile(imageBinary, origin.getOriginalFilename()); + } + private Member findMemberById(Long memberId) { return memberRepository.findById(memberId) .orElseThrow(() -> new MemberException(MemberExceptionType.NOT_FOUND)); diff --git a/src/main/java/capstone/facefriend/member/service/MemberService.java b/src/main/java/capstone/facefriend/member/service/MemberService.java index 7dfbc017a0..10455b2ef2 100644 --- a/src/main/java/capstone/facefriend/member/service/MemberService.java +++ b/src/main/java/capstone/facefriend/member/service/MemberService.java @@ -9,6 +9,8 @@ import capstone.facefriend.member.domain.basicInfo.BasicInfo; import capstone.facefriend.member.domain.basicInfo.BasicInfoRepository; import capstone.facefriend.member.domain.faceInfo.FaceInfo; +import capstone.facefriend.member.domain.faceInfo.FaceInfoByLevel; +import capstone.facefriend.member.domain.faceInfo.FaceInfoByLevelRepository; import capstone.facefriend.member.domain.faceInfo.FaceInfoRepository; import capstone.facefriend.member.domain.member.Member; import capstone.facefriend.member.domain.member.MemberRepository; @@ -41,6 +43,7 @@ public class MemberService { private final MemberRepository memberRepository; private final BasicInfoRepository basicInfoRepository; private final FaceInfoRepository faceInfoRepository; + private final FaceInfoByLevelRepository faceInfoByLevelRepository; private final AnalysisInfoRepository analysisInfoRepository; private final PasswordEncoder passwordEncoder; @@ -114,6 +117,11 @@ public SignupResponse signUp(SignUpRequest request) { .generatedS3url(defaultFaceInfoS3url) .build(); faceInfoRepository.save(faceInfo); +// +// FaceInfoByLevel faceInfoByLevel = FaceInfoByLevel.builder() +// .generatedByLevelS3url(defaultFaceInfoS3url) +// .build(); +// faceInfoByLevelRepository.save(faceInfoByLevel); // 관상 분석 초기값 AnalysisInfo analysisInfo = AnalysisInfo.builder() From 0603bf95b607d311fc03bd5068037ad86afcabcc Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Fri, 17 May 2024 15:57:54 +0900 Subject: [PATCH 246/265] =?UTF-8?q?fix:=20sender,=20receiver=20=EC=97=90?= =?UTF-8?q?=20=EB=94=B0=EB=9D=BC=20faceInfoByLevel=20=EB=8B=A4=EB=A5=B4?= =?UTF-8?q?=EA=B2=8C=20=EB=B0=98=ED=99=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../chat/service/MessageService.java | 40 +++++++++++++------ .../resume/controller/ResumeController.java | 2 +- .../resume/domain/ResumeRepositoryCustom.java | 2 +- .../resume/domain/ResumeRepositoryImpl.java | 11 ++++- .../resume/service/ResumeService.java | 4 +- 5 files changed, 42 insertions(+), 17 deletions(-) diff --git a/src/main/java/capstone/facefriend/chat/service/MessageService.java b/src/main/java/capstone/facefriend/chat/service/MessageService.java index 94d4b52927..d9407a4c3e 100644 --- a/src/main/java/capstone/facefriend/chat/service/MessageService.java +++ b/src/main/java/capstone/facefriend/chat/service/MessageService.java @@ -136,22 +136,38 @@ public void sendMessage(MessageRequest messageRequest, Long senderId) { ChatRoomMember chatRoomMember = findChatRoomMember(chatRoom); // 영속 + // getSender() 는 하트틀 보내는 사람(방 개설자)을 의미 + // senderId() 는 메세지를 보내는 사람을 의미 MessageResponse messageResponse = new MessageResponse(); - messageResponse.setMethod("receiveChat"); - messageResponse.setRoomId(chatMessage.getChatRoom().getId()); - messageResponse.setSenderId(senderId); - messageResponse.setReceiveId(receiver.getId()); - messageResponse.setSenderNickname(sender.getBasicInfo().getNickname()); - messageResponse.setSenderFaceInfoS3Url(chatRoomMember.getSenderFaceInfoByLevel().getGeneratedByLevelS3url()); // 수정한 부분 - messageResponse.setContent(chatMessage.getContent()); - messageResponse.setType("message"); - messageResponse.setCreatedAt(chatMessage.getSendTime()); - messageResponse.setIsRead(chatMessage.isRead()); - String topic = channelTopic.getTopic(); + if (chatRoomMember.getSender().equals(findMemberByid(senderId))) { + messageResponse.setMethod("receiveChat"); + messageResponse.setRoomId(chatMessage.getChatRoom().getId()); + messageResponse.setSenderId(senderId); + messageResponse.setReceiveId(receiver.getId()); + messageResponse.setSenderNickname(sender.getBasicInfo().getNickname()); + messageResponse.setSenderFaceInfoS3Url(chatRoomMember.getSenderFaceInfoByLevel().getGeneratedByLevelS3url()); // 수정한 부분 + messageResponse.setContent(chatMessage.getContent()); + messageResponse.setType("message"); + messageResponse.setCreatedAt(chatMessage.getSendTime()); + messageResponse.setIsRead(chatMessage.isRead()); + } - redisTemplate.convertAndSend(topic, messageResponse); + if (chatRoomMember.getReceiver().equals(findMemberByid(senderId))) { + messageResponse.setMethod("receiveChat"); + messageResponse.setRoomId(chatMessage.getChatRoom().getId()); + messageResponse.setSenderId(senderId); + messageResponse.setReceiveId(receiver.getId()); + messageResponse.setSenderNickname(sender.getBasicInfo().getNickname()); + messageResponse.setSenderFaceInfoS3Url(chatRoomMember.getReceiverFaceInfoByLevel().getGeneratedByLevelS3url()); // 수정한 부분 + messageResponse.setContent(chatMessage.getContent()); + messageResponse.setType("message"); + messageResponse.setCreatedAt(chatMessage.getSendTime()); + messageResponse.setIsRead(chatMessage.isRead()); + } + String topic = channelTopic.getTopic(); + redisTemplate.convertAndSend(topic, messageResponse); } private ChatRoomMember findChatRoomMember(ChatRoom chatRoom) { diff --git a/src/main/java/capstone/facefriend/resume/controller/ResumeController.java b/src/main/java/capstone/facefriend/resume/controller/ResumeController.java index 63c4e27abf..ee12db1aa1 100644 --- a/src/main/java/capstone/facefriend/resume/controller/ResumeController.java +++ b/src/main/java/capstone/facefriend/resume/controller/ResumeController.java @@ -74,6 +74,6 @@ public Page getResumesByCategory( @RequestParam("category") String category, Pageable pageable ) { - return resumeService.getResumesByCategory(category, pageable); + return resumeService.getResumesByCategory(memberId, category, pageable); } } diff --git a/src/main/java/capstone/facefriend/resume/domain/ResumeRepositoryCustom.java b/src/main/java/capstone/facefriend/resume/domain/ResumeRepositoryCustom.java index 618615f5c2..5f4168dbf2 100644 --- a/src/main/java/capstone/facefriend/resume/domain/ResumeRepositoryCustom.java +++ b/src/main/java/capstone/facefriend/resume/domain/ResumeRepositoryCustom.java @@ -7,5 +7,5 @@ public interface ResumeRepositoryCustom { Page getResumesByGoodCombi(Long memberId, Pageable pageable); // 좋은 궁합 - Page getResumesByCategory(String category, Pageable pageable); // 카테고리별 + Page getResumesByCategory(Long memberId, String category, Pageable pageable); // 카테고리별 } diff --git a/src/main/java/capstone/facefriend/resume/domain/ResumeRepositoryImpl.java b/src/main/java/capstone/facefriend/resume/domain/ResumeRepositoryImpl.java index 25bddc86a1..03e6eff8e9 100644 --- a/src/main/java/capstone/facefriend/resume/domain/ResumeRepositoryImpl.java +++ b/src/main/java/capstone/facefriend/resume/domain/ResumeRepositoryImpl.java @@ -56,6 +56,8 @@ public Page getResumesByGoodCombi(Long memberId, Pagea break; } + Member member = findMemberById(memberId); + List content = queryFactory .select(new QResumeHomeDetailResponse( resume.id.as("resumeId"), @@ -63,6 +65,7 @@ public Page getResumesByGoodCombi(Long memberId, Pagea .from(resume) .leftJoin(resume.member, QMember.member) // left join .where(builder) // boolean builder + .where(resume.member.ne(member)) .orderBy(resume.id.desc()) .offset(pageable.getOffset()) .limit(pageable.getPageSize()) @@ -79,8 +82,13 @@ public Page getResumesByGoodCombi(Long memberId, Pagea return new PageImpl<>(content, pageable, total); } + + // 카테고리별 동적 쿼리 - public Page getResumesByCategory(String category, Pageable pageable) { + public Page getResumesByCategory(Long memberId, String category, Pageable pageable) { + + Member member = findMemberById(memberId); + List content = queryFactory .select(new QResumeHomeDetailResponse( resume.id.as("resumeId"), @@ -88,6 +96,7 @@ public Page getResumesByCategory(String category, Page .from(resume) .leftJoin(resume.member, QMember.member) // left join .where(resume.categories.contains(Resume.Category.valueOf(category))) + .where(resume.member.ne(member)) .orderBy(resume.id.desc()) .offset(pageable.getOffset()) .limit(pageable.getPageSize()) diff --git a/src/main/java/capstone/facefriend/resume/service/ResumeService.java b/src/main/java/capstone/facefriend/resume/service/ResumeService.java index cb4975ca85..eece9046da 100644 --- a/src/main/java/capstone/facefriend/resume/service/ResumeService.java +++ b/src/main/java/capstone/facefriend/resume/service/ResumeService.java @@ -161,8 +161,8 @@ public Page getResumesByGoodCombi(Long memberId, Pagea return resumeRepository.getResumesByGoodCombi(memberId, pageable); } - public Page getResumesByCategory(String category, Pageable pageable) { - return resumeRepository.getResumesByCategory(category, pageable); + public Page getResumesByCategory(Long memberId, String category, Pageable pageable) { + return resumeRepository.getResumesByCategory(memberId, category, pageable); } // 내부 로직용 From a84c7c226a16d454f60f70aba83f1a3bdd12fe9d Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Fri, 17 May 2024 16:10:11 +0900 Subject: [PATCH 247/265] =?UTF-8?q?fix:=20=EB=B2=84=EA=B7=B8=ED=94=BD?= =?UTF-8?q?=EC=8A=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../facefriend/resume/domain/ResumeRepositoryImpl.java | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/main/java/capstone/facefriend/resume/domain/ResumeRepositoryImpl.java b/src/main/java/capstone/facefriend/resume/domain/ResumeRepositoryImpl.java index 03e6eff8e9..5334fb3112 100644 --- a/src/main/java/capstone/facefriend/resume/domain/ResumeRepositoryImpl.java +++ b/src/main/java/capstone/facefriend/resume/domain/ResumeRepositoryImpl.java @@ -56,8 +56,6 @@ public Page getResumesByGoodCombi(Long memberId, Pagea break; } - Member member = findMemberById(memberId); - List content = queryFactory .select(new QResumeHomeDetailResponse( resume.id.as("resumeId"), @@ -65,7 +63,6 @@ public Page getResumesByGoodCombi(Long memberId, Pagea .from(resume) .leftJoin(resume.member, QMember.member) // left join .where(builder) // boolean builder - .where(resume.member.ne(member)) .orderBy(resume.id.desc()) .offset(pageable.getOffset()) .limit(pageable.getPageSize()) @@ -87,8 +84,6 @@ public Page getResumesByGoodCombi(Long memberId, Pagea // 카테고리별 동적 쿼리 public Page getResumesByCategory(Long memberId, String category, Pageable pageable) { - Member member = findMemberById(memberId); - List content = queryFactory .select(new QResumeHomeDetailResponse( resume.id.as("resumeId"), @@ -96,7 +91,6 @@ public Page getResumesByCategory(Long memberId, String .from(resume) .leftJoin(resume.member, QMember.member) // left join .where(resume.categories.contains(Resume.Category.valueOf(category))) - .where(resume.member.ne(member)) .orderBy(resume.id.desc()) .offset(pageable.getOffset()) .limit(pageable.getPageSize()) From 195c32b17bb6a50448d9ccae2b4cbe6201b417f7 Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Fri, 17 May 2024 18:19:55 +0900 Subject: [PATCH 248/265] =?UTF-8?q?fix:=20=EB=B2=84=EA=B7=B8=20=ED=94=BD?= =?UTF-8?q?=EC=8A=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../capstone/facefriend/chat/service/MessageService.java | 7 ++++--- .../capstone/facefriend/chat/service/RedisSubscriber.java | 5 +++-- .../facefriend/resume/domain/ResumeRepositoryImpl.java | 5 +++-- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/main/java/capstone/facefriend/chat/service/MessageService.java b/src/main/java/capstone/facefriend/chat/service/MessageService.java index d9407a4c3e..1e5656f312 100644 --- a/src/main/java/capstone/facefriend/chat/service/MessageService.java +++ b/src/main/java/capstone/facefriend/chat/service/MessageService.java @@ -139,8 +139,7 @@ public void sendMessage(MessageRequest messageRequest, Long senderId) { // getSender() 는 하트틀 보내는 사람(방 개설자)을 의미 // senderId() 는 메세지를 보내는 사람을 의미 MessageResponse messageResponse = new MessageResponse(); - - if (chatRoomMember.getSender().equals(findMemberByid(senderId))) { + if (chatRoomMember.getSender().equals(sender)) { messageResponse.setMethod("receiveChat"); messageResponse.setRoomId(chatMessage.getChatRoom().getId()); messageResponse.setSenderId(senderId); @@ -153,7 +152,7 @@ public void sendMessage(MessageRequest messageRequest, Long senderId) { messageResponse.setIsRead(chatMessage.isRead()); } - if (chatRoomMember.getReceiver().equals(findMemberByid(senderId))) { + if (chatRoomMember.getReceiver().equals(receiver)) { messageResponse.setMethod("receiveChat"); messageResponse.setRoomId(chatMessage.getChatRoom().getId()); messageResponse.setSenderId(senderId); @@ -167,7 +166,9 @@ public void sendMessage(MessageRequest messageRequest, Long senderId) { } String topic = channelTopic.getTopic(); + redisTemplate.convertAndSend(topic, messageResponse); + } private ChatRoomMember findChatRoomMember(ChatRoom chatRoom) { diff --git a/src/main/java/capstone/facefriend/chat/service/RedisSubscriber.java b/src/main/java/capstone/facefriend/chat/service/RedisSubscriber.java index 48df5291d3..bad7abd6d6 100644 --- a/src/main/java/capstone/facefriend/chat/service/RedisSubscriber.java +++ b/src/main/java/capstone/facefriend/chat/service/RedisSubscriber.java @@ -39,14 +39,15 @@ public void onMessage(Message message, byte[] pattern) { log.info(messageResponse.toString()); GetMessageResponse chatMessageResponse = new GetMessageResponse(messageResponse); log.info(chatMessageResponse.toString()); + if (isExistSubscriber(messageResponse.getReceiveId())) { messagingTemplate.convertAndSend("/sub/chat/" + messageResponse.getReceiveId(), chatMessageResponse); } else { saveUnReadMessage("/sub/chat" + messageResponse.getReceiveId() + "message", messageResponse); } + messagingTemplate.convertAndSend("/sub/chat/" + messageResponse.getSenderId(), chatMessageResponse); - messagingTemplate.convertAndSend("/sub/chat/" + messageResponse.getReceiveId(), chatMessageResponse); } else if (publishMessage.contains("Heart")) { SendHeartResponse sendHeartResponse = objectMapper.readValue(publishMessage, SendHeartResponse.class); @@ -57,7 +58,7 @@ public void onMessage(Message message, byte[] pattern) { saveUnReadHeart("/sub/chat" + sendHeartResponse.getMemberId() + "heart", sendHeartResponse); } - messagingTemplate.convertAndSend("/sub/chat/" + sendHeartResponse.getMemberId(), chatSendHeartResponse); + messagingTemplate.convertAndSend("/sub/chat/" + sendHeartResponse.getSenderId(), chatSendHeartResponse); } } catch (IOException e) { throw new RuntimeException("Failed to process message", e); diff --git a/src/main/java/capstone/facefriend/resume/domain/ResumeRepositoryImpl.java b/src/main/java/capstone/facefriend/resume/domain/ResumeRepositoryImpl.java index 5334fb3112..0bcf461352 100644 --- a/src/main/java/capstone/facefriend/resume/domain/ResumeRepositoryImpl.java +++ b/src/main/java/capstone/facefriend/resume/domain/ResumeRepositoryImpl.java @@ -63,6 +63,7 @@ public Page getResumesByGoodCombi(Long memberId, Pagea .from(resume) .leftJoin(resume.member, QMember.member) // left join .where(builder) // boolean builder + .where(resume.member.ne(me)) .orderBy(resume.id.desc()) .offset(pageable.getOffset()) .limit(pageable.getPageSize()) @@ -79,10 +80,9 @@ public Page getResumesByGoodCombi(Long memberId, Pagea return new PageImpl<>(content, pageable, total); } - - // 카테고리별 동적 쿼리 public Page getResumesByCategory(Long memberId, String category, Pageable pageable) { + Member me = findMemberById(memberId); List content = queryFactory .select(new QResumeHomeDetailResponse( @@ -91,6 +91,7 @@ public Page getResumesByCategory(Long memberId, String .from(resume) .leftJoin(resume.member, QMember.member) // left join .where(resume.categories.contains(Resume.Category.valueOf(category))) + .where(resume.member.ne(me)) .orderBy(resume.id.desc()) .offset(pageable.getOffset()) .limit(pageable.getPageSize()) From 74a69cd364ca7013ff3bba02a4acb08f8f88b5fd Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Fri, 17 May 2024 22:01:00 +0900 Subject: [PATCH 249/265] =?UTF-8?q?feat:=20=EC=9D=B4=EB=AF=B8=EC=A7=80=20?= =?UTF-8?q?=EA=B0=80=EC=A4=91=EC=B9=98=20=EB=B6=80=EC=97=AC=20=EC=8B=9C?= =?UTF-8?q?=EC=A0=90=20=EC=84=B8=EB=B6=84=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../capstone/facefriend/chat/aop/ChatAop.java | 44 ++++++++++--------- .../chat/service/ChatRoomService.java | 1 + 2 files changed, 25 insertions(+), 20 deletions(-) diff --git a/src/main/java/capstone/facefriend/chat/aop/ChatAop.java b/src/main/java/capstone/facefriend/chat/aop/ChatAop.java index 81d43d75a8..9375a0fc0a 100644 --- a/src/main/java/capstone/facefriend/chat/aop/ChatAop.java +++ b/src/main/java/capstone/facefriend/chat/aop/ChatAop.java @@ -52,9 +52,12 @@ public class ChatAop { @Value("${spring.cloud.aws.s3.bucket}") private String BUCKET_NAME; - public static final int LEVEL_TWO = 5; - public static final int LEVEL_THREE = 10; - public static final int LEVEL_FOUR = 15; + private static final int LEVEL_TWO = 5; + private static final int LEVEL_THREE = 10; + private static final int LEVEL_FOUR = 15; + private static final int LEVEL_FIVE = 20; + private static final int LEVEL_SIX = 25; + private static final int LEVEL_SEVEN = 30; @Pointcut("execution(* capstone.facefriend.chat.service.MessageService.sendHeart(..))") private void sendHeart() { @@ -157,35 +160,36 @@ public void beforeSaveChatMessage(JoinPoint joinPoint) throws IOException { Long receiverId = receiver.getId(); switch (chatMessageCount) { - case LEVEL_TWO: + case LEVEL_TWO: // 5 ByteArrayMultipartFile senderGeneratedByLevelTwo = faceInfoService.generateByLevel(senderOrigin, senderId, senderStyleId, 2); - ByteArrayMultipartFile receiverGeneratedByLevelTwo = faceInfoService.generateByLevel(receiverOrigin, receiverId, receiverStyleId, 2); - String senderGeneratedByLevelTwoS3url = bucketService.updateGeneratedByLevel(senderGeneratedByLevelTwo, roomId); - String receiverGeneratedByLevelTwoS3url = bucketService.updateGeneratedByLevel(receiverGeneratedByLevelTwo, roomId); - senderFaceInfoByLevel.setGeneratedByLevelS3url(senderGeneratedByLevelTwoS3url); // dirty check + break; + case LEVEL_THREE: // 10 + ByteArrayMultipartFile receiverGeneratedByLevelTwo = faceInfoService.generateByLevel(receiverOrigin, receiverId, receiverStyleId, 2); + String receiverGeneratedByLevelTwoS3url = bucketService.updateGeneratedByLevel(receiverGeneratedByLevelTwo, roomId); receiverFaceInfoByLevel.setGeneratedByLevelS3url(receiverGeneratedByLevelTwoS3url); // dirty check - - case LEVEL_THREE: + break; + case LEVEL_FOUR: // 15 ByteArrayMultipartFile senderGeneratedByLevelThree = faceInfoService.generateByLevel(senderOrigin, senderId, senderStyleId, 3); - ByteArrayMultipartFile receiverGeneratedByLevelThree = faceInfoService.generateByLevel(receiverOrigin, receiverId, receiverStyleId, 3); - String senderGeneratedByLevelThreeS3url = bucketService.updateGeneratedByLevel(senderGeneratedByLevelThree, roomId); - String receiverGeneratedByLevelThreeS3url = bucketService.updateGeneratedByLevel(receiverGeneratedByLevelThree, roomId); - senderFaceInfoByLevel.setGeneratedByLevelS3url(senderGeneratedByLevelThreeS3url); // dirty check + break; + case LEVEL_FIVE: // 20 + ByteArrayMultipartFile receiverGeneratedByLevelThree = faceInfoService.generateByLevel(receiverOrigin, receiverId, receiverStyleId, 3); + String receiverGeneratedByLevelThreeS3url = bucketService.updateGeneratedByLevel(receiverGeneratedByLevelThree, roomId); receiverFaceInfoByLevel.setGeneratedByLevelS3url(receiverGeneratedByLevelThreeS3url); // dirty check - - case LEVEL_FOUR: + break; + case LEVEL_SIX: // 25 ByteArrayMultipartFile senderGeneratedByLevelFour = faceInfoService.generateByLevel(senderOrigin, senderId, senderStyleId, 4); - ByteArrayMultipartFile receiverGeneratedByLevelFour = faceInfoService.generateByLevel(receiverOrigin, receiverId, receiverStyleId, 4); - String senderGeneratedByLevelFourS3url = bucketService.updateGeneratedByLevel(senderGeneratedByLevelFour, roomId); - String receiverGeneratedByLevelFourS3url = bucketService.updateGeneratedByLevel(receiverGeneratedByLevelFour, roomId); - senderFaceInfoByLevel.setGeneratedByLevelS3url(senderGeneratedByLevelFourS3url); // dirty check + break; + case LEVEL_SEVEN: // 30 + ByteArrayMultipartFile receiverGeneratedByLevelFour = faceInfoService.generateByLevel(receiverOrigin, receiverId, receiverStyleId, 4); + String receiverGeneratedByLevelFourS3url = bucketService.updateGeneratedByLevel(receiverGeneratedByLevelFour, roomId); receiverFaceInfoByLevel.setGeneratedByLevelS3url(receiverGeneratedByLevelFourS3url); // dirty check + break; } } diff --git a/src/main/java/capstone/facefriend/chat/service/ChatRoomService.java b/src/main/java/capstone/facefriend/chat/service/ChatRoomService.java index 035a3c1af5..a01d43358f 100644 --- a/src/main/java/capstone/facefriend/chat/service/ChatRoomService.java +++ b/src/main/java/capstone/facefriend/chat/service/ChatRoomService.java @@ -120,6 +120,7 @@ public Map getChatRoomList(Long memberId) { Member sender = identifySender(chatRoomMember, memberId); ChatRoomOpenResponse chatRoomOpenResponse = ChatRoomOpenResponse.of(member, sender, chatRoomMember.getChatRoom(), OPEN_MESSAGE); chatRoomsOpen.add(chatRoomOpenResponse); + } else if (status == ChatRoom.Status.close) { Member leftMember = identifyLeftMember(memberId, chatRoomMember); if (member != leftMember) { From 07b9bd1a269a7c55af56727eed73d5f2c9549166 Mon Sep 17 00:00:00 2001 From: imjanghyeok Date: Sun, 19 May 2024 11:18:24 +0900 Subject: [PATCH 250/265] =?UTF-8?q?Fix:=20ChatAop=20=EC=97=90=EC=84=9C=20J?= =?UTF-8?q?oinPoint=EB=A1=9C=20=EA=B0=80=EC=A0=B8=EC=98=A8=20=ED=8C=8C?= =?UTF-8?q?=EB=9D=BC=EB=AF=B8=ED=84=B0=20=ED=8C=8C=EC=8B=B1=20=EC=98=A4?= =?UTF-8?q?=EB=A5=98=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/capstone/facefriend/chat/aop/ChatAop.java | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/main/java/capstone/facefriend/chat/aop/ChatAop.java b/src/main/java/capstone/facefriend/chat/aop/ChatAop.java index 9375a0fc0a..5195d272c4 100644 --- a/src/main/java/capstone/facefriend/chat/aop/ChatAop.java +++ b/src/main/java/capstone/facefriend/chat/aop/ChatAop.java @@ -31,6 +31,7 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.lang.reflect.Method; +import java.util.Arrays; import static capstone.facefriend.chat.exception.ChatExceptionType.NOT_FOUND_CHAT_ROOM_MEMBER; import static capstone.facefriend.member.exception.member.MemberExceptionType.NOT_FOUND; @@ -77,10 +78,10 @@ public void afterSendHeart(JoinPoint joinPoint) throws IOException { Long receiveId = -1L; for (int i = 0; i < method.getParameters().length; i++) { String paramName = method.getParameters()[i].getName(); - if (paramName.equals("senderId")) { + if (paramName.equals("arg0")) { senderId = (Long) params[i]; } - if (paramName.equals("receiveId")) { + if (paramName.equals("arg1")) { receiveId = (Long) params[i]; } } @@ -130,7 +131,7 @@ public void beforeSaveChatMessage(JoinPoint joinPoint) throws IOException { ChatMessage chatMessage = null; for (int i = 0; i < method.getParameters().length; i++) { String paramName = method.getParameters()[i].getName(); - if (paramName.equals("chatMessage")) { + if (paramName.equals("arg0")) { chatMessage = (ChatMessage) params[i]; } } @@ -209,10 +210,10 @@ public void beforeLeftRoom(JoinPoint joinPoint) { Long memberId = null; for (int i = 0; i < method.getParameters().length; i++) { String paramName = method.getParameters()[i].getName(); - if (paramName.equals("roomId")) { + if (paramName.equals("arg0")) { roomId = (Long) params[i]; } - if (paramName.equals("memberId")) { + if (paramName.equals("arg1")) { memberId = (Long) params[i]; } } From c1f9b1b4ae67d7d3c218c8ecfe64e1d6552378c1 Mon Sep 17 00:00:00 2001 From: imjanghyeok Date: Sun, 19 May 2024 11:34:37 +0900 Subject: [PATCH 251/265] =?UTF-8?q?Refactor:=20level=EC=97=90=20=EB=A7=9E?= =?UTF-8?q?=EB=8A=94=20=EC=83=81=EB=8C=80=EB=B0=A9=20=EC=9D=B4=EB=AF=B8?= =?UTF-8?q?=EC=A7=80=20=EB=9D=84=EC=9A=B0=EA=B8=B0=20=EC=9C=84=ED=95=9C=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20refactoring?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../chat/service/ChatRoomService.java | 40 ++++++++++++++++--- 1 file changed, 34 insertions(+), 6 deletions(-) diff --git a/src/main/java/capstone/facefriend/chat/service/ChatRoomService.java b/src/main/java/capstone/facefriend/chat/service/ChatRoomService.java index a01d43358f..d752f6f8f0 100644 --- a/src/main/java/capstone/facefriend/chat/service/ChatRoomService.java +++ b/src/main/java/capstone/facefriend/chat/service/ChatRoomService.java @@ -5,7 +5,6 @@ import capstone.facefriend.chat.domain.ChatRoomInfo; import capstone.facefriend.chat.domain.ChatRoomMember; import capstone.facefriend.chat.exception.ChatException; -import capstone.facefriend.chat.exception.ChatExceptionType; import capstone.facefriend.chat.repository.ChatMessageRepository; import capstone.facefriend.chat.repository.ChatRoomInfoRedisRepository; import capstone.facefriend.chat.repository.ChatRoomMemberRepository; @@ -27,7 +26,8 @@ import java.util.List; import java.util.Map; -import static capstone.facefriend.chat.exception.ChatExceptionType.*; +import static capstone.facefriend.chat.exception.ChatExceptionType.NOT_FOUND_CHAT_ROOM; +import static capstone.facefriend.chat.exception.ChatExceptionType.NOT_FOUND_CHAT_ROOM_MEMBER; @Service @Slf4j @@ -105,22 +105,50 @@ public Map getChatRoomList(Long memberId) { ChatRoom.Status status = chatRoomMember.getChatRoom().getStatus(); if (status == ChatRoom.Status.set) { + String memberFaceInfo = ""; + String senderFaceInfo = ""; Member sender = identifySender(chatRoomMember, memberId); Boolean isSender = isSender(chatRoomMember, memberId); - ChatRoomHeartResponse chatRoomHeartResponse = ChatRoomHeartResponse.of(member, sender, chatRoomMember.getChatRoom(), isSender); + if(isSender == true) { + memberFaceInfo = chatRoomMember.getSenderFaceInfoByLevel().getGeneratedByLevelS3url(); + senderFaceInfo = chatRoomMember.getReceiverFaceInfoByLevel().getGeneratedByLevelS3url(); + } else { + memberFaceInfo = chatRoomMember.getReceiverFaceInfoByLevel().getGeneratedByLevelS3url(); + senderFaceInfo = chatRoomMember.getSenderFaceInfoByLevel().getGeneratedByLevelS3url(); + } + ChatRoomHeartResponse chatRoomHeartResponse = ChatRoomHeartResponse.of(member, sender, chatRoomMember.getChatRoom(), senderFaceInfo, memberFaceInfo,isSender); chatRoomsHeart.add(chatRoomHeartResponse); } else if (status == ChatRoom.Status.progress) { + String memberFaceInfo = ""; + String senderFaceInfo = ""; Member sender = identifySender(chatRoomMember, memberId); ChatMessage chatMessage = chatMessageRepository.findFirstByChatRoomIdOrderBySendTimeDesc(chatRoomMember.getChatRoom().getId()); - ChatRoomMessageResponse chatRoomResponse = ChatRoomMessageResponse.of(member, sender, chatRoomMember.getChatRoom(), chatMessage); + Boolean isSender = isSender(chatRoomMember, memberId); + if(isSender == true) { + memberFaceInfo = chatRoomMember.getSenderFaceInfoByLevel().getGeneratedByLevelS3url(); + senderFaceInfo = chatRoomMember.getReceiverFaceInfoByLevel().getGeneratedByLevelS3url(); + } else { + memberFaceInfo = chatRoomMember.getReceiverFaceInfoByLevel().getGeneratedByLevelS3url(); + senderFaceInfo = chatRoomMember.getSenderFaceInfoByLevel().getGeneratedByLevelS3url(); + } + ChatRoomMessageResponse chatRoomResponse = ChatRoomMessageResponse.of(member, sender, chatRoomMember.getChatRoom(), senderFaceInfo, memberFaceInfo, chatMessage); chatRoomsMessage.add(chatRoomResponse); } else if (status == ChatRoom.Status.open) { + String memberFaceInfo = ""; + String senderFaceInfo = ""; Member sender = identifySender(chatRoomMember, memberId); - ChatRoomOpenResponse chatRoomOpenResponse = ChatRoomOpenResponse.of(member, sender, chatRoomMember.getChatRoom(), OPEN_MESSAGE); + Boolean isSender = isSender(chatRoomMember, memberId); + if(isSender == true) { + memberFaceInfo = chatRoomMember.getSenderFaceInfoByLevel().getGeneratedByLevelS3url(); + senderFaceInfo = chatRoomMember.getReceiverFaceInfoByLevel().getGeneratedByLevelS3url(); + } else { + memberFaceInfo = chatRoomMember.getReceiverFaceInfoByLevel().getGeneratedByLevelS3url(); + senderFaceInfo = chatRoomMember.getSenderFaceInfoByLevel().getGeneratedByLevelS3url(); + } + ChatRoomOpenResponse chatRoomOpenResponse = ChatRoomOpenResponse.of(member, sender, chatRoomMember.getChatRoom(), senderFaceInfo, memberFaceInfo, OPEN_MESSAGE); chatRoomsOpen.add(chatRoomOpenResponse); - } else if (status == ChatRoom.Status.close) { Member leftMember = identifyLeftMember(memberId, chatRoomMember); if (member != leftMember) { From baf64a096607549f0c43e9fd4c0a4f2a7db0ade7 Mon Sep 17 00:00:00 2001 From: imjanghyeok Date: Sun, 19 May 2024 11:35:06 +0900 Subject: [PATCH 252/265] =?UTF-8?q?Refactor:=20level=EC=97=90=20=EB=A7=9E?= =?UTF-8?q?=EB=8A=94=20=EC=83=81=EB=8C=80=EB=B0=A9=20=EC=9D=B4=EB=AF=B8?= =?UTF-8?q?=EC=A7=80=EB=A5=BC=20=EB=9D=84=EC=9A=B0=EA=B8=B0=20=EC=9C=84?= =?UTF-8?q?=ED=95=9C=20open=20=EC=83=81=ED=83=9C=EC=9D=98=20chatroom=20dto?= =?UTF-8?q?=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../chat/service/dto/chatroom/ChatRoomOpenResponse.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/capstone/facefriend/chat/service/dto/chatroom/ChatRoomOpenResponse.java b/src/main/java/capstone/facefriend/chat/service/dto/chatroom/ChatRoomOpenResponse.java index a968a98ac5..3be802340a 100644 --- a/src/main/java/capstone/facefriend/chat/service/dto/chatroom/ChatRoomOpenResponse.java +++ b/src/main/java/capstone/facefriend/chat/service/dto/chatroom/ChatRoomOpenResponse.java @@ -15,18 +15,18 @@ public record ChatRoomOpenResponse ( ChatRoom chatRoom, String message ) { - public static ChatRoomOpenResponse of(Member member, Member sender, ChatRoom chatRoom, String message) { + public static ChatRoomOpenResponse of(Member member, Member sender, ChatRoom chatRoom, String senderFaceInfo, String memberFaceInfo, String openMessage) { return new ChatRoomOpenResponse( member.getId(), member.getBasicInfo().getNickname(), - member.getFaceInfo().getGeneratedS3url(), + memberFaceInfo, member.getFaceInfo().getOriginS3url(), sender.getId(), sender.getBasicInfo().getNickname(), - sender.getFaceInfo().getGeneratedS3url(), + senderFaceInfo, sender.getFaceInfo().getOriginS3url(), chatRoom, - message + openMessage ); } } \ No newline at end of file From 65b052588c55da9f725ecfb68e72bf1111aa57f8 Mon Sep 17 00:00:00 2001 From: imjanghyeok Date: Sun, 19 May 2024 11:35:14 +0900 Subject: [PATCH 253/265] =?UTF-8?q?Refactor:=20level=EC=97=90=20=EB=A7=9E?= =?UTF-8?q?=EB=8A=94=20=EC=83=81=EB=8C=80=EB=B0=A9=20=EC=9D=B4=EB=AF=B8?= =?UTF-8?q?=EC=A7=80=EB=A5=BC=20=EB=9D=84=EC=9A=B0=EA=B8=B0=20=EC=9C=84?= =?UTF-8?q?=ED=95=9C=20progress=20=EC=83=81=ED=83=9C=EC=9D=98=20chatroom?= =?UTF-8?q?=20dto=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../chat/service/dto/chatroom/ChatRoomMessageResponse.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/capstone/facefriend/chat/service/dto/chatroom/ChatRoomMessageResponse.java b/src/main/java/capstone/facefriend/chat/service/dto/chatroom/ChatRoomMessageResponse.java index cd4287dd5d..99d95ea279 100644 --- a/src/main/java/capstone/facefriend/chat/service/dto/chatroom/ChatRoomMessageResponse.java +++ b/src/main/java/capstone/facefriend/chat/service/dto/chatroom/ChatRoomMessageResponse.java @@ -17,15 +17,15 @@ public record ChatRoomMessageResponse( String content ){ - public static ChatRoomMessageResponse of(Member member, Member sender, ChatRoom chatRoom, ChatMessage message) { + public static ChatRoomMessageResponse of(Member member, Member sender, ChatRoom chatRoom, String senderFaceInfo, String memberFaceInfo, ChatMessage message) { return new ChatRoomMessageResponse( member.getId(), member.getBasicInfo().getNickname(), - member.getFaceInfo().getGeneratedS3url(), + memberFaceInfo, member.getFaceInfo().getOriginS3url(), sender.getId(), sender.getBasicInfo().getNickname(), - sender.getFaceInfo().getGeneratedS3url(), + senderFaceInfo, sender.getFaceInfo().getOriginS3url(), chatRoom, message.getContent() From 5ca9b235213e323ed11642c43522ba5be7611750 Mon Sep 17 00:00:00 2001 From: imjanghyeok Date: Sun, 19 May 2024 11:35:20 +0900 Subject: [PATCH 254/265] =?UTF-8?q?Refactor:=20level=EC=97=90=20=EB=A7=9E?= =?UTF-8?q?=EB=8A=94=20=EC=83=81=EB=8C=80=EB=B0=A9=20=EC=9D=B4=EB=AF=B8?= =?UTF-8?q?=EC=A7=80=EB=A5=BC=20=EB=9D=84=EC=9A=B0=EA=B8=B0=20=EC=9C=84?= =?UTF-8?q?=ED=95=9C=20set=20=EC=83=81=ED=83=9C=EC=9D=98=20chatroom=20dto?= =?UTF-8?q?=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../chat/service/dto/chatroom/ChatRoomHeartResponse.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/capstone/facefriend/chat/service/dto/chatroom/ChatRoomHeartResponse.java b/src/main/java/capstone/facefriend/chat/service/dto/chatroom/ChatRoomHeartResponse.java index 341fc1f584..48a59d1b9c 100644 --- a/src/main/java/capstone/facefriend/chat/service/dto/chatroom/ChatRoomHeartResponse.java +++ b/src/main/java/capstone/facefriend/chat/service/dto/chatroom/ChatRoomHeartResponse.java @@ -16,15 +16,15 @@ public record ChatRoomHeartResponse ( Boolean isSender ) { - public static ChatRoomHeartResponse of(Member member, Member sender, ChatRoom chatRoom, Boolean isSender) { + public static ChatRoomHeartResponse of(Member member, Member sender, ChatRoom chatRoom, String senderFaceInfo, String memberFaceInfo, Boolean isSender) { return new ChatRoomHeartResponse( member.getId(), member.getBasicInfo().getNickname(), - member.getFaceInfo().getGeneratedS3url(), + memberFaceInfo, member.getFaceInfo().getOriginS3url(), sender.getId(), sender.getBasicInfo().getNickname(), - sender.getFaceInfo().getGeneratedS3url(), + senderFaceInfo, sender.getFaceInfo().getOriginS3url(), chatRoom, isSender From 9bda406a6f4e7d577328b44622210e8cd357ff25 Mon Sep 17 00:00:00 2001 From: imjanghyeok Date: Sun, 19 May 2024 12:35:18 +0900 Subject: [PATCH 255/265] =?UTF-8?q?Refactor:=20=EC=9B=90=EB=9E=98=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/capstone/facefriend/chat/aop/ChatAop.java | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/main/java/capstone/facefriend/chat/aop/ChatAop.java b/src/main/java/capstone/facefriend/chat/aop/ChatAop.java index 5195d272c4..f1dc47569a 100644 --- a/src/main/java/capstone/facefriend/chat/aop/ChatAop.java +++ b/src/main/java/capstone/facefriend/chat/aop/ChatAop.java @@ -31,7 +31,6 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.lang.reflect.Method; -import java.util.Arrays; import static capstone.facefriend.chat.exception.ChatExceptionType.NOT_FOUND_CHAT_ROOM_MEMBER; import static capstone.facefriend.member.exception.member.MemberExceptionType.NOT_FOUND; @@ -78,10 +77,10 @@ public void afterSendHeart(JoinPoint joinPoint) throws IOException { Long receiveId = -1L; for (int i = 0; i < method.getParameters().length; i++) { String paramName = method.getParameters()[i].getName(); - if (paramName.equals("arg0")) { + if (paramName.equals("senderId")) { senderId = (Long) params[i]; } - if (paramName.equals("arg1")) { + if (paramName.equals("receiveId")) { receiveId = (Long) params[i]; } } @@ -131,7 +130,7 @@ public void beforeSaveChatMessage(JoinPoint joinPoint) throws IOException { ChatMessage chatMessage = null; for (int i = 0; i < method.getParameters().length; i++) { String paramName = method.getParameters()[i].getName(); - if (paramName.equals("arg0")) { + if (paramName.equals("chatMessage")) { chatMessage = (ChatMessage) params[i]; } } @@ -210,10 +209,10 @@ public void beforeLeftRoom(JoinPoint joinPoint) { Long memberId = null; for (int i = 0; i < method.getParameters().length; i++) { String paramName = method.getParameters()[i].getName(); - if (paramName.equals("arg0")) { + if (paramName.equals("roomId")) { roomId = (Long) params[i]; } - if (paramName.equals("arg1")) { + if (paramName.equals("memberId")) { memberId = (Long) params[i]; } } @@ -272,4 +271,4 @@ private ChatRoomMember findChatRoomMemberByRoomId(Long roomId) { return chatRoomMemberRepository.findByChatRoomId(roomId) .orElseThrow(() -> new ChatException(NOT_FOUND_CHAT_ROOM_MEMBER)); // 영속 } -} +} \ No newline at end of file From f76b95b0966c762feceff34987ac01e24a471019 Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Sun, 19 May 2024 16:22:28 +0900 Subject: [PATCH 256/265] =?UTF-8?q?feat:=20senderId=20=EB=A1=9C=20?= =?UTF-8?q?=EC=9E=90=EC=86=8C=EC=84=9C=20=EC=A1=B0=ED=9A=8C=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../facefriend/auth/config/AuthConfig.java | 3 +++ .../resume/controller/ResumeController.java | 12 ++++++++-- .../resume/service/ResumeService.java | 22 ++++++++++++++++++- 3 files changed, 34 insertions(+), 3 deletions(-) diff --git a/src/main/java/capstone/facefriend/auth/config/AuthConfig.java b/src/main/java/capstone/facefriend/auth/config/AuthConfig.java index 1f58b1f7c6..5f2890ba9a 100644 --- a/src/main/java/capstone/facefriend/auth/config/AuthConfig.java +++ b/src/main/java/capstone/facefriend/auth/config/AuthConfig.java @@ -53,6 +53,7 @@ private HandlerInterceptor loginCheckInterceptor() { .addIncludePathPattern("/face-info", ANY) .addIncludePathPattern("/analysis-info", ANY) .addIncludePathPattern("/resume", ANY) + .addIncludePathPattern("/sender-resume", ANY) .addIncludePathPattern("/my-resume", ANY) .addIncludePathPattern("/resume-by-good-combi", ANY) .addIncludePathPattern("/resume-by-category", ANY) @@ -75,6 +76,7 @@ private HandlerInterceptor loginInterceptor() { .addIncludePathPattern("/face-info", ANY) .addIncludePathPattern("/analysis-info/**", ANY) .addIncludePathPattern("/resume", ANY) + .addIncludePathPattern("/sender-resume", ANY) .addIncludePathPattern("/my-resume", ANY) .addIncludePathPattern("/resume-by-good-combi", ANY) .addIncludePathPattern("/resume-by-category", ANY) @@ -104,6 +106,7 @@ private HandlerInterceptor tokenBlackListInterceptor() { .addIncludePathPattern("/face-info", ANY) .addIncludePathPattern("/analysis-info/**", ANY) .addIncludePathPattern("/resume", ANY) + .addIncludePathPattern("/sender-resume", ANY) .addIncludePathPattern("/my-resume", ANY) .addIncludePathPattern("/resume-by-good-combi", ANY) .addIncludePathPattern("/resume-by-category", ANY) diff --git a/src/main/java/capstone/facefriend/resume/controller/ResumeController.java b/src/main/java/capstone/facefriend/resume/controller/ResumeController.java index ee12db1aa1..ee574cb7bf 100644 --- a/src/main/java/capstone/facefriend/resume/controller/ResumeController.java +++ b/src/main/java/capstone/facefriend/resume/controller/ResumeController.java @@ -30,11 +30,19 @@ public ResponseEntity postMyResume( } @GetMapping("/resume") - public ResponseEntity getResume( + public ResponseEntity getResumeByResumeId( @AuthMember Long memberId, @RequestParam("resumeId") Long resumeId ) { - return ResponseEntity.ok(resumeService.getResume(memberId, resumeId)); + return ResponseEntity.ok(resumeService.getResumeByResumeId(memberId, resumeId)); + } + + @GetMapping("/sender-resume") + public ResponseEntity getResumeBySenderId( + @AuthMember Long memberId, + @RequestParam("senderId") Long senderId + ) { + return ResponseEntity.ok(resumeService.getResumeBySenderId(memberId, senderId)); } @GetMapping("/my-resume") diff --git a/src/main/java/capstone/facefriend/resume/service/ResumeService.java b/src/main/java/capstone/facefriend/resume/service/ResumeService.java index eece9046da..1fe0aa2ae0 100644 --- a/src/main/java/capstone/facefriend/resume/service/ResumeService.java +++ b/src/main/java/capstone/facefriend/resume/service/ResumeService.java @@ -66,7 +66,7 @@ public ResumePostPutResponse postMyResume( ); } - public ResumeGetResponse getResume( + public ResumeGetResponse getResumeByResumeId( Long memberId, Long resumeId ) { @@ -91,6 +91,26 @@ public ResumeGetResponse getResume( ); } + public ResumeGetResponse getResumeBySenderId( + Long memberId, + Long senderId + ) { + Member sender = findMemberById(senderId); + Resume senderResume = findResumeByMember(sender); + + return new ResumeGetResponse( + senderResume.getId(), + senderResume.getMember().getId(), + senderResume.getResumeImageS3urls(), + senderResume.getMember().getFaceInfo(), + senderResume.getMember().getBasicInfo(), + senderResume.getMember().getAnalysisInfo(), + senderResume.getCategories(), + senderResume.getContent(), + Boolean.FALSE + ); + } + public ResumeGetResponse getMyResume( Long memberId ) { From 331a7f2e1d1e481eda4f7ab59fbc986d3f3de8bf Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Mon, 20 May 2024 01:11:35 +0900 Subject: [PATCH 257/265] =?UTF-8?q?feat:=20@after=20=EB=A1=9C=20=EA=B0=80?= =?UTF-8?q?=EC=A4=91=EC=B9=98=20=EC=9D=B4=EB=AF=B8=EC=A7=80=20=EB=A1=9C?= =?UTF-8?q?=EB=94=A9=EC=8B=9C=EC=A0=90=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/capstone/facefriend/chat/aop/ChatAop.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/capstone/facefriend/chat/aop/ChatAop.java b/src/main/java/capstone/facefriend/chat/aop/ChatAop.java index f1dc47569a..8b4dc6b3f5 100644 --- a/src/main/java/capstone/facefriend/chat/aop/ChatAop.java +++ b/src/main/java/capstone/facefriend/chat/aop/ChatAop.java @@ -120,8 +120,8 @@ private void saveChatMessage() { // 수준에 따라 가중치를 조절해 generate_face_by_level() 호출하여 인공지능 서버에 이미지 생성을 요청한다. // 또한 가중치 이미지를 s3에 업데이트하고 그 url을 db에 저장한다. @Transactional - @Before("saveChatMessage()") - public void beforeSaveChatMessage(JoinPoint joinPoint) throws IOException { + @After("saveChatMessage()") + public void afterSaveChatMessage(JoinPoint joinPoint) throws IOException { Object[] params = joinPoint.getArgs(); MethodSignature signature = (MethodSignature) joinPoint.getSignature(); @@ -199,8 +199,8 @@ private void leftRoom() { // 채팅방을 나가면 sender, receiver 의 generatedByLevel 을 모두 삭제한다. @Transactional - @Before("leftRoom()") - public void beforeLeftRoom(JoinPoint joinPoint) { + @After("leftRoom()") + public void afterLeftRoom(JoinPoint joinPoint) { Object[] params = joinPoint.getArgs(); MethodSignature signature = (MethodSignature) joinPoint.getSignature(); Method method = signature.getMethod(); From 62749dc20ee22cc918a3be82fc49ee2cc4a0e41a Mon Sep 17 00:00:00 2001 From: imjanghyeok Date: Mon, 20 May 2024 22:59:54 +0900 Subject: [PATCH 258/265] =?UTF-8?q?Fix:=20=ED=95=98=ED=8A=B8=20=EB=B0=9B?= =?UTF-8?q?=EC=9D=80=20=EC=9C=A0=EC=A0=80=EA=B0=80=20=EB=B3=B4=EB=82=B8=20?= =?UTF-8?q?=EC=9C=A0=EC=A0=80=EC=97=90=EA=B2=8C=20=EC=95=88=EA=B0=80?= =?UTF-8?q?=EB=8A=94=20=ED=98=84=EC=83=81=20fix?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../capstone/facefriend/chat/service/MessageService.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/main/java/capstone/facefriend/chat/service/MessageService.java b/src/main/java/capstone/facefriend/chat/service/MessageService.java index 1e5656f312..973165d35a 100644 --- a/src/main/java/capstone/facefriend/chat/service/MessageService.java +++ b/src/main/java/capstone/facefriend/chat/service/MessageService.java @@ -2,7 +2,6 @@ import capstone.facefriend.chat.domain.*; import capstone.facefriend.chat.exception.ChatException; -import capstone.facefriend.chat.exception.ChatExceptionType; import capstone.facefriend.chat.repository.*; import capstone.facefriend.chat.service.dto.heart.HeartReplyRequest; import capstone.facefriend.chat.service.dto.heart.HeartReplyResponse; @@ -15,7 +14,6 @@ import capstone.facefriend.member.domain.member.Member; import capstone.facefriend.member.domain.member.MemberRepository; import capstone.facefriend.member.exception.member.MemberException; -import capstone.facefriend.member.exception.member.MemberExceptionType; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.data.domain.PageRequest; @@ -33,7 +31,7 @@ import java.util.List; import static capstone.facefriend.chat.exception.ChatExceptionType.*; -import static capstone.facefriend.member.exception.member.MemberExceptionType.*; +import static capstone.facefriend.member.exception.member.MemberExceptionType.NOT_FOUND; @Service @Slf4j @@ -152,7 +150,7 @@ public void sendMessage(MessageRequest messageRequest, Long senderId) { messageResponse.setIsRead(chatMessage.isRead()); } - if (chatRoomMember.getReceiver().equals(receiver)) { + if (chatRoomMember.getReceiver().equals(sender)) { messageResponse.setMethod("receiveChat"); messageResponse.setRoomId(chatMessage.getChatRoom().getId()); messageResponse.setSenderId(senderId); From 714c4ad927b91118c120ce878bf3c027ab36bacd Mon Sep 17 00:00:00 2001 From: imjanghyeok Date: Tue, 21 May 2024 20:27:42 +0900 Subject: [PATCH 259/265] =?UTF-8?q?Refactor:=20=EC=95=88=EC=93=B0=EA=B2=8C?= =?UTF-8?q?=20=EB=90=9C=20enter,=20exit=20api=20=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../chat/controller/ChatRoomController.java | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/src/main/java/capstone/facefriend/chat/controller/ChatRoomController.java b/src/main/java/capstone/facefriend/chat/controller/ChatRoomController.java index 6f9d82155a..918e2f141b 100644 --- a/src/main/java/capstone/facefriend/chat/controller/ChatRoomController.java +++ b/src/main/java/capstone/facefriend/chat/controller/ChatRoomController.java @@ -24,23 +24,6 @@ ResponseEntity> getChatRoomList( return ResponseEntity.ok(chatRoomService.getChatRoomList(memberId)); } - @PostMapping("/room/{roomId}/enter") - public ResponseEntity enterChatRoom( - @PathVariable("roomId") Long roomId, - @AuthMember Long memberId, - @RequestParam(required = false, defaultValue = "0", value = "page") int pageNo - ){ - return ResponseEntity.ok(chatRoomService.enterRoom(roomId, memberId)); - } - - @PostMapping("/room/{roomId}/exit") - public ResponseEntity exitChatRoom( - @PathVariable("roomId") Long roomId, - @AuthMember Long memberId - ){ - return ResponseEntity.ok(chatRoomService.exitRoom(roomId, memberId)); - } - @PostMapping("/room/{roomId}/left") public ResponseEntity leftChatRoom( @PathVariable("roomId") Long roomId, From 5b26c3afb9b006a24a5ea1d4985b886f07a285b1 Mon Sep 17 00:00:00 2001 From: imjanghyeok Date: Tue, 21 May 2024 20:27:59 +0900 Subject: [PATCH 260/265] =?UTF-8?q?Refactor:=20=EC=95=88=EC=93=B0=EA=B2=8C?= =?UTF-8?q?=20=EB=90=9C=20enter,=20exit=20service=20code=20=EC=A0=95?= =?UTF-8?q?=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../chat/service/ChatRoomService.java | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/src/main/java/capstone/facefriend/chat/service/ChatRoomService.java b/src/main/java/capstone/facefriend/chat/service/ChatRoomService.java index d752f6f8f0..2f178683c7 100644 --- a/src/main/java/capstone/facefriend/chat/service/ChatRoomService.java +++ b/src/main/java/capstone/facefriend/chat/service/ChatRoomService.java @@ -166,23 +166,6 @@ public Map getChatRoomList(Long memberId) { return chatRooms; } - public ChatRoomEnterResponse enterRoom(Long roomId, Long memberId) { - String chatRoomInfoId = roomId + "/member/" + memberId; - ChatRoomInfo chatRoomInfo = new ChatRoomInfo(); - chatRoomInfo.setChatRoomInfoId(chatRoomInfoId); - chatRoomInfo.setEnterTime(LocalDateTime.now()); - chatRoomInfoRedisRepository.save(chatRoomInfo); - return ChatRoomEnterResponse.of(roomId, memberId, chatRoomInfo); - } - - public ChatRoomExitResponse exitRoom(Long roomId, Long memberId) { - String chatRoomInfoId = roomId + "/member/" + memberId; - ChatRoomInfo chatRoomInfo = findChatRoomInfo(chatRoomInfoId); - chatRoomInfoRedisRepository.delete(chatRoomInfo); - LocalDateTime exitChatRoomTime = LocalDateTime.now(); - return ChatRoomExitResponse.of(roomId, memberId, exitChatRoomTime); - } - @Transactional public String leftRoom(Long roomId, Long memberId) { ChatRoom chatRoom = findRoomById(roomId); From 579095fcd6f8009cf442156a273aea2647f05e37 Mon Sep 17 00:00:00 2001 From: imjanghyeok Date: Tue, 21 May 2024 20:28:15 +0900 Subject: [PATCH 261/265] Refactor: sender, receiver code fix --- .../java/capstone/facefriend/chat/service/MessageService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/capstone/facefriend/chat/service/MessageService.java b/src/main/java/capstone/facefriend/chat/service/MessageService.java index 1e5656f312..af358955a2 100644 --- a/src/main/java/capstone/facefriend/chat/service/MessageService.java +++ b/src/main/java/capstone/facefriend/chat/service/MessageService.java @@ -152,7 +152,7 @@ public void sendMessage(MessageRequest messageRequest, Long senderId) { messageResponse.setIsRead(chatMessage.isRead()); } - if (chatRoomMember.getReceiver().equals(receiver)) { + if (chatRoomMember.getReceiver().equals(sender)) { messageResponse.setMethod("receiveChat"); messageResponse.setRoomId(chatMessage.getChatRoom().getId()); messageResponse.setSenderId(senderId); From 54fe2591d9067a542bfeebd1362cf0aec4b6e9f8 Mon Sep 17 00:00:00 2001 From: imjanghyeok Date: Tue, 21 May 2024 20:28:42 +0900 Subject: [PATCH 262/265] =?UTF-8?q?Refactor:=20=EC=95=88=20=EC=93=B0?= =?UTF-8?q?=EA=B2=8C=EB=90=9C=20ObjectMapper=20=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../facefriend/chat/config/RedisConfig.java | 25 +++++++++++-------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/src/main/java/capstone/facefriend/chat/config/RedisConfig.java b/src/main/java/capstone/facefriend/chat/config/RedisConfig.java index 07af53c8f7..7110fed66c 100644 --- a/src/main/java/capstone/facefriend/chat/config/RedisConfig.java +++ b/src/main/java/capstone/facefriend/chat/config/RedisConfig.java @@ -16,7 +16,7 @@ import org.springframework.data.redis.listener.RedisMessageListenerContainer; import org.springframework.data.redis.listener.adapter.MessageListenerAdapter; import org.springframework.data.redis.repository.configuration.EnableRedisRepositories; -import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; +import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer; import org.springframework.data.redis.serializer.StringRedisSerializer; @Configuration @@ -27,7 +27,6 @@ public class RedisConfig { @Value("${spring.data.redis.port}") private int redisPort; - @Bean public RedisConnectionFactory redisConnectionFactory() { RedisStandaloneConfiguration redisStandaloneConfiguration = @@ -43,7 +42,6 @@ public RedisMessageListenerContainer redisMessageListenerContainer( ) { RedisMessageListenerContainer container = new RedisMessageListenerContainer(); container.setConnectionFactory(connectionFactory); - // RedisMessageListenerContainer 에 Bean 으로 등록한 listenerAdapter, channelTopic 추가 container.addMessageListener(listenerAdapter, channelTopic); return container; } @@ -53,12 +51,8 @@ public RedisTemplate redisTemplate(RedisConnectionFactory redisC RedisTemplate redisTemplate = new RedisTemplate<>(); redisTemplate.setConnectionFactory(redisConnectionFactory); - // Jackson2JsonRedisSerializer를 사용하여 RedisTemplate 설정 - Jackson2JsonRedisSerializer serializer = new Jackson2JsonRedisSerializer<>(Object.class); - ObjectMapper objectMapper = new ObjectMapper(); - objectMapper.registerModule(new JavaTimeModule()); // Java 8 시간 모듈 등록 - objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); // 날짜를 타임스탬프로 변환하지 않도록 설정 - serializer.setObjectMapper(objectMapper); + // GenericJackson2JsonRedisSerializer를 사용하여 RedisTemplate 설정 + GenericJackson2JsonRedisSerializer serializer = new GenericJackson2JsonRedisSerializer(objectMapper()); // 문자열 직렬화 설정 redisTemplate.setKeySerializer(new StringRedisSerializer()); @@ -81,6 +75,15 @@ public MessageListenerAdapter listenerAdapter(RedisSubscriber subscriber) { } @Bean - public ChannelTopic channelTopic() {return new ChannelTopic("chatroom");} + public ChannelTopic channelTopic() { + return new ChannelTopic("chatroom"); + } -} \ No newline at end of file + @Bean + public ObjectMapper objectMapper() { + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.registerModule(new JavaTimeModule()); + objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); + return objectMapper; + } +} From 42c791a6e08865b37dd5b51ee27f4c7ce131bc20 Mon Sep 17 00:00:00 2001 From: imjanghyeok Date: Tue, 21 May 2024 20:29:05 +0900 Subject: [PATCH 263/265] =?UTF-8?q?Refactor:=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20=EC=A1=B0=EA=B1=B4=EB=AC=B8=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../chat/service/RedisSubscriber.java | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/src/main/java/capstone/facefriend/chat/service/RedisSubscriber.java b/src/main/java/capstone/facefriend/chat/service/RedisSubscriber.java index bad7abd6d6..17c22a621a 100644 --- a/src/main/java/capstone/facefriend/chat/service/RedisSubscriber.java +++ b/src/main/java/capstone/facefriend/chat/service/RedisSubscriber.java @@ -77,25 +77,13 @@ private Boolean isExistSubscriber(Long memberId) { private void saveUnReadMessage(String destination, MessageResponse messageResponse) { - Boolean isUnRead = redisTemplate.hasKey(destination); - log.info(isUnRead.toString()); - if (isUnRead) { - messageResponse.setMethod("connectChat"); - redisTemplate.opsForList().rightPush(destination, messageResponse); - } else { - messageResponse.setMethod("connectChat"); - redisTemplate.opsForList().rightPush(destination, messageResponse); - } + messageResponse.setMethod("connectChat"); + redisTemplate.opsForList().rightPush(destination, messageResponse); + } private void saveUnReadHeart(String destination, SendHeartResponse sendHeartResponse) { - Boolean isUnRead = redisTemplate.hasKey(destination); - if (isUnRead) { sendHeartResponse.setMethod("connectHeart"); redisTemplate.opsForList().rightPush(destination, sendHeartResponse); - } else { - sendHeartResponse.setMethod("connectHeart"); - redisTemplate.opsForList().rightPush(destination, sendHeartResponse); - } } } \ No newline at end of file From e615ed22b535d8dbd074f7da9035c117a00f8dbf Mon Sep 17 00:00:00 2001 From: KimChanJin97 Date: Wed, 22 May 2024 16:41:08 +0900 Subject: [PATCH 264/265] =?UTF-8?q?fix:=20analysis=20response=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../member/controller/AnalysisInfoController.java | 2 +- .../facefriend/member/service/AnalysisInfoService.java | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/capstone/facefriend/member/controller/AnalysisInfoController.java b/src/main/java/capstone/facefriend/member/controller/AnalysisInfoController.java index 097899cf07..c2e4f9174c 100644 --- a/src/main/java/capstone/facefriend/member/controller/AnalysisInfoController.java +++ b/src/main/java/capstone/facefriend/member/controller/AnalysisInfoController.java @@ -25,7 +25,7 @@ public class AnalysisInfoController { // 관상 분석할 때 사용 @PutMapping("/analysis-info") - public ResponseEntity analyze( + public ResponseEntity analyze( @RequestPart("origin") MultipartFile origin, @AuthMember Long memberId ) throws IOException { diff --git a/src/main/java/capstone/facefriend/member/service/AnalysisInfoService.java b/src/main/java/capstone/facefriend/member/service/AnalysisInfoService.java index 1095305afc..bbc6251cb8 100644 --- a/src/main/java/capstone/facefriend/member/service/AnalysisInfoService.java +++ b/src/main/java/capstone/facefriend/member/service/AnalysisInfoService.java @@ -50,7 +50,7 @@ public class AnalysisInfoService { @Transactional - public AnalysisInfoFullResponse analyze(MultipartFile origin, Long memberId) throws IOException { + public AnalysisInfoFullShortResponse analyze(MultipartFile origin, Long memberId) throws IOException { // convert MultipartFile into ByteArrayResource ByteArrayResource resource = new ByteArrayResource(origin.getBytes()) { @Override @@ -88,10 +88,10 @@ public String getFilename() { Member member = findMemberById(memberId); // 영속 상태 member.getAnalysisInfo().setAnalysisFull(analysisFull); // dirty - member.getAnalysisInfo().setAnalysisShort(analysisShort); - member.getAnalysisInfo().setFaceShapeIdNum(faceShapeIdNum); + member.getAnalysisInfo().setAnalysisShort(analysisShort); // dirty + member.getAnalysisInfo().setFaceShapeIdNum(faceShapeIdNum); // dirty - return new AnalysisInfoFullResponse(analysisFull); + return new AnalysisInfoFullShortResponse(analysisFull, analysisShort); } private Map extractAnalysisInfoFull(AnalysisInfoTotal total) { From 0927fa9e07fd1936361dba7fed473f6b8a675398 Mon Sep 17 00:00:00 2001 From: imjanghyeok Date: Wed, 22 May 2024 17:31:55 +0900 Subject: [PATCH 265/265] =?UTF-8?q?refactor:=20leftroom=20logic=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../capstone/facefriend/DummyInitializer.java | 362 +++++++++--------- .../chat/service/ChatRoomService.java | 6 +- 2 files changed, 184 insertions(+), 184 deletions(-) diff --git a/src/main/java/capstone/facefriend/DummyInitializer.java b/src/main/java/capstone/facefriend/DummyInitializer.java index ccce12efdb..4c95ca03df 100644 --- a/src/main/java/capstone/facefriend/DummyInitializer.java +++ b/src/main/java/capstone/facefriend/DummyInitializer.java @@ -1,181 +1,181 @@ -package capstone.facefriend; - -import capstone.facefriend.member.domain.analysisInfo.AnalysisInfo; -import capstone.facefriend.member.domain.analysisInfo.AnalysisInfoRepository; -import capstone.facefriend.member.domain.member.Member; -import capstone.facefriend.member.domain.member.MemberRepository; -import capstone.facefriend.member.service.AnalysisInfoService; -import capstone.facefriend.member.service.MemberService; -import capstone.facefriend.member.service.dto.member.SignUpRequest; -import capstone.facefriend.resume.domain.Resume; -import capstone.facefriend.resume.domain.ResumeRepository; -import capstone.facefriend.resume.service.ResumeService; -import jakarta.annotation.PostConstruct; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Component; -import org.springframework.transaction.annotation.Transactional; - -import java.util.ArrayList; -import java.util.List; -import java.util.Random; -import java.util.Set; -import java.util.stream.Collectors; - -@Component -@Slf4j -@RequiredArgsConstructor -public class DummyInitializer { - - private final MemberService memberService; - private final MemberRepository memberRepository; - - private final AnalysisInfoRepository analysisInfoRepository; - private final ResumeRepository resumeRepository; - - @PostConstruct - @Transactional - public void init() { - Random random = new Random(); - - List> CATEGORY = List.of( - List.of("FOOD"), - List.of("WORKOUT"), - List.of("MOVIE"), - List.of("FASHION"), - List.of("DATING"), - List.of("STUDY"), - List.of("ETC"), - List.of("FOOD", "WORKOUT"), - List.of("FOOD", "MOVIE"), - List.of("FOOD", "FASHION"), - List.of("FOOD", "DATING"), - List.of("FOOD", "STUDY"), - List.of("FOOD", "ETC"), - List.of("WORKOUT", "MOVIE"), - List.of("WORKOUT", "FASHION"), - List.of("WORKOUT", "DATING"), - List.of("WORKOUT", "STUDY"), - List.of("WORKOUT", "ETC"), - List.of("MOVIE", "FASHION"), - List.of("MOVIE", "DATING"), - List.of("MOVIE", "STUDY"), - List.of("MOVIE", "ETC"), - List.of("FASHION", "DATING"), - List.of("FASHION", "STUDY"), - List.of("FASHION", "ETC"), - List.of("DATING", "STUDY"), - List.of("DATING", "ETC"), - List.of("STUDY", "ETC"), - List.of("FOOD", "WORKOUT", "MOVIE"), - List.of("FOOD", "WORKOUT", "FASHION"), - List.of("FOOD", "WORKOUT", "DATING"), - List.of("FOOD", "WORKOUT", "STUDY"), - List.of("FOOD", "WORKOUT", "ETC"), - List.of("FOOD", "MOVIE", "FASHION"), - List.of("FOOD", "MOVIE", "DATING"), - List.of("FOOD", "MOVIE", "STUDY"), - List.of("FOOD", "MOVIE", "ETC"), - List.of("FOOD", "FASHION", "DATING"), - List.of("FOOD", "FASHION", "STUDY"), - List.of("FOOD", "FASHION", "ETC"), - List.of("FOOD", "DATING", "STUDY"), - List.of("FOOD", "DATING", "ETC"), - List.of("FOOD", "STUDY", "ETC"), - List.of("WORKOUT", "MOVIE", "FASHION"), - List.of("WORKOUT", "MOVIE", "DATING"), - List.of("WORKOUT", "MOVIE", "STUDY"), - List.of("WORKOUT", "MOVIE", "ETC"), - List.of("WORKOUT", "FASHION", "DATING"), - List.of("WORKOUT", "FASHION", "STUDY"), - List.of("WORKOUT", "FASHION", "ETC"), - List.of("WORKOUT", "DATING", "STUDY"), - List.of("WORKOUT", "DATING", "ETC"), - List.of("WORKOUT", "STUDY", "ETC"), - List.of("MOVIE", "FASHION", "DATING"), - List.of("MOVIE", "FASHION", "STUDY"), - List.of("MOVIE", "FASHION", "ETC"), - List.of("MOVIE", "DATING", "STUDY"), - List.of("MOVIE", "DATING", "ETC"), - List.of("MOVIE", "STUDY", "ETC"), - List.of("FASHION", "DATING", "STUDY"), - List.of("FASHION", "DATING", "ETC"), - List.of("FASHION", "STUDY", "ETC"), - List.of("DATING", "STUDY", "ETC"), - List.of("FOOD", "WORKOUT", "MOVIE", "FASHION"), - List.of("FOOD", "WORKOUT", "MOVIE", "DATING"), - List.of("FOOD", "WORKOUT", "MOVIE", "STUDY"), - List.of("FOOD", "WORKOUT", "MOVIE", "ETC"), - List.of("FOOD", "WORKOUT", "FASHION", "DATING"), - List.of("FOOD", "WORKOUT", "FASHION", "STUDY"), - List.of("FOOD", "WORKOUT", "FASHION", "ETC"), - List.of("FOOD", "WORKOUT", "DATING", "STUDY"), - List.of("FOOD", "WORKOUT", "DATING", "ETC"), - List.of("FOOD", "WORKOUT", "STUDY", "ETC"), - List.of("FOOD", "MOVIE", "FASHION", "DATING"), - List.of("FOOD", "MOVIE", "FASHION", "STUDY"), - List.of("FOOD", "MOVIE", "FASHION", "ETC"), - List.of("FOOD", "MOVIE", "DATING", "STUDY"), - List.of("FOOD", "MOVIE", "DATING", "ETC"), - List.of("FOOD", "MOVIE", "STUDY", "ETC"), - List.of("FOOD", "FASHION", "DATING", "STUDY"), - List.of("FOOD", "FASHION", "DATING", "ETC"), - List.of("FOOD", "FASHION", "STUDY", "ETC"), - List.of("FOOD", "DATING", "STUDY", "ETC"), - List.of("WORKOUT", "MOVIE", "FASHION", "DATING"), - List.of("WORKOUT", "MOVIE", "FASHION", "STUDY"), - List.of("WORKOUT", "MOVIE", "FASHION", "ETC"), - List.of("WORKOUT", "MOVIE", "DATING", "STUDY"), - List.of("WORKOUT", "MOVIE", "DATING", "ETC"), - List.of("WORKOUT", "MOVIE", "STUDY", "ETC"), - List.of("WORKOUT", "FASHION", "DATING", "STUDY"), - List.of("WORKOUT", "FASHION", "DATING", "ETC"), - List.of("WORKOUT", "FASHION", "STUDY", "ETC"), - List.of("WORKOUT", "DATING", "STUDY", "ETC"), - List.of("MOVIE", "FASHION", "DATING", "STUDY"), - List.of("MOVIE", "FASHION", "DATING", "ETC"), - List.of("MOVIE", "FASHION", "STUDY", "ETC"), - List.of("MOVIE", "DATING", "STUDY", "ETC"), - List.of("FASHION", "DATING", "STUDY", "ETC"), - List.of("FOOD", "WORKOUT", "MOVIE", "FASHION", "DATING"), - List.of("FOOD", "WORKOUT", "MOVIE", "FASHION", "STUDY"), - List.of("FOOD", "WORKOUT", "MOVIE", "FASHION", "ETC"), - List.of("FOOD", "WORKOUT", "MOVIE", "DATING", "STUDY"), - List.of("FOOD", "WORKOUT", "MOVIE", "DATING", "ETC"), - List.of("FOOD", "WORKOUT", "MOVIE", "STUDY", "ETC"), - List.of("FOOD", "WORKOUT", "FASHION", "DATING", "STUDY"), - List.of("FOOD", "WORKOUT", "FASHION", "DATING", "ETC"), - List.of("FOOD", "WORKOUT", "FASHION", "STUDY", "ETC"), - List.of("FOOD", "WORKOUT", "DATING", "STUDY", "ETC"), - List.of("FOOD", "MOVIE", "FASHION", "DATING", "STUDY"), - List.of("FOOD", "MOVIE", "FASHION", "DATING", "ETC"), - List.of("FOOD", "MOVIE", "FASHION", "STUDY", "ETC"), - List.of("FOOD", "MOVIE", "DATING", "STUDY", "ETC"), - List.of("FOOD", "FASHION", "DATING", "STUDY", "ETC"), - List.of("WORKOUT", "MOVIE", "FASHION", "DATING", "STUDY"), - List.of("WORKOUT", "MOVIE", "FASHION", "DATING", "ETC"), - List.of("WORKOUT", "MOVIE", "FASHION", "STUDY", "ETC"), - List.of("WORKOUT", "MOVIE", "DATING", "STUDY", "ETC"), - List.of("WORKOUT", "FASHION", "DATING", "STUDY", "ETC"), - List.of("MOVIE", "FASHION", "DATING", "STUDY", "ETC") - ); - - for (int i = 1; i <= 50; i++) { - // 회원 가입 - memberService.signUp(new SignUpRequest(i + "@" + i + ".com", "123", "123")); - Member member = memberRepository.findByEmail(i + "@" + i + ".com").get(); - - // 관상 분석 - AnalysisInfo analysisInfo = member.getAnalysisInfo(); - analysisInfo.setFaceShapeIdNum(random.nextInt(5)); // 얼굴형 넘버 랜덤 - analysisInfoRepository.save(analysisInfo); - - // 자기소개서 - Resume resume = Resume.builder() - .categories(CATEGORY.get(random.nextInt(CATEGORY.size() - 1)).stream().map(str -> Resume.Category.valueOf(str)).collect(Collectors.toSet())) // 카테고리 랜던 - .member(member) - .build(); - resumeRepository.save(resume); - } - } -} +//package capstone.facefriend; +// +//import capstone.facefriend.member.domain.analysisInfo.AnalysisInfo; +//import capstone.facefriend.member.domain.analysisInfo.AnalysisInfoRepository; +//import capstone.facefriend.member.domain.member.Member; +//import capstone.facefriend.member.domain.member.MemberRepository; +//import capstone.facefriend.member.service.AnalysisInfoService; +//import capstone.facefriend.member.service.MemberService; +//import capstone.facefriend.member.service.dto.member.SignUpRequest; +//import capstone.facefriend.resume.domain.Resume; +//import capstone.facefriend.resume.domain.ResumeRepository; +//import capstone.facefriend.resume.service.ResumeService; +//import jakarta.annotation.PostConstruct; +//import lombok.RequiredArgsConstructor; +//import lombok.extern.slf4j.Slf4j; +//import org.springframework.stereotype.Component; +//import org.springframework.transaction.annotation.Transactional; +// +//import java.util.ArrayList; +//import java.util.List; +//import java.util.Random; +//import java.util.Set; +//import java.util.stream.Collectors; +// +//@Component +//@Slf4j +//@RequiredArgsConstructor +//public class DummyInitializer { +// +// private final MemberService memberService; +// private final MemberRepository memberRepository; +// +// private final AnalysisInfoRepository analysisInfoRepository; +// private final ResumeRepository resumeRepository; +// +// @PostConstruct +// @Transactional +// public void init() { +// Random random = new Random(); +// +// List> CATEGORY = List.of( +// List.of("FOOD"), +// List.of("WORKOUT"), +// List.of("MOVIE"), +// List.of("FASHION"), +// List.of("DATING"), +// List.of("STUDY"), +// List.of("ETC"), +// List.of("FOOD", "WORKOUT"), +// List.of("FOOD", "MOVIE"), +// List.of("FOOD", "FASHION"), +// List.of("FOOD", "DATING"), +// List.of("FOOD", "STUDY"), +// List.of("FOOD", "ETC"), +// List.of("WORKOUT", "MOVIE"), +// List.of("WORKOUT", "FASHION"), +// List.of("WORKOUT", "DATING"), +// List.of("WORKOUT", "STUDY"), +// List.of("WORKOUT", "ETC"), +// List.of("MOVIE", "FASHION"), +// List.of("MOVIE", "DATING"), +// List.of("MOVIE", "STUDY"), +// List.of("MOVIE", "ETC"), +// List.of("FASHION", "DATING"), +// List.of("FASHION", "STUDY"), +// List.of("FASHION", "ETC"), +// List.of("DATING", "STUDY"), +// List.of("DATING", "ETC"), +// List.of("STUDY", "ETC"), +// List.of("FOOD", "WORKOUT", "MOVIE"), +// List.of("FOOD", "WORKOUT", "FASHION"), +// List.of("FOOD", "WORKOUT", "DATING"), +// List.of("FOOD", "WORKOUT", "STUDY"), +// List.of("FOOD", "WORKOUT", "ETC"), +// List.of("FOOD", "MOVIE", "FASHION"), +// List.of("FOOD", "MOVIE", "DATING"), +// List.of("FOOD", "MOVIE", "STUDY"), +// List.of("FOOD", "MOVIE", "ETC"), +// List.of("FOOD", "FASHION", "DATING"), +// List.of("FOOD", "FASHION", "STUDY"), +// List.of("FOOD", "FASHION", "ETC"), +// List.of("FOOD", "DATING", "STUDY"), +// List.of("FOOD", "DATING", "ETC"), +// List.of("FOOD", "STUDY", "ETC"), +// List.of("WORKOUT", "MOVIE", "FASHION"), +// List.of("WORKOUT", "MOVIE", "DATING"), +// List.of("WORKOUT", "MOVIE", "STUDY"), +// List.of("WORKOUT", "MOVIE", "ETC"), +// List.of("WORKOUT", "FASHION", "DATING"), +// List.of("WORKOUT", "FASHION", "STUDY"), +// List.of("WORKOUT", "FASHION", "ETC"), +// List.of("WORKOUT", "DATING", "STUDY"), +// List.of("WORKOUT", "DATING", "ETC"), +// List.of("WORKOUT", "STUDY", "ETC"), +// List.of("MOVIE", "FASHION", "DATING"), +// List.of("MOVIE", "FASHION", "STUDY"), +// List.of("MOVIE", "FASHION", "ETC"), +// List.of("MOVIE", "DATING", "STUDY"), +// List.of("MOVIE", "DATING", "ETC"), +// List.of("MOVIE", "STUDY", "ETC"), +// List.of("FASHION", "DATING", "STUDY"), +// List.of("FASHION", "DATING", "ETC"), +// List.of("FASHION", "STUDY", "ETC"), +// List.of("DATING", "STUDY", "ETC"), +// List.of("FOOD", "WORKOUT", "MOVIE", "FASHION"), +// List.of("FOOD", "WORKOUT", "MOVIE", "DATING"), +// List.of("FOOD", "WORKOUT", "MOVIE", "STUDY"), +// List.of("FOOD", "WORKOUT", "MOVIE", "ETC"), +// List.of("FOOD", "WORKOUT", "FASHION", "DATING"), +// List.of("FOOD", "WORKOUT", "FASHION", "STUDY"), +// List.of("FOOD", "WORKOUT", "FASHION", "ETC"), +// List.of("FOOD", "WORKOUT", "DATING", "STUDY"), +// List.of("FOOD", "WORKOUT", "DATING", "ETC"), +// List.of("FOOD", "WORKOUT", "STUDY", "ETC"), +// List.of("FOOD", "MOVIE", "FASHION", "DATING"), +// List.of("FOOD", "MOVIE", "FASHION", "STUDY"), +// List.of("FOOD", "MOVIE", "FASHION", "ETC"), +// List.of("FOOD", "MOVIE", "DATING", "STUDY"), +// List.of("FOOD", "MOVIE", "DATING", "ETC"), +// List.of("FOOD", "MOVIE", "STUDY", "ETC"), +// List.of("FOOD", "FASHION", "DATING", "STUDY"), +// List.of("FOOD", "FASHION", "DATING", "ETC"), +// List.of("FOOD", "FASHION", "STUDY", "ETC"), +// List.of("FOOD", "DATING", "STUDY", "ETC"), +// List.of("WORKOUT", "MOVIE", "FASHION", "DATING"), +// List.of("WORKOUT", "MOVIE", "FASHION", "STUDY"), +// List.of("WORKOUT", "MOVIE", "FASHION", "ETC"), +// List.of("WORKOUT", "MOVIE", "DATING", "STUDY"), +// List.of("WORKOUT", "MOVIE", "DATING", "ETC"), +// List.of("WORKOUT", "MOVIE", "STUDY", "ETC"), +// List.of("WORKOUT", "FASHION", "DATING", "STUDY"), +// List.of("WORKOUT", "FASHION", "DATING", "ETC"), +// List.of("WORKOUT", "FASHION", "STUDY", "ETC"), +// List.of("WORKOUT", "DATING", "STUDY", "ETC"), +// List.of("MOVIE", "FASHION", "DATING", "STUDY"), +// List.of("MOVIE", "FASHION", "DATING", "ETC"), +// List.of("MOVIE", "FASHION", "STUDY", "ETC"), +// List.of("MOVIE", "DATING", "STUDY", "ETC"), +// List.of("FASHION", "DATING", "STUDY", "ETC"), +// List.of("FOOD", "WORKOUT", "MOVIE", "FASHION", "DATING"), +// List.of("FOOD", "WORKOUT", "MOVIE", "FASHION", "STUDY"), +// List.of("FOOD", "WORKOUT", "MOVIE", "FASHION", "ETC"), +// List.of("FOOD", "WORKOUT", "MOVIE", "DATING", "STUDY"), +// List.of("FOOD", "WORKOUT", "MOVIE", "DATING", "ETC"), +// List.of("FOOD", "WORKOUT", "MOVIE", "STUDY", "ETC"), +// List.of("FOOD", "WORKOUT", "FASHION", "DATING", "STUDY"), +// List.of("FOOD", "WORKOUT", "FASHION", "DATING", "ETC"), +// List.of("FOOD", "WORKOUT", "FASHION", "STUDY", "ETC"), +// List.of("FOOD", "WORKOUT", "DATING", "STUDY", "ETC"), +// List.of("FOOD", "MOVIE", "FASHION", "DATING", "STUDY"), +// List.of("FOOD", "MOVIE", "FASHION", "DATING", "ETC"), +// List.of("FOOD", "MOVIE", "FASHION", "STUDY", "ETC"), +// List.of("FOOD", "MOVIE", "DATING", "STUDY", "ETC"), +// List.of("FOOD", "FASHION", "DATING", "STUDY", "ETC"), +// List.of("WORKOUT", "MOVIE", "FASHION", "DATING", "STUDY"), +// List.of("WORKOUT", "MOVIE", "FASHION", "DATING", "ETC"), +// List.of("WORKOUT", "MOVIE", "FASHION", "STUDY", "ETC"), +// List.of("WORKOUT", "MOVIE", "DATING", "STUDY", "ETC"), +// List.of("WORKOUT", "FASHION", "DATING", "STUDY", "ETC"), +// List.of("MOVIE", "FASHION", "DATING", "STUDY", "ETC") +// ); +// +// for (int i = 1; i <= 50; i++) { +// // 회원 가입 +// memberService.signUp(new SignUpRequest(i + "@" + i + ".com", "123", "123")); +// Member member = memberRepository.findByEmail(i + "@" + i + ".com").get(); +// +// // 관상 분석 +// AnalysisInfo analysisInfo = member.getAnalysisInfo(); +// analysisInfo.setFaceShapeIdNum(random.nextInt(5)); // 얼굴형 넘버 랜덤 +// analysisInfoRepository.save(analysisInfo); +// +// // 자기소개서 +// Resume resume = Resume.builder() +// .categories(CATEGORY.get(random.nextInt(CATEGORY.size() - 1)).stream().map(str -> Resume.Category.valueOf(str)).collect(Collectors.toSet())) // 카테고리 랜던 +// .member(member) +// .build(); +// resumeRepository.save(resume); +// } +// } +//} diff --git a/src/main/java/capstone/facefriend/chat/service/ChatRoomService.java b/src/main/java/capstone/facefriend/chat/service/ChatRoomService.java index 2f178683c7..4f44fe9246 100644 --- a/src/main/java/capstone/facefriend/chat/service/ChatRoomService.java +++ b/src/main/java/capstone/facefriend/chat/service/ChatRoomService.java @@ -174,7 +174,7 @@ public String leftRoom(Long roomId, Long memberId) { Member member = findMemberById(memberId); Member sender = identifySender(chatRoomMember, memberId); Member leftMember = identifyLeftMember(memberId, chatRoomMember); - if (status== ChatRoom.Status.close) { + if (status == ChatRoom.Status.close) { if (member != leftMember) { chatRoomMemberRepository.delete(chatRoomMember); chatRoomRepository.delete(chatRoom); @@ -188,9 +188,9 @@ public String leftRoom(Long roomId, Long memberId) { if(!chatMessages.isEmpty()){ chatMessageRepository.deleteAll(chatMessages); } - if (chatRoomMember.getSender() == member) { + if (chatRoomMember.getSender().equals(member)) { chatRoomMember.setSenderExist(false); - } else if (chatRoomMember.getReceiver() == member){ + } else if (chatRoomMember.getReceiver().equals(member)){ chatRoomMember.setReceiverExist(false); } else { return "속해있지 않은 채팅방입니다.";