From b66313008398c8949e3590786d19db796d81b31e Mon Sep 17 00:00:00 2001 From: lensferno Date: Sun, 5 Mar 2023 21:05:38 +0800 Subject: [PATCH] =?UTF-8?q?=E4=B8=80=E5=91=A8=E7=9B=AE=E5=AD=98=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 107 ++++++++ README.md | 43 ++++ disillusion-client-jfx17/pom.xml | 140 +++++++++++ .../disillusion/client/ClientMain.java | 48 ++++ .../disillusion/client/config/Constants.java | 104 ++++++++ .../controller/CommunicativeController.java | 8 + .../controller/LoginMainController.java | 180 ++++++++++++++ .../components/ComponentFactory.java | 54 ++++ .../application/ApplicationDealPane.java | 32 +++ .../application/ApplicationListItem.java | 96 +++++++ .../application/ApplicationListPage.java | 75 ++++++ .../application/ApplicationOfferCard.java | 106 ++++++++ .../application/ApplicationTableItem.java | 24 ++ .../application/ResumeDownloadPane.java | 69 +++++ .../employer/application/StudentInfoPage.java | 90 +++++++ .../employer/offer/EmployerOfferCard.java | 134 ++++++++++ .../offer/EmployerOfferDetailPane.java | 93 +++++++ .../student/application/ApplicationCard.java | 140 +++++++++++ .../application/ApplicationPostPane.java | 83 +++++++ .../student/offer/EmployerInfoPage.java | 119 +++++++++ .../student/offer/OfferDetailPane.java | 95 +++++++ .../student/offer/StudentOfferCard.java | 174 +++++++++++++ .../components/student/resume/ResumeCard.java | 172 +++++++++++++ .../student/resume/ResumeUploadPane.java | 95 +++++++ .../EmployerApplicationManagePage.java | 91 +++++++ .../page/employer/EmployerMainController.java | 136 ++++++++++ .../employer/EmployerOfferManagePage.java | 114 +++++++++ .../page/employer/EmployerProfilePage.java | 206 +++++++++++++++ .../page/student/ApplicationManagePage.java | 106 ++++++++ .../page/student/OfferSquarePage.java | 204 +++++++++++++++ .../page/student/ResumeManagePage.java | 194 +++++++++++++++ .../page/student/StudentMainController.java | 174 +++++++++++++ .../page/student/StudentProfilePage.java | 235 ++++++++++++++++++ .../disillusion/client/data/ResponseCode.java | 45 ++++ .../data/request/ApplicationRequest.java | 20 ++ .../data/request/OfferPublishRequest.java | 36 +++ .../client/data/request/ResumeRequest.java | 21 ++ .../data/response/ApplicationResponse.java | 37 +++ .../data/response/EmployerInfoResponse.java | 41 +++ .../client/data/response/OfferResponse.java | 61 +++++ .../response/PublicEmployerInfoResponse.java | 40 +++ .../response/PublicStudentInfoResponse.java | 38 +++ .../client/data/response/ResumeResponse.java | 31 +++ .../data/response/StudentInfoResponse.java | 43 ++++ .../disillusion/client/util/Consumer.java | 8 + .../disillusion/client/util/DialogMaker.java | 130 ++++++++++ .../disillusion/client/util/ImageUtil.java | 20 ++ .../disillusion/client/util/OkhttpUtil.java | 173 +++++++++++++ .../client/util/ResponseHandler.java | 72 ++++++ .../client/util/ThreadSafeDialogMaker.java | 43 ++++ .../src/main/resources/META-INF/MANIFEST.MF | 3 + .../src/main/resources/exampleFile.txt | 13 + .../lensfrex/disillusion/client/css/style.css | 60 +++++ .../application/applicationDealPane.fxml | 32 +++ .../application/applicationListItem.fxml | 55 ++++ .../application/applicationListPage.fxml | 15 ++ .../application/applicationManagePage.fxml | 57 +++++ .../application/applicationOfferCard.fxml | 50 ++++ .../application/resumeDownloadPane.fxml | 22 ++ .../employer/application/studentProfile.fxml | 174 +++++++++++++ .../client/fxml/employer/employerMain.fxml | 113 +++++++++ .../client/fxml/employer/offer/offerCard.fxml | 59 +++++ .../fxml/employer/offer/offerDetailPane.fxml | 110 ++++++++ .../fxml/employer/offer/offerManagePage.fxml | 53 ++++ .../client/fxml/employer/profile.fxml | 183 ++++++++++++++ .../disillusion/client/fxml/loginMain.fxml | 89 +++++++ .../student/application/applicationCard.fxml | 59 +++++ .../application/applicationManagePage.fxml | 42 ++++ .../student/application/applicationPost.fxml | 22 ++ .../application/applicationPostPage.fxml | 22 ++ .../fxml/student/offer/employerProfile.fxml | 170 +++++++++++++ .../client/fxml/student/offer/offerCard.fxml | 78 ++++++ .../fxml/student/offer/offerDetail.fxml | 131 ++++++++++ .../fxml/student/offer/offerSquare.fxml | 67 +++++ .../client/fxml/student/profile.fxml | 218 ++++++++++++++++ .../fxml/student/resume/resumeCard.fxml | 59 +++++ .../fxml/student/resume/resumeManagePage.fxml | 53 ++++ .../fxml/student/resume/resumeUpload.fxml | 77 ++++++ .../client/fxml/student/studentMain.fxml | 121 +++++++++ disillusion/.gitignore | 67 +++++ disillusion/backend/.flattened-pom.xml | 101 ++++++++ disillusion/backend/pom.xml | 170 +++++++++++++ .../net/lensfrex/disillusion/BackendMain.java | 13 + .../lensfrex/disillusion/DocumentMain.java | 24 ++ .../disillusion/annotation/CheckAuth.java | 10 + .../configure/ApplicationContextHelper.java | 24 ++ .../configure/GlobalConfigure.java | 24 ++ .../configure/InterceptorConfigure.java | 38 +++ .../configure/SwaggerConfigure.java | 35 +++ .../net/lensfrex/disillusion/dao/DaoBase.java | 83 +++++++ .../dao/OfferApplicationTable.java | 14 ++ .../disillusion/dao/OfferStatusTable.java | 12 + .../lensfrex/disillusion/dao/OfferTable.java | 14 ++ .../lensfrex/disillusion/dao/ResumeTable.java | 14 ++ .../disillusion/dao/UserInfoTable.java | 14 ++ .../lensfrex/disillusion/dao/UserTable.java | 14 ++ .../dao/index/EmployerOfferIndex.java | 16 ++ .../dao/index/StudentResumeIndex.java | 15 ++ .../application/EmployerApplicationIndex.java | 15 ++ .../application/OfferApplicationIndex.java | 15 ++ .../application/StudentApplicationIndex.java | 15 ++ .../lensfrex/disillusion/util/TokenUtil.java | 46 ++++ .../web/controller/OfferController.java | 158 ++++++++++++ .../web/controller/ResumeController.java | 47 ++++ .../web/controller/TestController.java | 19 ++ .../employer/EmolpyerOfferController.java | 99 ++++++++ .../EmployerOfferApplicationController.java | 75 ++++++ .../StudentOfferApplicationController.java | 109 ++++++++ .../student/StudentResumeController.java | 101 ++++++++ .../web/controller/user/UserController.java | 127 ++++++++++ .../controller/user/UserInfoController.java | 168 +++++++++++++ .../web/interceptor/TokenInterceptor.java | 52 ++++ .../web/request/OfferApplicationRequest.java | 32 +++ .../web/request/OfferPublishRequest.java | 36 +++ .../web/request/ResumeRequest.java | 27 ++ .../web/response/ApplicationResponse.java | 37 +++ .../response/EmployerApplicationResponse.java | 28 +++ .../web/response/OfferListResponse.java | 51 ++++ .../user/PublicEmployerInfoResponse.java | 40 +++ .../user/PublicStudentInfoResponse.java | 40 +++ .../response/user/UserInfoResponseBase.java | 4 + .../web/service/employer/OfferService.java | 191 ++++++++++++++ .../service/student/ApplicationService.java | 169 +++++++++++++ .../web/service/student/ResumeService.java | 131 ++++++++++ .../web/service/user/UserInfoService.java | 41 +++ .../web/service/user/UserService.java | 36 +++ .../src/main/resources/application.properties | 6 + .../disillusion/BackendApplicationTests.java | 13 + disillusion/common/.flattened-pom.xml | 40 +++ disillusion/common/pom.xml | 27 ++ .../lensfrex/disillusion/data/ArrayList.java | 178 +++++++++++++ .../net/lensfrex/disillusion/data/Hash.java | 120 +++++++++ .../lensfrex/disillusion/data/LinkedList.java | 217 ++++++++++++++++ .../net/lensfrex/disillusion/data/List.java | 29 +++ .../net/lensfrex/disillusion/data/Map.java | 6 + .../lensfrex/disillusion/data/db/Table.java | 40 +++ .../disillusion/model/MultiIndex.java | 44 ++++ .../disillusion/model/OfferApplication.java | 42 ++++ .../disillusion/model/OfferStatus.java | 8 + .../lensfrex/disillusion/model/StoreData.java | 14 ++ .../net/lensfrex/disillusion/model/User.java | 61 +++++ .../lensfrex/disillusion/model/UserInfo.java | 5 + .../disillusion/model/employer/Employer.java | 60 +++++ .../disillusion/model/employer/Offer.java | 63 +++++ .../disillusion/model/student/Resume.java | 34 +++ .../disillusion/model/student/Student.java | 66 +++++ .../response/general/Response.java | 32 +++ .../response/general/ResponseCode.java | 45 ++++ .../net/lensfrex/disillusion/util/IOUtil.java | 48 ++++ .../main/resources/61212844_p0_compress.png | Bin 0 -> 25882 bytes .../main/resources/defaultAvatarBase64.txt | 1 + disillusion/pom.xml | 99 ++++++++ .../virtual-database/.flattened-pom.xml | 46 ++++ disillusion/virtual-database/pom.xml | 33 +++ .../dao/database/DatabaseOperator.java | 79 ++++++ .../disillusion/dao/database/Table.java | 11 + .../dao/database/VirtualDatabase.java | 17 ++ 157 files changed, 10902 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 disillusion-client-jfx17/pom.xml create mode 100644 disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/ClientMain.java create mode 100644 disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/config/Constants.java create mode 100644 disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/controller/CommunicativeController.java create mode 100644 disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/controller/LoginMainController.java create mode 100644 disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/controller/components/ComponentFactory.java create mode 100644 disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/controller/components/employer/application/ApplicationDealPane.java create mode 100644 disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/controller/components/employer/application/ApplicationListItem.java create mode 100644 disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/controller/components/employer/application/ApplicationListPage.java create mode 100644 disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/controller/components/employer/application/ApplicationOfferCard.java create mode 100644 disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/controller/components/employer/application/ApplicationTableItem.java create mode 100644 disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/controller/components/employer/application/ResumeDownloadPane.java create mode 100644 disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/controller/components/employer/application/StudentInfoPage.java create mode 100644 disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/controller/components/employer/offer/EmployerOfferCard.java create mode 100644 disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/controller/components/employer/offer/EmployerOfferDetailPane.java create mode 100644 disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/controller/components/student/application/ApplicationCard.java create mode 100644 disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/controller/components/student/application/ApplicationPostPane.java create mode 100644 disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/controller/components/student/offer/EmployerInfoPage.java create mode 100644 disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/controller/components/student/offer/OfferDetailPane.java create mode 100644 disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/controller/components/student/offer/StudentOfferCard.java create mode 100644 disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/controller/components/student/resume/ResumeCard.java create mode 100644 disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/controller/components/student/resume/ResumeUploadPane.java create mode 100644 disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/controller/page/employer/EmployerApplicationManagePage.java create mode 100644 disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/controller/page/employer/EmployerMainController.java create mode 100644 disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/controller/page/employer/EmployerOfferManagePage.java create mode 100644 disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/controller/page/employer/EmployerProfilePage.java create mode 100644 disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/controller/page/student/ApplicationManagePage.java create mode 100644 disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/controller/page/student/OfferSquarePage.java create mode 100644 disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/controller/page/student/ResumeManagePage.java create mode 100644 disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/controller/page/student/StudentMainController.java create mode 100644 disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/controller/page/student/StudentProfilePage.java create mode 100644 disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/data/ResponseCode.java create mode 100644 disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/data/request/ApplicationRequest.java create mode 100644 disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/data/request/OfferPublishRequest.java create mode 100644 disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/data/request/ResumeRequest.java create mode 100644 disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/data/response/ApplicationResponse.java create mode 100644 disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/data/response/EmployerInfoResponse.java create mode 100644 disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/data/response/OfferResponse.java create mode 100644 disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/data/response/PublicEmployerInfoResponse.java create mode 100644 disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/data/response/PublicStudentInfoResponse.java create mode 100644 disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/data/response/ResumeResponse.java create mode 100644 disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/data/response/StudentInfoResponse.java create mode 100644 disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/util/Consumer.java create mode 100644 disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/util/DialogMaker.java create mode 100644 disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/util/ImageUtil.java create mode 100644 disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/util/OkhttpUtil.java create mode 100644 disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/util/ResponseHandler.java create mode 100644 disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/util/ThreadSafeDialogMaker.java create mode 100644 disillusion-client-jfx17/src/main/resources/META-INF/MANIFEST.MF create mode 100644 disillusion-client-jfx17/src/main/resources/exampleFile.txt create mode 100644 disillusion-client-jfx17/src/main/resources/net/lensfrex/disillusion/client/css/style.css create mode 100644 disillusion-client-jfx17/src/main/resources/net/lensfrex/disillusion/client/fxml/employer/application/applicationDealPane.fxml create mode 100644 disillusion-client-jfx17/src/main/resources/net/lensfrex/disillusion/client/fxml/employer/application/applicationListItem.fxml create mode 100644 disillusion-client-jfx17/src/main/resources/net/lensfrex/disillusion/client/fxml/employer/application/applicationListPage.fxml create mode 100644 disillusion-client-jfx17/src/main/resources/net/lensfrex/disillusion/client/fxml/employer/application/applicationManagePage.fxml create mode 100644 disillusion-client-jfx17/src/main/resources/net/lensfrex/disillusion/client/fxml/employer/application/applicationOfferCard.fxml create mode 100644 disillusion-client-jfx17/src/main/resources/net/lensfrex/disillusion/client/fxml/employer/application/resumeDownloadPane.fxml create mode 100644 disillusion-client-jfx17/src/main/resources/net/lensfrex/disillusion/client/fxml/employer/application/studentProfile.fxml create mode 100644 disillusion-client-jfx17/src/main/resources/net/lensfrex/disillusion/client/fxml/employer/employerMain.fxml create mode 100644 disillusion-client-jfx17/src/main/resources/net/lensfrex/disillusion/client/fxml/employer/offer/offerCard.fxml create mode 100644 disillusion-client-jfx17/src/main/resources/net/lensfrex/disillusion/client/fxml/employer/offer/offerDetailPane.fxml create mode 100644 disillusion-client-jfx17/src/main/resources/net/lensfrex/disillusion/client/fxml/employer/offer/offerManagePage.fxml create mode 100644 disillusion-client-jfx17/src/main/resources/net/lensfrex/disillusion/client/fxml/employer/profile.fxml create mode 100644 disillusion-client-jfx17/src/main/resources/net/lensfrex/disillusion/client/fxml/loginMain.fxml create mode 100644 disillusion-client-jfx17/src/main/resources/net/lensfrex/disillusion/client/fxml/student/application/applicationCard.fxml create mode 100644 disillusion-client-jfx17/src/main/resources/net/lensfrex/disillusion/client/fxml/student/application/applicationManagePage.fxml create mode 100644 disillusion-client-jfx17/src/main/resources/net/lensfrex/disillusion/client/fxml/student/application/applicationPost.fxml create mode 100644 disillusion-client-jfx17/src/main/resources/net/lensfrex/disillusion/client/fxml/student/application/applicationPostPage.fxml create mode 100644 disillusion-client-jfx17/src/main/resources/net/lensfrex/disillusion/client/fxml/student/offer/employerProfile.fxml create mode 100644 disillusion-client-jfx17/src/main/resources/net/lensfrex/disillusion/client/fxml/student/offer/offerCard.fxml create mode 100644 disillusion-client-jfx17/src/main/resources/net/lensfrex/disillusion/client/fxml/student/offer/offerDetail.fxml create mode 100644 disillusion-client-jfx17/src/main/resources/net/lensfrex/disillusion/client/fxml/student/offer/offerSquare.fxml create mode 100644 disillusion-client-jfx17/src/main/resources/net/lensfrex/disillusion/client/fxml/student/profile.fxml create mode 100644 disillusion-client-jfx17/src/main/resources/net/lensfrex/disillusion/client/fxml/student/resume/resumeCard.fxml create mode 100644 disillusion-client-jfx17/src/main/resources/net/lensfrex/disillusion/client/fxml/student/resume/resumeManagePage.fxml create mode 100644 disillusion-client-jfx17/src/main/resources/net/lensfrex/disillusion/client/fxml/student/resume/resumeUpload.fxml create mode 100644 disillusion-client-jfx17/src/main/resources/net/lensfrex/disillusion/client/fxml/student/studentMain.fxml create mode 100644 disillusion/.gitignore create mode 100644 disillusion/backend/.flattened-pom.xml create mode 100644 disillusion/backend/pom.xml create mode 100644 disillusion/backend/src/main/java/net/lensfrex/disillusion/BackendMain.java create mode 100644 disillusion/backend/src/main/java/net/lensfrex/disillusion/DocumentMain.java create mode 100644 disillusion/backend/src/main/java/net/lensfrex/disillusion/annotation/CheckAuth.java create mode 100644 disillusion/backend/src/main/java/net/lensfrex/disillusion/configure/ApplicationContextHelper.java create mode 100644 disillusion/backend/src/main/java/net/lensfrex/disillusion/configure/GlobalConfigure.java create mode 100644 disillusion/backend/src/main/java/net/lensfrex/disillusion/configure/InterceptorConfigure.java create mode 100644 disillusion/backend/src/main/java/net/lensfrex/disillusion/configure/SwaggerConfigure.java create mode 100644 disillusion/backend/src/main/java/net/lensfrex/disillusion/dao/DaoBase.java create mode 100644 disillusion/backend/src/main/java/net/lensfrex/disillusion/dao/OfferApplicationTable.java create mode 100644 disillusion/backend/src/main/java/net/lensfrex/disillusion/dao/OfferStatusTable.java create mode 100644 disillusion/backend/src/main/java/net/lensfrex/disillusion/dao/OfferTable.java create mode 100644 disillusion/backend/src/main/java/net/lensfrex/disillusion/dao/ResumeTable.java create mode 100644 disillusion/backend/src/main/java/net/lensfrex/disillusion/dao/UserInfoTable.java create mode 100644 disillusion/backend/src/main/java/net/lensfrex/disillusion/dao/UserTable.java create mode 100644 disillusion/backend/src/main/java/net/lensfrex/disillusion/dao/index/EmployerOfferIndex.java create mode 100644 disillusion/backend/src/main/java/net/lensfrex/disillusion/dao/index/StudentResumeIndex.java create mode 100644 disillusion/backend/src/main/java/net/lensfrex/disillusion/dao/index/application/EmployerApplicationIndex.java create mode 100644 disillusion/backend/src/main/java/net/lensfrex/disillusion/dao/index/application/OfferApplicationIndex.java create mode 100644 disillusion/backend/src/main/java/net/lensfrex/disillusion/dao/index/application/StudentApplicationIndex.java create mode 100644 disillusion/backend/src/main/java/net/lensfrex/disillusion/util/TokenUtil.java create mode 100644 disillusion/backend/src/main/java/net/lensfrex/disillusion/web/controller/OfferController.java create mode 100644 disillusion/backend/src/main/java/net/lensfrex/disillusion/web/controller/ResumeController.java create mode 100644 disillusion/backend/src/main/java/net/lensfrex/disillusion/web/controller/TestController.java create mode 100644 disillusion/backend/src/main/java/net/lensfrex/disillusion/web/controller/employer/EmolpyerOfferController.java create mode 100644 disillusion/backend/src/main/java/net/lensfrex/disillusion/web/controller/employer/EmployerOfferApplicationController.java create mode 100644 disillusion/backend/src/main/java/net/lensfrex/disillusion/web/controller/student/StudentOfferApplicationController.java create mode 100644 disillusion/backend/src/main/java/net/lensfrex/disillusion/web/controller/student/StudentResumeController.java create mode 100644 disillusion/backend/src/main/java/net/lensfrex/disillusion/web/controller/user/UserController.java create mode 100644 disillusion/backend/src/main/java/net/lensfrex/disillusion/web/controller/user/UserInfoController.java create mode 100644 disillusion/backend/src/main/java/net/lensfrex/disillusion/web/interceptor/TokenInterceptor.java create mode 100644 disillusion/backend/src/main/java/net/lensfrex/disillusion/web/request/OfferApplicationRequest.java create mode 100644 disillusion/backend/src/main/java/net/lensfrex/disillusion/web/request/OfferPublishRequest.java create mode 100644 disillusion/backend/src/main/java/net/lensfrex/disillusion/web/request/ResumeRequest.java create mode 100644 disillusion/backend/src/main/java/net/lensfrex/disillusion/web/response/ApplicationResponse.java create mode 100644 disillusion/backend/src/main/java/net/lensfrex/disillusion/web/response/EmployerApplicationResponse.java create mode 100644 disillusion/backend/src/main/java/net/lensfrex/disillusion/web/response/OfferListResponse.java create mode 100644 disillusion/backend/src/main/java/net/lensfrex/disillusion/web/response/user/PublicEmployerInfoResponse.java create mode 100644 disillusion/backend/src/main/java/net/lensfrex/disillusion/web/response/user/PublicStudentInfoResponse.java create mode 100644 disillusion/backend/src/main/java/net/lensfrex/disillusion/web/response/user/UserInfoResponseBase.java create mode 100644 disillusion/backend/src/main/java/net/lensfrex/disillusion/web/service/employer/OfferService.java create mode 100644 disillusion/backend/src/main/java/net/lensfrex/disillusion/web/service/student/ApplicationService.java create mode 100644 disillusion/backend/src/main/java/net/lensfrex/disillusion/web/service/student/ResumeService.java create mode 100644 disillusion/backend/src/main/java/net/lensfrex/disillusion/web/service/user/UserInfoService.java create mode 100644 disillusion/backend/src/main/java/net/lensfrex/disillusion/web/service/user/UserService.java create mode 100644 disillusion/backend/src/main/resources/application.properties create mode 100644 disillusion/backend/src/test/java/net/lensfrex/disillusion/BackendApplicationTests.java create mode 100644 disillusion/common/.flattened-pom.xml create mode 100644 disillusion/common/pom.xml create mode 100644 disillusion/common/src/main/java/net/lensfrex/disillusion/data/ArrayList.java create mode 100644 disillusion/common/src/main/java/net/lensfrex/disillusion/data/Hash.java create mode 100644 disillusion/common/src/main/java/net/lensfrex/disillusion/data/LinkedList.java create mode 100644 disillusion/common/src/main/java/net/lensfrex/disillusion/data/List.java create mode 100644 disillusion/common/src/main/java/net/lensfrex/disillusion/data/Map.java create mode 100644 disillusion/common/src/main/java/net/lensfrex/disillusion/data/db/Table.java create mode 100644 disillusion/common/src/main/java/net/lensfrex/disillusion/model/MultiIndex.java create mode 100644 disillusion/common/src/main/java/net/lensfrex/disillusion/model/OfferApplication.java create mode 100644 disillusion/common/src/main/java/net/lensfrex/disillusion/model/OfferStatus.java create mode 100644 disillusion/common/src/main/java/net/lensfrex/disillusion/model/StoreData.java create mode 100644 disillusion/common/src/main/java/net/lensfrex/disillusion/model/User.java create mode 100644 disillusion/common/src/main/java/net/lensfrex/disillusion/model/UserInfo.java create mode 100644 disillusion/common/src/main/java/net/lensfrex/disillusion/model/employer/Employer.java create mode 100644 disillusion/common/src/main/java/net/lensfrex/disillusion/model/employer/Offer.java create mode 100644 disillusion/common/src/main/java/net/lensfrex/disillusion/model/student/Resume.java create mode 100644 disillusion/common/src/main/java/net/lensfrex/disillusion/model/student/Student.java create mode 100644 disillusion/common/src/main/java/net/lensfrex/disillusion/response/general/Response.java create mode 100644 disillusion/common/src/main/java/net/lensfrex/disillusion/response/general/ResponseCode.java create mode 100644 disillusion/common/src/main/java/net/lensfrex/disillusion/util/IOUtil.java create mode 100644 disillusion/common/src/main/resources/61212844_p0_compress.png create mode 100644 disillusion/common/src/main/resources/defaultAvatarBase64.txt create mode 100644 disillusion/pom.xml create mode 100644 disillusion/virtual-database/.flattened-pom.xml create mode 100644 disillusion/virtual-database/pom.xml create mode 100644 disillusion/virtual-database/src/main/java/net/lensfrex/disillusion/dao/database/DatabaseOperator.java create mode 100644 disillusion/virtual-database/src/main/java/net/lensfrex/disillusion/dao/database/Table.java create mode 100644 disillusion/virtual-database/src/main/java/net/lensfrex/disillusion/dao/database/VirtualDatabase.java diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b3ea45a --- /dev/null +++ b/.gitignore @@ -0,0 +1,107 @@ +HELP.md +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr +.mvn + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ + +*/.flattened-pom.xml +/.flattened-pom.xml + +out + +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ +migrate_working_dir/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock. +/pubspec.lock +**/doc/api/ +.dart_tool/ +.packages +build/ + +HELP.md +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ + +*.log +*.class +mvnw +mvnw.cmd + +/database/ +/delombok/ +*/.mvn diff --git a/README.md b/README.md new file mode 100644 index 0000000..554b3e2 --- /dev/null +++ b/README.md @@ -0,0 +1,43 @@ +# disillusion + +某个课设的完整工程代码 + +其实核心就只是后端部分模拟的数据库以及后端对数据的处理 + +用的是java + +写得不咋的,纯纯的答辩 + +一大堆东西只是写着玩的,基本上就是写着写着写嗨了的产物 + +这里的git仓库是一周目的存档,原仓库是分开来的,这里都放到了一起 + +以后基本上不会再碰这个东西了 + +嘛,反正也没人看 + +--- + +环境要求: + +后端: +- Java 17+ + +客户端: +- Java 17+,带Javafx SDK运行环境 + +--- + +关于客户端的运行 + +由于Java高版本(8+后版本以及大部分openjdk发行版)将javafx库从标准库中分离成为了独立项目,因此如要运行客户端,需要手动加上jvm运行参数: + +`--module-path "./javafx-sdk/lib" --add-modules javafx.controls,javafx.fxml --add-opens java.base/java.lang.reflect=ALL-UNNAMED --add-exports javafx.graphics/com.sun.javafx.scene=ALL-UNNAMED` + +可参见:[【Java】错误: 缺少 JavaFX 运行时组件, 需要使用该组件来运行此应用程序 的解决方案](https://blog.csdn.net/weixin_44746306/article/details/117768300) + +Javafx sdk可在:[JavaFX](https://gluonhq.com/products/javafx/)下载 + +当然,也可以[直接下载release](https://git.ciduid.top/lensfrex/disillusion-archive/releases) + +~~真的会有人看来吗?~~ \ No newline at end of file diff --git a/disillusion-client-jfx17/pom.xml b/disillusion-client-jfx17/pom.xml new file mode 100644 index 0000000..fee31f9 --- /dev/null +++ b/disillusion-client-jfx17/pom.xml @@ -0,0 +1,140 @@ + + + 4.0.0 + + net.lensfrex + disillusion-client-jfx11 + 1.0-SNAPSHOT + disillusion-client-jfx11 + + + UTF-8 + 5.9.1 + + + + + org.junit.jupiter + junit-jupiter-api + ${junit.version} + test + + + org.junit.jupiter + junit-jupiter-engine + ${junit.version} + test + + + + + com.jfoenix + jfoenix + 9.0.10 + + + + com.squareup.okhttp3 + okhttp + 4.10.0 + + + org.projectlombok + lombok + 1.18.24 + + + ch.qos.logback + logback-classic + 1.4.5 + + + + cn.hutool + hutool-core + 5.8.13 + + + + cn.hutool + hutool-json + 5.8.12 + + + + cn.hutool + hutool-crypto + 5.8.13 + + + net.coobird + thumbnailator + 0.4.19 + + + + io.github.typhon0 + AnimateFX + 1.2.4 + + + + + org.openjfx + javafx + 17 + pom + + + org.openjfx + javafx-base + 17 + + + + org.openjfx + javafx-controls + 17 + + + org.openjfx + javafx-fxml + 17 + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 17 + 17 + + 3.8.1 + + + org.openjfx + javafx-maven-plugin + 0.0.8 + + net.lensfrex.disillusion.client.ClientMain + + + + + com.gluonhq + gluonfx-maven-plugin + 1.0.16 + + host + net.lensfrex.disillusion.client.ClientMain + + + + + + \ No newline at end of file diff --git a/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/ClientMain.java b/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/ClientMain.java new file mode 100644 index 0000000..df6b61e --- /dev/null +++ b/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/ClientMain.java @@ -0,0 +1,48 @@ +package net.lensfrex.disillusion.client; + +import cn.hutool.core.thread.ThreadUtil; +import javafx.application.Application; +import javafx.fxml.FXMLLoader; +import javafx.scene.Scene; +import javafx.stage.Stage; +import net.lensfrex.disillusion.client.config.Constants; +import net.lensfrex.disillusion.client.controller.LoginMainController; +import okhttp3.OkHttpClient; + +import java.io.File; +import java.io.IOException; + +public class ClientMain extends Application { + private static final OkHttpClient okhttp = Constants.globalClient; + + private static Stage stage; + + public static Stage getStage() { + return stage; + } + + @Override + public void start(Stage stage) throws IOException { + ClientMain.stage = stage; + + // 抗锯齿 + System.setProperty("prism.lcdtext", "false"); + File tokenFile = new File(Constants.tokenFile); + if (tokenFile.exists()) { + + } + + FXMLLoader fxmlLoader = new FXMLLoader(ClientMain.class.getResource("fxml/loginMain.fxml")); + Scene scene = new Scene(fxmlLoader.load(), 1000, 600); + stage.setTitle("disillusion - login"); + stage.setScene(scene); + stage.show(); + + LoginMainController loginMainController = fxmlLoader.getController(); + loginMainController.setMainStage(stage); + } + + public static void main(String[] args) { + launch(); + } +} \ No newline at end of file diff --git a/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/config/Constants.java b/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/config/Constants.java new file mode 100644 index 0000000..5545ae3 --- /dev/null +++ b/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/config/Constants.java @@ -0,0 +1,104 @@ +package net.lensfrex.disillusion.client.config; + +import okhttp3.OkHttpClient; + +import java.net.InetSocketAddress; +import java.net.Proxy; + +public class Constants { + public static final String tokenFile = "token"; + + public static final String numberPattern = "^\\d+$"; + +// public static final OkHttpClient globalClient = new OkHttpClient.Builder() +// .proxy(new Proxy(Proxy.Type.HTTP, new InetSocketAddress("127.0.0.1", 8080))) +// .build(); + + public static final OkHttpClient globalClient = new OkHttpClient(); + + public static final String SERVER_ADDRESS = "http://127.0.0.1:8888"; + public static final String BASE_URL = "/api"; + + public static class UserRequestUrls { + public static final String LOGIN_URL = SERVER_ADDRESS + BASE_URL + "/user/login"; + public static final String REGISTER_URL = SERVER_ADDRESS + BASE_URL + "/user/register"; + public static final String TOKEN_REFRESH_URL = SERVER_ADDRESS + BASE_URL + "/user/refresh"; + + public static final String USER_INFO_EDIT_URL = SERVER_ADDRESS + BASE_URL + "/user/info/edit"; + public static final String USER_INFO_VIEW_URL = SERVER_ADDRESS + BASE_URL + "/user/info/view"; + + public static final String USER_PUBLIC_INFO_VIEW_URL = SERVER_ADDRESS + BASE_URL + "/user/info/view/public"; + + public static final String USER_AVATAR_VIEW_URL = SERVER_ADDRESS + BASE_URL + "/user/info/avatar"; + public static final String USER_AVATAR_EDIT_URL = SERVER_ADDRESS + BASE_URL + "/user/info/avatar/upload"; + } + + public static class ResumeRequestUrls { + public static final String RESUME_UPLOAD_URL = SERVER_ADDRESS + BASE_URL + "/student/resume/add"; + public static final String RESUME_GET_ALL_URL = SERVER_ADDRESS + BASE_URL + "/student/resume/all"; + public static final String RESUME_DELETE_URL = SERVER_ADDRESS + BASE_URL + "/student/resume/delete"; + public static final String RESUME_FILE_URL = SERVER_ADDRESS + BASE_URL + "/student/resume/getFile"; + public static final String RESUME_UPDATE_URL = SERVER_ADDRESS + BASE_URL + "/student/resume/update"; + public static final String RESUME_VIEW_URL = SERVER_ADDRESS + BASE_URL + "/student/resume/view"; + } + + public static class ApplicationRequestUrls { + public static final String APPLICATION_PUBLISH_URL = SERVER_ADDRESS + BASE_URL + "/student/application/publish"; + public static final String APPLICATION_DELETE_URL = SERVER_ADDRESS + BASE_URL + "/student/application/delete"; + public static final String APPLICATION_STUDENT_VIEW_URL = SERVER_ADDRESS + BASE_URL + "/student/application/view"; + + public static final String APPLICATION_EMPLOYER_VIEW_URL = SERVER_ADDRESS + BASE_URL + "/employer/application/view"; + public static final String APPLICATION_DEAL_URL = SERVER_ADDRESS + BASE_URL + "/employer/application/deal"; + } + + public static class OfferRequestUrls { + + public static final String OFFER_FILTER_EMPLOYER = "by_employer"; + public static final String OFFER_FILTER_JOB_NAME = "by_job_name"; + public static final String OFFER_FILTER_SALARY = "by_salary"; + + public static final String GET_ALL_OFFER_URL = SERVER_ADDRESS + BASE_URL + "/offer/getAll"; + public static final String GET_OFFER_URL = SERVER_ADDRESS + BASE_URL + "/offer/get"; + public static final String OFFER_PROGRESS_URL = SERVER_ADDRESS + BASE_URL + "/offer/progress"; + + public static final String OFFER_GET_BY_EMPLOYER_SELF_URL = SERVER_ADDRESS + BASE_URL + "/employer/offer/view"; + public static final String OFFER_DELETE_URL = SERVER_ADDRESS + BASE_URL + "/employer/offer/delete"; + public static final String OFFER_PUBLISH_URL = SERVER_ADDRESS + BASE_URL + "/employer/offer/publish"; + public static final String OFFER_UPDATE_URL = SERVER_ADDRESS + BASE_URL + "/employer/offer/update"; + + public static final String OFFER_SEARCH_URL = SERVER_ADDRESS + BASE_URL + "/offer/search"; + } + + public static final String USER_TYPE_STUDENT = "0"; + public static final String USER_TYPE_EMPLOYER = "1"; + + public static final String sample = " 前期经过自愿报名并审核成功的学生,根据所报微专业的培养方案,自行选择相应课程。\n" + + "\n" + + " 9.认真对照培养方案,选课时务必仔细核对课程信息,确保“课程代码”、“课程名称”、“学分”这三项重要信息与培养方案要求相符。\n" + + "\n" + + " 10.禁止同一时间修读两门及以上课程,系统会提示时间冲突并禁止选课。提前了解专业教学进程(实习、实践、课程设计等)情况,不能选择与本专业其它教学任务周次有冲突的课堂。\n" + + "\n" + + " 11.因培养方案变动、转专业等原因,计划修读的课程确定不再开课的,请先查询本科生课程学分认定对照表并咨询开课学院,再进行选课。待课程修读完获得学分后,再按照本科生院发布的相关要求办理学分认定。\n" + + "\n" + + " 12.学生选课、听课、考核必须一致。学生不能参加未选择课程的学习和考核,即使参加了学习和考核,如果没有选课,也是无效的,将不记录成绩。\n" + + "\n" + + " 13.学生凭学号、密码选课,认真对待,并对自己的选课行为负责。密码必须妥善保管,不得代替他人选课,不得借用、盗用他人学号及密码选课。\n" + + "\n" + + " 14.学生登录本科教学综合管理系统需通过学校统一身份认证平台登录,登录方式、密码重置方式详见以下两个通知:https://jwc.wust.edu.cn/2022/0226/c1925a255654/page.htm、https://jwc.wust.edu.cn/2022/0302/c1925a255786/page.htm。"; + + public static class Svg { + public static final String deleteSvg = "M96 128h832v64H96zM128 256h768l-89.024 704H217.024z M384 64h256v96h-256z"; + + public static final String sendSvg = + "M1008 6.286q18.857 13.714 15.429 36.571l-146.286 877.714q-2.857 16.571-18.286 25.714-8 4.571-17.714 4.571-6.286 " + + "0-13.714-2.857l-258.857-105.714-138.286 168.571q-10.286 13.143-28 13.143-7.429 " + + "0-12.571-2.286-10.857-4-17.429-13.429t-6.571-20.857v-199.429l493.714-605.143-610.857 " + + "528.571-225.714-92.571q-21.143-8-22.857-31.429-1.143-22.857 18.286-33.714l950.857-548.571q8.571-5.143 18.286-5.143 " + + "11.429 0 20.571 6.286z"; + + public static final String viewSvg = + "M320.06 255.58m28 0l328.07 0q28 0 28 28l0 0q0 28-28 28l-328.07 0q-28 0-28-28l0 0q0-28 28-28Z " + + "M320.06 420.21m28 0l200.42 0q28 0 28 28l0 0q0 28-28 28l-200.42 0q-28 0-28-28l0 0q0-28 28-28Z " + + "M888.35 320h-56.19V135.61a72 72 0 0 0-72-72H264a72 72 0 0 0-72 72V320h-55.79a72 72 0 0 0-72 72v496.22a72 72 0 0 0 72 72h752.14a72 72 0 0 0 72-72V392a72 72 0 0 0-72-72z m0 72v47.59c-0.53 0.27-1.05 0.56-1.57 0.86L832.16 472v-80zM264 135.61h496.16v377l-151.33 88.31-96.55 55.74L264 513.33V135.61zM192 392v79.77l-55.83-32.24V392z m-55.79 496.22V522.67l56.2 32.45 298 173A43 43 0 0 0 502 732.7a35.88 35.88 0 0 0 9.52 1.51h1.61a35.58 35.58 0 0 0 11.65-2.21 42.58 42.58 0 0 0 9-3.94L645 663.17l243.35-140.5v365.55z"; + } +} diff --git a/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/controller/CommunicativeController.java b/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/controller/CommunicativeController.java new file mode 100644 index 0000000..9d8445b --- /dev/null +++ b/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/controller/CommunicativeController.java @@ -0,0 +1,8 @@ +package net.lensfrex.disillusion.client.controller; + +// 可通信的Controller +public interface CommunicativeController { + void loadData(T initData); + void initialize(); + class InitData {} +} diff --git a/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/controller/LoginMainController.java b/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/controller/LoginMainController.java new file mode 100644 index 0000000..dac2768 --- /dev/null +++ b/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/controller/LoginMainController.java @@ -0,0 +1,180 @@ +package net.lensfrex.disillusion.client.controller; + +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import com.jfoenix.controls.*; +import javafx.application.Platform; +import javafx.event.ActionEvent; +import javafx.fxml.FXML; +import javafx.fxml.FXMLLoader; +import javafx.scene.Scene; +import javafx.scene.control.ToggleGroup; +import javafx.scene.layout.StackPane; +import javafx.scene.layout.VBox; +import javafx.stage.Stage; +import lombok.extern.slf4j.Slf4j; +import net.lensfrex.disillusion.client.ClientMain; +import net.lensfrex.disillusion.client.config.Constants; +import net.lensfrex.disillusion.client.controller.page.employer.EmployerMainController; +import net.lensfrex.disillusion.client.controller.page.student.StudentMainController; +import net.lensfrex.disillusion.client.util.Consumer; +import net.lensfrex.disillusion.client.util.OkhttpUtil; +import net.lensfrex.disillusion.client.util.ResponseHandler; +import net.lensfrex.disillusion.client.util.ThreadSafeDialogMaker; +import okhttp3.OkHttpClient; +import okhttp3.Response; + +import java.net.URL; + +@Slf4j +public class LoginMainController { + + @FXML + private StackPane rootPane; + + @FXML + private JFXTextField usernameTextField; + + @FXML + private JFXPasswordField passwordTextField; + + private ThreadSafeDialogMaker dialogMaker; + + private Stage mainStage; + + public void initialize() { + dialogMaker = new ThreadSafeDialogMaker(rootPane); + } + + public void setMainStage(Stage mainStage) { + this.mainStage = mainStage; + } + + @FXML + void doLogin(ActionEvent event) { + new OkhttpUtil().url(Constants.UserRequestUrls.LOGIN_URL) + .form("username", usernameTextField.getText()) + .form("password", passwordTextField.getText()) + .failCallback(e -> Platform.runLater(() -> { + dialogMaker.createMessageDialog("啊哈", "登录时发生了网络问题:\n" + e); + })) + .successCallback(loginSuccessCallback) + .executeAsync(OkhttpUtil.RequestMethod.POST); + } + + private final Consumer loginSuccessCallback = response -> { + ResponseHandler.Result result = ResponseHandler.parse(response, s -> dialogMaker.createMessageDialog("啊哈", nullSafe(s))); + if (!result.success) { + return; + } + + JSONObject data = JSONUtil.parseObj(result.data); + String token = data.getStr("token"); + String userType = data.getStr("user_type"); + + switch (userType) { + case "0" -> Platform.runLater(() -> this.jumpStudentMainWindow(usernameTextField.getText(), token)); + case "1" -> Platform.runLater(() -> this.jumpEmployMainWindow(usernameTextField.getText(), token)); + } + }; + + private static final URL studentMainWindowUrl = ClientMain.class.getResource("fxml/student/studentMain.fxml"); + + private static final URL employerMainWindowUrl = ClientMain.class.getResource("fxml/employer/employerMain.fxml"); + + private void jumpStudentMainWindow(String username, String token) { + try { + FXMLLoader fxmlLoader = new FXMLLoader(studentMainWindowUrl); + Scene scene = new Scene(fxmlLoader.load(), 1572, 832); + mainStage.setScene(scene); + scene.getStylesheets().add(ClientMain.class.getResource("css/style.css").toExternalForm()); + + StudentMainController.InitData initData = StudentMainController.InitData.builder() + .username(username) + .token(token) + .build(); + + StudentMainController controller = fxmlLoader.getController(); + controller.setData(initData); + + mainStage.setTitle("disillusion - student"); + mainStage.setOnCloseRequest(v -> System.exit(0)); + mainStage.setX(100); + mainStage.setY(100); + mainStage.show(); + } catch (Exception e) { + e.printStackTrace(); + dialogMaker.createMessageDialog("Oppos...😱>︿<", "程序内部出错...请联系@lensfrex这家伙\n调试信息:\n" + e); + } + } + + private void jumpEmployMainWindow(String username, String token) { + try { + FXMLLoader fxmlLoader = new FXMLLoader(employerMainWindowUrl); + Scene scene = new Scene(fxmlLoader.load(), 1572, 832); + mainStage.setScene(scene); + scene.getStylesheets().add(ClientMain.class.getResource("css/style.css").toExternalForm()); + + EmployerMainController controller = fxmlLoader.getController(); + controller.setData(token); + + mainStage.setTitle("disillusion - employer"); + mainStage.setOnCloseRequest(v -> System.exit(0)); + mainStage.setX(100); + mainStage.setY(100); + mainStage.show(); + } catch (Exception e) { + e.printStackTrace(); + dialogMaker.createMessageDialog("Oppos...😱>︿<", "程序内部出错...请联系@lensfrex这家伙\n调试信息:\n" + e); + } + } + + @FXML + void doRegister(ActionEvent event) { + JFXRadioButton studentChoice = new JFXRadioButton("学生"); + studentChoice.setUserData(Constants.USER_TYPE_STUDENT); + JFXRadioButton employerChoice = new JFXRadioButton("用人单位"); + employerChoice.setUserData(Constants.USER_TYPE_EMPLOYER); + + ToggleGroup userTypeGroup = new ToggleGroup(); + studentChoice.setToggleGroup(userTypeGroup); + employerChoice.setToggleGroup(userTypeGroup); + userTypeGroup.selectToggle(studentChoice); + + VBox choiceBox = new VBox(studentChoice, employerChoice); + choiceBox.setSpacing(10); + + JFXButton cancelButton = new JFXButton("取消"); + JFXButton okButton = new JFXButton("好的"); + okButton.setOnAction(actionEvent -> { + String userTypeString = userTypeGroup.getSelectedToggle().getUserData().toString(); + this.register(userTypeString); + }); + + dialogMaker.createDialog("选择要注册的用户类型", choiceBox, cancelButton, okButton); + } + + private void register(String userTypeString) { + new OkhttpUtil() + .url(Constants.UserRequestUrls.REGISTER_URL) + .form("username", usernameTextField.getText()) + .form("password", passwordTextField.getText()) + .form("userType", userTypeString) + .failCallback(e -> { + dialogMaker.createMessageDialog("啊哈", "注册时发生了网络问题:\n" + e); + }) + .successCallback(response -> { + ResponseHandler.Result result = ResponseHandler.parse(response, msg -> dialogMaker.createMessageDialog("啊哈", nullSafe(msg))); + if (!result.success) { + return; + } + + dialogMaker.createMessageDialog("Message", "好了"); + }) + .executeAsync(OkhttpUtil.RequestMethod.POST); + } + + private static String nullSafe(String str) { + return str == null ? "null" : str; + } +} diff --git a/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/controller/components/ComponentFactory.java b/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/controller/components/ComponentFactory.java new file mode 100644 index 0000000..7a28ef3 --- /dev/null +++ b/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/controller/components/ComponentFactory.java @@ -0,0 +1,54 @@ +package net.lensfrex.disillusion.client.controller.components; + +import com.jfoenix.svg.SVGGlyph; +import javafx.animation.KeyFrame; +import javafx.animation.KeyValue; +import javafx.animation.Timeline; +import javafx.fxml.FXMLLoader; +import javafx.scene.Node; +import javafx.scene.control.Button; +import javafx.scene.paint.Color; +import javafx.util.Duration; +import net.lensfrex.disillusion.client.ClientMain; +import net.lensfrex.disillusion.client.controller.components.employer.offer.EmployerOfferDetailPane; + +import java.io.IOException; + +import static javafx.animation.Interpolator.EASE_BOTH; + +public class ComponentFactory { + public static T loadComponent(String location, Class clazz) + throws IOException, InstantiationException, IllegalAccessException { + + T controller = clazz.newInstance(); + return loadComponent(location, controller); + } + + public static T loadComponent(String location, T controller) + throws IOException { + + FXMLLoader loader = new FXMLLoader(ClientMain.class.getResource(location)); + return loadComponent(loader, controller); + } + + public static T loadComponent(FXMLLoader loader, T controller) + throws IOException { + + loader.setRoot(controller); + loader.setController(controller); + + return loader.load(); + } + + public static void setCardIcon(Button button, String svg) { + SVGGlyph glyph = new SVGGlyph(-1, "send", svg, Color.WHITE); + glyph.setSize(20, 20); + button.setGraphic(glyph); + + Timeline animation = new Timeline(new KeyFrame(Duration.millis(240), + new KeyValue(button.scaleXProperty(), 1, EASE_BOTH), + new KeyValue(button.scaleYProperty(), 1, EASE_BOTH))); + animation.setDelay(Duration.millis(100 + 100)); + animation.play(); + } +} diff --git a/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/controller/components/employer/application/ApplicationDealPane.java b/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/controller/components/employer/application/ApplicationDealPane.java new file mode 100644 index 0000000..769c501 --- /dev/null +++ b/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/controller/components/employer/application/ApplicationDealPane.java @@ -0,0 +1,32 @@ +package net.lensfrex.disillusion.client.controller.components.employer.application; + +import com.jfoenix.controls.JFXRadioButton; +import javafx.fxml.FXML; +import javafx.scene.control.ToggleGroup; +import javafx.scene.layout.HBox; +import javafx.scene.layout.VBox; + +public class ApplicationDealPane extends VBox { + + @FXML + private JFXRadioButton acceptRadioButton; + + @FXML + private JFXRadioButton rejectRadioButton; + + private final ToggleGroup opinionGroup = new ToggleGroup(); + + public void initialize() { + acceptRadioButton.setToggleGroup(opinionGroup); + acceptRadioButton.setUserData("accept"); + + rejectRadioButton.setToggleGroup(opinionGroup); + rejectRadioButton.setUserData("reject"); + + opinionGroup.selectToggle(acceptRadioButton); + } + + public String getOpinion() { + return (String) opinionGroup.getSelectedToggle().getUserData(); + } +} diff --git a/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/controller/components/employer/application/ApplicationListItem.java b/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/controller/components/employer/application/ApplicationListItem.java new file mode 100644 index 0000000..a7e991f --- /dev/null +++ b/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/controller/components/employer/application/ApplicationListItem.java @@ -0,0 +1,96 @@ +package net.lensfrex.disillusion.client.controller.components.employer.application; + +import cn.hutool.json.JSONUtil; +import javafx.event.ActionEvent; +import javafx.fxml.FXML; +import javafx.scene.input.MouseEvent; +import javafx.scene.layout.HBox; +import javafx.scene.text.Text; +import lombok.extern.slf4j.Slf4j; +import net.lensfrex.disillusion.client.config.Constants; +import net.lensfrex.disillusion.client.controller.components.ComponentFactory; +import net.lensfrex.disillusion.client.data.response.ApplicationResponse; +import net.lensfrex.disillusion.client.data.response.PublicStudentInfoResponse; +import net.lensfrex.disillusion.client.util.OkhttpUtil; +import net.lensfrex.disillusion.client.util.ResponseHandler; +import net.lensfrex.disillusion.client.util.ThreadSafeDialogMaker; + +import java.io.IOException; + +@Slf4j +public class ApplicationListItem extends HBox { + private final ApplicationResponse application; + private final ThreadSafeDialogMaker dialogMaker; + private final String token; + + private PublicStudentInfoResponse studentInfo; + + @FXML + private Text studentNameText; + + @FXML + private Text describeText; + + public ApplicationListItem(ApplicationResponse application, + ThreadSafeDialogMaker dialogMaker, String token) { + + this.application = application; + this.dialogMaker = dialogMaker; + this.token = token; + } + + public void initialize() { + this.getStudentInfo(); + } + + private void getStudentInfo() { + new OkhttpUtil() + .url(Constants.UserRequestUrls.USER_PUBLIC_INFO_VIEW_URL) + .addQueryParam("user", application.getPublisherId()) + .failCallback(e -> log.warn("获取学生信息时发生网络错误:\n" + e)) + .successCallback(response -> new ResponseHandler(response) + .errorCallback(s -> log.warn("获取学生信息不成功:\n" + s)) + .successCallback(s -> { + studentInfo = JSONUtil.toBean(s, PublicStudentInfoResponse.class); + studentNameText.setText(studentInfo.getRealName()); + describeText.setText(studentInfo.getShortDescribe()); + }) + .process()) + .executeAsync(OkhttpUtil.RequestMethod.GET); + } + + @FXML + void dealApplication(ActionEvent event) throws IOException, InstantiationException, IllegalAccessException { + ApplicationDealPane dealPane = ComponentFactory.loadComponent("fxml/employer/application/applicationDealPane.fxml", + ApplicationDealPane.class); + dialogMaker.createDialogWithOKAndCancel("处理Offer申请", dealPane, event1 -> this.dealApplication(dealPane.getOpinion())); + } + + private void dealApplication(String opinion) { + new OkhttpUtil() + .url(Constants.ApplicationRequestUrls.APPLICATION_DEAL_URL) + .token(token) + .addQueryParam("id", application.getId()) + .addQueryParam("opinion", opinion) + .failCallback(e -> dialogMaker.createMessageDialog("啊哈", "处理offer申请时出现网络错误:\n" + e)) + .successCallback(response -> new ResponseHandler(response) + .errorCallback(s -> dialogMaker.createMessageDialog("啊哈", "处理offer申请失败:\n" + s)) + .successCallback(s -> dialogMaker.createMessageDialog("好好好", "处理offer申请成功:\n" + opinion)) + .process()) + .executeAsync(OkhttpUtil.RequestMethod.POST); + } + + @FXML + void viewResume(ActionEvent event) throws IOException { + ResumeDownloadPane downloadPane = ComponentFactory.loadComponent("fxml/employer/application/resumeDownloadPane.fxml", + new ResumeDownloadPane(application.getResumeId(), studentInfo.getRealName(), dialogMaker, token)); + dialogMaker.createDialogWithOneBtn("下载简历", downloadPane); + } + + @FXML + void viewStudentInfo(MouseEvent event) throws IOException { + StudentInfoPage page = ComponentFactory.loadComponent("fxml/employer/application/studentProfile.fxml", + new StudentInfoPage(studentInfo)); + dialogMaker.createDialogWithOneBtn("学生信息", page); + } +} diff --git a/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/controller/components/employer/application/ApplicationListPage.java b/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/controller/components/employer/application/ApplicationListPage.java new file mode 100644 index 0000000..354bee5 --- /dev/null +++ b/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/controller/components/employer/application/ApplicationListPage.java @@ -0,0 +1,75 @@ +package net.lensfrex.disillusion.client.controller.components.employer.application; + +import cn.hutool.json.JSONUtil; +import javafx.application.Platform; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import javafx.fxml.FXML; +import javafx.scene.layout.StackPane; +import javafx.scene.layout.VBox; +import javafx.scene.text.Font; +import javafx.scene.text.Text; +import lombok.extern.slf4j.Slf4j; +import net.lensfrex.disillusion.client.config.Constants; +import net.lensfrex.disillusion.client.controller.components.ComponentFactory; +import net.lensfrex.disillusion.client.data.response.ApplicationResponse; +import net.lensfrex.disillusion.client.data.response.OfferResponse; +import net.lensfrex.disillusion.client.util.OkhttpUtil; +import net.lensfrex.disillusion.client.util.ResponseHandler; +import net.lensfrex.disillusion.client.util.ThreadSafeDialogMaker; + +import java.io.IOException; +import java.util.List; + +@Slf4j +public class ApplicationListPage extends StackPane { + @FXML + private VBox applicationContainer; + + private final OfferResponse offer; + + private final ThreadSafeDialogMaker dialogMaker; + private final String token; + + public ApplicationListPage(ThreadSafeDialogMaker dialogMaker, String token, OfferResponse offer) { + this.dialogMaker = dialogMaker; + this.token = token; + this.offer = offer; + } + + public void initialize() { + new OkhttpUtil() + .url(Constants.ApplicationRequestUrls.APPLICATION_EMPLOYER_VIEW_URL) + .token(token) + .addQueryParam("offer_id", offer.getId()) + .failCallback(e -> dialogMaker.createMessageDialog("啊哈", "获取求职申请列表时发生了网络问题:\n" + e)) + .successCallback(response -> new ResponseHandler(response) + .errorCallback(s -> dialogMaker.createMessageDialog("啊哈", "获取求职申请不成功:\n" + s)) + .successCallback(s -> Platform.runLater(() -> this.showApplicationList(s))) + .process()) + .executeAsync(OkhttpUtil.RequestMethod.GET); + } + + private void showApplicationList(String jsonData) { + try { + List applications = JSONUtil.toList(jsonData, ApplicationResponse.class); + if (applications.isEmpty()) { + Text text = new Text("这里空空如也..."); + text.setFont(Font.font(20)); + applicationContainer.getChildren().add(text); + } + + ObservableList items = FXCollections.observableArrayList(); + for (ApplicationResponse application : applications) { + ApplicationListItem item = ComponentFactory.loadComponent("fxml/employer/application/applicationListItem.fxml", + new ApplicationListItem(application, dialogMaker, token)); + items.add(item); + } + + applicationContainer.getChildren().addAll(items); + } catch (IOException e) { + e.printStackTrace(); + log.error("加载offer申请页面时出错"); + } + } +} \ No newline at end of file diff --git a/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/controller/components/employer/application/ApplicationOfferCard.java b/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/controller/components/employer/application/ApplicationOfferCard.java new file mode 100644 index 0000000..c115bef --- /dev/null +++ b/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/controller/components/employer/application/ApplicationOfferCard.java @@ -0,0 +1,106 @@ +package net.lensfrex.disillusion.client.controller.components.employer.application; + +import javafx.fxml.FXML; +import javafx.scene.control.Label; +import javafx.scene.input.MouseEvent; +import javafx.scene.layout.StackPane; +import javafx.scene.text.Text; +import lombok.extern.slf4j.Slf4j; +import net.lensfrex.disillusion.client.config.Constants; +import net.lensfrex.disillusion.client.controller.components.ComponentFactory; +import net.lensfrex.disillusion.client.data.response.OfferResponse; +import net.lensfrex.disillusion.client.util.OkhttpUtil; +import net.lensfrex.disillusion.client.util.ResponseHandler; +import net.lensfrex.disillusion.client.util.ThreadSafeDialogMaker; + +@Slf4j +public class ApplicationOfferCard extends StackPane { + private final OfferResponse offer; + + private ThreadSafeDialogMaker dialogMaker; + + private String token; + + private static final String[] colors = new String[]{ + "-fx-background-color: #8F3F7E", + "-fx-background-color: #B5305F", + "-fx-background-color: #CE584A", + "-fx-background-color: #DB8D5C", + "-fx-background-color: #DA854E", + "-fx-background-color: #E9AB44", + "-fx-background-color: #99C286", + "-fx-background-color: #01A05E", + "-fx-background-color: #4A8895", + "-fx-background-color: #16669B", + "-fx-background-color: #2F65A5", + }; + + public ApplicationOfferCard(OfferResponse offer) { + this.setUserData(offer); + this.offer = offer; + } + + public void loadData(String token, ThreadSafeDialogMaker dialogMaker) { + this.token = token; + this.dialogMaker = dialogMaker; + + // 查询offer招募进度 + new OkhttpUtil() + .url(Constants.OfferRequestUrls.OFFER_PROGRESS_URL) + .token(token) + .addQueryParam("id", offer.getId()) + .failCallback(e -> log.warn("获取offer进度时出现网络错误:", e)) + .successCallback(response -> new ResponseHandler(response) + .errorCallback(s -> log.warn("获取offer进度不成功:" + s)) + .successCallback(s -> processText.setText("招募进度:" + s)) + .process() + ) + .executeAsync(OkhttpUtil.RequestMethod.GET); + } + + public void initialize() { + String color = colors[Math.abs(offer.getJobName().hashCode()) % colors.length]; + header.setStyle(header.getStyle() + color); + + jobName.setText(offer.getJobName()); + } + + @FXML + private StackPane rootPane; + + @FXML + private StackPane header; + + @FXML + private Label jobName; + + @FXML + private StackPane body; + + @FXML + private Text processText; + + @FXML + void viewApplications(MouseEvent event) { + new OkhttpUtil() + .url(Constants.ApplicationRequestUrls.APPLICATION_EMPLOYER_VIEW_URL) + .token(token) + .addQueryParam("offer_id", offer.getId()) + .failCallback(e -> dialogMaker.createMessageDialog("啊哈", "获取求职申请列表时发生了网络问题:\n" + e)) + .successCallback(response -> new ResponseHandler(response) + .errorCallback(s -> dialogMaker.createMessageDialog("啊哈", "获取求职申请不成功:\n" + s)) + .successCallback(s -> showApplicationListPage()) + .process()) + .executeAsync(OkhttpUtil.RequestMethod.GET); + } + + private void showApplicationListPage() { + try { + ApplicationListPage applicationListPage = ComponentFactory.loadComponent("fxml/employer/application/applicationListPage.fxml", + new ApplicationListPage(dialogMaker, token, offer)); + dialogMaker.createDialogWithOneBtn(String.format("投递给 '%s' 的求职申请", offer.getJobName()), applicationListPage); + } catch (Exception e) { + log.error("显示简历申请列表页面时出错", e); + } + } +} \ No newline at end of file diff --git a/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/controller/components/employer/application/ApplicationTableItem.java b/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/controller/components/employer/application/ApplicationTableItem.java new file mode 100644 index 0000000..7e6baa5 --- /dev/null +++ b/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/controller/components/employer/application/ApplicationTableItem.java @@ -0,0 +1,24 @@ +package net.lensfrex.disillusion.client.controller.components.employer.application; + + +import com.jfoenix.controls.datamodels.treetable.RecursiveTreeObject; +import net.lensfrex.disillusion.client.data.response.ApplicationResponse; +import net.lensfrex.disillusion.client.data.response.PublicStudentInfoResponse; + +public class ApplicationTableItem extends RecursiveTreeObject { + private final ApplicationResponse application; + private final PublicStudentInfoResponse student; + + public ApplicationTableItem(ApplicationResponse application, PublicStudentInfoResponse student) { + this.application = application; + this.student = student; + } + + public ApplicationResponse getApplication() { + return application; + } + + public PublicStudentInfoResponse getStudent() { + return student; + } +} \ No newline at end of file diff --git a/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/controller/components/employer/application/ResumeDownloadPane.java b/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/controller/components/employer/application/ResumeDownloadPane.java new file mode 100644 index 0000000..322fe0f --- /dev/null +++ b/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/controller/components/employer/application/ResumeDownloadPane.java @@ -0,0 +1,69 @@ +package net.lensfrex.disillusion.client.controller.components.employer.application; + +import cn.hutool.core.codec.Base64; +import cn.hutool.core.io.FileUtil; +import javafx.event.ActionEvent; +import javafx.fxml.FXML; +import javafx.scene.layout.VBox; +import javafx.stage.DirectoryChooser; +import net.lensfrex.disillusion.client.config.Constants; +import net.lensfrex.disillusion.client.util.OkhttpUtil; +import net.lensfrex.disillusion.client.util.ResponseHandler; +import net.lensfrex.disillusion.client.util.ThreadSafeDialogMaker; + +import java.io.File; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardOpenOption; + +public class ResumeDownloadPane extends VBox { + @FXML + private VBox rootPane; + + private final String fileId; + private final String fileName; + + private final ThreadSafeDialogMaker dialogMaker; + private final String token; + + public ResumeDownloadPane(String fileId, String fileName, ThreadSafeDialogMaker dialogMaker, String token) { + this.fileId = fileId; + this.fileName = fileName; + this.dialogMaker = dialogMaker; + this.token = token; + } + + @FXML + void download(ActionEvent event) { + DirectoryChooser directoryChooser = new DirectoryChooser(); + directoryChooser.setTitle("要将简历文件保存到哪?"); + File saveFile = directoryChooser.showDialog(rootPane.getScene().getWindow()); + if (saveFile == null) { + return; + } + + downloadResume(saveFile.getAbsolutePath(), fileName); + } + + private void downloadResume(String saveDir, String fileName) { + Path target = Paths.get(saveDir, fileName); + + new OkhttpUtil() + .url(Constants.ResumeRequestUrls.RESUME_FILE_URL) + .addHeader("token", token) + .addQueryParam("id", fileId) + .failCallback(e -> dialogMaker.createMessageDialog("啊哈", "下载简历文件时出现网络问题:\n" + e)) + .successCallback(response -> new ResponseHandler(response) + .errorCallback(s -> dialogMaker.createMessageDialog("啊哈", "下载简历文件不成功:" + s)) + .successCallback(bytes -> { + Files.write(target, Base64.decode(bytes), StandardOpenOption.CREATE, StandardOpenOption.WRITE); + Path saved = FileUtil.rename(target, + String.format("%s_%s.%s", fileName, fileId, FileUtil.getType(target.toFile())), + true); + dialogMaker.createMessageDialog("好好好", "下载完成,保存至:\n" + saved); + }) + .process()) + .executeAsync(OkhttpUtil.RequestMethod.GET); + } +} diff --git a/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/controller/components/employer/application/StudentInfoPage.java b/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/controller/components/employer/application/StudentInfoPage.java new file mode 100644 index 0000000..6531c52 --- /dev/null +++ b/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/controller/components/employer/application/StudentInfoPage.java @@ -0,0 +1,90 @@ +package net.lensfrex.disillusion.client.controller.components.employer.application; + +import com.jfoenix.controls.JFXTextArea; +import com.jfoenix.controls.JFXTextField; +import javafx.application.Platform; +import javafx.fxml.FXML; +import javafx.scene.image.Image; +import javafx.scene.image.ImageView; +import javafx.scene.layout.HBox; +import javafx.scene.text.Text; +import lombok.extern.slf4j.Slf4j; +import net.lensfrex.disillusion.client.config.Constants; +import net.lensfrex.disillusion.client.data.response.PublicStudentInfoResponse; +import net.lensfrex.disillusion.client.util.OkhttpUtil; +import okhttp3.Response; + +import java.io.ByteArrayInputStream; + +@Slf4j +public class StudentInfoPage extends HBox { + @FXML + private HBox rootPane; + + @FXML + private ImageView avatarImageView; + + @FXML + private Text usernameText; + + @FXML + private Text signText; + + @FXML + private JFXTextField realNameTextField; + + @FXML + private JFXTextField ageTextField; + + @FXML + private Text sexText; + + @FXML + private JFXTextField contactTextField; + + @FXML + private JFXTextField signTextField; + + @FXML + private JFXTextArea describeTextArea; + + private final PublicStudentInfoResponse studentInfo; + + public StudentInfoPage(PublicStudentInfoResponse studentInfo) { + this.studentInfo = studentInfo; + } + + @FXML + public void initialize() { + realNameTextField.setText(studentInfo.getRealName()); + signText.setText(studentInfo.getShortDescribe()); + signTextField.setText(studentInfo.getShortDescribe()); + describeTextArea.setText(studentInfo.getDescribe()); + contactTextField.setText(studentInfo.getPhoneNumber()); + ageTextField.setText(String.valueOf(studentInfo.getAge())); + sexText.setText(studentInfo.getSex() == 0 ? "男" : "女"); + + new OkhttpUtil() + .url(Constants.UserRequestUrls.USER_AVATAR_VIEW_URL) + .addQueryParam("user", studentInfo.getId()) + .failCallback(e -> log.warn("获取头像时发生网络错误:\n" + e)) + .successCallback(response -> Platform.runLater(() -> setAvatar(avatarImageView, response))) + .executeAsync(OkhttpUtil.RequestMethod.GET); + } + + public static void setAvatar(ImageView avatarImageView, Response response) { + try { + assert response.body() != null; + byte[] data = response.body().bytes(); + + setAvatar(avatarImageView, data); + } catch (Exception e) { + log.warn("获取头像数据时出现异常", e); + } + } + + public static void setAvatar(ImageView avatarImageView, byte[] data) { + Image avatarImage = new Image(new ByteArrayInputStream(data)); + avatarImageView.setImage(avatarImage); + } +} diff --git a/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/controller/components/employer/offer/EmployerOfferCard.java b/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/controller/components/employer/offer/EmployerOfferCard.java new file mode 100644 index 0000000..a3806ee --- /dev/null +++ b/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/controller/components/employer/offer/EmployerOfferCard.java @@ -0,0 +1,134 @@ +package net.lensfrex.disillusion.client.controller.components.employer.offer; + +import com.jfoenix.controls.JFXButton; +import javafx.event.ActionEvent; +import javafx.fxml.FXML; +import javafx.scene.control.Label; +import javafx.scene.input.MouseEvent; +import javafx.scene.layout.StackPane; +import javafx.scene.text.Text; +import lombok.extern.slf4j.Slf4j; +import net.lensfrex.disillusion.client.config.Constants; +import net.lensfrex.disillusion.client.controller.components.ComponentFactory; +import net.lensfrex.disillusion.client.data.request.OfferPublishRequest; +import net.lensfrex.disillusion.client.data.response.OfferResponse; +import net.lensfrex.disillusion.client.util.Consumer; +import net.lensfrex.disillusion.client.util.OkhttpUtil; +import net.lensfrex.disillusion.client.util.ResponseHandler; +import net.lensfrex.disillusion.client.util.ThreadSafeDialogMaker; + +import java.io.IOException; + +@Slf4j +public class EmployerOfferCard extends StackPane { + private final OfferResponse offer; + + private ThreadSafeDialogMaker dialogMaker; + + private String token; + + private Consumer deleteCallback; + private Consumer updateCallback; + + private static final String[] colors = new String[]{ + "-fx-background-color: #8F3F7E", + "-fx-background-color: #B5305F", + "-fx-background-color: #CE584A", + "-fx-background-color: #DB8D5C", + "-fx-background-color: #DA854E", + "-fx-background-color: #E9AB44", + "-fx-background-color: #99C286", + "-fx-background-color: #01A05E", + "-fx-background-color: #4A8895", + "-fx-background-color: #16669B", + "-fx-background-color: #2F65A5", + }; + + public EmployerOfferCard(OfferResponse offer) { + this.setUserData(offer); + this.offer = offer; + } + + public void loadData(String token, ThreadSafeDialogMaker dialogMaker, + Consumer deleteCallback, Consumer updateCallback) { + this.token = token; + this.dialogMaker = dialogMaker; + this.deleteCallback = deleteCallback; + this.updateCallback = updateCallback; + + new OkhttpUtil() + .url(Constants.OfferRequestUrls.OFFER_PROGRESS_URL) + .token(token) + .addQueryParam("id", offer.getId()) + .failCallback(e -> log.warn("获取offer进度时出现网络错误:", e)) + .successCallback(response -> new ResponseHandler(response) + .errorCallback(s -> log.warn("获取offer进度不成功:" + s)) + .successCallback(s -> processText.setText("招募进度:" + s)) + .process()) + .executeAsync(OkhttpUtil.RequestMethod.GET); + } + + public void initialize() { + String color = colors[Math.abs(offer.getJobName().hashCode()) % colors.length]; + header.setStyle(header.getStyle() + color); + + jobName.setText(offer.getJobName()); + + ComponentFactory.setCardIcon(offerDeleteBtn, Constants.Svg.deleteSvg); + } + + @FXML + private StackPane rootPane; + + @FXML + private StackPane header; + + @FXML + private Label jobName; + + @FXML + private StackPane body; + + @FXML + private Text processText; + + @FXML + private JFXButton offerDeleteBtn; + + @FXML + void deleteOffer(ActionEvent event) { + dialogMaker.createDialogWithOKAndCancel("Really?", String.format("真的要删除Offer '%s'", offer.getJobName()), event1 -> new OkhttpUtil() + .url(Constants.OfferRequestUrls.OFFER_DELETE_URL) + .token(token) + .addQueryParam("id", offer.getId()) + .failCallback(e -> dialogMaker.createMessageDialog("啊哈", "删除offer时出现网络问题:\n" + e)) + .successCallback(response -> new ResponseHandler(response) + .errorCallback(s -> dialogMaker.createMessageDialog("啊哈", "删除offer不成功:\n" + s)) + .successCallback(deleteCallback) + .process()) + .executeAsync(OkhttpUtil.RequestMethod.POST) + ); + } + + @FXML + void viewDetail(MouseEvent event) throws IOException { + EmployerOfferDetailPane offerDetailPane = + ComponentFactory.loadComponent("fxml/employer/offer/offerDetailPane.fxml", new EmployerOfferDetailPane(offer)); + + dialogMaker.createDialogWithOKAndCancel("更新Offer信息", offerDetailPane, e -> this.updateOffer(offerDetailPane.getData())); + } + + private void updateOffer(OfferPublishRequest offerPublishRequest) { + new OkhttpUtil() + .url(Constants.OfferRequestUrls.OFFER_UPDATE_URL) + .token(token) + .addQueryParam("id", offer.getId()) + .body(offerPublishRequest) + .failCallback(e -> dialogMaker.createMessageDialog("啊哈", "更新offer信息时发生了网络问题:\n" + e)) + .successCallback(response -> new ResponseHandler(response) + .errorCallback(s -> dialogMaker.createMessageDialog("啊哈", "更新offer信息不成功:\n" + s)) + .successCallback(s -> updateCallback.accept(s)) + .process()) + .executeAsync(OkhttpUtil.RequestMethod.POST); + } +} \ No newline at end of file diff --git a/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/controller/components/employer/offer/EmployerOfferDetailPane.java b/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/controller/components/employer/offer/EmployerOfferDetailPane.java new file mode 100644 index 0000000..982e77d --- /dev/null +++ b/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/controller/components/employer/offer/EmployerOfferDetailPane.java @@ -0,0 +1,93 @@ +package net.lensfrex.disillusion.client.controller.components.employer.offer; + +import cn.hutool.core.util.RandomUtil; +import com.jfoenix.controls.JFXTextArea; +import com.jfoenix.controls.JFXTextField; +import javafx.fxml.FXML; +import javafx.scene.layout.VBox; +import net.lensfrex.disillusion.client.config.Constants; +import net.lensfrex.disillusion.client.data.request.OfferPublishRequest; +import net.lensfrex.disillusion.client.data.response.OfferResponse; + +public class EmployerOfferDetailPane extends VBox { + @FXML + private VBox rootPane; + + @FXML + private JFXTextField jobNameTextField; + + @FXML + private JFXTextField salaryTextField; + + @FXML + private JFXTextField planNumberTextField; + + @FXML + private JFXTextField placeTextField; + + @FXML + private JFXTextField requirementTextField; + + @FXML + private JFXTextArea detailTextArea; + + private OfferResponse offer; + + // 无参构造方法,勿删 + public EmployerOfferDetailPane() { + } + + public EmployerOfferDetailPane(OfferResponse offer) { + this.offer = offer; + } + + public void initialize() { + if (offer == null) { + jobNameTextField.setText(RandomUtil.randomString(Constants.sample, RandomUtil.randomInt(3, 5))); + placeTextField.setText(RandomUtil.randomString(Constants.sample, RandomUtil.randomInt(3, 5))); + requirementTextField.setText(RandomUtil.randomString(Constants.sample, RandomUtil.randomInt(5, 10))); + salaryTextField.setText(RandomUtil.randomNumbers(5)); + planNumberTextField.setText(RandomUtil.randomNumbers(2)); + detailTextArea.setText(RandomUtil.randomString(Constants.sample, RandomUtil.randomInt(300, 700))); + return; + } + + String jobName = offer.getJobName(); + String place = offer.getPlace(); + String requirement = offer.getRequirement(); + Integer salary = offer.getSalary(); + Integer targetNumber = offer.getTargetNumber(); + String detail = offer.getDetail(); + + jobNameTextField.setText(jobName); + placeTextField.setText(place); + requirementTextField.setText(requirement); + salaryTextField.setText(salary.toString()); + planNumberTextField.setText(targetNumber.toString()); + detailTextArea.setText(detail); + } + + public OfferPublishRequest getData() { + OfferPublishRequest request = new OfferPublishRequest(); + request.setJobName(jobNameTextField.getText()); + request.setPlace(placeTextField.getText()); + request.setRequirement(requirementTextField.getText()); + request.setDetail(detailTextArea.getText()); + + String targetNumberText = planNumberTextField.getText(); + if (!targetNumberText.matches(Constants.numberPattern)) { + request.setTargetNumber(-1); + } else { + request.setTargetNumber(Integer.parseInt(targetNumberText)); + } + + String salaryText = salaryTextField.getText(); + if (!salaryText.matches(Constants.numberPattern)) { + request.setSalary(-1); + } else { + request.setSalary(Integer.parseInt(salaryText)); + } + + return request; + } +} diff --git a/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/controller/components/student/application/ApplicationCard.java b/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/controller/components/student/application/ApplicationCard.java new file mode 100644 index 0000000..450e77c --- /dev/null +++ b/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/controller/components/student/application/ApplicationCard.java @@ -0,0 +1,140 @@ +package net.lensfrex.disillusion.client.controller.components.student.application; + +import cn.hutool.json.JSONUtil; +import com.jfoenix.controls.JFXButton; +import javafx.application.Platform; +import javafx.event.ActionEvent; +import javafx.fxml.FXML; +import javafx.scene.control.Label; +import javafx.scene.input.MouseEvent; +import javafx.scene.layout.StackPane; +import javafx.scene.paint.Color; +import javafx.scene.text.Text; +import lombok.extern.slf4j.Slf4j; +import net.lensfrex.disillusion.client.config.Constants; +import net.lensfrex.disillusion.client.controller.components.ComponentFactory; +import net.lensfrex.disillusion.client.controller.components.student.offer.OfferDetailPane; +import net.lensfrex.disillusion.client.data.response.ApplicationResponse; +import net.lensfrex.disillusion.client.data.response.OfferResponse; +import net.lensfrex.disillusion.client.util.Consumer; +import net.lensfrex.disillusion.client.util.OkhttpUtil; +import net.lensfrex.disillusion.client.util.ResponseHandler; +import net.lensfrex.disillusion.client.util.ThreadSafeDialogMaker; + +import java.io.IOException; + +@Slf4j +public class ApplicationCard extends StackPane { + private static final String[] colors = new String[]{ + "-fx-background-color: #8F3F7E", + "-fx-background-color: #B5305F", + "-fx-background-color: #CE584A", + "-fx-background-color: #DB8D5C", + "-fx-background-color: #DA854E", + "-fx-background-color: #E9AB44", + "-fx-background-color: #99C286", + "-fx-background-color: #01A05E", + "-fx-background-color: #4A8895", + "-fx-background-color: #16669B", + "-fx-background-color: #2F65A5", + }; + + private final String token; + private final ThreadSafeDialogMaker dialogMaker; + private final ApplicationResponse application; + + private final Consumer deleteCallback; + + private OfferResponse offer; + + public ApplicationCard(String token, + ThreadSafeDialogMaker dialogMaker, + ApplicationResponse application, + Consumer deleteCallback) { + + this.token = token; + this.dialogMaker = dialogMaker; + this.application = application; + this.deleteCallback = deleteCallback; + } + + @FXML + private StackPane rootPane; + + @FXML + private StackPane header; + + @FXML + private Label jobName; + + @FXML + private StackPane body; + + @FXML + private Text statusText; + + @FXML + private JFXButton applicationDeleteBtn; + + public void initialize() { + String color = colors[Math.abs(application.getOfferId().hashCode()) % colors.length]; + header.setStyle(header.getStyle() + color); + + this.showJobName(); + + statusText.setText("状态:" + getStatusText(application.getStatus())); + if ("REJECTED".equals(application.getStatus())){ + statusText.setFill(Color.valueOf("#C62828")); + } else if ("ACCEPTED".equals(application.getStatus())) { + statusText.setFill(Color.valueOf("#2E7D32")); + } + ComponentFactory.setCardIcon(applicationDeleteBtn, Constants.Svg.deleteSvg); + } + + private void showJobName() { + new OkhttpUtil() + .url(Constants.OfferRequestUrls.GET_OFFER_URL) + .addQueryParam("id", application.getOfferId()) + .failCallback(e -> dialogMaker.createMessageDialog("啊哈", "获取职位申请时发生了错误:" + e)) + .successCallback(response -> new ResponseHandler(response).successCallback(data -> { + this.offer = JSONUtil.toBean(data, OfferResponse.class); + Platform.runLater(() -> jobName.setText(offer.getJobName())); + }).process()) + .executeAsync(OkhttpUtil.RequestMethod.GET); + } + + private String getStatusText(String status) { + switch (status) { + case "WAITING": + return "等待中"; + case "ACCEPTED": + return "已接受"; + case "REJECTED": + return "已拒绝"; + default: + return status; + } + } + + @FXML + void deleteApplication(ActionEvent event) { + dialogMaker.createDialogWithOKAndCancel("确认操作?", String.format("真的要删除 '%s' 的申请吗?", offer.getJobName()), + e -> new OkhttpUtil() + .url(Constants.ApplicationRequestUrls.APPLICATION_DELETE_URL) + .addQueryParam("id", application.getId()) + .token(token) + .failCallback(ex -> dialogMaker.createMessageDialog("啊哈", "获取职位申请时发生了错误:" + ex)) + .successCallback(response -> new ResponseHandler(response) + .errorCallback(s -> dialogMaker.createMessageDialog("啊哈", "删除offer申请不成功:\n" + s)) + .successCallback(deleteCallback) + .process()) + .executeAsync(OkhttpUtil.RequestMethod.POST)); + } + + @FXML + void viewDetail(MouseEvent event) throws IOException { + OfferDetailPane detailPage = + ComponentFactory.loadComponent("fxml/student/offer/offerDetail.fxml", new OfferDetailPane(dialogMaker, this.offer)); + dialogMaker.createDialogWithOneBtn("Offer详情", detailPage); + } +} diff --git a/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/controller/components/student/application/ApplicationPostPane.java b/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/controller/components/student/application/ApplicationPostPane.java new file mode 100644 index 0000000..7d4f0c3 --- /dev/null +++ b/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/controller/components/student/application/ApplicationPostPane.java @@ -0,0 +1,83 @@ +package net.lensfrex.disillusion.client.controller.components.student.application; + +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import com.jfoenix.controls.JFXComboBox; +import javafx.application.Platform; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import javafx.fxml.FXML; +import javafx.scene.layout.HBox; +import lombok.extern.slf4j.Slf4j; +import net.lensfrex.disillusion.client.config.Constants; +import net.lensfrex.disillusion.client.data.ResponseCode; +import net.lensfrex.disillusion.client.data.response.ResumeResponse; +import net.lensfrex.disillusion.client.util.OkhttpUtil; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@Slf4j +public class ApplicationPostPane extends HBox { + private final ObservableList resumeChoiceList = FXCollections.observableArrayList(); + private Map resumeNameIdMap; + + @FXML + private JFXComboBox resumeComboBox; + + private final String token; + + public ApplicationPostPane(String token) { + this.token = token; + } + + public void initialize() { + this.getResumes(); + } + + public String getChooseResumeId() { + String selectedName = resumeComboBox.getSelectionModel().getSelectedItem(); + if (selectedName == null) { + return null; + } + + return resumeNameIdMap.get(selectedName); + } + + private void getResumes() { + new OkhttpUtil() + .url(Constants.ResumeRequestUrls.RESUME_GET_ALL_URL) + .token(token) + .successCallback(response -> { + assert response.body() != null; + String responseString = response.body().string(); + JSONObject responseJson = JSONUtil.parseObj(responseString); + + Integer code = responseJson.getInt("code"); + if (code == null || !code.equals(ResponseCode.SUCCESS.getCode())) { + String message = responseJson.getStr("message"); + log.warn("获取简历时发生错误:" + message); + } + + List resumes = JSONUtil.toList(responseJson.getJSONArray("data"), ResumeResponse.class); + Platform.runLater(() -> this.addResumeItems(resumes)); + }) + .failCallback(e -> log.warn("获取简历时发生错误:" + e)) + .executeAsync(OkhttpUtil.RequestMethod.GET); + } + + private void addResumeItems(List resumes) { + resumeNameIdMap = new HashMap<>(resumes.size()); + for (ResumeResponse resume : resumes) { + if (resume == null) { + continue; + } + resumeChoiceList.add(resume.getTitle()); + resumeNameIdMap.put(resume.getTitle(), resume.getId()); + } + + resumeComboBox.setItems(resumeChoiceList); + resumeComboBox.getSelectionModel().select(0); + } +} diff --git a/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/controller/components/student/offer/EmployerInfoPage.java b/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/controller/components/student/offer/EmployerInfoPage.java new file mode 100644 index 0000000..24fad80 --- /dev/null +++ b/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/controller/components/student/offer/EmployerInfoPage.java @@ -0,0 +1,119 @@ +package net.lensfrex.disillusion.client.controller.components.student.offer; + +import cn.hutool.core.codec.Base64; +import cn.hutool.core.io.FileUtil; +import com.jfoenix.controls.JFXTextArea; +import com.jfoenix.controls.JFXTextField; +import javafx.application.Platform; +import javafx.event.ActionEvent; +import javafx.fxml.FXML; +import javafx.scene.image.Image; +import javafx.scene.image.ImageView; +import javafx.scene.input.MouseEvent; +import javafx.scene.layout.HBox; +import javafx.scene.text.Text; +import javafx.stage.FileChooser; +import lombok.extern.slf4j.Slf4j; +import net.lensfrex.disillusion.client.config.Constants; +import net.lensfrex.disillusion.client.data.response.EmployerInfoResponse; +import net.lensfrex.disillusion.client.data.response.PublicEmployerInfoResponse; +import net.lensfrex.disillusion.client.util.ImageUtil; +import net.lensfrex.disillusion.client.util.OkhttpUtil; +import net.lensfrex.disillusion.client.util.ResponseHandler; +import net.lensfrex.disillusion.client.util.ThreadSafeDialogMaker; +import okhttp3.Response; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.function.Consumer; + +@Slf4j +public class EmployerInfoPage extends HBox { + @FXML + private HBox rootPane; + + @FXML + private ImageView avatarImageView; + + @FXML + private Text usernameText; + + @FXML + private Text shortDescribeText; + + @FXML + private JFXTextField employerNameTextField; + + @FXML + private JFXTextField industryTextField; + + @FXML + private JFXTextField contactTextField; + + @FXML + private JFXTextField addressTextField; + + @FXML + private JFXTextField shortDescribeTextField; + + @FXML + private JFXTextArea describeTextArea; + + private File newAvatarFile; + + private ThreadSafeDialogMaker dialogMaker; + + private final PublicEmployerInfoResponse employerInfo; + + public EmployerInfoPage(PublicEmployerInfoResponse employerInfo) { + this.employerInfo = employerInfo; + } + + public void initialize() { + this.updateDisplay(); + + new OkhttpUtil() + .url(Constants.UserRequestUrls.USER_AVATAR_VIEW_URL) + .addQueryParam("user", employerInfo.getId()) + .successCallback(response -> Platform.runLater(() -> setAvatar(avatarImageView, response))) + .executeAsync(OkhttpUtil.RequestMethod.GET); + } + + private void updateDisplay() { + String name = employerInfo.getName(); + String place = employerInfo.getPlace(); + String contract = employerInfo.getContract(); + String shortDescribe = employerInfo.getShortDescribe(); + String describe = employerInfo.getDescribe(); + String industry = employerInfo.getIndustry(); + + usernameText.setText(name); + shortDescribeText.setText(shortDescribe); + + employerNameTextField.setText(name); + industryTextField.setText(industry); + contactTextField.setText(contract); + addressTextField.setText(place); + shortDescribeTextField.setText(shortDescribe); + describeTextArea.setText(describe); + } + + public static void setAvatar(ImageView avatarImageView, Response response) { + try { + assert response.body() != null; + byte[] data = response.body().bytes(); + + setAvatar(avatarImageView, data); + } catch (Exception e) { + log.warn("获取头像数据时出现异常", e); + } + } + + public static void setAvatar(ImageView avatarImageView, byte[] data) { + Image avatarImage = new Image(new ByteArrayInputStream(data)); + avatarImageView.setImage(avatarImage); + } +} diff --git a/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/controller/components/student/offer/OfferDetailPane.java b/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/controller/components/student/offer/OfferDetailPane.java new file mode 100644 index 0000000..fc9bde2 --- /dev/null +++ b/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/controller/components/student/offer/OfferDetailPane.java @@ -0,0 +1,95 @@ +package net.lensfrex.disillusion.client.controller.components.student.offer; + +import cn.hutool.json.JSONUtil; +import com.jfoenix.controls.JFXTextArea; +import javafx.fxml.FXML; +import javafx.scene.layout.VBox; +import javafx.scene.text.Text; +import lombok.extern.slf4j.Slf4j; +import net.lensfrex.disillusion.client.config.Constants; +import net.lensfrex.disillusion.client.controller.components.ComponentFactory; +import net.lensfrex.disillusion.client.data.response.OfferResponse; +import net.lensfrex.disillusion.client.data.response.PublicEmployerInfoResponse; +import net.lensfrex.disillusion.client.util.OkhttpUtil; +import net.lensfrex.disillusion.client.util.ResponseHandler; +import net.lensfrex.disillusion.client.util.ThreadSafeDialogMaker; + +import java.io.IOException; + +@Slf4j +public class OfferDetailPane extends VBox { + + private final ThreadSafeDialogMaker dialogMaker; + + public OfferDetailPane(ThreadSafeDialogMaker dialogMaker, OfferResponse offer, + PublicEmployerInfoResponse employerInfo) { + this.dialogMaker = dialogMaker; + this.offer = offer; + this.employerInfo = employerInfo; + } + + public OfferDetailPane(ThreadSafeDialogMaker dialogMaker, OfferResponse offer) { + this.dialogMaker = dialogMaker; + this.offer = offer; + this.getEmployerInfo(); + } + + private void getEmployerInfo() { + new OkhttpUtil() + .url(Constants.UserRequestUrls.USER_PUBLIC_INFO_VIEW_URL) + .addQueryParam("user", offer.getPublisherId()) + .failCallback(e -> log.warn("获取企业信息时发生网络错误:\n" + e)) + .successCallback(response -> new ResponseHandler(response) + .errorCallback(s -> log.warn("获取企业信息不成功:\n" + s)) + .successCallback(s -> { + employerInfo = JSONUtil.toBean(s, PublicEmployerInfoResponse.class); + employerText.setText(employerInfo.getName()); + }) + .process()) + .executeAsync(OkhttpUtil.RequestMethod.GET); + } + + public void initialize() { + if (employerInfo != null) { + employerText.setText(employerInfo.getName()); + } + + jobNameText.setText(offer.getJobName()); + placeText.setText(offer.getPlace()); + requirementText.setText(offer.getRequirement()); + salaryText.setText(offer.getSalary().toString()); + targetNumberText.setText(offer.getTargetNumber().toString()); + detailText.setText(offer.getDetail()); + } + + @FXML + void showEmployerDetail() throws IOException { + EmployerInfoPage employerInfoPage = + ComponentFactory.loadComponent("fxml/student/offer/employerProfile.fxml", new EmployerInfoPage(employerInfo)); + dialogMaker.createDialogWithOneBtn("企业详情", employerInfoPage); + } + + private final OfferResponse offer; + private PublicEmployerInfoResponse employerInfo; + + @FXML + private Text jobNameText; + + @FXML + private Text salaryText; + + @FXML + private Text employerText; + + @FXML + private Text placeText; + + @FXML + private Text targetNumberText; + + @FXML + private Text requirementText; + + @FXML + private JFXTextArea detailText; +} diff --git a/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/controller/components/student/offer/StudentOfferCard.java b/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/controller/components/student/offer/StudentOfferCard.java new file mode 100644 index 0000000..86d1b3b --- /dev/null +++ b/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/controller/components/student/offer/StudentOfferCard.java @@ -0,0 +1,174 @@ +package net.lensfrex.disillusion.client.controller.components.student.offer; + +import cn.hutool.json.JSONUtil; +import com.jfoenix.controls.JFXButton; +import javafx.application.Platform; +import javafx.event.ActionEvent; +import javafx.event.Event; +import javafx.fxml.FXML; +import javafx.scene.control.Label; +import javafx.scene.layout.StackPane; +import javafx.scene.text.Text; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.extern.slf4j.Slf4j; +import net.lensfrex.disillusion.client.config.Constants; +import net.lensfrex.disillusion.client.controller.CommunicativeController; +import net.lensfrex.disillusion.client.controller.components.ComponentFactory; +import net.lensfrex.disillusion.client.controller.components.student.application.ApplicationPostPane; +import net.lensfrex.disillusion.client.data.request.ApplicationRequest; +import net.lensfrex.disillusion.client.data.response.OfferResponse; +import net.lensfrex.disillusion.client.data.response.PublicEmployerInfoResponse; +import net.lensfrex.disillusion.client.util.OkhttpUtil; +import net.lensfrex.disillusion.client.util.ResponseHandler; +import net.lensfrex.disillusion.client.util.ThreadSafeDialogMaker; + +import java.io.IOException; + +@Slf4j +public class StudentOfferCard extends StackPane implements CommunicativeController { + private final OfferResponse offer; + private PublicEmployerInfoResponse employerInfo; + + private ThreadSafeDialogMaker dialogMaker; + + private String token; + + private static final String[] colors = new String[]{ + "-fx-background-color: #8F3F7E", + "-fx-background-color: #B5305F", + "-fx-background-color: #CE584A", + "-fx-background-color: #DB8D5C", + "-fx-background-color: #DA854E", + "-fx-background-color: #E9AB44", + "-fx-background-color: #99C286", + "-fx-background-color: #01A05E", + "-fx-background-color: #4A8895", + "-fx-background-color: #16669B", + "-fx-background-color: #2F65A5", + }; + + public StudentOfferCard(OfferResponse offer) { + this.offer = offer; + } + + @Override + public void loadData(InitData initData) { + this.dialogMaker = initData.dialogMaker; + this.token = initData.token; + } + + public void initialize() { + String color = colors[Math.abs(offer.getJobName().hashCode()) % colors.length]; + header.setStyle(header.getStyle() + color); + + jobName.setText(offer.getJobName()); + + getEmployerInfo(); + workingPlaceText.setText("工作地点:" + offer.getPlace()); + + if (offer.getSalary() >= 1_0000) { + salaryText.setText("薪资:" + offer.getSalary() / 1_0000.0 + "w"); + } else { + salaryText.setText("薪资:" + offer.getSalary() / 1000.0 + "k"); + } + + ComponentFactory.setCardIcon(applicationSendBtn, Constants.Svg.sendSvg); + + new OkhttpUtil() + .url(Constants.OfferRequestUrls.OFFER_PROGRESS_URL) + .addQueryParam("id", offer.getId()) + .failCallback(e -> log.warn("获取offer进度时出现网络问题:" + e)) + .successCallback(response -> new ResponseHandler(response) + .errorCallback(s -> log.warn("获取offer进度失败:" + s)) + .successCallback(s -> Platform.runLater(() -> progressText.setText(s))) + .process()) + .executeAsync(OkhttpUtil.RequestMethod.GET); + } + + private void getEmployerInfo() { + new OkhttpUtil() + .url(Constants.UserRequestUrls.USER_PUBLIC_INFO_VIEW_URL) + .addQueryParam("user", offer.getPublisherId()) + .failCallback(e -> log.warn("获取企业信息时发生网络错误:\n" + e)) + .successCallback(response -> new ResponseHandler(response) + .errorCallback(s -> log.warn("获取企业信息不成功:\n" + s)) + .successCallback(s -> { + employerInfo = JSONUtil.toBean(s, PublicEmployerInfoResponse.class); + Platform.runLater(() -> employerNameText.setText("工作单位:" + employerInfo.getName())); + }) + .process()) + .executeAsync(OkhttpUtil.RequestMethod.GET); + } + + @Data + @Builder + @EqualsAndHashCode(callSuper = false) + public static class InitData extends CommunicativeController.InitData { + private ThreadSafeDialogMaker dialogMaker; + private String token; + } + + @FXML + private StackPane rootPane; + + @FXML + private StackPane header; + + @FXML + private Label jobName; + + @FXML + private StackPane body; + + @FXML + private Text progressText; + + @FXML + private Text salaryText; + + @FXML + private Text employerNameText; + + @FXML + private Text workingPlaceText; + + @FXML + private JFXButton applicationSendBtn; + + @FXML + void viewDetail(Event event) throws IOException { + OfferDetailPane detailPage = ComponentFactory.loadComponent("fxml/student/offer/offerDetail.fxml", + new OfferDetailPane(dialogMaker, this.offer, employerInfo)); + dialogMaker.createDialogWithOneBtn("Offer详情", detailPage); + } + + @FXML + void sendApplication(ActionEvent event) { + try { + ApplicationPostPane page = ComponentFactory.loadComponent("fxml/student/application/applicationPostPage.fxml", + new ApplicationPostPane(this.token)); + + dialogMaker.createDialogWithOKAndCancel("发送求职申请", page, + e -> this.sendApplication(page.getChooseResumeId()) + ); + } catch (Exception e) { + e.printStackTrace(); + } + } + + private void sendApplication(String resumeId) { + ApplicationRequest request = new ApplicationRequest(offer.getId(), resumeId); + new OkhttpUtil() + .url(Constants.ApplicationRequestUrls.APPLICATION_PUBLISH_URL) + .body(request) + .addHeader("token", token) + .failCallback(e -> dialogMaker.createMessageDialog("啊哈", "发送offer申请时出现网络错误")) + .successCallback(response -> new ResponseHandler(response) + .errorCallback(s -> dialogMaker.createMessageDialog("啊哈", "offer申请发送失败:\n" + s)) + .successCallback(s -> dialogMaker.createMessageDialog("好好好", "offer申请已发送,请等待处理")) + .process()) + .executeAsync(OkhttpUtil.RequestMethod.POST); + } +} diff --git a/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/controller/components/student/resume/ResumeCard.java b/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/controller/components/student/resume/ResumeCard.java new file mode 100644 index 0000000..562628c --- /dev/null +++ b/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/controller/components/student/resume/ResumeCard.java @@ -0,0 +1,172 @@ +package net.lensfrex.disillusion.client.controller.components.student.resume; + +import cn.hutool.core.codec.Base64; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import com.jfoenix.controls.JFXButton; +import javafx.application.Platform; +import javafx.event.ActionEvent; +import javafx.fxml.FXML; +import javafx.scene.control.Label; +import javafx.scene.input.MouseEvent; +import javafx.scene.layout.StackPane; +import javafx.scene.text.Text; +import net.lensfrex.disillusion.client.config.Constants; +import net.lensfrex.disillusion.client.controller.components.ComponentFactory; +import net.lensfrex.disillusion.client.data.ResponseCode; +import net.lensfrex.disillusion.client.data.request.ResumeRequest; +import net.lensfrex.disillusion.client.data.response.ResumeResponse; +import net.lensfrex.disillusion.client.util.OkhttpUtil; +import net.lensfrex.disillusion.client.util.ResponseHandler; +import net.lensfrex.disillusion.client.util.ThreadSafeDialogMaker; +import okhttp3.*; +import org.jetbrains.annotations.NotNull; + +import java.io.File; +import java.io.IOException; +import java.util.function.Consumer; + +public class ResumeCard extends StackPane { + private static final String[] colors = new String[]{ + "-fx-background-color: #8F3F7E", + "-fx-background-color: #B5305F", + "-fx-background-color: #CE584A", + "-fx-background-color: #DB8D5C", + "-fx-background-color: #DA854E", + "-fx-background-color: #E9AB44", + "-fx-background-color: #99C286", + "-fx-background-color: #01A05E", + "-fx-background-color: #4A8895", + "-fx-background-color: #16669B", + "-fx-background-color: #2F65A5", + }; + + private static final OkHttpClient okhttpClient = Constants.globalClient; + + private final ResumeResponse resume; + + private final String token; + + private final ThreadSafeDialogMaker dialogMaker; + + private final Consumer updateCallback; + private final Consumer deleteCallback; + + public ResumeCard(ResumeResponse resume, String token, ThreadSafeDialogMaker dialogMaker, + Consumer updateCallback, Consumer deleteCallback) { + this.resume = resume; + this.token = token; + this.dialogMaker = dialogMaker; + + this.updateCallback = updateCallback; + this.deleteCallback = deleteCallback; + } + + @FXML + private StackPane rootPane; + + @FXML + private StackPane header; + + @FXML + private Label resumeNameText; + + @FXML + private StackPane body; + + @FXML + private Text remarkText; + + @FXML + private JFXButton resumeDeleteBtn; + + public void initialize() { + String color = colors[Math.abs(resume.getTitle().hashCode()) % colors.length]; + header.setStyle(header.getStyle() + color); + + resumeNameText.setText(resume.getTitle()); + remarkText.setText(resume.getRemark()); + + ComponentFactory.setCardIcon(resumeDeleteBtn, Constants.Svg.deleteSvg); + } + + @FXML + void showResumeDetail(MouseEvent event) throws IOException { + ResumeUploadPane resumeUploadPane = + ComponentFactory.loadComponent("fxml/student/resume/resumeUpload.fxml", new ResumeUploadPane(dialogMaker)); + + ResumeUploadPane.InitData initData = new ResumeUploadPane.InitData(); + initData.setName(resume.getTitle()); + initData.setRemark(resume.getRemark()); + resumeUploadPane.loadData(initData); + + dialogMaker.createDialogWithOKAndCancel("更新简历", resumeUploadPane, e -> { + try { + ResumeRequest request = new ResumeRequest(); + request.setTitle(resumeUploadPane.getResumeName()); + request.setRemark(resumeUploadPane.getRemark()); + + File resumeFile = resumeUploadPane.getResumeFile(); + if (resumeFile != null) { + request.setResumeFileBase64(Base64.encode(resumeFile)); + } + + this.updateResume(request); + } catch (Exception ex) { + ex.printStackTrace(); + dialogMaker.createMessageDialog("啊哈", "读取简历文件时发生错误...\n" + ex); + } + }); + } + + private void updateResume(ResumeRequest resumeRequest) { + String json = JSONUtil.toJsonStr(resumeRequest); + RequestBody requestBody = RequestBody.create(json, MediaType.get("application/json")); + + Request uploadRequest = new Request.Builder() + .url(Constants.ResumeRequestUrls.RESUME_UPDATE_URL + "?id=" + resume.getId()) + .addHeader("token", token) + .post(requestBody) + .build(); + + okhttpClient.newCall(uploadRequest).enqueue(resumeUpdateCallback); + } + + private final Callback resumeUpdateCallback = new Callback() { + @Override + public void onFailure(@NotNull Call call, @NotNull IOException e) { + dialogMaker.createMessageDialog("啊哈", "上传简历时发生了网络错误\n" + e); + } + + @Override + public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException { + assert response.body() != null; + String responseString = response.body().string(); + JSONObject responseJson = JSONUtil.parseObj(responseString); + + Integer code = responseJson.getInt("code"); + if (code == null || !code.equals(ResponseCode.SUCCESS.getCode())) { + String message = responseJson.getStr("message"); + dialogMaker.createMessageDialog("啊哈", "获取用户信息时出错了...\n" + message); + } else { + dialogMaker.createMessageDialog("好耶", "上传成功"); + Platform.runLater(() -> updateCallback.accept(response)); + } + } + }; + + @FXML + void deleteResume(ActionEvent event) { + dialogMaker.createDialogWithOKAndCancel("Really?", String.format("真的要删除简历 '%s' 吗?", resume.getTitle()), + e -> new OkhttpUtil() + .url(Constants.ResumeRequestUrls.RESUME_DELETE_URL) + .token(token) + .addQueryParam("id", resume.getId()) + .failCallback(e1 -> dialogMaker.createMessageDialog("啊哈", "删除简历时发生了网络错误\n" + e1)) + .successCallback(response -> new ResponseHandler(response) + .errorCallback(s -> dialogMaker.createMessageDialog("啊哈", "删除简历时出错了...\n" + s)) + .successCallback(response1 -> Platform.runLater(() -> deleteCallback.accept(response))) + .process()) + .executeAsync(OkhttpUtil.RequestMethod.POST)); + } +} diff --git a/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/controller/components/student/resume/ResumeUploadPane.java b/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/controller/components/student/resume/ResumeUploadPane.java new file mode 100644 index 0000000..da8b84e --- /dev/null +++ b/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/controller/components/student/resume/ResumeUploadPane.java @@ -0,0 +1,95 @@ +package net.lensfrex.disillusion.client.controller.components.student.resume; + +import cn.hutool.core.io.FileUtil; +import cn.hutool.core.util.RandomUtil; +import com.jfoenix.controls.JFXTextField; +import javafx.event.ActionEvent; +import javafx.fxml.FXML; +import javafx.scene.layout.VBox; +import javafx.scene.text.Text; +import javafx.stage.FileChooser; +import lombok.Data; +import lombok.EqualsAndHashCode; +import net.lensfrex.disillusion.client.config.Constants; +import net.lensfrex.disillusion.client.controller.CommunicativeController; +import net.lensfrex.disillusion.client.util.ThreadSafeDialogMaker; + +import java.io.File; + +public class ResumeUploadPane extends VBox implements CommunicativeController { + private final ThreadSafeDialogMaker dialogMaker; + + public ResumeUploadPane(ThreadSafeDialogMaker dialogMaker) { + this.dialogMaker = dialogMaker; + } + + @Override + public void loadData(InitData initData) { + resumeNameTextField.setText(initData.getName()); + remarkTextField.setText(initData.getRemark()); + } + + @Override + public void initialize() { + resumeNameTextField.setText(RandomUtil.randomString(Constants.sample, 5)); + remarkTextField.setText(RandomUtil.randomString(Constants.sample, 15)); + } + + @Data + @EqualsAndHashCode(callSuper = false) + public static class InitData extends CommunicativeController.InitData { + private String name; + private String remark; + } + + @FXML + private VBox rootPane; + + @FXML + private JFXTextField resumeNameTextField; + + @FXML + private JFXTextField remarkTextField; + + @FXML + private Text fileNameText; + + private File resumeFile; + + @FXML + void addFile(ActionEvent event) { + FileChooser fileChooser = new FileChooser(); + fileChooser.setTitle("告诉我在哪?"); + File chooseFile = fileChooser.showOpenDialog(rootPane.getScene().getWindow()); + + if (chooseFile != null) { + String fileType = FileUtil.getType(chooseFile); + switch (fileType) { + case "png": + case "jpg": + case "jpeg": + case "webm": + case "pdf": + case "doc": + case "docx": + fileNameText.setText(chooseFile.getName()); + resumeFile = chooseFile; + break; + default: + dialogMaker.createMessageDialog("(;´д`)ゞ", "简历文件不支持:" + fileType + "\n仅支持png, jpg, webm, pdf, word"); + } + } + } + + public String getResumeName() { + return resumeNameTextField.getText(); + } + + public String getRemark() { + return remarkTextField.getText(); + } + + public File getResumeFile() { + return resumeFile; + } +} diff --git a/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/controller/page/employer/EmployerApplicationManagePage.java b/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/controller/page/employer/EmployerApplicationManagePage.java new file mode 100644 index 0000000..972061b --- /dev/null +++ b/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/controller/page/employer/EmployerApplicationManagePage.java @@ -0,0 +1,91 @@ +package net.lensfrex.disillusion.client.controller.page.employer; + +import cn.hutool.json.JSONUtil; +import com.jfoenix.effects.JFXDepthManager; +import javafx.application.Platform; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import javafx.event.ActionEvent; +import javafx.fxml.FXML; +import javafx.scene.Node; +import javafx.scene.control.ScrollPane; +import javafx.scene.layout.FlowPane; +import javafx.scene.layout.VBox; +import lombok.extern.slf4j.Slf4j; +import net.lensfrex.disillusion.client.config.Constants; +import net.lensfrex.disillusion.client.controller.components.ComponentFactory; +import net.lensfrex.disillusion.client.controller.components.employer.application.ApplicationOfferCard; +import net.lensfrex.disillusion.client.controller.components.student.resume.ResumeCard; +import net.lensfrex.disillusion.client.data.response.OfferResponse; +import net.lensfrex.disillusion.client.util.OkhttpUtil; +import net.lensfrex.disillusion.client.util.ResponseHandler; +import net.lensfrex.disillusion.client.util.ThreadSafeDialogMaker; + +import java.util.List; + +@Slf4j +public class EmployerApplicationManagePage extends VBox { + @FXML + private VBox rootPane; + + @FXML + private ScrollPane scrollPane; + + @FXML + private FlowPane offerContainer; + + private String token; + private ThreadSafeDialogMaker dialogMaker; + + @FXML + void refresh(ActionEvent event) { + this.refreshOfferList(); + } + + public void loadData(String token, ThreadSafeDialogMaker dialogMaker) { + this.token = token; + this.dialogMaker = dialogMaker; + this.refreshOfferList(); + } + + public void initialize() { + offerContainer.getChildren().addAll(offerLost); + } + + private final ObservableList offerLost = FXCollections.observableArrayList(); + + private void refreshOfferList() { + new OkhttpUtil() + .url(Constants.OfferRequestUrls.OFFER_GET_BY_EMPLOYER_SELF_URL) + .token(token) + .failCallback(e -> dialogMaker.createMessageDialog("啊哈", "获取offer列表时发生了网络错误\n" + e)) + .successCallback(response -> new ResponseHandler(response) + .errorCallback(s -> dialogMaker.createMessageDialog("啊哈", "获取offer列表不成功\n" + s)) + .successCallback(data -> { + List result = JSONUtil.toList(data, OfferResponse.class); + Platform.runLater(() -> showOffers(result)); + }) + .process()) + .executeAsync(OkhttpUtil.RequestMethod.GET); + } + + private void showOffers(List offers) { + try { + ObservableList cards = offerContainer.getChildren(); + cards.clear(); + for (OfferResponse offer : offers) { + if (offer == null) { + continue; + } + ApplicationOfferCard card = + ComponentFactory.loadComponent("fxml/employer/application/applicationOfferCard.fxml", new ApplicationOfferCard(offer)); + card.loadData(token, dialogMaker); + JFXDepthManager.setDepth(card, 1); + cards.add(card); + } + } catch (Exception e) { + e.printStackTrace(); + log.warn("展示offer卡片时发生错误"); + } + } +} diff --git a/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/controller/page/employer/EmployerMainController.java b/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/controller/page/employer/EmployerMainController.java new file mode 100644 index 0000000..469be6e --- /dev/null +++ b/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/controller/page/employer/EmployerMainController.java @@ -0,0 +1,136 @@ +package net.lensfrex.disillusion.client.controller.page.employer; + +import animatefx.animation.FadeIn; +import animatefx.animation.FadeOut; +import cn.hutool.json.JSONUtil; +import javafx.application.Platform; +import javafx.collections.ObservableList; +import javafx.event.ActionEvent; +import javafx.fxml.FXML; +import javafx.scene.Node; +import javafx.scene.image.ImageView; +import javafx.scene.layout.Pane; +import javafx.scene.layout.StackPane; +import javafx.scene.text.Text; +import lombok.extern.slf4j.Slf4j; +import net.lensfrex.disillusion.client.config.Constants; +import net.lensfrex.disillusion.client.controller.components.ComponentFactory; +import net.lensfrex.disillusion.client.controller.page.student.StudentProfilePage; +import net.lensfrex.disillusion.client.data.response.EmployerInfoResponse; +import net.lensfrex.disillusion.client.util.DialogMaker; +import net.lensfrex.disillusion.client.util.OkhttpUtil; +import net.lensfrex.disillusion.client.util.ResponseHandler; +import net.lensfrex.disillusion.client.util.ThreadSafeDialogMaker; + +import java.util.HashMap; +import java.util.Map; + +@Slf4j +public class EmployerMainController { + @FXML + private StackPane rootPane; + + @FXML + private ImageView avatarImageView; + + @FXML + private Text employerName; + + @FXML + private Text usernameText; + + @FXML + private Text shortDescribeText; + + @FXML + private StackPane containerPane; + + private ThreadSafeDialogMaker threadSafeDialogMaker; + + private String token; + + private EmployerInfoResponse employerInfo; + + private final Map pages = new HashMap<>(4); + + public void setData(String token) { + this.token = token; + this.fetchUserInfo(); + } + + public void initialize() { + threadSafeDialogMaker = new ThreadSafeDialogMaker(rootPane); + try { + pages.put("profile", ComponentFactory.loadComponent("fxml/employer/profile.fxml", EmployerProfilePage.class)); + pages.put("offerManage", ComponentFactory.loadComponent("fxml/employer/offer/offerManagePage.fxml", EmployerOfferManagePage.class)); + pages.put("applicationManage", ComponentFactory.loadComponent("fxml/employer/application/applicationManagePage.fxml", EmployerApplicationManagePage.class)); + } catch (Exception e) { + e.printStackTrace(); + DialogMaker.errorMessageDialog("出错啦,请联系lensfrex这家伙\n调试信息:\n" + e); + } + } + + private void fetchUserInfo() { + new OkhttpUtil() + .url(Constants.UserRequestUrls.USER_INFO_VIEW_URL) + .token(token) + .failCallback(e -> log.error("请求用户数据时发生网络错误")) + .successCallback(response -> new ResponseHandler(response) + .errorCallback(s -> threadSafeDialogMaker.createMessageDialog("啊哈", "获取用户信息不成功:" + s)) + .successCallback(this::updateUserInfoUI) + .process()) + .executeAsync(OkhttpUtil.RequestMethod.GET); + } + + private void updateUserInfoUI(String data) { + employerInfo = JSONUtil.toBean(data, EmployerInfoResponse.class); + Platform.runLater(() -> { + employerName.setText(employerInfo.getName()); + usernameText.setText("@" + employerInfo.getId()); + shortDescribeText.setText(employerInfo.getShortDescribe()); + }); + + this.updateAvatar(); + } + + private void updateAvatar() { + new OkhttpUtil() + .url(Constants.UserRequestUrls.USER_AVATAR_VIEW_URL) + .token(token) + .failCallback(e -> log.warn("获取头像时发生网络错误:\n" + e)) + .successCallback(avatarResponse -> Platform.runLater(() -> StudentProfilePage.setAvatar(avatarImageView, avatarResponse))) + .executeAsync(OkhttpUtil.RequestMethod.GET); + } + + @FXML + void showApplicationDealPage(ActionEvent event) { + EmployerApplicationManagePage page = (EmployerApplicationManagePage) this.switchPage("applicationManage"); + page.loadData(token, threadSafeDialogMaker); + } + + @FXML + void showOfferManagePage(ActionEvent event) { + EmployerOfferManagePage page = (EmployerOfferManagePage) this.switchPage("offerManage"); + page.loadData(token, threadSafeDialogMaker); + } + + @FXML + void showProfile(ActionEvent event) { + EmployerProfilePage page = (EmployerProfilePage) this.switchPage("profile"); + page.loadData(employerInfo, token, threadSafeDialogMaker, r -> fetchUserInfo()); + } + + private Node switchPage(String page) { + ObservableList list = containerPane.getChildren(); + + new FadeOut(list.get(0)).play(); + Pane pagePane = pages.get(page); + if (pagePane != null) { + list.clear(); + list.add(pagePane); + new FadeIn(pagePane).play(); + } + + return pagePane; + } +} \ No newline at end of file diff --git a/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/controller/page/employer/EmployerOfferManagePage.java b/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/controller/page/employer/EmployerOfferManagePage.java new file mode 100644 index 0000000..f1c7a26 --- /dev/null +++ b/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/controller/page/employer/EmployerOfferManagePage.java @@ -0,0 +1,114 @@ +package net.lensfrex.disillusion.client.controller.page.employer; + +import cn.hutool.json.JSONUtil; +import com.jfoenix.effects.JFXDepthManager; +import javafx.application.Platform; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import javafx.event.ActionEvent; +import javafx.fxml.FXML; +import javafx.scene.Node; +import javafx.scene.control.ScrollPane; +import javafx.scene.layout.FlowPane; +import javafx.scene.layout.VBox; +import lombok.extern.slf4j.Slf4j; +import net.lensfrex.disillusion.client.config.Constants; +import net.lensfrex.disillusion.client.controller.components.ComponentFactory; +import net.lensfrex.disillusion.client.controller.components.employer.offer.EmployerOfferCard; +import net.lensfrex.disillusion.client.controller.components.employer.offer.EmployerOfferDetailPane; +import net.lensfrex.disillusion.client.controller.components.student.resume.ResumeCard; +import net.lensfrex.disillusion.client.data.request.OfferPublishRequest; +import net.lensfrex.disillusion.client.data.response.OfferResponse; +import net.lensfrex.disillusion.client.util.Consumer; +import net.lensfrex.disillusion.client.util.OkhttpUtil; +import net.lensfrex.disillusion.client.util.ResponseHandler; +import net.lensfrex.disillusion.client.util.ThreadSafeDialogMaker; + +import java.io.IOException; +import java.util.List; + +@Slf4j +public class EmployerOfferManagePage extends VBox { + @FXML + private VBox rootPane; + + @FXML + private ScrollPane scrollPane; + + @FXML + private FlowPane offerContainer; + + private String token; + private ThreadSafeDialogMaker dialogMaker; + + public void loadData(String token, ThreadSafeDialogMaker dialogMaker) { + this.token = token; + this.dialogMaker = dialogMaker; + this.refreshOfferList(); + } + + public void initialize() { + offerContainer.getChildren().addAll(offerLost); + } + + private final ObservableList offerLost = FXCollections.observableArrayList(); + + @FXML + void addOffer(ActionEvent event) throws IOException, InstantiationException, IllegalAccessException { + EmployerOfferDetailPane offerDetailPane = + ComponentFactory.loadComponent("fxml/employer/offer/offerDetailPane.fxml", EmployerOfferDetailPane.class); + dialogMaker.createDialogWithOKAndCancel("发布新的offer信息", offerDetailPane, e -> this.publishOffer(offerDetailPane.getData())); + } + + private void publishOffer(OfferPublishRequest offerPublishRequest) { + new OkhttpUtil() + .url(Constants.OfferRequestUrls.OFFER_PUBLISH_URL) + .token(token) + .body(offerPublishRequest) + .failCallback(e -> dialogMaker.createMessageDialog("啊哈", "发布offer时发生了网络问题:\n" + e)) + .successCallback(response -> new ResponseHandler(response) + .errorCallback(s -> dialogMaker.createMessageDialog("啊哈", "发布offer不成功:\n" + s)) + .successCallback(s -> this.refreshOfferList()) + .process()) + .executeAsync(OkhttpUtil.RequestMethod.POST); + } + + private void refreshOfferList() { + new OkhttpUtil() + .url(Constants.OfferRequestUrls.OFFER_GET_BY_EMPLOYER_SELF_URL) + .token(token) + .failCallback(e -> dialogMaker.createMessageDialog("啊哈", "获取offer列表时发生了网络错误\n" + e)) + .successCallback(response -> new ResponseHandler(response) + .errorCallback(s -> dialogMaker.createMessageDialog("啊哈", "获取offer列表不成功\n" + s)) + .successCallback(data -> { + List result = JSONUtil.toList(data, OfferResponse.class); + Platform.runLater(() -> showOffers(result)); + }) + .process()) + .executeAsync(OkhttpUtil.RequestMethod.GET); + } + + private void showOffers(List offers) { + try { + ObservableList cards = offerContainer.getChildren(); + cards.clear(); + for (OfferResponse offer : offers) { + if (offer == null) { + continue; + } + EmployerOfferCard card = + ComponentFactory.loadComponent("fxml/employer/offer/offerCard.fxml", new EmployerOfferCard(offer)); + card.loadData(token, dialogMaker, this.deleteCallback, this.updateCallback); + JFXDepthManager.setDepth(card, 1); + cards.add(card); + } + } catch (Exception e) { + e.printStackTrace(); + log.warn("展示offer卡片时发生错误"); + } + } + + private final Consumer updateCallback = s -> refreshOfferList(); + + private final Consumer deleteCallback = s -> refreshOfferList(); +} diff --git a/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/controller/page/employer/EmployerProfilePage.java b/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/controller/page/employer/EmployerProfilePage.java new file mode 100644 index 0000000..45d6417 --- /dev/null +++ b/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/controller/page/employer/EmployerProfilePage.java @@ -0,0 +1,206 @@ +package net.lensfrex.disillusion.client.controller.page.employer; + +import cn.hutool.core.codec.Base64; +import cn.hutool.core.io.FileUtil; +import com.jfoenix.controls.JFXTextArea; +import com.jfoenix.controls.JFXTextField; +import javafx.application.Platform; +import javafx.event.ActionEvent; +import javafx.fxml.FXML; +import javafx.scene.image.Image; +import javafx.scene.image.ImageView; +import javafx.scene.input.MouseEvent; +import javafx.scene.layout.HBox; +import javafx.scene.text.Text; +import javafx.stage.FileChooser; +import lombok.extern.slf4j.Slf4j; +import net.lensfrex.disillusion.client.config.Constants; +import net.lensfrex.disillusion.client.data.response.EmployerInfoResponse; +import net.lensfrex.disillusion.client.util.ImageUtil; +import net.lensfrex.disillusion.client.util.OkhttpUtil; +import net.lensfrex.disillusion.client.util.ResponseHandler; +import net.lensfrex.disillusion.client.util.ThreadSafeDialogMaker; +import okhttp3.Response; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.function.Consumer; + +@Slf4j +public class EmployerProfilePage extends HBox { + @FXML + private HBox rootPane; + + @FXML + private ImageView avatarImageView; + + @FXML + private Text usernameText; + + @FXML + private Text shortDescribeText; + + @FXML + private JFXTextField employerNameTextField; + + @FXML + private JFXTextField industryTextField; + + @FXML + private JFXTextField contactTextField; + + @FXML + private JFXTextField addressTextField; + + @FXML + private JFXTextField shortDescribeTextField; + + @FXML + private JFXTextArea describeTextArea; + + private File newAvatarFile; + + private String token; + + private ThreadSafeDialogMaker dialogMaker; + + @FXML + void save(ActionEvent event) { + employerInfo.setName(employerNameTextField.getText()); + employerInfo.setPlace(addressTextField.getText()); + employerInfo.setContract(contactTextField.getText()); + employerInfo.setShortDescribe(shortDescribeTextField.getText()); + employerInfo.setDescribe(describeTextArea.getText()); + employerInfo.setIndustry(industryTextField.getText()); + + new OkhttpUtil() + .url(Constants.UserRequestUrls.USER_INFO_EDIT_URL) + .token(token) + .body(employerInfo) + .failCallback(e -> dialogMaker.createMessageDialog("啊哈", "更新上传用户信息的时候出错啦:\n" + e)) + .successCallback(response -> new ResponseHandler(response) + .errorCallback(s -> dialogMaker.createMessageDialog("啊哈", "更新上传用户信息不成功:" + s)) + .successCallback(s -> { + updateCallback.accept(employerInfo); + dialogMaker.createMessageDialog("好耶", "更新成功"); + Platform.runLater(this::updateDisplay); + }) + .process()) + .executeAsync(OkhttpUtil.RequestMethod.POST); + } + + @FXML + void replaceAvatar(MouseEvent event) { + FileChooser fileChooser = new FileChooser(); + fileChooser.setTitle("选择图片"); + File chooseFile = fileChooser.showOpenDialog(rootPane.getScene().getWindow()); + + if (chooseFile != null) { + String fileType = FileUtil.getType(chooseFile); + switch (fileType) { + case "png": + case "jpg": + case "jpeg": + case "gif": + case "webm": + this.uploadAvatar(chooseFile); + break; + default: + dialogMaker.createMessageDialog( + "(;´д`)ゞ", + "头像文件不支持:" + fileType + "\n仅支持png, jpg, gif, webm" + ); + } + } + } + + private void uploadAvatar(File avatarFile) { + try { + byte[] avatarData; + // 大于1m的图片就进行压缩 + long fileLength = avatarFile.length(); + if (fileLength > 1048576) { + // 压缩比例,文件越大压缩程度越大 + float percent = (float) Math.pow(0.96, fileLength / 1048576f); + avatarData = ImageUtil.processImage(avatarFile, 0.8f, percent, "jpg"); + } else { + avatarData = Files.readAllBytes(Paths.get(avatarFile.toURI())); + } + + new OkhttpUtil() + .url(Constants.UserRequestUrls.USER_AVATAR_EDIT_URL) + .token(token) + .body(Base64.encode(avatarData), "plain/text") + .failCallback(e -> dialogMaker.createMessageDialog("啊哈", "上传头像文件时发生网络错误:" + e)) + .successCallback(response -> new ResponseHandler(response) + .errorCallback(s -> dialogMaker.createMessageDialog("啊哈", "获取头像时出现问题:" + s)) + .successCallback(s -> { + Platform.runLater(() -> setAvatar(avatarImageView, avatarData)); + updateCallback.accept(employerInfo); + }) + .process()) + .executeAsync(OkhttpUtil.RequestMethod.POST); + } catch (IOException e) { + dialogMaker.createMessageDialog("啊哈", "读取头像文件时发生错误:" + e); + } + } + + private EmployerInfoResponse employerInfo; + + private Consumer updateCallback; + + public void loadData(EmployerInfoResponse employerInfo, + String token, ThreadSafeDialogMaker dialogMaker, + Consumer updateCallback) { + this.token = token; + this.employerInfo = employerInfo; + this.dialogMaker = dialogMaker; + this.updateCallback = updateCallback; + + this.updateDisplay(); + + new OkhttpUtil() + .url(Constants.UserRequestUrls.USER_AVATAR_VIEW_URL) + .token(token) + .successCallback(response -> Platform.runLater(() -> setAvatar(avatarImageView, response))) + .executeAsync(OkhttpUtil.RequestMethod.GET); + } + + private void updateDisplay() { + String name = employerInfo.getName(); + String place = employerInfo.getPlace(); + String contract = employerInfo.getContract(); + String shortDescribe = employerInfo.getShortDescribe(); + String describe = employerInfo.getDescribe(); + String industry = employerInfo.getIndustry(); + + usernameText.setText(name); + shortDescribeText.setText(shortDescribe); + + employerNameTextField.setText(name); + industryTextField.setText(industry); + contactTextField.setText(contract); + addressTextField.setText(place); + shortDescribeTextField.setText(shortDescribe); + describeTextArea.setText(describe); + } + + public static void setAvatar(ImageView avatarImageView, Response response) { + try { + assert response.body() != null; + byte[] data = response.body().bytes(); + + setAvatar(avatarImageView, data); + } catch (Exception e) { + log.warn("获取头像数据时出现异常", e); + } + } + + public static void setAvatar(ImageView avatarImageView, byte[] data) { + Image avatarImage = new Image(new ByteArrayInputStream(data)); + avatarImageView.setImage(avatarImage); + } +} diff --git a/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/controller/page/student/ApplicationManagePage.java b/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/controller/page/student/ApplicationManagePage.java new file mode 100644 index 0000000..fa35a43 --- /dev/null +++ b/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/controller/page/student/ApplicationManagePage.java @@ -0,0 +1,106 @@ +package net.lensfrex.disillusion.client.controller.page.student; + +import cn.hutool.json.JSONUtil; +import com.jfoenix.effects.JFXDepthManager; +import javafx.application.Platform; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import javafx.fxml.FXML; +import javafx.scene.Node; +import javafx.scene.control.ScrollPane; +import javafx.scene.layout.FlowPane; +import javafx.scene.layout.VBox; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.extern.slf4j.Slf4j; +import net.lensfrex.disillusion.client.config.Constants; +import net.lensfrex.disillusion.client.controller.CommunicativeController; +import net.lensfrex.disillusion.client.controller.components.ComponentFactory; +import net.lensfrex.disillusion.client.controller.components.student.application.ApplicationCard; +import net.lensfrex.disillusion.client.data.response.ApplicationResponse; +import net.lensfrex.disillusion.client.util.OkhttpUtil; +import net.lensfrex.disillusion.client.util.ResponseHandler; +import net.lensfrex.disillusion.client.util.ThreadSafeDialogMaker; +import okhttp3.*; + +import java.util.List; + +@Slf4j +public class ApplicationManagePage extends VBox implements CommunicativeController { + @FXML + private VBox rootPane; + + @FXML + private ScrollPane scrollPane; + + @FXML + private FlowPane applicationContainer; + + private String token; + private ThreadSafeDialogMaker dialogMaker; + + private static final OkHttpClient okhttpClient = Constants.globalClient; + + @Data + @Builder + @EqualsAndHashCode(callSuper = false) + public static class InitData extends CommunicativeController.InitData { + private String token; + private ThreadSafeDialogMaker dialogMaker; + } + + @Override + public void loadData(ApplicationManagePage.InitData initData) { + this.token = initData.token; + this.dialogMaker = initData.dialogMaker; + + this.refreshApplicationList(); + } + + public void initialize() { + applicationContainer.getChildren().addAll(applicationList); + } + + private final ObservableList applicationList = FXCollections.observableArrayList(); + + private void refreshApplicationList() { + new OkhttpUtil() + .url(Constants.ApplicationRequestUrls.APPLICATION_STUDENT_VIEW_URL) + .token(token) + .failCallback(e -> dialogMaker.createMessageDialog("啊哈", "获取简历时发生了网络错误\n" + e)) + .successCallback(response -> { + ResponseHandler.Result responseData = ResponseHandler.parse(response, + s -> dialogMaker.createMessageDialog("啊哈", "获取简历时出错了...\n" + s)); + if (responseData.data == null || !responseData.success) { + return; + } + + List result = JSONUtil.toList(responseData.data, ApplicationResponse.class); + Platform.runLater(() -> listApplications(result)); + }) + .executeAsync(OkhttpUtil.RequestMethod.GET); + } + + private void listApplications(List applications) { + try { + ObservableList items = applicationContainer.getChildren(); + items.clear(); + for (ApplicationResponse application : applications) { + if (application == null) { + continue; + } + + ApplicationCard card = + ComponentFactory.loadComponent("fxml/student/application/applicationCard.fxml", + new ApplicationCard(token, dialogMaker, application, s -> this.refreshApplicationList())); + JFXDepthManager.setDepth(card, 1); + card.setUserData(applications); + items.add(card); + } + } catch (Exception e) { + e.printStackTrace(); + log.warn("展示求职申请卡片时发生错误"); + } + } +} diff --git a/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/controller/page/student/OfferSquarePage.java b/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/controller/page/student/OfferSquarePage.java new file mode 100644 index 0000000..9a0d6be --- /dev/null +++ b/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/controller/page/student/OfferSquarePage.java @@ -0,0 +1,204 @@ +package net.lensfrex.disillusion.client.controller.page.student; + +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import com.jfoenix.controls.JFXComboBox; +import com.jfoenix.controls.JFXTextField; +import com.jfoenix.effects.JFXDepthManager; +import javafx.application.Platform; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import javafx.event.ActionEvent; +import javafx.fxml.FXML; +import javafx.scene.Node; +import javafx.scene.control.Label; +import javafx.scene.control.ScrollPane; +import javafx.scene.input.ScrollEvent; +import javafx.scene.layout.FlowPane; +import javafx.scene.layout.Pane; +import javafx.scene.layout.VBox; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.extern.slf4j.Slf4j; +import net.lensfrex.disillusion.client.config.Constants; +import net.lensfrex.disillusion.client.controller.CommunicativeController; +import net.lensfrex.disillusion.client.controller.components.ComponentFactory; +import net.lensfrex.disillusion.client.controller.components.student.offer.StudentOfferCard; +import net.lensfrex.disillusion.client.data.ResponseCode; +import net.lensfrex.disillusion.client.data.response.OfferResponse; +import net.lensfrex.disillusion.client.util.Consumer; +import net.lensfrex.disillusion.client.util.OkhttpUtil; +import net.lensfrex.disillusion.client.util.ResponseHandler; +import net.lensfrex.disillusion.client.util.ThreadSafeDialogMaker; +import okhttp3.OkHttpClient; +import okhttp3.Response; + +import java.util.List; + +@Slf4j +public class OfferSquarePage + extends VBox + implements CommunicativeController { + + private static final OkHttpClient okhttpClient = Constants.globalClient; + + @FXML + private VBox rootPane; + + @FXML + private JFXComboBox filterType; + + @FXML + private JFXTextField filterString; + + @FXML + private FlowPane offersPane; + + @FXML + private ScrollPane scrollPane; + + @FXML + private Label pageLabel; + + private double totalDrag = 0; + + private String token; + private Pane parentRootPane; + + private ThreadSafeDialogMaker dialogMaker; + + @Override + public void loadData(InitData data) { + this.token = data.token; + this.dialogMaker = data.dialogMaker; + + this.refresh(null); + } + + @Data + @Builder + @EqualsAndHashCode(callSuper = true) + public static class InitData extends CommunicativeController.InitData { + private String token; + private ThreadSafeDialogMaker dialogMaker; + } + + public void initialize() { + ObservableList filterList = FXCollections.observableArrayList(); + filterList.add("用人单位"); + filterList.add("职位名称"); + filterList.add("最低薪资"); + filterType.setItems(filterList); + filterType.getSelectionModel().select(0); + + scrollPane.addEventHandler(ScrollEvent.SCROLL, e -> { + double downScrollLength = -e.getDeltaY(); + if (downScrollLength < 0) { + return; + } + + totalDrag += downScrollLength; + if (totalDrag > offersPane.getPrefHeight()) { + refresh(null); + } + }); + } + + private int currentPage = 0; + + private static final int DEFAULT_PAGE_SIZE = 5; + + @FXML + void refresh(ActionEvent event) { + this.getOffers(currentPage + 1, DEFAULT_PAGE_SIZE); + } + + private void getOffers(int page, int size) { + new OkhttpUtil() + .addQueryParam("page", String.valueOf(page)) + .addQueryParam("size", String.valueOf(size)) + .url(Constants.OfferRequestUrls.GET_ALL_OFFER_URL) + .failCallback(e -> dialogMaker.createMessageDialog("啊哈", "请求Offer列表时发生了一下错误\n" + e)) + .successCallback(getOfferSuccessCallback) + .executeAsync(OkhttpUtil.RequestMethod.GET); + } + + private static final OfferResponse[] EMPTY_OFFERS = new OfferResponse[]{}; + + private final Consumer getOfferSuccessCallback = response -> { + assert response.body() != null; + String responseString = response.body().string(); + JSONObject responseJson = JSONUtil.parseObj(responseString); + + Integer code = responseJson.getInt("code"); + if (code == null || !code.equals(ResponseCode.SUCCESS.getCode())) { + dialogMaker.createMessageDialog("啊哈", "获取offer列表时出错了...\n" + responseString); + } + + JSONObject data = responseJson.getJSONObject("data"); + + List offers = JSONUtil.toList(data.getJSONArray("result"), OfferResponse.class); + + Platform.runLater(() -> { + this.showOfferCards(offers); + currentPage = offers.size() < DEFAULT_PAGE_SIZE ? 0 : data.getInt("page"); + }); + }; + + private void showOfferCards(List offers) { + try { + ObservableList items = offersPane.getChildren(); + if (currentPage == 0) { + items.clear(); + } + + for (OfferResponse offer : offers) { + StudentOfferCard studentOfferCard = new StudentOfferCard(offer); + Node item = ComponentFactory.loadComponent("fxml/student/offer/offerCard.fxml", studentOfferCard); + + studentOfferCard.loadData(StudentOfferCard.InitData.builder().token(token).dialogMaker(dialogMaker).build()); + + studentOfferCard.setUserData("offer"); + JFXDepthManager.setDepth(item, 1); + + items.add(new Pane(item)); + } + } catch (Exception e) { + log.error("解析Offer对象数据时出错:\n{}", e.toString()); + e.printStackTrace(); + } + } + + @FXML + void search(ActionEvent event) { + String selected = filterType.getSelectionModel().getSelectedItem(); + String filterType = ""; + switch (selected) { + case "用人单位": + filterType = Constants.OfferRequestUrls.OFFER_FILTER_EMPLOYER; + break; + case "职位名称": + filterType = Constants.OfferRequestUrls.OFFER_FILTER_JOB_NAME; + break; + case "最低薪资": + filterType = Constants.OfferRequestUrls.OFFER_FILTER_SALARY; + break; + } + + new OkhttpUtil() + .url(Constants.OfferRequestUrls.OFFER_SEARCH_URL) + .addQueryParam("filter", filterType) + .addQueryParam("value", filterString.getText()) + .failCallback(e -> dialogMaker.createMessageDialog("啊哈", "搜索offer时发生了错误:\n" + e)) + .successCallback(response -> new ResponseHandler(response) + .errorCallback(s -> dialogMaker.createMessageDialog("啊哈", "搜索offer不成功:\n" + s)) + .successCallback(s -> Platform.runLater(() -> { + offersPane.getChildren().clear(); + List offers = JSONUtil.toList(s, OfferResponse.class); + this.showOfferCards(offers); + })) + .process()) + .executeAsync(OkhttpUtil.RequestMethod.GET); + } +} diff --git a/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/controller/page/student/ResumeManagePage.java b/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/controller/page/student/ResumeManagePage.java new file mode 100644 index 0000000..2f94692 --- /dev/null +++ b/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/controller/page/student/ResumeManagePage.java @@ -0,0 +1,194 @@ +package net.lensfrex.disillusion.client.controller.page.student; + +import cn.hutool.core.codec.Base64; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import com.jfoenix.effects.JFXDepthManager; +import javafx.application.Platform; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import javafx.event.ActionEvent; +import javafx.fxml.FXML; +import javafx.scene.Node; +import javafx.scene.layout.FlowPane; +import javafx.scene.layout.VBox; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.extern.slf4j.Slf4j; +import net.lensfrex.disillusion.client.config.Constants; +import net.lensfrex.disillusion.client.controller.CommunicativeController; +import net.lensfrex.disillusion.client.controller.components.ComponentFactory; +import net.lensfrex.disillusion.client.controller.components.student.resume.ResumeCard; +import net.lensfrex.disillusion.client.controller.components.student.resume.ResumeUploadPane; +import net.lensfrex.disillusion.client.data.ResponseCode; +import net.lensfrex.disillusion.client.data.request.ResumeRequest; +import net.lensfrex.disillusion.client.data.response.ResumeResponse; +import net.lensfrex.disillusion.client.util.ThreadSafeDialogMaker; +import okhttp3.*; +import org.jetbrains.annotations.NotNull; + +import java.io.File; +import java.io.IOException; +import java.util.List; +import java.util.function.Consumer; + +@Slf4j +public class ResumeManagePage extends VBox + implements CommunicativeController { + + private static final OkHttpClient okhttpClient = Constants.globalClient; + + private String token; + private ThreadSafeDialogMaker dialogMaker; + + @Override + public void loadData(InitData initData) { + this.token = initData.token; + this.dialogMaker = initData.dialogMaker; + this.refreshResumeList(); + } + + @Data + @EqualsAndHashCode(callSuper = false) + public static class InitData extends CommunicativeController.InitData { + private String token; + private ThreadSafeDialogMaker dialogMaker; + } + + public void initialize() { + resumeContainer.getChildren().addAll(resumeList); + } + + @FXML + private VBox rootPane; + + @FXML + private FlowPane resumeContainer; + + private final ObservableList resumeList = FXCollections.observableArrayList(); + + @FXML + void addResume(ActionEvent event) { + try { + ResumeUploadPane resumeUploadPane = + ComponentFactory.loadComponent("fxml/student/resume/resumeUpload.fxml", new ResumeUploadPane(dialogMaker)); + + dialogMaker.createDialogWithOKAndCancel("上传新的简历", resumeUploadPane, e -> { + try { + ResumeRequest request = new ResumeRequest(); + request.setTitle(resumeUploadPane.getResumeName()); + request.setRemark(resumeUploadPane.getRemark()); + + File resumeFile = resumeUploadPane.getResumeFile(); + if (resumeFile == null) { + dialogMaker.createMessageDialog("啊哈", "请选择上传一个文件"); + return; + } + request.setResumeFileBase64(Base64.encode(resumeFile)); + + this.uploadResume(request); + } catch (Exception ex) { + ex.printStackTrace(); + dialogMaker.createMessageDialog("啊哈", "读取简历文件时发生错误...\n" + ex); + } + }); + } catch (Exception e) { + e.printStackTrace(); + } + } + + private void uploadResume(ResumeRequest resumeRequest) { + String json = JSONUtil.toJsonStr(resumeRequest); + RequestBody requestBody = RequestBody.create(json, MediaType.get("application/json")); + + Request uploadRequest = new Request.Builder() + .url(Constants.ResumeRequestUrls.RESUME_UPLOAD_URL) + .addHeader("token", token) + .post(requestBody) + .build(); + + okhttpClient.newCall(uploadRequest).enqueue(resumeUploadCallback); + } + + private final Callback resumeUploadCallback = new Callback() { + @Override + public void onFailure(@NotNull Call call, @NotNull IOException e) { + dialogMaker.createMessageDialog("啊哈", "上传简历时发生了网络错误\n" + e); + } + + @Override + public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException { + assert response.body() != null; + String responseString = response.body().string(); + JSONObject responseJson = JSONUtil.parseObj(responseString); + + Integer code = responseJson.getInt("code"); + if (code == null || !code.equals(ResponseCode.SUCCESS.getCode())) { + String message = responseJson.getStr("message"); + dialogMaker.createMessageDialog("啊哈", "获取用户信息时出错了...\n" + message); + } else { + dialogMaker.createMessageDialog("好耶", "上传成功"); + Platform.runLater(ResumeManagePage.this::refreshResumeList); + } + } + }; + + private void refreshResumeList() { + Request uploadRequest = new Request.Builder() + .url(Constants.ResumeRequestUrls.RESUME_GET_ALL_URL) + .addHeader("token", token) + .get() + .build(); + + okhttpClient.newCall(uploadRequest).enqueue(resumeFetchCallback); + } + + private final Callback resumeFetchCallback = new Callback() { + @Override + public void onFailure(@NotNull Call call, @NotNull IOException e) { + dialogMaker.createMessageDialog("啊哈", "获取简历时发生了网络错误\n" + e); + } + + @Override + public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException { + assert response.body() != null; + String responseString = response.body().string(); + JSONObject responseJson = JSONUtil.parseObj(responseString); + + Integer code = responseJson.getInt("code"); + if (code == null || !code.equals(ResponseCode.SUCCESS.getCode())) { + String message = responseJson.getStr("message"); + dialogMaker.createMessageDialog("啊哈", "获取简历时出错了...\n" + message); + } + + List result = JSONUtil.toList(responseJson.getJSONArray("data"), ResumeResponse.class); + Platform.runLater(() -> listResumes(result)); + } + }; + + private void listResumes(List resumes) { + try { + ObservableList items = resumeContainer.getChildren(); + items.clear(); + for (ResumeResponse resume : resumes) { + if (resume == null) { + continue; + } + + ResumeCard itemController = new ResumeCard(resume, token, dialogMaker, updateCallback, deleteCallback); + ResumeCard item = + ComponentFactory.loadComponent("fxml/student/resume/resumeCard.fxml", itemController); + JFXDepthManager.setDepth(item, 1); + item.setUserData(resume); + items.add(item); + } + } catch (Exception e) { + e.printStackTrace(); + log.warn("展示简历卡片时发生错误"); + } + } + + private final Consumer updateCallback = response -> refreshResumeList(); + + private final Consumer deleteCallback = response -> refreshResumeList(); +} diff --git a/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/controller/page/student/StudentMainController.java b/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/controller/page/student/StudentMainController.java new file mode 100644 index 0000000..40c5342 --- /dev/null +++ b/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/controller/page/student/StudentMainController.java @@ -0,0 +1,174 @@ +package net.lensfrex.disillusion.client.controller.page.student; + +import animatefx.animation.FadeIn; +import animatefx.animation.FadeOut; +import cn.hutool.json.JSONUtil; +import javafx.application.Platform; +import javafx.collections.ObservableList; +import javafx.event.ActionEvent; +import javafx.fxml.FXML; +import javafx.scene.Node; +import javafx.scene.image.ImageView; +import javafx.scene.layout.Pane; +import javafx.scene.layout.StackPane; +import javafx.scene.text.Text; +import lombok.Builder; +import lombok.Data; +import lombok.extern.slf4j.Slf4j; +import net.lensfrex.disillusion.client.config.Constants; +import net.lensfrex.disillusion.client.controller.components.ComponentFactory; +import net.lensfrex.disillusion.client.data.response.StudentInfoResponse; +import net.lensfrex.disillusion.client.util.DialogMaker; +import net.lensfrex.disillusion.client.util.OkhttpUtil; +import net.lensfrex.disillusion.client.util.ResponseHandler; +import net.lensfrex.disillusion.client.util.ThreadSafeDialogMaker; +import okhttp3.*; + +import java.util.HashMap; +import java.util.Map; + +@Slf4j +public class StudentMainController { + private static final OkHttpClient okhttpClient = Constants.globalClient; + + @FXML + private StackPane rootPane; + + @FXML + private ImageView avatarImageView; + + @FXML + private Text usernameText; + + @FXML + private Text realNameText; + + @FXML + private StackPane containerPane; + + @FXML + private Text signText; + + private ThreadSafeDialogMaker threadSafeDialogMaker; + private DialogMaker dialogMaker; + + private String token; + private String username; + + private StudentInfoResponse studentInfo; + + private final Map pages = new HashMap<>(4); + + public void initialize() { + threadSafeDialogMaker = new ThreadSafeDialogMaker(rootPane); + dialogMaker = new DialogMaker(rootPane); + try { + pages.put("offerSquare", ComponentFactory.loadComponent("fxml/student/offer/offerSquare.fxml", OfferSquarePage.class)); + pages.put("profile", ComponentFactory.loadComponent("fxml/student/profile.fxml", StudentProfilePage.class)); + pages.put("applicationManage", ComponentFactory.loadComponent("fxml/student/application/applicationManagePage.fxml", ApplicationManagePage.class)); + pages.put("resumeManage", ComponentFactory.loadComponent("fxml/student/resume/resumeManagePage.fxml", ResumeManagePage.class)); + } catch (Exception e) { + e.printStackTrace(); + DialogMaker.errorMessageDialog("出错啦,请联系lensfrex这家伙\n调试信息:\n" + e); + } + } + + public void setData(InitData data) { + this.token = data.token; + this.username = data.username; + + this.updateUserInfo(); + } + + private void updateUserInfo() { + new OkhttpUtil() + .url(Constants.UserRequestUrls.USER_INFO_VIEW_URL) + .token(token) + .failCallback(e -> log.error("请求用户数据时发生网络错误")) + .successCallback(response -> { + ResponseHandler.Result result = ResponseHandler.parse(response, + s -> threadSafeDialogMaker.createMessageDialog("啊哈", "获取用户信息时出错:" + s)); + if (!result.success) { + return; + } + + studentInfo = JSONUtil.toBean(result.data, StudentInfoResponse.class); + Platform.runLater(() -> { + usernameText.setText(username); + realNameText.textProperty().set("@" + studentInfo.getRealName()); + signText.setText(studentInfo.getShortDescribe()); + }); + + new OkhttpUtil() + .url(Constants.UserRequestUrls.USER_AVATAR_VIEW_URL) + .token(token) + .successCallback(avatarResponse -> Platform.runLater(() -> StudentProfilePage.setAvatar(avatarImageView, avatarResponse))) + .executeAsync(OkhttpUtil.RequestMethod.GET); + }) + .executeAsync(OkhttpUtil.RequestMethod.GET); + } + + @FXML + void showApplications(ActionEvent event) { + ApplicationManagePage controller = (ApplicationManagePage) this.switchPage("applicationManage"); + controller.loadData(ApplicationManagePage.InitData.builder() + .token(token) + .dialogMaker(threadSafeDialogMaker) + .build() + ); + } + + @FXML + void showOfferSquare(ActionEvent event) { + OfferSquarePage controller = (OfferSquarePage) this.switchPage("offerSquare"); + controller.loadData(OfferSquarePage.InitData.builder() + .token(token) + .dialogMaker(threadSafeDialogMaker) + .build() + ); + } + + @FXML + void showProfile(ActionEvent event) { + StudentProfilePage controller = (StudentProfilePage) this.switchPage("profile"); + controller.setData(studentInfo, token, threadSafeDialogMaker, info -> this.updateUserInfo()); + } + + @FXML + void showResumeManage(ActionEvent event) { + ResumeManagePage controller = (ResumeManagePage) this.switchPage("resumeManage"); + ResumeManagePage.InitData data = new ResumeManagePage.InitData(); + data.setToken(token); + data.setDialogMaker(threadSafeDialogMaker); + + controller.loadData(data); + } + + private String currentWindow = ""; + + private Node switchPage(String page) { + if (currentWindow.equals(page)) { + return pages.get(page); + } + + ObservableList list = containerPane.getChildren(); + + new FadeOut(list.get(0)).play(); + Pane pagePane = pages.get(page); + if (pagePane != null) { + list.clear(); + list.add(pagePane); + new FadeIn(pagePane).play(); + currentWindow = page; + } + + return pagePane; + } + + @Data + @Builder + public static class InitData { + private String token; + private String username; + } +} diff --git a/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/controller/page/student/StudentProfilePage.java b/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/controller/page/student/StudentProfilePage.java new file mode 100644 index 0000000..c6d1b43 --- /dev/null +++ b/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/controller/page/student/StudentProfilePage.java @@ -0,0 +1,235 @@ +package net.lensfrex.disillusion.client.controller.page.student; + +import cn.hutool.core.codec.Base64; +import cn.hutool.core.io.FileUtil; +import com.jfoenix.controls.JFXRadioButton; +import com.jfoenix.controls.JFXTextArea; +import com.jfoenix.controls.JFXTextField; +import javafx.application.Platform; +import javafx.event.ActionEvent; +import javafx.fxml.FXML; +import javafx.scene.control.ToggleGroup; +import javafx.scene.image.Image; +import javafx.scene.image.ImageView; +import javafx.scene.input.MouseEvent; +import javafx.scene.layout.HBox; +import javafx.scene.text.Text; +import javafx.stage.FileChooser; +import lombok.extern.slf4j.Slf4j; +import net.lensfrex.disillusion.client.config.Constants; +import net.lensfrex.disillusion.client.data.response.StudentInfoResponse; +import net.lensfrex.disillusion.client.util.ImageUtil; +import net.lensfrex.disillusion.client.util.OkhttpUtil; +import net.lensfrex.disillusion.client.util.ResponseHandler; +import net.lensfrex.disillusion.client.util.ThreadSafeDialogMaker; +import okhttp3.Response; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.function.Consumer; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +@Slf4j +public class StudentProfilePage extends HBox { + @FXML + private HBox rootPane; + + @FXML + private ImageView avatarImageView; + + @FXML + private Text usernameText; + + @FXML + private Text signText; + + @FXML + private JFXTextField signEditText; + + @FXML + private JFXTextArea describeTextArea; + + @FXML + private JFXTextField realNameTextField; + + @FXML + private JFXTextField ageTextField; + + @FXML + private JFXRadioButton maleRadioButton; + + @FXML + private JFXRadioButton femaleRadioButton; + + @FXML + private JFXTextField contactTextField; + + @FXML + private JFXTextField idNumberTextField; + + private File newAvatarFile; + + private String token; + + private static final Pattern agePattern = Pattern.compile("^\\d{2}$"); + + private ThreadSafeDialogMaker dialogMaker; + + @FXML + void save(ActionEvent event) { + studentInfo.setRealName(realNameTextField.getText()); + studentInfo.setShortDescribe(signEditText.getText()); + studentInfo.setDescribe(describeTextArea.getText()); + studentInfo.setPhoneNumber(contactTextField.getText()); + studentInfo.setIdNumber(idNumberTextField.getText()); + + String inputAge = ageTextField.getText(); + Matcher ageMatcher = agePattern.matcher(inputAge); + if (!ageMatcher.matches()) { + dialogMaker.createMessageDialog("啊哈", "年龄格式不正确(2位数字)"); + } + + studentInfo.setAge(Integer.parseInt(inputAge)); + + studentInfo.setSex(sexGroup.getSelectedToggle().getUserData().equals("0") ? 0 : 1); + + new OkhttpUtil() + .url(Constants.UserRequestUrls.USER_INFO_EDIT_URL) + .token(token) + .body(studentInfo) + .failCallback(e -> dialogMaker.createMessageDialog("啊哈", "更新上传用户信息的时候出错啦:\n" + e)) + .successCallback(response -> new ResponseHandler(response) + .errorCallback(s -> dialogMaker.createMessageDialog("啊哈", "更新上传用户信息不成功:" + s)) + .successCallback(s -> { + updateCallback.accept(studentInfo); + dialogMaker.createMessageDialog("好耶", "更新成功"); + }) + .process()) + .executeAsync(OkhttpUtil.RequestMethod.POST); + } + + @FXML + void replaceAvatar(MouseEvent event) { + FileChooser fileChooser = new FileChooser(); + fileChooser.setTitle("选择图片"); + File chooseFile = fileChooser.showOpenDialog(rootPane.getScene().getWindow()); + + if (chooseFile != null) { + String fileType = FileUtil.getType(chooseFile); + switch (fileType) { + case "png": + case "jpg": + case "jpeg": + case "gif": + case "webm": + this.uploadAvatar(chooseFile); + break; + default: + dialogMaker.createMessageDialog( + "(;´д`)ゞ", + "头像文件不支持:" + fileType + "\n仅支持png, jpg, gif, webm" + ); + } + } + } + + private void uploadAvatar(File avatarFile) { + try { + byte[] avatarData; + // 大于1m的图片就进行压缩 + long fileLength = avatarFile.length(); + if (fileLength > 1048576) { + // 压缩比例,文件越大压缩程度越大 + float percent = (float) Math.pow(0.96, fileLength / 1048576f); + avatarData = ImageUtil.processImage(avatarFile, 0.8f, percent, "jpg"); + } else { + avatarData = Files.readAllBytes(Paths.get(avatarFile.toURI())); + } + + new OkhttpUtil() + .url(Constants.UserRequestUrls.USER_AVATAR_EDIT_URL) + .token(token) + .body(Base64.encode(avatarData), "plain/text") + .failCallback(e -> dialogMaker.createMessageDialog("啊哈", "上传头像文件时发生网络错误:" + e)) + .successCallback(response -> new ResponseHandler(response) + .errorCallback(s -> dialogMaker.createMessageDialog("啊哈", "获取头像时出现问题:" + s)) + .successCallback(s -> { + updateCallback.accept(studentInfo); + Platform.runLater(() -> setAvatar(avatarImageView, avatarData)); + }) + .process()) + .executeAsync(OkhttpUtil.RequestMethod.POST); + } catch (IOException e) { + dialogMaker.createMessageDialog("啊哈", "读取头像文件时发生错误:" + e); + } + } + + private final ToggleGroup sexGroup = new ToggleGroup(); + + @FXML + public void initialize() { + maleRadioButton.setUserData("0"); + femaleRadioButton.setUserData("1"); + + maleRadioButton.setToggleGroup(sexGroup); + femaleRadioButton.setToggleGroup(sexGroup); + } + + private StudentInfoResponse studentInfo; + private Consumer updateCallback; + + public void setData(StudentInfoResponse studentInfo, + String token, ThreadSafeDialogMaker dialogMaker, + Consumer updateCallback) { + + this.studentInfo = studentInfo; + this.token = token; + this.dialogMaker = dialogMaker; + this.updateCallback = updateCallback; + + usernameText.setText(studentInfo.getId()); + realNameTextField.setText(studentInfo.getRealName()); + signText.setText(studentInfo.getShortDescribe()); + signEditText.setText(studentInfo.getShortDescribe()); + describeTextArea.setText(studentInfo.getDescribe()); + contactTextField.setText(studentInfo.getPhoneNumber()); + idNumberTextField.setText(studentInfo.getIdNumber()); + ageTextField.setText(studentInfo.getAge() + ""); + + switch (studentInfo.getSex()) { + case 0: + sexGroup.selectToggle(maleRadioButton); + break; + case 1: + sexGroup.selectToggle(femaleRadioButton); + break; + } + + new OkhttpUtil() + .url(Constants.UserRequestUrls.USER_AVATAR_VIEW_URL) + .token(token) + .successCallback(response -> Platform.runLater(() -> setAvatar(avatarImageView, response))) + .executeAsync(OkhttpUtil.RequestMethod.GET); + } + + public static void setAvatar(ImageView avatarImageView, Response response) { + try { + assert response.body() != null; + byte[] data = response.body().bytes(); + + setAvatar(avatarImageView, data); + } catch (Exception e) { + log.warn("获取头像数据时出现异常", e); + } + } + + public static void setAvatar(ImageView avatarImageView, byte[] data) { + Image avatarImage = new Image(new ByteArrayInputStream(data)); + avatarImageView.setImage(avatarImage); + } +} \ No newline at end of file diff --git a/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/data/ResponseCode.java b/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/data/ResponseCode.java new file mode 100644 index 0000000..4ee65e2 --- /dev/null +++ b/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/data/ResponseCode.java @@ -0,0 +1,45 @@ +/* + * Class created by lensfrex. + */ + +package net.lensfrex.disillusion.client.data; + +public enum ResponseCode { + SUCCESS(20000, "成功"), + REQUEST_TOO_FAST(20001, "技能冷却中..."), + + INVALID_REQUEST(30000, "非法请求"), + PARAM_WRONG(30001, "参数错误"), + + PERMISSION_DENIED(40000, "权限不足"), + TOKEN_EXPIRED(40001, "token过期"), + TOKEN_INVALID(40002, "token无效"), + + IDENTIFY_ERROR(40100, "用户不存在或密码错误"), + USER_ALREADY_EXISTS(40101, "用户名已经被注册"), + + USER_NOT_EXISTS(40103, "用户不存在"), + + SERVER_INTERNAL_ERROR(50000, "服务器内部错误"), + + API_NOT_IMPLEMENT(0, "接口未实现"), + + REQUEST_FILE_DOES_NOT_EXIST(60701, "请求的文件不存在"); + + private final int code; + + private final String message; + + ResponseCode(int code, String message) { + this.code = code; + this.message = message; + } + + public int getCode() { + return code; + } + + public String getMessage() { + return message; + } +} diff --git a/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/data/request/ApplicationRequest.java b/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/data/request/ApplicationRequest.java new file mode 100644 index 0000000..2b86690 --- /dev/null +++ b/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/data/request/ApplicationRequest.java @@ -0,0 +1,20 @@ +package net.lensfrex.disillusion.client.data.request; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class ApplicationRequest { + /** + * 申请的offerId + */ + private String offerId; + + /** + * 简历id + */ + private String resumeId; +} diff --git a/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/data/request/OfferPublishRequest.java b/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/data/request/OfferPublishRequest.java new file mode 100644 index 0000000..bc6fe75 --- /dev/null +++ b/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/data/request/OfferPublishRequest.java @@ -0,0 +1,36 @@ +package net.lensfrex.disillusion.client.data.request; + +import lombok.Data; + +@Data +public class OfferPublishRequest { + /** + * 岗位名称 + */ + private String jobName; + + /** + * 工作地点 + */ + private String place; + + /** + * 要求 + */ + private String requirement; + + /** + * 预期薪资 + */ + private int salary; + + /** + * 目标人数 + */ + private int targetNumber; + + /** + * 详细信息 + */ + private String detail; +} diff --git a/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/data/request/ResumeRequest.java b/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/data/request/ResumeRequest.java new file mode 100644 index 0000000..4193441 --- /dev/null +++ b/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/data/request/ResumeRequest.java @@ -0,0 +1,21 @@ +package net.lensfrex.disillusion.client.data.request; + +import lombok.Data; + +@Data +public class ResumeRequest { + /** + * 简历标题(名称) + */ + private String title; + + /** + * 简历备注信息 + */ + private String remark; + + /** + * 简历文件数据base64 + */ + private String resumeFileBase64; +} diff --git a/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/data/response/ApplicationResponse.java b/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/data/response/ApplicationResponse.java new file mode 100644 index 0000000..ac39d92 --- /dev/null +++ b/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/data/response/ApplicationResponse.java @@ -0,0 +1,37 @@ +package net.lensfrex.disillusion.client.data.response; + +import com.jfoenix.controls.datamodels.treetable.RecursiveTreeObject; +import lombok.Data; + +/** + * 求职申请 + */ +@Data +public class ApplicationResponse { + private String id; + + /** + * 发布者id + */ + private String publisherId; + + /** + * 目标公司id + */ + private String targetId; + + /** + * 申请的offerId + */ + private String offerId; + + /** + * 简历id + */ + private String resumeId; + + /** + * 申请状态 + */ + private String status; +} \ No newline at end of file diff --git a/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/data/response/EmployerInfoResponse.java b/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/data/response/EmployerInfoResponse.java new file mode 100644 index 0000000..959590a --- /dev/null +++ b/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/data/response/EmployerInfoResponse.java @@ -0,0 +1,41 @@ +package net.lensfrex.disillusion.client.data.response; + +import lombok.Data; + +/** + * 用人单位信息 + */ +@Data +public class EmployerInfoResponse { + private String id; + + /** + * 公司名称 + */ + private String name; + + /** + * 总部地址 + */ + private String place; + + /** + * 联系方式 + */ + private String contract; + + /** + * 公司简要介绍 + */ + private String shortDescribe; + + /** + * 公司介绍 + */ + private String describe; + + /** + * 所属行业 + */ + private String industry; +} diff --git a/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/data/response/OfferResponse.java b/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/data/response/OfferResponse.java new file mode 100644 index 0000000..4587f33 --- /dev/null +++ b/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/data/response/OfferResponse.java @@ -0,0 +1,61 @@ +package net.lensfrex.disillusion.client.data.response; + +import lombok.Data; + +import java.util.Objects; + +/** + * 招聘信息 + */ +@Data +public class OfferResponse { + private String id; + + /** + * 发布公司 + */ + private String publisherId; + + /** + * 岗位名称 + */ + private String jobName; + + /** + * 工作地点 + */ + private String place; + + /** + * 要求 + */ + private String requirement; + + /** + * 预期薪资 + */ + private Integer salary; + + /** + * 目标人数 + */ + private Integer targetNumber; + + /** + * 详细信息 + */ + private String detail; + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + OfferResponse offer = (OfferResponse) o; + return id.equals(offer.id); + } + + @Override + public int hashCode() { + return Objects.hash(id); + } +} diff --git a/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/data/response/PublicEmployerInfoResponse.java b/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/data/response/PublicEmployerInfoResponse.java new file mode 100644 index 0000000..c634920 --- /dev/null +++ b/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/data/response/PublicEmployerInfoResponse.java @@ -0,0 +1,40 @@ +package net.lensfrex.disillusion.client.data.response; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +@Data +@EqualsAndHashCode(callSuper = false) +public class PublicEmployerInfoResponse { + private String id; + + /** + * 公司名称 + */ + private String name; + + /** + * 总部地址 + */ + private String place; + + /** + * 联系方式 + */ + private String contract; + + /** + * 公司简要介绍 + */ + private String shortDescribe; + + /** + * 公司介绍 + */ + private String describe; + + /** + * 所属行业 + */ + private String industry; +} diff --git a/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/data/response/PublicStudentInfoResponse.java b/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/data/response/PublicStudentInfoResponse.java new file mode 100644 index 0000000..409a9ee --- /dev/null +++ b/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/data/response/PublicStudentInfoResponse.java @@ -0,0 +1,38 @@ +package net.lensfrex.disillusion.client.data.response; + +import lombok.Data; + +@Data +public class PublicStudentInfoResponse { + private String id; + + /** + * 真名 + */ + private String realName; + + /** + * 简介 + */ + private String shortDescribe; + + /** + * 介绍 + */ + private String describe; + + /** + * 电话号 + */ + private String phoneNumber; + + /** + * 年龄 + */ + private int age; + + /** + * 性别,0:男;1:女 + */ + private int sex; +} diff --git a/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/data/response/ResumeResponse.java b/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/data/response/ResumeResponse.java new file mode 100644 index 0000000..e0ec324 --- /dev/null +++ b/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/data/response/ResumeResponse.java @@ -0,0 +1,31 @@ +package net.lensfrex.disillusion.client.data.response; + +import lombok.Data; + +/** + * 简历 + */ +@Data +public class ResumeResponse { + private String id; + + /** + * 学生id + */ + private String ownerId; + + /** + * 简历标题(名称) + */ + private String title; + + /** + * 简历备注信息 + */ + private String remark; + + /** + * 简历文件id,一般来说等同于简历id + */ + private String resumeFileId; +} diff --git a/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/data/response/StudentInfoResponse.java b/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/data/response/StudentInfoResponse.java new file mode 100644 index 0000000..a556a57 --- /dev/null +++ b/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/data/response/StudentInfoResponse.java @@ -0,0 +1,43 @@ +package net.lensfrex.disillusion.client.data.response; + +import lombok.Data; + +@Data +public class StudentInfoResponse { + private String id; + + /** + * 真名 + */ + private String realName; + + /** + * 简介 + */ + private String shortDescribe; + + /** + * 介绍 + */ + private String describe; + + /** + * 电话号 + */ + private String phoneNumber; + + /** + * 身份证号 + */ + private String idNumber; + + /** + * 年龄 + */ + private int age; + + /** + * 性别,0:男;1:女 + */ + private int sex; +} diff --git a/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/util/Consumer.java b/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/util/Consumer.java new file mode 100644 index 0000000..8818421 --- /dev/null +++ b/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/util/Consumer.java @@ -0,0 +1,8 @@ +package net.lensfrex.disillusion.client.util; + +import java.io.IOException; + +public interface Consumer { + + void accept(T t) throws IOException; +} \ No newline at end of file diff --git a/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/util/DialogMaker.java b/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/util/DialogMaker.java new file mode 100644 index 0000000..d3f689f --- /dev/null +++ b/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/util/DialogMaker.java @@ -0,0 +1,130 @@ +package net.lensfrex.disillusion.client.util; + +import com.jfoenix.controls.JFXButton; +import com.jfoenix.controls.JFXDialog; +import com.jfoenix.controls.JFXDialogLayout; +import javafx.beans.NamedArg; +import javafx.event.ActionEvent; +import javafx.event.EventHandler; +import javafx.geometry.Pos; +import javafx.scene.Node; +import javafx.scene.Scene; +import javafx.scene.control.Label; +import javafx.scene.control.TextArea; +import javafx.scene.layout.HBox; +import javafx.scene.layout.Pane; +import javafx.scene.layout.StackPane; +import javafx.scene.paint.Paint; +import javafx.scene.text.Font; +import javafx.scene.text.FontWeight; +import javafx.scene.text.Text; +import javafx.stage.Stage; + +public class DialogMaker { + Pane rootPane; + + public DialogMaker(@NamedArg("rootPane") Pane rootPane) { + this.rootPane = rootPane; + } + + public void createMessageDialog(@NamedArg("title") String title, @NamedArg("message") String message) { + JFXButton OKButton = new JFXButton("了解!"); + OKButton.setFont(Font.font("Microsoft YaHei", FontWeight.BOLD, 12)); + OKButton.setPrefWidth(60); + OKButton.setPrefHeight(30); + + Text messageText = new Text(message); + messageText.setFont(Font.font("Microsoft YaHei", 14)); + + createDialog(title, messageText, OKButton); + } + + //创建只有一个按钮的dialog + public void createDialogWithOneBtn(@NamedArg("title") String title, @NamedArg("theBody") Node body, @NamedArg("OKEvent") EventHandler event) { + //dialog.setPrefHeight(rootPane.getPrefHeight()); + //dialog.setPrefWidth(rootPane.getPrefWidth()); + + JFXButton OKButton = new JFXButton("好的!"); + OKButton.setFont(Font.font("Microsoft YaHei", FontWeight.BOLD, 12)); + OKButton.setPrefWidth(60); + OKButton.setPrefHeight(30); + + createDialog(title, body, OKButton); + + if (event != null) { + OKButton.addEventHandler(ActionEvent.ACTION, event); + } + } + + public void createDialogWithOneBtn(@NamedArg("title") String title, @NamedArg("theBody") Node body) { + createDialogWithOneBtn(title, body, null); + } + + //创建有OK和cancel按钮的dialog + public void createDialogWithOKAndCancel(@NamedArg("title") String title, @NamedArg("message") String message, @NamedArg("OKEvent") EventHandler OKEvent) { + Text messageText = new Text(message); + messageText.setFont(Font.font("Microsoft YaHei", 14)); + + createDialogWithOKAndCancel(title, messageText, OKEvent); + } + + //创建有OK和cancel按钮的dialog + public void createDialogWithOKAndCancel(@NamedArg("title") String title, @NamedArg("body") Node body, @NamedArg("OKEvent") EventHandler OKEvent) { + JFXButton CancelButton = new JFXButton("手滑了"); + CancelButton.setFont(Font.font("Microsoft YaHei", FontWeight.BOLD, 12)); + CancelButton.setPrefWidth(60); + CancelButton.setPrefHeight(30); + + JFXButton OKButton = new JFXButton("是!"); + OKButton.setFont(Font.font("Microsoft YaHei", FontWeight.BOLD, 12)); + OKButton.setPrefWidth(60); + OKButton.setPrefHeight(30); + OKButton.setTextFill(Paint.valueOf("red")); + OKButton.addEventHandler(ActionEvent.ACTION, OKEvent); + + createDialog(title, body, CancelButton, OKButton); + } + + public void createDialog(@NamedArg("title") String title, @NamedArg("theBody") Node body, @NamedArg("buttons") JFXButton... buttons) { + + JFXDialogLayout content = new JFXDialogLayout(); + + Label titleLabel = new Label(title); + titleLabel.setFont(Font.font("Microsoft YaHei", FontWeight.BOLD, 22)); + content.setHeading(titleLabel); + + content.setBody(body); + content.setAlignment(Pos.CENTER); + + StackPane tempPane = new StackPane(); + tempPane.setPrefHeight(rootPane.getPrefHeight()); + tempPane.setPrefWidth(rootPane.getPrefWidth()); + rootPane.getChildren().add(tempPane); + + JFXDialog dialog = new JFXDialog(tempPane, content, JFXDialog.DialogTransition.TOP); + + dialog.setOnDialogClosed(event -> rootPane.getChildren().remove(tempPane)); + + for (JFXButton button : buttons) { + button.addEventHandler(ActionEvent.ACTION, e -> dialog.close()); + } + + content.setActions(buttons); + + dialog.show(); + } + + + public static void errorMessageDialog(String message) { + HBox box = new HBox(new TextArea(message)); + box.setAlignment(Pos.CENTER); + + Scene scene = new Scene(box, 500, 350); + + Stage stage = new Stage(); + stage.setTitle("出事了"); + stage.setScene(scene); + stage.show(); + } + +} diff --git a/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/util/ImageUtil.java b/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/util/ImageUtil.java new file mode 100644 index 0000000..4f94c9f --- /dev/null +++ b/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/util/ImageUtil.java @@ -0,0 +1,20 @@ +package net.lensfrex.disillusion.client.util; + +import net.coobird.thumbnailator.Thumbnails; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; + +public class ImageUtil { + public static byte[] processImage(File inputFile, float scale, float level, String format) throws IOException { + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + Thumbnails.of(inputFile) + .scale(scale) + .outputQuality(level) + .outputFormat(format) + .toOutputStream(byteArrayOutputStream); + + return byteArrayOutputStream.toByteArray(); + } +} diff --git a/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/util/OkhttpUtil.java b/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/util/OkhttpUtil.java new file mode 100644 index 0000000..edd9cbb --- /dev/null +++ b/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/util/OkhttpUtil.java @@ -0,0 +1,173 @@ +package net.lensfrex.disillusion.client.util; + +import cn.hutool.core.util.URLUtil; +import cn.hutool.json.JSONUtil; +import net.lensfrex.disillusion.client.config.Constants; +import okhttp3.*; +import org.jetbrains.annotations.NotNull; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.HashMap; +import java.util.Map; + +public class OkhttpUtil { + private static final OkHttpClient client = Constants.globalClient; + + private Request request; + private RequestBody requestBody; + + private final Request.Builder requestBuilder; + private FormBody.Builder formBuilder; + + private Consumer successCallback; + private java.util.function.Consumer failCallback; + + private String url; + + private final Callback callbackWrapper = new Callback() { + @Override + public void onFailure(@NotNull Call call, @NotNull IOException e) { + try { + failCallback.accept(e); + } catch (Exception ex) { + e.printStackTrace(); + } + } + + @Override + public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException { + try { + successCallback.accept(response); + } catch (Exception e) { + e.printStackTrace(); + } + } + }; + + public OkhttpUtil() { + requestBuilder = new Request.Builder(); + } + + public OkhttpUtil url(String url) { + this.url = url; + return this; + } + + private Map queryParams; + + public OkhttpUtil addQueryParam(String key, String value) { + if (queryParams == null) { + queryParams = new HashMap<>(10); + } + + queryParams.put(key, value); + + return this; + } + + public OkhttpUtil token(String token) { + return this.addHeader("token", token); + } + + public OkhttpUtil addHeader(String key, String value) { + requestBuilder.addHeader(key, value); + + return this; + } + + public OkhttpUtil successCallback(Consumer successCallback) { + this.successCallback = successCallback; + + return this; + } + + public OkhttpUtil failCallback(java.util.function.Consumer failCallbackCallback) { + this.failCallback = failCallbackCallback; + + return this; + } + + public OkhttpUtil callback(Consumer successCallback, java.util.function.Consumer failCallbackCallback) { + this.successCallback = successCallback; + this.failCallback = failCallbackCallback; + + return this; + } + + public OkhttpUtil form(String key, String value) { + if (formBuilder == null) { + formBuilder = new FormBody.Builder(); + } + + formBuilder.add(key, value); + + return this; + } + + public OkhttpUtil body(String data, String contentType) { + requestBody = RequestBody.create(data, MediaType.parse(contentType)); + + return this; + } + + public OkhttpUtil body(Object data) { + String jsonData = JSONUtil.toJsonStr(data); + return body(jsonData, "application/json"); + } + + public OkhttpUtil body(byte[] data, String contentType) { + requestBody = RequestBody.create(data, MediaType.parse(contentType)); + + return this; + } + + private static final byte[] EMPTY_DATA = "".getBytes(StandardCharsets.UTF_8); + + public void executeAsync(RequestMethod method) { + this.execute(method).enqueue(callbackWrapper); + } + + public Response executeSync(RequestMethod method) { + try { + return execute(method).execute(); + } catch (IOException e) { + if (failCallback != null) { + failCallback.accept(e); + } + } + + return null; + } + + private Call execute(RequestMethod method) { + if (queryParams == null) { + requestBuilder.url(URLUtil.url(url)); + } else { + String queryParamString = URLUtil.buildQuery(queryParams, StandardCharsets.UTF_8); + requestBuilder.url(URLUtil.url(url + "?" + queryParamString)); + } + + switch (method) { + case GET: + request = requestBuilder.get().build(); + return client.newCall(request); + case POST: + if (requestBody == null) { + if (formBuilder == null) { + requestBody = RequestBody.create(EMPTY_DATA); + } else { + requestBody = formBuilder.build(); + } + } + request = requestBuilder.post(requestBody).build(); + return client.newCall(request); + default: + throw new IllegalArgumentException(); + } + } + + public static enum RequestMethod { + GET, POST + } +} \ No newline at end of file diff --git a/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/util/ResponseHandler.java b/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/util/ResponseHandler.java new file mode 100644 index 0000000..cb56630 --- /dev/null +++ b/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/util/ResponseHandler.java @@ -0,0 +1,72 @@ +package net.lensfrex.disillusion.client.util; + +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import net.lensfrex.disillusion.client.data.ResponseCode; +import okhttp3.Response; + +import java.io.IOException; + +@Slf4j +public class ResponseHandler { + @SafeVarargs + public static Result parse(Response response, Consumer... errorCallbacks) throws IOException { + assert response.body() != null; + String responseString = response.body().string(); + JSONObject responseJson = JSONUtil.parseObj(responseString); + + boolean success = true; + Integer code = responseJson.getInt("code"); + String message = responseJson.getStr("message"); + if (code == null || !code.equals(ResponseCode.SUCCESS.getCode())) { + if (message == null) { + message = responseString; + } + if (errorCallbacks != null) { + for (Consumer errorCallback : errorCallbacks) { + errorCallback.accept(message); + } + } + + success = false; + log.warn("请求错误:" + message); + } + + return new Result(success, message, responseJson.getStr("data")); + } + + private final Response response; + + private Consumer errorCallback; + private Consumer successCallback; + + public ResponseHandler(Response response) { + this.response = response; + } + + public ResponseHandler errorCallback(Consumer errorCallback) { + this.errorCallback = errorCallback; + return this; + } + + public ResponseHandler successCallback(Consumer successCallback) { + this.successCallback = successCallback; + return this; + } + + public void process() throws IOException { + Result result = parse(response, errorCallback); + if (result.success) { + successCallback.accept(result.data); + } + } + + @AllArgsConstructor + public static class Result { + public final boolean success; + public final String message; + public final String data; + } +} diff --git a/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/util/ThreadSafeDialogMaker.java b/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/util/ThreadSafeDialogMaker.java new file mode 100644 index 0000000..8f1f9c4 --- /dev/null +++ b/disillusion-client-jfx17/src/main/java/net/lensfrex/disillusion/client/util/ThreadSafeDialogMaker.java @@ -0,0 +1,43 @@ +package net.lensfrex.disillusion.client.util; + +import com.jfoenix.controls.JFXButton; +import com.jfoenix.controls.events.JFXDialogEvent; +import javafx.application.Platform; +import javafx.beans.NamedArg; +import javafx.event.ActionEvent; +import javafx.event.EventHandler; +import javafx.scene.Node; +import javafx.scene.layout.Pane; + +public class ThreadSafeDialogMaker extends DialogMaker { + public ThreadSafeDialogMaker(@NamedArg("rootPane") Pane rootPane) { + super(rootPane); + } + + public void createMessageDialog(@NamedArg("title") String title, @NamedArg("message") String message) { + Platform.runLater(() -> super.createMessageDialog(title, message)); + } + + //创建只有一个按钮的dialog + public void createDialogWithOneBtn(@NamedArg("title") String title, @NamedArg("theBody") Node body, @NamedArg("OKEvent") EventHandler event) { + Platform.runLater(() -> super.createDialogWithOneBtn(title, body, event)); + } + + public void createDialogWithOneBtn(@NamedArg("title") String title, @NamedArg("theBody") Node body) { + createDialogWithOneBtn(title, body, null); + } + + //创建有OK和cancel按钮的dialog + public void createDialogWithOKAndCancel(@NamedArg("title") String title, @NamedArg("message") String message, @NamedArg("OKEvent") EventHandler OKEvent) { + Platform.runLater(() -> super.createDialogWithOKAndCancel(title, message, OKEvent)); + } + + //创建有OK和cancel按钮的dialog + public void createDialogWithOKAndCancel(@NamedArg("title") String title, @NamedArg("body") Node body, @NamedArg("OKEvent") EventHandler OKEvent) { + Platform.runLater(() -> super.createDialogWithOKAndCancel(title, body, OKEvent)); + } + + public void createDialog(@NamedArg("title") String title, @NamedArg("theBody") Node body, @NamedArg("buttons") JFXButton... buttons) { + Platform.runLater(() -> super.createDialog(title, body, buttons)); + } +} diff --git a/disillusion-client-jfx17/src/main/resources/META-INF/MANIFEST.MF b/disillusion-client-jfx17/src/main/resources/META-INF/MANIFEST.MF new file mode 100644 index 0000000..2bae2da --- /dev/null +++ b/disillusion-client-jfx17/src/main/resources/META-INF/MANIFEST.MF @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +Main-Class: net.lensfrex.disillusion.client.ClientMain + diff --git a/disillusion-client-jfx17/src/main/resources/exampleFile.txt b/disillusion-client-jfx17/src/main/resources/exampleFile.txt new file mode 100644 index 0000000..0e9ca85 --- /dev/null +++ b/disillusion-client-jfx17/src/main/resources/exampleFile.txt @@ -0,0 +1,13 @@ + 前期经过自愿报名并审核成功的学生,根据所报微专业的培养方案,自行选择相应课程。 + + 9.认真对照培养方案,选课时务必仔细核对课程信息,确保“课程代码”、“课程名称”、“学分”这三项重要信息与培养方案要求相符。 + + 10.禁止同一时间修读两门及以上课程,系统会提示时间冲突并禁止选课。提前了解专业教学进程(实习、实践、课程设计等)情况,不能选择与本专业其它教学任务周次有冲突的课堂。 + + 11.因培养方案变动、转专业等原因,计划修读的课程确定不再开课的,请先查询本科生课程学分认定对照表并咨询开课学院,再进行选课。待课程修读完获得学分后,再按照本科生院发布的相关要求办理学分认定。 + + 12.学生选课、听课、考核必须一致。学生不能参加未选择课程的学习和考核,即使参加了学习和考核,如果没有选课,也是无效的,将不记录成绩。 + + 13.学生凭学号、密码选课,认真对待,并对自己的选课行为负责。密码必须妥善保管,不得代替他人选课,不得借用、盗用他人学号及密码选课。 + + 14.学生登录本科教学综合管理系统需通过学校统一身份认证平台登录,登录方式、密码重置方式详见以下两个通知:https://jwc.wust.edu.cn/2022/0226/c1925a255654/page.htm、https://jwc.wust.edu.cn/2022/0302/c1925a255786/page.htm。 \ No newline at end of file diff --git a/disillusion-client-jfx17/src/main/resources/net/lensfrex/disillusion/client/css/style.css b/disillusion-client-jfx17/src/main/resources/net/lensfrex/disillusion/client/css/style.css new file mode 100644 index 0000000..5811ea0 --- /dev/null +++ b/disillusion-client-jfx17/src/main/resources/net/lensfrex/disillusion/client/css/style.css @@ -0,0 +1,60 @@ +/******************************************************************************* + * * + * Scroll Bar * + * * + ******************************************************************************/ + +.scroll-bar:vertical > .track-background, .scroll-bar:horizontal > .track-background { + -fx-background-color: #F1F1F1; + -fx-background-insets: 0.0; +} + +.scroll-bar:vertical > .thumb, .scroll-bar:horizontal > .thumb { + -fx-background-color: #BCBCBC; + -fx-background-insets: 0.0; + -fx-background-radius: 1.0; +} + +.scroll-bar > .increment-button, .scroll-bar > .decrement-button { + -fx-padding: 5 2 5 2; +} + +.scroll-bar > .increment-button, .scroll-bar > .decrement-button, .scroll-bar:hover > .increment-button, .scroll-bar:hover > .decrement-button { + -fx-background-color: transparent; +} + +.scroll-bar > .increment-button > .increment-arrow, .scroll-bar > .decrement-button > .decrement-arrow { + -fx-background-color: rgb(150.0, 150.0, 150.0); +} + +.scroll-bar > .increment-button > .increment-arrow { + -fx-shape: "M298 426h428l-214 214z"; +} + +.scroll-bar > .decrement-button > .decrement-arrow { + -fx-shape: "M298 598l214-214 214 214h-428z"; +} + +/******************************************************************************* + * * + * Scroll Pane * + * * + ******************************************************************************/ + +.scroll-pane { + -fx-background: white; + -fx-background-insets: 0; + -fx-padding: 0; +} + +.scroll-pane:focused { + -fx-background-insets: 0; +} + +.scroll-pane .corner { + -fx-background-insets: 0; +} + +.jfx-list-cell:odd:selected .jfx-rippler .jfx-list-cell:even:selected { + -fx-background-color: rgba(255, 0, 0, 0.2); +} \ No newline at end of file diff --git a/disillusion-client-jfx17/src/main/resources/net/lensfrex/disillusion/client/fxml/employer/application/applicationDealPane.fxml b/disillusion-client-jfx17/src/main/resources/net/lensfrex/disillusion/client/fxml/employer/application/applicationDealPane.fxml new file mode 100644 index 0000000..c73d8c6 --- /dev/null +++ b/disillusion-client-jfx17/src/main/resources/net/lensfrex/disillusion/client/fxml/employer/application/applicationDealPane.fxml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/disillusion-client-jfx17/src/main/resources/net/lensfrex/disillusion/client/fxml/employer/application/applicationListItem.fxml b/disillusion-client-jfx17/src/main/resources/net/lensfrex/disillusion/client/fxml/employer/application/applicationListItem.fxml new file mode 100644 index 0000000..968d155 --- /dev/null +++ b/disillusion-client-jfx17/src/main/resources/net/lensfrex/disillusion/client/fxml/employer/application/applicationListItem.fxml @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/disillusion-client-jfx17/src/main/resources/net/lensfrex/disillusion/client/fxml/employer/application/applicationListPage.fxml b/disillusion-client-jfx17/src/main/resources/net/lensfrex/disillusion/client/fxml/employer/application/applicationListPage.fxml new file mode 100644 index 0000000..9b986e9 --- /dev/null +++ b/disillusion-client-jfx17/src/main/resources/net/lensfrex/disillusion/client/fxml/employer/application/applicationListPage.fxml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/disillusion-client-jfx17/src/main/resources/net/lensfrex/disillusion/client/fxml/employer/application/applicationManagePage.fxml b/disillusion-client-jfx17/src/main/resources/net/lensfrex/disillusion/client/fxml/employer/application/applicationManagePage.fxml new file mode 100644 index 0000000..1ef2996 --- /dev/null +++ b/disillusion-client-jfx17/src/main/resources/net/lensfrex/disillusion/client/fxml/employer/application/applicationManagePage.fxml @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/disillusion-client-jfx17/src/main/resources/net/lensfrex/disillusion/client/fxml/employer/application/applicationOfferCard.fxml b/disillusion-client-jfx17/src/main/resources/net/lensfrex/disillusion/client/fxml/employer/application/applicationOfferCard.fxml new file mode 100644 index 0000000..3b3cb7d --- /dev/null +++ b/disillusion-client-jfx17/src/main/resources/net/lensfrex/disillusion/client/fxml/employer/application/applicationOfferCard.fxml @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/disillusion-client-jfx17/src/main/resources/net/lensfrex/disillusion/client/fxml/employer/application/resumeDownloadPane.fxml b/disillusion-client-jfx17/src/main/resources/net/lensfrex/disillusion/client/fxml/employer/application/resumeDownloadPane.fxml new file mode 100644 index 0000000..21cdd17 --- /dev/null +++ b/disillusion-client-jfx17/src/main/resources/net/lensfrex/disillusion/client/fxml/employer/application/resumeDownloadPane.fxml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + diff --git a/disillusion-client-jfx17/src/main/resources/net/lensfrex/disillusion/client/fxml/employer/application/studentProfile.fxml b/disillusion-client-jfx17/src/main/resources/net/lensfrex/disillusion/client/fxml/employer/application/studentProfile.fxml new file mode 100644 index 0000000..3847b55 --- /dev/null +++ b/disillusion-client-jfx17/src/main/resources/net/lensfrex/disillusion/client/fxml/employer/application/studentProfile.fxml @@ -0,0 +1,174 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 100 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/disillusion-client-jfx17/src/main/resources/net/lensfrex/disillusion/client/fxml/employer/employerMain.fxml b/disillusion-client-jfx17/src/main/resources/net/lensfrex/disillusion/client/fxml/employer/employerMain.fxml new file mode 100644 index 0000000..f6a51f9 --- /dev/null +++ b/disillusion-client-jfx17/src/main/resources/net/lensfrex/disillusion/client/fxml/employer/employerMain.fxml @@ -0,0 +1,113 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 32 + + + + + 5 + 2.0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/disillusion-client-jfx17/src/main/resources/net/lensfrex/disillusion/client/fxml/employer/offer/offerCard.fxml b/disillusion-client-jfx17/src/main/resources/net/lensfrex/disillusion/client/fxml/employer/offer/offerCard.fxml new file mode 100644 index 0000000..d008d80 --- /dev/null +++ b/disillusion-client-jfx17/src/main/resources/net/lensfrex/disillusion/client/fxml/employer/offer/offerCard.fxml @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/disillusion-client-jfx17/src/main/resources/net/lensfrex/disillusion/client/fxml/employer/offer/offerDetailPane.fxml b/disillusion-client-jfx17/src/main/resources/net/lensfrex/disillusion/client/fxml/employer/offer/offerDetailPane.fxml new file mode 100644 index 0000000..a0ff90b --- /dev/null +++ b/disillusion-client-jfx17/src/main/resources/net/lensfrex/disillusion/client/fxml/employer/offer/offerDetailPane.fxml @@ -0,0 +1,110 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/disillusion-client-jfx17/src/main/resources/net/lensfrex/disillusion/client/fxml/employer/offer/offerManagePage.fxml b/disillusion-client-jfx17/src/main/resources/net/lensfrex/disillusion/client/fxml/employer/offer/offerManagePage.fxml new file mode 100644 index 0000000..47bec6e --- /dev/null +++ b/disillusion-client-jfx17/src/main/resources/net/lensfrex/disillusion/client/fxml/employer/offer/offerManagePage.fxml @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/disillusion-client-jfx17/src/main/resources/net/lensfrex/disillusion/client/fxml/employer/profile.fxml b/disillusion-client-jfx17/src/main/resources/net/lensfrex/disillusion/client/fxml/employer/profile.fxml new file mode 100644 index 0000000..9eec6be --- /dev/null +++ b/disillusion-client-jfx17/src/main/resources/net/lensfrex/disillusion/client/fxml/employer/profile.fxml @@ -0,0 +1,183 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 100 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/disillusion-client-jfx17/src/main/resources/net/lensfrex/disillusion/client/fxml/loginMain.fxml b/disillusion-client-jfx17/src/main/resources/net/lensfrex/disillusion/client/fxml/loginMain.fxml new file mode 100644 index 0000000..c968a87 --- /dev/null +++ b/disillusion-client-jfx17/src/main/resources/net/lensfrex/disillusion/client/fxml/loginMain.fxml @@ -0,0 +1,89 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/disillusion-client-jfx17/src/main/resources/net/lensfrex/disillusion/client/fxml/student/application/applicationCard.fxml b/disillusion-client-jfx17/src/main/resources/net/lensfrex/disillusion/client/fxml/student/application/applicationCard.fxml new file mode 100644 index 0000000..a19e81b --- /dev/null +++ b/disillusion-client-jfx17/src/main/resources/net/lensfrex/disillusion/client/fxml/student/application/applicationCard.fxml @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/disillusion-client-jfx17/src/main/resources/net/lensfrex/disillusion/client/fxml/student/application/applicationManagePage.fxml b/disillusion-client-jfx17/src/main/resources/net/lensfrex/disillusion/client/fxml/student/application/applicationManagePage.fxml new file mode 100644 index 0000000..bb51bee --- /dev/null +++ b/disillusion-client-jfx17/src/main/resources/net/lensfrex/disillusion/client/fxml/student/application/applicationManagePage.fxml @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/disillusion-client-jfx17/src/main/resources/net/lensfrex/disillusion/client/fxml/student/application/applicationPost.fxml b/disillusion-client-jfx17/src/main/resources/net/lensfrex/disillusion/client/fxml/student/application/applicationPost.fxml new file mode 100644 index 0000000..f4e846c --- /dev/null +++ b/disillusion-client-jfx17/src/main/resources/net/lensfrex/disillusion/client/fxml/student/application/applicationPost.fxml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + diff --git a/disillusion-client-jfx17/src/main/resources/net/lensfrex/disillusion/client/fxml/student/application/applicationPostPage.fxml b/disillusion-client-jfx17/src/main/resources/net/lensfrex/disillusion/client/fxml/student/application/applicationPostPage.fxml new file mode 100644 index 0000000..4f511dc --- /dev/null +++ b/disillusion-client-jfx17/src/main/resources/net/lensfrex/disillusion/client/fxml/student/application/applicationPostPage.fxml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + diff --git a/disillusion-client-jfx17/src/main/resources/net/lensfrex/disillusion/client/fxml/student/offer/employerProfile.fxml b/disillusion-client-jfx17/src/main/resources/net/lensfrex/disillusion/client/fxml/student/offer/employerProfile.fxml new file mode 100644 index 0000000..46288a9 --- /dev/null +++ b/disillusion-client-jfx17/src/main/resources/net/lensfrex/disillusion/client/fxml/student/offer/employerProfile.fxml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 100 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/disillusion-client-jfx17/src/main/resources/net/lensfrex/disillusion/client/fxml/student/offer/offerCard.fxml b/disillusion-client-jfx17/src/main/resources/net/lensfrex/disillusion/client/fxml/student/offer/offerCard.fxml new file mode 100644 index 0000000..a152354 --- /dev/null +++ b/disillusion-client-jfx17/src/main/resources/net/lensfrex/disillusion/client/fxml/student/offer/offerCard.fxml @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/disillusion-client-jfx17/src/main/resources/net/lensfrex/disillusion/client/fxml/student/offer/offerDetail.fxml b/disillusion-client-jfx17/src/main/resources/net/lensfrex/disillusion/client/fxml/student/offer/offerDetail.fxml new file mode 100644 index 0000000..7d7b01f --- /dev/null +++ b/disillusion-client-jfx17/src/main/resources/net/lensfrex/disillusion/client/fxml/student/offer/offerDetail.fxml @@ -0,0 +1,131 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/disillusion-client-jfx17/src/main/resources/net/lensfrex/disillusion/client/fxml/student/offer/offerSquare.fxml b/disillusion-client-jfx17/src/main/resources/net/lensfrex/disillusion/client/fxml/student/offer/offerSquare.fxml new file mode 100644 index 0000000..0a37abc --- /dev/null +++ b/disillusion-client-jfx17/src/main/resources/net/lensfrex/disillusion/client/fxml/student/offer/offerSquare.fxml @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/disillusion-client-jfx17/src/main/resources/net/lensfrex/disillusion/client/fxml/student/profile.fxml b/disillusion-client-jfx17/src/main/resources/net/lensfrex/disillusion/client/fxml/student/profile.fxml new file mode 100644 index 0000000..f09fd8f --- /dev/null +++ b/disillusion-client-jfx17/src/main/resources/net/lensfrex/disillusion/client/fxml/student/profile.fxml @@ -0,0 +1,218 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 100 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/disillusion-client-jfx17/src/main/resources/net/lensfrex/disillusion/client/fxml/student/resume/resumeCard.fxml b/disillusion-client-jfx17/src/main/resources/net/lensfrex/disillusion/client/fxml/student/resume/resumeCard.fxml new file mode 100644 index 0000000..8bb220d --- /dev/null +++ b/disillusion-client-jfx17/src/main/resources/net/lensfrex/disillusion/client/fxml/student/resume/resumeCard.fxml @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/disillusion-client-jfx17/src/main/resources/net/lensfrex/disillusion/client/fxml/student/resume/resumeManagePage.fxml b/disillusion-client-jfx17/src/main/resources/net/lensfrex/disillusion/client/fxml/student/resume/resumeManagePage.fxml new file mode 100644 index 0000000..f0ecb7a --- /dev/null +++ b/disillusion-client-jfx17/src/main/resources/net/lensfrex/disillusion/client/fxml/student/resume/resumeManagePage.fxml @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/disillusion-client-jfx17/src/main/resources/net/lensfrex/disillusion/client/fxml/student/resume/resumeUpload.fxml b/disillusion-client-jfx17/src/main/resources/net/lensfrex/disillusion/client/fxml/student/resume/resumeUpload.fxml new file mode 100644 index 0000000..f8856e5 --- /dev/null +++ b/disillusion-client-jfx17/src/main/resources/net/lensfrex/disillusion/client/fxml/student/resume/resumeUpload.fxml @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/disillusion-client-jfx17/src/main/resources/net/lensfrex/disillusion/client/fxml/student/studentMain.fxml b/disillusion-client-jfx17/src/main/resources/net/lensfrex/disillusion/client/fxml/student/studentMain.fxml new file mode 100644 index 0000000..19ea114 --- /dev/null +++ b/disillusion-client-jfx17/src/main/resources/net/lensfrex/disillusion/client/fxml/student/studentMain.fxml @@ -0,0 +1,121 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 32 + + + + + 5 + 2.0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/disillusion/.gitignore b/disillusion/.gitignore new file mode 100644 index 0000000..603d7e1 --- /dev/null +++ b/disillusion/.gitignore @@ -0,0 +1,67 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ +migrate_working_dir/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock. +/pubspec.lock +**/doc/api/ +.dart_tool/ +.packages +build/ + +HELP.md +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ + +*.log +*.class +mvnw +mvnw.cmd + +/database/ +/delombok/ +*/.mvn diff --git a/disillusion/backend/.flattened-pom.xml b/disillusion/backend/.flattened-pom.xml new file mode 100644 index 0000000..bae2fd0 --- /dev/null +++ b/disillusion/backend/.flattened-pom.xml @@ -0,0 +1,101 @@ + + + 4.0.0 + net.lensfrex + backend + 0.0.1-SNAPSHOT + + + org.springframework.boot + spring-boot-starter-web + 3.0.2 + compile + + + org.springframework.boot + spring-boot-configuration-processor + 3.0.2 + compile + true + + + org.slf4j + slf4j-api + 2.0.6 + compile + + + org.mindrot + jbcrypt + 0.4 + compile + + + com.auth0 + java-jwt + 4.3.0 + compile + + + cn.hutool + hutool-core + 5.8.12 + compile + + + org.springdoc + springdoc-openapi-starter-webmvc-ui + 2.0.2 + compile + + + org.webjars + swagger-ui + 4.15.5 + compile + + + commons-lang + commons-lang + 2.6 + compile + + + net.lensfrex + common + 0.0.1-SNAPSHOT + compile + + + net.lensfrex + virtual-database + 0.0.1-SNAPSHOT + compile + + + jakarta.annotation + jakarta.annotation-api + 2.1.1 + compile + + + javax.annotation + javax.annotation-api + 1.3.2 + compile + + + org.projectlombok + lombok + 1.18.22 + compile + + + org.mapstruct + mapstruct + 1.5.3.Final + compile + + + diff --git a/disillusion/backend/pom.xml b/disillusion/backend/pom.xml new file mode 100644 index 0000000..3ae0fed --- /dev/null +++ b/disillusion/backend/pom.xml @@ -0,0 +1,170 @@ + + + 4.0.0 + + net.lensfrex + disillusion + ${revision} + + + backend + 0.0.1-SNAPSHOT + backend + backend + + 17 + + 3.0.2 + + 3.0.0 + 1.9.6 + + + + org.springframework.boot + spring-boot-starter-web + ${springboot.version} + + + org.springframework.boot + spring-boot-configuration-processor + true + ${springboot.version} + + + + org.slf4j + slf4j-api + 2.0.6 + + + + org.springframework.boot + ${springboot.version} + spring-boot-starter-test + test + + + + + org.mindrot + jbcrypt + 0.4 + + + + + com.auth0 + java-jwt + 4.3.0 + + + + + + + + + + + + + cn.hutool + hutool-core + 5.8.12 + + + + org.springdoc + springdoc-openapi-starter-webmvc-ui + 2.0.2 + + + + org.webjars + swagger-ui + 4.15.5 + + + + commons-lang + commons-lang + 2.6 + + + + net.lensfrex + common + ${revision} + + + net.lensfrex + virtual-database + ${revision} + + + + + + + org.graalvm.buildtools + native-maven-plugin + 0.9.19 + + + org.springframework.boot + spring-boot-maven-plugin + ${springboot.version} + + + + repackage + + + + + + + org.projectlombok + lombok + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 17 + 17 + + + + + + diff --git a/disillusion/backend/src/main/java/net/lensfrex/disillusion/BackendMain.java b/disillusion/backend/src/main/java/net/lensfrex/disillusion/BackendMain.java new file mode 100644 index 0000000..ea2c3c1 --- /dev/null +++ b/disillusion/backend/src/main/java/net/lensfrex/disillusion/BackendMain.java @@ -0,0 +1,13 @@ +package net.lensfrex.disillusion; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class BackendMain { + + public static void main(String[] args) { + SpringApplication.run(BackendMain.class, args); + } + +} diff --git a/disillusion/backend/src/main/java/net/lensfrex/disillusion/DocumentMain.java b/disillusion/backend/src/main/java/net/lensfrex/disillusion/DocumentMain.java new file mode 100644 index 0000000..a5419b3 --- /dev/null +++ b/disillusion/backend/src/main/java/net/lensfrex/disillusion/DocumentMain.java @@ -0,0 +1,24 @@ +//package net.lensfrex.disillusion; +// +//import io.github.yedaxia.apidocs.Docs; +//import io.github.yedaxia.apidocs.DocsConfig; +// +//public class DocumentMain { +// +// public static void main(String[] args) { +// DocsConfig config = new DocsConfig(); +// // 项目根目录 +// config.setProjectPath("./"); +// // 项目名称 +// config.setProjectName("Assembly"); +// // 声明该API的版本 +// config.setApiVersion("V2.0"); +// // 生成API 文档所在目录 +// config.setDocsPath("./doc"); +// // 配置自动生成 +// config.setAutoGenerate(Boolean.TRUE); +// // 执行生成文档 +// Docs.buildHtmlDocs(config); +// } +// +//} diff --git a/disillusion/backend/src/main/java/net/lensfrex/disillusion/annotation/CheckAuth.java b/disillusion/backend/src/main/java/net/lensfrex/disillusion/annotation/CheckAuth.java new file mode 100644 index 0000000..3ccdf6f --- /dev/null +++ b/disillusion/backend/src/main/java/net/lensfrex/disillusion/annotation/CheckAuth.java @@ -0,0 +1,10 @@ +package net.lensfrex.disillusion.annotation; + +import java.lang.annotation.*; + +@Documented +@Inherited +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface CheckAuth { +} diff --git a/disillusion/backend/src/main/java/net/lensfrex/disillusion/configure/ApplicationContextHelper.java b/disillusion/backend/src/main/java/net/lensfrex/disillusion/configure/ApplicationContextHelper.java new file mode 100644 index 0000000..a3795f8 --- /dev/null +++ b/disillusion/backend/src/main/java/net/lensfrex/disillusion/configure/ApplicationContextHelper.java @@ -0,0 +1,24 @@ +package net.lensfrex.disillusion.configure; + +import org.springframework.beans.BeansException; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class ApplicationContextHelper implements ApplicationContextAware { + public static ApplicationContext applicationContext; + + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { + ApplicationContextHelper.applicationContext = applicationContext; + } + + public static T get(Class clazz) { + return applicationContext.getBean(clazz); + } + + public static Object get(String name) { + return applicationContext.getBean(name); + } +} diff --git a/disillusion/backend/src/main/java/net/lensfrex/disillusion/configure/GlobalConfigure.java b/disillusion/backend/src/main/java/net/lensfrex/disillusion/configure/GlobalConfigure.java new file mode 100644 index 0000000..4ab18f1 --- /dev/null +++ b/disillusion/backend/src/main/java/net/lensfrex/disillusion/configure/GlobalConfigure.java @@ -0,0 +1,24 @@ +package net.lensfrex.disillusion.configure; + +import lombok.Data; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; + +@Data +@Configuration +@ConfigurationProperties("disillusion") +public class GlobalConfigure { + private String databaseDir; + + private static String jwtSecret; + + public static String getJwtSecret() { + return jwtSecret; + } + + @Value("${disillusion.tokenSecretKey}") + public void setJwtSecret(String jwtSecret) { + GlobalConfigure.jwtSecret = jwtSecret; + } +} diff --git a/disillusion/backend/src/main/java/net/lensfrex/disillusion/configure/InterceptorConfigure.java b/disillusion/backend/src/main/java/net/lensfrex/disillusion/configure/InterceptorConfigure.java new file mode 100644 index 0000000..5adeb42 --- /dev/null +++ b/disillusion/backend/src/main/java/net/lensfrex/disillusion/configure/InterceptorConfigure.java @@ -0,0 +1,38 @@ +package net.lensfrex.disillusion.configure; + +import net.lensfrex.disillusion.web.interceptor.TokenInterceptor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; +import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport; + +@Configuration +public class InterceptorConfigure extends WebMvcConfigurationSupport { + @Override + protected void addInterceptors(InterceptorRegistry registry) { + super.addInterceptors(registry); + registry.addInterceptor(this.getTokenInterceptor()).addPathPatterns("/**") + .excludePathPatterns("/user/login") + .excludePathPatterns("/user/register") + .excludePathPatterns("/swagger-ui") + .order(0) + ; + + super.addInterceptors(registry); + } + + @Override + public void addResourceHandlers(ResourceHandlerRegistry registry) { + super.addResourceHandlers(registry); + registry. + addResourceHandler("/swagger-ui/**") + .addResourceLocations("classpath:/META-INF/resources/webjars/swagger-ui/") + .resourceChain(false); + } + + @Bean + public TokenInterceptor getTokenInterceptor() { + return new TokenInterceptor(); + } +} diff --git a/disillusion/backend/src/main/java/net/lensfrex/disillusion/configure/SwaggerConfigure.java b/disillusion/backend/src/main/java/net/lensfrex/disillusion/configure/SwaggerConfigure.java new file mode 100644 index 0000000..5cf2797 --- /dev/null +++ b/disillusion/backend/src/main/java/net/lensfrex/disillusion/configure/SwaggerConfigure.java @@ -0,0 +1,35 @@ +//package net.lensfrex.disillusion.configure; +// +//import com.github.xiaoymin.knife4j.spring.annotations.EnableKnife4j; +//import org.springframework.context.annotation.Bean; +//import org.springframework.context.annotation.Configuration; +//import springfox.documentation.builders.ApiInfoBuilder; +//import springfox.documentation.builders.PathSelectors; +//import springfox.documentation.builders.RequestHandlerSelectors; +//import springfox.documentation.service.ApiInfo; +//import springfox.documentation.spi.DocumentationType; +//import springfox.documentation.spring.web.plugins.Docket; +//import springfox.documentation.swagger2.annotations.EnableSwagger2; +// +//@Configuration +//@EnableSwagger2 +//@EnableKnife4j +//public class SwaggerConfigure { +// @Bean +// public Docket createRestBmbsApi() { +// return new Docket(DocumentationType.SWAGGER_2) +// .groupName("users") +// .apiInfo(apiInfo()) +// .select() +// .apis(RequestHandlerSelectors.basePackage("net.lensfrex.disillusion.web.controller")) +// .paths(PathSelectors.any()) +// .build(); +// } +// +// private ApiInfo apiInfo() { +// return new ApiInfoBuilder() +// .title("disillusion API") +// .version("0.0.1") +// .build(); +// } +//} diff --git a/disillusion/backend/src/main/java/net/lensfrex/disillusion/dao/DaoBase.java b/disillusion/backend/src/main/java/net/lensfrex/disillusion/dao/DaoBase.java new file mode 100644 index 0000000..4a00163 --- /dev/null +++ b/disillusion/backend/src/main/java/net/lensfrex/disillusion/dao/DaoBase.java @@ -0,0 +1,83 @@ +package net.lensfrex.disillusion.dao; + +import net.lensfrex.disillusion.configure.GlobalConfigure; +import net.lensfrex.disillusion.dao.database.Table; +import net.lensfrex.disillusion.dao.database.VirtualDatabase; +import net.lensfrex.disillusion.data.Hash; +import net.lensfrex.disillusion.data.List; +import net.lensfrex.disillusion.model.StoreData; + +import java.io.IOException; + +public abstract class DaoBase implements Table { + // 表名,如"resume", "offer", "user_info"等 + private final String tableName; + + public DaoBase(GlobalConfigure globalConfigure, String tableName) + throws IOException, ClassNotFoundException { + + this.tableName = tableName; + VirtualDatabase.init(globalConfigure.getDatabaseDir()); + updateData(); + } + + // 存放实际数据Hash表,泛型T需要符合StoreData接口,即具有id字段 + // 此时id作为hash表的key进行存取 + private Hash table; + + @Override + public T select(String id) throws IOException, ClassNotFoundException { + updateData(); + return table.get(id); + } + + /** + * 获取所有数据 + */ + public List getAll() throws IOException, ClassNotFoundException { + updateData(); + return table.allValues(); + } + + @Override + public void delete(T record) throws IOException, ClassNotFoundException { + updateData(); + delete(record.getId()); + } + + /** + * 按照id删除一条数据 + */ + @Override + public void delete(String id) throws IOException, ClassNotFoundException { + updateData(); + table.delete(id); + VirtualDatabase.saveTable(tableName, table); + } + + /** + * 插入或者更新一条数据,当数据记录不存在时则新增,否则更新该条数据 + */ + @Override + public void insertOrUpdate(T record) throws IOException, ClassNotFoundException { + updateData(); + table.put(record.getId(), record); + VirtualDatabase.saveTable(tableName, table); + } + + @Override + public void update(T record) throws IOException, ClassNotFoundException { + updateData(); + table.put(record.getId(), record); + VirtualDatabase.saveTable(tableName, table); + } + + @SuppressWarnings("unchecked") + private void updateData() throws IOException, ClassNotFoundException { + table = (Hash) VirtualDatabase.getTable(tableName, Hash.class); + if (table == null) { + table = new Hash<>(); + VirtualDatabase.saveTable(tableName, table); + } + } +} diff --git a/disillusion/backend/src/main/java/net/lensfrex/disillusion/dao/OfferApplicationTable.java b/disillusion/backend/src/main/java/net/lensfrex/disillusion/dao/OfferApplicationTable.java new file mode 100644 index 0000000..cd89903 --- /dev/null +++ b/disillusion/backend/src/main/java/net/lensfrex/disillusion/dao/OfferApplicationTable.java @@ -0,0 +1,14 @@ +package net.lensfrex.disillusion.dao; + +import net.lensfrex.disillusion.configure.GlobalConfigure; +import net.lensfrex.disillusion.model.OfferApplication; +import org.springframework.stereotype.Component; + +import java.io.IOException; + +@Component +public class OfferApplicationTable extends DaoBase { + public OfferApplicationTable(GlobalConfigure globalConfigure) throws IOException, ClassNotFoundException { + super(globalConfigure, "offer_application"); + } +} diff --git a/disillusion/backend/src/main/java/net/lensfrex/disillusion/dao/OfferStatusTable.java b/disillusion/backend/src/main/java/net/lensfrex/disillusion/dao/OfferStatusTable.java new file mode 100644 index 0000000..a96f7d4 --- /dev/null +++ b/disillusion/backend/src/main/java/net/lensfrex/disillusion/dao/OfferStatusTable.java @@ -0,0 +1,12 @@ +package net.lensfrex.disillusion.dao; + +import net.lensfrex.disillusion.configure.GlobalConfigure; +import net.lensfrex.disillusion.model.OfferStatus; + +import java.io.IOException; + +public class OfferStatusTable extends DaoBase { + public OfferStatusTable(GlobalConfigure globalConfigure) throws IOException, ClassNotFoundException { + super(globalConfigure, "offer_status"); + } +} diff --git a/disillusion/backend/src/main/java/net/lensfrex/disillusion/dao/OfferTable.java b/disillusion/backend/src/main/java/net/lensfrex/disillusion/dao/OfferTable.java new file mode 100644 index 0000000..7574fb5 --- /dev/null +++ b/disillusion/backend/src/main/java/net/lensfrex/disillusion/dao/OfferTable.java @@ -0,0 +1,14 @@ +package net.lensfrex.disillusion.dao; + +import net.lensfrex.disillusion.configure.GlobalConfigure; +import net.lensfrex.disillusion.model.employer.Offer; +import org.springframework.stereotype.Component; + +import java.io.IOException; + +@Component +public class OfferTable extends DaoBase { + public OfferTable(GlobalConfigure globalConfigure) throws IOException, ClassNotFoundException { + super(globalConfigure, "offer"); + } +} diff --git a/disillusion/backend/src/main/java/net/lensfrex/disillusion/dao/ResumeTable.java b/disillusion/backend/src/main/java/net/lensfrex/disillusion/dao/ResumeTable.java new file mode 100644 index 0000000..7b60340 --- /dev/null +++ b/disillusion/backend/src/main/java/net/lensfrex/disillusion/dao/ResumeTable.java @@ -0,0 +1,14 @@ +package net.lensfrex.disillusion.dao; + +import net.lensfrex.disillusion.configure.GlobalConfigure; +import net.lensfrex.disillusion.model.student.Resume; +import org.springframework.stereotype.Component; + +import java.io.IOException; + +@Component +public class ResumeTable extends DaoBase { + public ResumeTable(GlobalConfigure globalConfigure) throws IOException, ClassNotFoundException { + super(globalConfigure, "resume"); + } +} diff --git a/disillusion/backend/src/main/java/net/lensfrex/disillusion/dao/UserInfoTable.java b/disillusion/backend/src/main/java/net/lensfrex/disillusion/dao/UserInfoTable.java new file mode 100644 index 0000000..21b0b76 --- /dev/null +++ b/disillusion/backend/src/main/java/net/lensfrex/disillusion/dao/UserInfoTable.java @@ -0,0 +1,14 @@ +package net.lensfrex.disillusion.dao; + +import net.lensfrex.disillusion.configure.GlobalConfigure; +import net.lensfrex.disillusion.model.UserInfo; +import org.springframework.stereotype.Component; + +import java.io.IOException; + +@Component +public class UserInfoTable extends DaoBase { + public UserInfoTable(GlobalConfigure globalConfigure) throws IOException, ClassNotFoundException { + super(globalConfigure, "user_info"); + } +} diff --git a/disillusion/backend/src/main/java/net/lensfrex/disillusion/dao/UserTable.java b/disillusion/backend/src/main/java/net/lensfrex/disillusion/dao/UserTable.java new file mode 100644 index 0000000..aae4822 --- /dev/null +++ b/disillusion/backend/src/main/java/net/lensfrex/disillusion/dao/UserTable.java @@ -0,0 +1,14 @@ +package net.lensfrex.disillusion.dao; + +import net.lensfrex.disillusion.configure.GlobalConfigure; +import net.lensfrex.disillusion.model.User; +import org.springframework.stereotype.Component; + +import java.io.IOException; + +@Component +public class UserTable extends DaoBase { + public UserTable(GlobalConfigure globalConfigure) throws IOException, ClassNotFoundException { + super(globalConfigure, "user"); + } +} diff --git a/disillusion/backend/src/main/java/net/lensfrex/disillusion/dao/index/EmployerOfferIndex.java b/disillusion/backend/src/main/java/net/lensfrex/disillusion/dao/index/EmployerOfferIndex.java new file mode 100644 index 0000000..665ccda --- /dev/null +++ b/disillusion/backend/src/main/java/net/lensfrex/disillusion/dao/index/EmployerOfferIndex.java @@ -0,0 +1,16 @@ +package net.lensfrex.disillusion.dao.index; + +import net.lensfrex.disillusion.configure.GlobalConfigure; +import net.lensfrex.disillusion.dao.DaoBase; +import net.lensfrex.disillusion.model.MultiIndex; +import org.springframework.stereotype.Component; + +import java.io.IOException; + +@Component +public class EmployerOfferIndex extends DaoBase { + public EmployerOfferIndex(GlobalConfigure globalConfigure) throws IOException, ClassNotFoundException { + super(globalConfigure, "employer_offer_index"); + } +} + diff --git a/disillusion/backend/src/main/java/net/lensfrex/disillusion/dao/index/StudentResumeIndex.java b/disillusion/backend/src/main/java/net/lensfrex/disillusion/dao/index/StudentResumeIndex.java new file mode 100644 index 0000000..3fc0b2d --- /dev/null +++ b/disillusion/backend/src/main/java/net/lensfrex/disillusion/dao/index/StudentResumeIndex.java @@ -0,0 +1,15 @@ +package net.lensfrex.disillusion.dao.index; + +import net.lensfrex.disillusion.configure.GlobalConfigure; +import net.lensfrex.disillusion.dao.DaoBase; +import net.lensfrex.disillusion.model.MultiIndex; +import org.springframework.stereotype.Component; + +import java.io.IOException; + +@Component +public class StudentResumeIndex extends DaoBase { + public StudentResumeIndex(GlobalConfigure globalConfigure) throws IOException, ClassNotFoundException { + super(globalConfigure, "student_resume_index"); + } +} diff --git a/disillusion/backend/src/main/java/net/lensfrex/disillusion/dao/index/application/EmployerApplicationIndex.java b/disillusion/backend/src/main/java/net/lensfrex/disillusion/dao/index/application/EmployerApplicationIndex.java new file mode 100644 index 0000000..d2ffe56 --- /dev/null +++ b/disillusion/backend/src/main/java/net/lensfrex/disillusion/dao/index/application/EmployerApplicationIndex.java @@ -0,0 +1,15 @@ +package net.lensfrex.disillusion.dao.index.application; + +import net.lensfrex.disillusion.configure.GlobalConfigure; +import net.lensfrex.disillusion.dao.DaoBase; +import net.lensfrex.disillusion.model.MultiIndex; +import org.springframework.stereotype.Component; + +import java.io.IOException; + +@Component +public class EmployerApplicationIndex extends DaoBase { + public EmployerApplicationIndex(GlobalConfigure globalConfigure) throws IOException, ClassNotFoundException { + super(globalConfigure, "employer_application_index"); + } +} diff --git a/disillusion/backend/src/main/java/net/lensfrex/disillusion/dao/index/application/OfferApplicationIndex.java b/disillusion/backend/src/main/java/net/lensfrex/disillusion/dao/index/application/OfferApplicationIndex.java new file mode 100644 index 0000000..f2a4be7 --- /dev/null +++ b/disillusion/backend/src/main/java/net/lensfrex/disillusion/dao/index/application/OfferApplicationIndex.java @@ -0,0 +1,15 @@ +package net.lensfrex.disillusion.dao.index.application; + +import net.lensfrex.disillusion.configure.GlobalConfigure; +import net.lensfrex.disillusion.dao.DaoBase; +import net.lensfrex.disillusion.model.MultiIndex; +import org.springframework.stereotype.Component; + +import java.io.IOException; + +@Component +public class OfferApplicationIndex extends DaoBase { + public OfferApplicationIndex(GlobalConfigure globalConfigure) throws IOException, ClassNotFoundException { + super(globalConfigure, "offer_application_index"); + } +} diff --git a/disillusion/backend/src/main/java/net/lensfrex/disillusion/dao/index/application/StudentApplicationIndex.java b/disillusion/backend/src/main/java/net/lensfrex/disillusion/dao/index/application/StudentApplicationIndex.java new file mode 100644 index 0000000..610f2d4 --- /dev/null +++ b/disillusion/backend/src/main/java/net/lensfrex/disillusion/dao/index/application/StudentApplicationIndex.java @@ -0,0 +1,15 @@ +package net.lensfrex.disillusion.dao.index.application; + +import net.lensfrex.disillusion.configure.GlobalConfigure; +import net.lensfrex.disillusion.dao.DaoBase; +import net.lensfrex.disillusion.model.MultiIndex; +import org.springframework.stereotype.Component; + +import java.io.IOException; + +@Component +public class StudentApplicationIndex extends DaoBase { + public StudentApplicationIndex(GlobalConfigure globalConfigure) throws IOException, ClassNotFoundException { + super(globalConfigure, "student_application_index"); + } +} diff --git a/disillusion/backend/src/main/java/net/lensfrex/disillusion/util/TokenUtil.java b/disillusion/backend/src/main/java/net/lensfrex/disillusion/util/TokenUtil.java new file mode 100644 index 0000000..726d9a8 --- /dev/null +++ b/disillusion/backend/src/main/java/net/lensfrex/disillusion/util/TokenUtil.java @@ -0,0 +1,46 @@ +package net.lensfrex.disillusion.util; + +import com.auth0.jwt.JWT; +import com.auth0.jwt.JWTVerifier; +import com.auth0.jwt.algorithms.Algorithm; +import com.auth0.jwt.exceptions.JWTVerificationException; +import net.lensfrex.disillusion.configure.GlobalConfigure; +import org.springframework.stereotype.Component; + +import java.time.Instant; +import java.time.temporal.ChronoUnit; + +@Component +public class TokenUtil { + + public TokenUtil(GlobalConfigure globalConfigure) { + } + + public static String signNewToken(String user) { + String secret = GlobalConfigure.getJwtSecret(); + return JWT.create() + .withClaim("user", user) + .withClaim("issue", Instant.now().getNano()) + .withExpiresAt(Instant.now().plus(30, ChronoUnit.DAYS)) + .sign(Algorithm.HMAC256(secret)); + } + + public static boolean verify(String token) { + String secret = GlobalConfigure.getJwtSecret(); + JWTVerifier verifier = JWT.require(Algorithm.HMAC256(secret)).build(); + try { + verifier.verify(token); + } catch (JWTVerificationException e) { + return false; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + + return true; + } + + public static String getUsernameFromToken(String token) { + return JWT.decode(token).getClaim("user").asString(); + } +} diff --git a/disillusion/backend/src/main/java/net/lensfrex/disillusion/web/controller/OfferController.java b/disillusion/backend/src/main/java/net/lensfrex/disillusion/web/controller/OfferController.java new file mode 100644 index 0000000..0a566f6 --- /dev/null +++ b/disillusion/backend/src/main/java/net/lensfrex/disillusion/web/controller/OfferController.java @@ -0,0 +1,158 @@ +package net.lensfrex.disillusion.web.controller; + +import net.lensfrex.disillusion.annotation.CheckAuth; +import net.lensfrex.disillusion.model.OfferApplication; +import net.lensfrex.disillusion.model.User; +import net.lensfrex.disillusion.model.employer.Offer; +import net.lensfrex.disillusion.response.general.Response; +import net.lensfrex.disillusion.response.general.ResponseCode; +import net.lensfrex.disillusion.util.TokenUtil; +import net.lensfrex.disillusion.web.response.OfferListResponse; +import net.lensfrex.disillusion.web.service.employer.OfferService; +import net.lensfrex.disillusion.web.service.student.ApplicationService; +import net.lensfrex.disillusion.web.service.user.UserService; +import org.springframework.beans.BeanUtils; +import org.springframework.web.bind.annotation.*; + +import java.io.IOException; +import java.util.Arrays; + +@RestController +@RequestMapping("/offer") +public class OfferController { + + private final OfferService offerService; + private final UserService userService; + private final ApplicationService applicationService; + + public OfferController(OfferService offerService, UserService userService, + ApplicationService applicationService) { + + this.offerService = offerService; + this.userService = userService; + this.applicationService = applicationService; + } + + private static final String OFFER_FILTER_EMPLOYER = "by_employer"; + private static final String OFFER_FILTER_JOB_NAME = "by_job_name"; + private static final String OFFER_FILTER_SALARY = "by_salary"; + + @GetMapping("/search") + public Response searchOffer(@RequestParam("filter") String filter, @RequestParam("value") String filterValue) + throws IOException, ClassNotFoundException { + + Offer[] result = switch (filter) { + case OFFER_FILTER_EMPLOYER -> offerService.searchOfferByUser(filterValue); + case OFFER_FILTER_JOB_NAME -> offerService.searchOfferByName(filterValue); + case OFFER_FILTER_SALARY -> offerService.getAllOfferBySalary(filterValue); + default -> throw new IllegalStateException("Unexpected value: " + filter); + }; + + return Response.success(result); + } + + private static final OfferListResponse.OfferResponse[] EMPTY_OFFER = new OfferListResponse.OfferResponse[]{}; + + @GetMapping("/getAll") + public Response getOffer(@RequestParam("page") Integer page, @RequestParam("size") Integer size) + throws IOException, ClassNotFoundException { + Offer[] allOffer = offerService.getAllOffer(); + + int start = (page - 1) * size; + if (start > allOffer.length) { + OfferListResponse response = new OfferListResponse(); + response.setTotal(0); + response.setPage(0); + response.setSize(0); + response.setResult(EMPTY_OFFER); + + return Response.success(response); + } + + Offer[] offers = Arrays.copyOfRange(allOffer, start, Math.min(start + size, allOffer.length)); + OfferListResponse response = new OfferListResponse(); + OfferListResponse.OfferResponse[] offerList = new OfferListResponse.OfferResponse[offers.length]; + for (int i = 0; i < offers.length; i++) { + Offer offer = offers[i]; + offerList[i] = new OfferListResponse.OfferResponse(); + BeanUtils.copyProperties(offer, offerList[i]); + } + + response.setTotal(allOffer.length); + response.setPage(page); + response.setSize(size); + response.setResult(offerList); + + return Response.success(response); + } + + @GetMapping("/get") + public Response getOffer(@RequestParam("id") String id) + throws IOException, ClassNotFoundException { + + Offer offer = offerService.getOffer(id); + if (offer == null) { + return Response.success(); + } + + OfferListResponse.OfferResponse response = new OfferListResponse.OfferResponse(); + User publisher = userService.getAccount(offer.getPublisherId()); + if (publisher == null) { + return Response.success(); + } + + BeanUtils.copyProperties(offer, response); + + return Response.success(response); + } + + @GetMapping("/progress") + public Response getStatus(@RequestParam("id") String id) + throws IOException, ClassNotFoundException { + Offer offer = offerService.getOffer(id); + if (offer == null) { + return Response.success("unknown"); + } + + int target = offer.getTargetNumber(); + + int count = 0; + + OfferApplication[] applications = applicationService.getAllApplicationByOfferId(id); + for (OfferApplication application : applications) { + if (application == null) { + continue; + } + if (application.getStatus() == OfferApplication.Status.ACCEPTED) { + count++; + } + } + + return Response.success(count + "/" + target); + } + + private static final String samples = """ + 8.微专业课程选课要求: + + 前期经过自愿报名并审核成功的学生,根据所报微专业的培养方案,自行选择相应课程。 + + 9.认真对照培养方案,选课时务必仔细核对课程信息,确保“课程代码”、“课程名称”、“学分”这三项重要信息与培养方案要求相符。 + + 10.禁止同一时间修读两门及以上课程,系统会提示时间冲突并禁止选课。提前了解专业教学进程(实习、实践、课程设计等)情况,不能选择与本专业其它教学任务周次有冲突的课堂。 + + 11.因培养方案变动、转专业等原因,计划修读的课程确定不再开课的,请先查询本科生课程学分认定对照表并咨询开课学院,再进行选课。待课程修读完获得学分后,再按照本科生院发布的相关要求办理学分认定。 + + 12.学生选课、听课、考核必须一致。学生不能参加未选择课程的学习和考核,即使参加了学习和考核,如果没有选课,也是无效的,将不记录成绩。 + + 13.学生凭学号、密码选课,认真对待,并对自己的选课行为负责。密码必须妥善保管,不得代替他人选课,不得借用、盗用他人学号及密码选课。 + + 14.学生登录本科教学综合管理系统需通过学校统一身份认证平台登录,登录方式、密码重置方式详见以下两个通知:https://jwc.wust.edu.cn/2022/0226/c1925a255654/page.htm、https://jwc.wust.edu.cn/2022/0302/c1925a255786/page.htm。 + + 对账号、密码有疑问的,请按照登录页面上“常见问题”、“忘记密码”等提示进行操作。 + + 15.本次选课为2022-2023学年第二学期课程的最后一次选课,请同学们严格按照时间安排进行退改选,尤其要在规定的时间内查看课表,以免出现课堂因人数不足取消而本人又没有及时参加补选,再次造成某些课程漏选的情况。无故不参加选课或错过选课机会,后果自负。\s + + 本科生院教务管理办公室 + + 二〇二三年二月二十三日"""; +} diff --git a/disillusion/backend/src/main/java/net/lensfrex/disillusion/web/controller/ResumeController.java b/disillusion/backend/src/main/java/net/lensfrex/disillusion/web/controller/ResumeController.java new file mode 100644 index 0000000..e9485b8 --- /dev/null +++ b/disillusion/backend/src/main/java/net/lensfrex/disillusion/web/controller/ResumeController.java @@ -0,0 +1,47 @@ +package net.lensfrex.disillusion.web.controller; + +import net.lensfrex.disillusion.annotation.CheckAuth; +import net.lensfrex.disillusion.model.User; +import net.lensfrex.disillusion.model.student.Resume; +import net.lensfrex.disillusion.response.general.Response; +import net.lensfrex.disillusion.response.general.ResponseCode; +import net.lensfrex.disillusion.util.TokenUtil; +import net.lensfrex.disillusion.web.service.student.ResumeService; +import net.lensfrex.disillusion.web.service.user.UserService; +import org.springframework.web.bind.annotation.*; + +import java.io.IOException; + +@RestController +@RequestMapping("/resume") +public class ResumeController { + private final ResumeService resumeService; + + private final UserService userService; + + public ResumeController(ResumeService resumeService, + UserService userService) { + this.resumeService = resumeService; + this.userService = userService; + } + + @CheckAuth + @GetMapping("/view") + public Response view(@RequestHeader("token") String token, @RequestParam("id") String id) + throws IOException, ClassNotFoundException { + + Resume result = resumeService.getResume(id); + + String username = TokenUtil.getUsernameFromToken(token); + User user = userService.getAccount(username); + if (result == null) { + return Response.success(); + } + + if (result.getOwnerId().equals(username) || user.getUserType() == User.UserType.EMPLOYER) { + return Response.success(result); + } else { + return Response.error(ResponseCode.PERMISSION_DENIED); + } + } +} diff --git a/disillusion/backend/src/main/java/net/lensfrex/disillusion/web/controller/TestController.java b/disillusion/backend/src/main/java/net/lensfrex/disillusion/web/controller/TestController.java new file mode 100644 index 0000000..b233ceb --- /dev/null +++ b/disillusion/backend/src/main/java/net/lensfrex/disillusion/web/controller/TestController.java @@ -0,0 +1,19 @@ +package net.lensfrex.disillusion.web.controller; + +import net.lensfrex.disillusion.annotation.CheckAuth; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class TestController { + @RequestMapping("/ping") + public String ping() { + return "pong"; + } + + @CheckAuth + @RequestMapping("/ping-token") + public String pingWithToken() { + return "pong"; + } +} diff --git a/disillusion/backend/src/main/java/net/lensfrex/disillusion/web/controller/employer/EmolpyerOfferController.java b/disillusion/backend/src/main/java/net/lensfrex/disillusion/web/controller/employer/EmolpyerOfferController.java new file mode 100644 index 0000000..98d62df --- /dev/null +++ b/disillusion/backend/src/main/java/net/lensfrex/disillusion/web/controller/employer/EmolpyerOfferController.java @@ -0,0 +1,99 @@ +package net.lensfrex.disillusion.web.controller.employer; + +import net.lensfrex.disillusion.annotation.CheckAuth; +import net.lensfrex.disillusion.model.OfferApplication; +import net.lensfrex.disillusion.model.User; +import net.lensfrex.disillusion.model.employer.Offer; +import net.lensfrex.disillusion.response.general.Response; +import net.lensfrex.disillusion.response.general.ResponseCode; +import net.lensfrex.disillusion.web.request.OfferPublishRequest; +import net.lensfrex.disillusion.web.service.employer.OfferService; +import net.lensfrex.disillusion.web.service.student.ApplicationService; +import net.lensfrex.disillusion.web.service.user.UserService; +import net.lensfrex.disillusion.util.TokenUtil; +import org.springframework.web.bind.annotation.*; + +import java.io.IOException; +import java.util.Arrays; + +@RestController +@RequestMapping("/employer/offer") +public class EmolpyerOfferController { + + private final UserService userService; + + private final OfferService offerService; + + private final ApplicationService applicationService; + + public EmolpyerOfferController(UserService userService, + OfferService offerService, + ApplicationService applicationService) { + + this.userService = userService; + this.offerService = offerService; + this.applicationService = applicationService; + } + + @CheckAuth + @PostMapping("/publish") + public Response publish(@RequestBody OfferPublishRequest offerRequest, @RequestHeader("token") String token) + throws IOException, ClassNotFoundException { + + String username = TokenUtil.getUsernameFromToken(token); + User user = userService.getAccount(username); + if (user.getUserType() != User.UserType.EMPLOYER) { + return Response.error(ResponseCode.PERMISSION_DENIED); + } + + offerService.addOffer(username, offerRequest); + return Response.success(); + } + + @CheckAuth + @PostMapping("/delete") + public Response delete(@RequestHeader("token") String token, @RequestParam("id") String id) + throws IOException, ClassNotFoundException { + + String username = TokenUtil.getUsernameFromToken(token); + if (!check(username, id)) { + return Response.error(ResponseCode.PERMISSION_DENIED); + } + + offerService.deleteOffer(username, id); + + return Response.success(); + } + + @CheckAuth + @GetMapping("/view") + public Response getAllOffer(@RequestHeader("token") String token) + throws IOException, ClassNotFoundException { + + String username = TokenUtil.getUsernameFromToken(token); + Offer[] offers = offerService.getAllOfferByUser(username); + + return Response.success(offers); + } + + @CheckAuth + @PostMapping("/update") + public Response update(@RequestHeader("token") String token, @RequestParam("id") String id, @RequestBody OfferPublishRequest offerRequest) + throws IOException, ClassNotFoundException { + + String user = TokenUtil.getUsernameFromToken(token); + if (!check(user, id)) { + return Response.error(ResponseCode.PERMISSION_DENIED); + } + + offerService.updateOffer(user, id, offerRequest); + + return Response.success(); + } + + private boolean check(String user, String id) throws IOException, ClassNotFoundException { + Offer offer = offerService.getOffer(id); + + return offer != null && offer.getPublisherId().equals(user); + } +} diff --git a/disillusion/backend/src/main/java/net/lensfrex/disillusion/web/controller/employer/EmployerOfferApplicationController.java b/disillusion/backend/src/main/java/net/lensfrex/disillusion/web/controller/employer/EmployerOfferApplicationController.java new file mode 100644 index 0000000..f47f8c3 --- /dev/null +++ b/disillusion/backend/src/main/java/net/lensfrex/disillusion/web/controller/employer/EmployerOfferApplicationController.java @@ -0,0 +1,75 @@ +package net.lensfrex.disillusion.web.controller.employer; + +import net.lensfrex.disillusion.annotation.CheckAuth; +import net.lensfrex.disillusion.model.OfferApplication; +import net.lensfrex.disillusion.model.User; +import net.lensfrex.disillusion.response.general.Response; +import net.lensfrex.disillusion.response.general.ResponseCode; +import net.lensfrex.disillusion.util.TokenUtil; +import net.lensfrex.disillusion.web.service.student.ApplicationService; +import net.lensfrex.disillusion.web.service.user.UserService; +import org.springframework.beans.BeanUtils; +import org.springframework.web.bind.annotation.*; + +import java.io.IOException; +import java.util.Arrays; + +@RestController +@RequestMapping("/employer/application") +public class EmployerOfferApplicationController { + private final UserService userService; + + private final ApplicationService applicationService; + + public EmployerOfferApplicationController(UserService userService, + ApplicationService applicationService) { + + this.userService = userService; + this.applicationService = applicationService; + } + + @CheckAuth + @PostMapping("/deal") + public Response deal(@RequestHeader("token") String token, @RequestParam("id") String id, @RequestParam("opinion") String opinion) throws IOException, ClassNotFoundException { + String username = TokenUtil.getUsernameFromToken(token); + User user = userService.getAccount(username); + if (user.getUserType() != User.UserType.EMPLOYER) { + return Response.error(ResponseCode.PERMISSION_DENIED); + } + + OfferApplication application = applicationService.getOfferApplication(id); + + if (!application.getTargetId().equals(username)) { + return Response.error(ResponseCode.PERMISSION_DENIED); + } + + OfferApplication.Status status = switch (opinion) { + case "accept" -> OfferApplication.Status.ACCEPTED; + case "reject" -> OfferApplication.Status.REJECTED; + default -> throw new IllegalStateException(); + }; + + applicationService.changeApplicationStatus(id, status); + + return Response.success(); + } + + @CheckAuth + @GetMapping("/view") + public Response view(@RequestHeader("token") String token, @RequestParam("offer_id") String offerId) + throws IOException, ClassNotFoundException { + + OfferApplication[] applications = applicationService.getAllApplicationByOfferId(offerId); + OfferApplication[] result = new OfferApplication[applications.length]; + int count = 0; + for (OfferApplication application : applications) { + if (application != null && application.getStatus() != OfferApplication.Status.ACCEPTED) { + result[count] = new OfferApplication(); + BeanUtils.copyProperties(application, result[count]); + count++; + } + } + + return Response.success(Arrays.copyOf(result, count)); + } +} diff --git a/disillusion/backend/src/main/java/net/lensfrex/disillusion/web/controller/student/StudentOfferApplicationController.java b/disillusion/backend/src/main/java/net/lensfrex/disillusion/web/controller/student/StudentOfferApplicationController.java new file mode 100644 index 0000000..2f66e21 --- /dev/null +++ b/disillusion/backend/src/main/java/net/lensfrex/disillusion/web/controller/student/StudentOfferApplicationController.java @@ -0,0 +1,109 @@ +package net.lensfrex.disillusion.web.controller.student; +import cn.hutool.core.util.RandomUtil; +import net.lensfrex.disillusion.model.OfferApplication.Status; + +import net.lensfrex.disillusion.annotation.CheckAuth; +import net.lensfrex.disillusion.model.OfferApplication; +import net.lensfrex.disillusion.response.general.Response; +import net.lensfrex.disillusion.response.general.ResponseCode; +import net.lensfrex.disillusion.util.TokenUtil; +import net.lensfrex.disillusion.web.request.OfferApplicationRequest; +import net.lensfrex.disillusion.web.response.ApplicationResponse; +import net.lensfrex.disillusion.web.service.student.ApplicationService; +import net.lensfrex.disillusion.web.service.user.UserService; +import org.springframework.beans.BeanUtils; +import org.springframework.web.bind.annotation.*; + +import java.io.IOException; + +@RestController +@RequestMapping("/student/application") +public class StudentOfferApplicationController { + private final UserService userService; + + private final ApplicationService applicationService; + + public StudentOfferApplicationController(UserService userService, + ApplicationService applicationService) { + + this.userService = userService; + this.applicationService = applicationService; + } + + private boolean check(String user, String id) throws IOException, ClassNotFoundException { + OfferApplication offerApplication = applicationService.getOfferApplication(id); + + return offerApplication != null && offerApplication.getPublisherId().equals(user); + } + + @CheckAuth + @PostMapping("/publish") + public Response publish(@RequestBody OfferApplicationRequest offerRequest, @RequestHeader String token) + throws IOException, ClassNotFoundException { + + String username = TokenUtil.getUsernameFromToken(token); + applicationService.addApplication(username, offerRequest); + + return Response.success(); + } + + @CheckAuth + @PostMapping("/delete") + public Response delete(@RequestHeader String token, @RequestParam("id") String id) + throws IOException, ClassNotFoundException { + + String user = TokenUtil.getUsernameFromToken(token); + if (!check(user, id)) { + return Response.error(ResponseCode.PERMISSION_DENIED); + } + + applicationService.deleteOfferApplication(user, id); + + return Response.success(); + } + + @CheckAuth + @GetMapping("/view") + public Response view(@RequestHeader("token") String token) throws IOException, ClassNotFoundException { + String username = TokenUtil.getUsernameFromToken(token); + + OfferApplication[] applications = applicationService.getAllApplicationByUser(username); + ApplicationResponse[] response = new ApplicationResponse[applications.length]; + int i = 0; + for (OfferApplication application : applications) { + if (application == null) { + continue; + } + response[i] = new ApplicationResponse(); + BeanUtils.copyProperties(application, response[i]); + i++; + } + + return Response.success(response); + } + + private static final String samples = """ + 8.微专业课程选课要求: + + 前期经过自愿报名并审核成功的学生,根据所报微专业的培养方案,自行选择相应课程。 + + 9.认真对照培养方案,选课时务必仔细核对课程信息,确保“课程代码”、“课程名称”、“学分”这三项重要信息与培养方案要求相符。 + + 10.禁止同一时间修读两门及以上课程,系统会提示时间冲突并禁止选课。提前了解专业教学进程(实习、实践、课程设计等)情况,不能选择与本专业其它教学任务周次有冲突的课堂。 + + 11.因培养方案变动、转专业等原因,计划修读的课程确定不再开课的,请先查询本科生课程学分认定对照表并咨询开课学院,再进行选课。待课程修读完获得学分后,再按照本科生院发布的相关要求办理学分认定。 + + 12.学生选课、听课、考核必须一致。学生不能参加未选择课程的学习和考核,即使参加了学习和考核,如果没有选课,也是无效的,将不记录成绩。 + + 13.学生凭学号、密码选课,认真对待,并对自己的选课行为负责。密码必须妥善保管,不得代替他人选课,不得借用、盗用他人学号及密码选课。 + + 14.学生登录本科教学综合管理系统需通过学校统一身份认证平台登录,登录方式、密码重置方式详见以下两个通知:https://jwc.wust.edu.cn/2022/0226/c1925a255654/page.htm、https://jwc.wust.edu.cn/2022/0302/c1925a255786/page.htm。 + + 对账号、密码有疑问的,请按照登录页面上“常见问题”、“忘记密码”等提示进行操作。 + + 15.本次选课为2022-2023学年第二学期课程的最后一次选课,请同学们严格按照时间安排进行退改选,尤其要在规定的时间内查看课表,以免出现课堂因人数不足取消而本人又没有及时参加补选,再次造成某些课程漏选的情况。无故不参加选课或错过选课机会,后果自负。\s + + 本科生院教务管理办公室 + + 二〇二三年二月二十三日"""; +} diff --git a/disillusion/backend/src/main/java/net/lensfrex/disillusion/web/controller/student/StudentResumeController.java b/disillusion/backend/src/main/java/net/lensfrex/disillusion/web/controller/student/StudentResumeController.java new file mode 100644 index 0000000..57f6085 --- /dev/null +++ b/disillusion/backend/src/main/java/net/lensfrex/disillusion/web/controller/student/StudentResumeController.java @@ -0,0 +1,101 @@ +package net.lensfrex.disillusion.web.controller.student; + +import net.lensfrex.disillusion.annotation.CheckAuth; +import net.lensfrex.disillusion.model.User; +import net.lensfrex.disillusion.model.student.Resume; +import net.lensfrex.disillusion.response.general.Response; +import net.lensfrex.disillusion.response.general.ResponseCode; +import net.lensfrex.disillusion.util.TokenUtil; +import net.lensfrex.disillusion.web.request.ResumeRequest; +import net.lensfrex.disillusion.web.service.student.ResumeService; +import net.lensfrex.disillusion.web.service.user.UserService; +import org.springframework.web.bind.annotation.*; + +import java.io.IOException; + +@RestController +@RequestMapping("/student/resume") +public class StudentResumeController { + private final ResumeService resumeService; + private final UserService userService; + + public StudentResumeController(ResumeService resumeService, UserService userService) { + this.resumeService = resumeService; + this.userService = userService; + } + + @CheckAuth + @PostMapping(value = "/add") + public Response add(@RequestBody ResumeRequest request, @RequestHeader("token") String token) + throws IOException, ClassNotFoundException { + + String user = TokenUtil.getUsernameFromToken(token); + resumeService.addResume(user, request); + + return Response.success(); + } + + @CheckAuth + @PostMapping(value = "/update") + public Response update(@RequestBody ResumeRequest resumeRequest, + @RequestParam("id") String id, @RequestHeader("token") String token) + throws IOException, ClassNotFoundException { + + String user = TokenUtil.getUsernameFromToken(token); + if (!check(user, id)) { + return Response.error(ResponseCode.PERMISSION_DENIED); + } + resumeService.updateResume(user, id, resumeRequest); + + return Response.success(); + } + + @CheckAuth + @PostMapping("/delete") + public Response deleteResume(@RequestParam("id") String id, @RequestHeader("token") String token) + throws IOException, ClassNotFoundException { + + String user = TokenUtil.getUsernameFromToken(token); + if (!check(user, id)) { + return Response.error(ResponseCode.PERMISSION_DENIED); + } + + resumeService.deleteResume(user, id); + + return Response.success(); + } + + @CheckAuth + @GetMapping("/all") + public Response getAllResume(@RequestHeader("token") String token) + throws IOException, ClassNotFoundException { + + String username = TokenUtil.getUsernameFromToken(token); + Resume[] result = resumeService.getAllResume(username); + + return Response.success(result); + } + + @CheckAuth + @GetMapping("/getFile") + public Response getFile(@RequestParam("id") String id, @RequestHeader("token") String token) + throws IOException, ClassNotFoundException { + + String username = TokenUtil.getUsernameFromToken(token); + if (!check(username, id)) { + User user = userService.getAccount(username); + if (user == null || user.getUserType() != User.UserType.EMPLOYER) { + return Response.error(ResponseCode.PERMISSION_DENIED); + } + } + + String fileData = resumeService.getResumeFileData(id); + + return Response.success(fileData); + } + + private boolean check(String user, String id) throws IOException, ClassNotFoundException { + Resume resume = resumeService.getResume(id); + return resume != null && resume.getOwnerId().equals(user); + } +} diff --git a/disillusion/backend/src/main/java/net/lensfrex/disillusion/web/controller/user/UserController.java b/disillusion/backend/src/main/java/net/lensfrex/disillusion/web/controller/user/UserController.java new file mode 100644 index 0000000..043284a --- /dev/null +++ b/disillusion/backend/src/main/java/net/lensfrex/disillusion/web/controller/user/UserController.java @@ -0,0 +1,127 @@ +package net.lensfrex.disillusion.web.controller.user; + +import lombok.extern.slf4j.Slf4j; +import net.lensfrex.disillusion.annotation.CheckAuth; +import net.lensfrex.disillusion.model.User; +import net.lensfrex.disillusion.model.UserInfo; +import net.lensfrex.disillusion.model.employer.Employer; +import net.lensfrex.disillusion.model.student.Student; +import net.lensfrex.disillusion.response.general.Response; +import net.lensfrex.disillusion.response.general.ResponseCode; +import net.lensfrex.disillusion.util.TokenUtil; +import net.lensfrex.disillusion.web.service.user.UserInfoService; +import net.lensfrex.disillusion.web.service.user.UserService; +import org.mindrot.jbcrypt.BCrypt; +import org.springframework.web.bind.annotation.*; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.nio.file.StandardOpenOption; +import java.util.HashMap; +import java.util.Map; + +@Slf4j +@RestController +@RequestMapping("/user") +public class UserController { + private final UserService userService; + private final UserInfoService userInfoService; + + public UserController(UserService userService, + UserInfoService userInfoService) { + + this.userService = userService; + this.userInfoService = userInfoService; + } + + @PostMapping(value = "/login", produces = "application/json;charset=utf-8") + public Response> login(@RequestParam("username") String username, + @RequestParam("password") String password) + throws IOException, ClassNotFoundException { + + User user = userService.getAccount(username); + if (user == null || !BCrypt.checkpw(password, user.getPassword())) { + return Response.error(ResponseCode.IDENTIFY_ERROR); + } + + String token = TokenUtil.signNewToken(username); + + Map responseData = new HashMap<>(2); + responseData.put("token", token); + responseData.put("user_type", user.getUserType().code + ""); + + return Response.success(responseData); + } + + @CheckAuth + @PostMapping(value = "/refresh", produces = "application/json;charset=utf-8") + public Response refreshToken(@RequestHeader("token") String token) throws IOException, ClassNotFoundException { + + String username = TokenUtil.getUsernameFromToken(token); + User user = userService.getAccount(username); + if (user == null) { + return Response.error(ResponseCode.USER_NOT_EXISTS); + } + + String newToken = TokenUtil.signNewToken(username); + + return Response.success(newToken); + } + + @PostMapping(value = "/register", produces = "application/json;charset=utf-8") + public Response register(@RequestParam("username") String username, + @RequestParam("password") String password, + @RequestParam("userType") String userType) + throws IOException, ClassNotFoundException { + + // 检查用户是否已经存在 + User user = userService.getAccount(username); + if (user != null) { + return Response.error(ResponseCode.USER_ALREADY_EXISTS); + } + + // 注册用户类型 + User.UserType enumUserType = getUserType(userType); + if (enumUserType == null) { + return Response.error(ResponseCode.PARAM_WRONG); + } + + // 注册用户 + userService.registerNewUser(username, password, enumUserType); + + // 生成默认的用户信息 + userInfoService.saveUserInfoById(getDefaultUserInfo(username, enumUserType)); + + return Response.success(); + } + + private User.UserType getUserType(String value) { + return switch (value) { + case "0" -> User.UserType.STUDENT; + case "1" -> User.UserType.EMPLOYER; + default -> null; + }; + } + + private UserInfo getDefaultUserInfo(String username, User.UserType userType) { + try { + File avatarBaseDir = new File(UserInfoController.AVATAR_BASE_DIR); + if (!avatarBaseDir.exists()) { + avatarBaseDir.mkdirs(); + } + + Files.write(Paths.get(UserInfoController.AVATAR_BASE_DIR, username), Student.DEFAULT_AVATAR, + StandardOpenOption.CREATE, StandardOpenOption.WRITE); + + } catch (IOException e) { + log.error("写入默认头像时发生异常", e); + } + + return switch (userType) { + case STUDENT -> Student.getDefaultInfo(username); + case EMPLOYER -> Employer.getDefaultInfo(username); + }; + } +} diff --git a/disillusion/backend/src/main/java/net/lensfrex/disillusion/web/controller/user/UserInfoController.java b/disillusion/backend/src/main/java/net/lensfrex/disillusion/web/controller/user/UserInfoController.java new file mode 100644 index 0000000..f3430cf --- /dev/null +++ b/disillusion/backend/src/main/java/net/lensfrex/disillusion/web/controller/user/UserInfoController.java @@ -0,0 +1,168 @@ +package net.lensfrex.disillusion.web.controller.user; + +import cn.hutool.core.codec.Base64; +import com.fasterxml.jackson.core.JsonParseException; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.extern.slf4j.Slf4j; +import net.lensfrex.disillusion.annotation.CheckAuth; +import net.lensfrex.disillusion.model.User; +import net.lensfrex.disillusion.model.UserInfo; +import net.lensfrex.disillusion.web.response.user.PublicEmployerInfoResponse; +import net.lensfrex.disillusion.web.response.user.PublicStudentInfoResponse; +import net.lensfrex.disillusion.web.response.user.UserInfoResponseBase; +import net.lensfrex.disillusion.model.employer.Employer; +import net.lensfrex.disillusion.model.student.Student; +import net.lensfrex.disillusion.response.general.Response; +import net.lensfrex.disillusion.response.general.ResponseCode; +import net.lensfrex.disillusion.util.IOUtil; +import net.lensfrex.disillusion.web.service.user.UserInfoService; +import net.lensfrex.disillusion.web.service.user.UserService; +import net.lensfrex.disillusion.util.TokenUtil; +import org.springframework.beans.BeanUtils; +import org.springframework.web.bind.annotation.*; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.nio.file.StandardOpenOption; + +@Slf4j +@RestController +@RequestMapping("/user/info") +public class UserInfoController { + public static final String AVATAR_BASE_DIR = "database/files/avatar"; + + private static final ObjectMapper objectMapper = new ObjectMapper(); + + private final UserService userService; + private final UserInfoService userInfoService; + + public UserInfoController(UserService userService, + UserInfoService userInfoService) { + + this.userService = userService; + this.userInfoService = userInfoService; + } + + // 获取自己的用户信息 + @CheckAuth + @GetMapping(value = "/view", produces = "application/json;charset=utf-8") + public Response getUserInfo(@RequestHeader("token") String token) + throws IOException, ClassNotFoundException { + + String username = TokenUtil.getUsernameFromToken(token); + User user = userService.getAccount(username); + if (user == null) { + return Response.error(ResponseCode.USER_NOT_EXISTS); + } + + UserInfo info = userInfoService.getUserInfoById(user.getId()); + return Response.success(info); + } + + @GetMapping(value = "/view/public", produces = "application/json") + public Response getUserInfoPublic(@RequestParam("user") String username) + throws IOException, ClassNotFoundException { + + User user = userService.getAccount(username); + if (user == null) { + return Response.error(ResponseCode.USER_NOT_EXISTS); + } + + UserInfo info = userInfoService.getUserInfoById(username); + + return switch (user.getUserType()) { + case STUDENT -> { + PublicStudentInfoResponse response = new PublicStudentInfoResponse(); + BeanUtils.copyProperties(info, response); + + yield Response.success(response); + } + case EMPLOYER -> { + PublicEmployerInfoResponse response = new PublicEmployerInfoResponse(); + BeanUtils.copyProperties(info, response); + + yield Response.success(response); + } + }; + } + + @CheckAuth + @PostMapping(value = "/edit", produces = "application/json;charset=utf-8") + public Response editUserInfo(@RequestHeader("token") String token, @RequestBody String infoJson) + throws IOException, ClassNotFoundException { + + String username = TokenUtil.getUsernameFromToken(token); + User user = userService.getAccount(username); + if (user == null) { + return Response.error(ResponseCode.USER_NOT_EXISTS); + } + + try { + UserInfo userInfo = switch (user.getUserType()) { + case STUDENT -> objectMapper.readValue(infoJson, Student.class); + case EMPLOYER -> objectMapper.readValue(infoJson, Employer.class); + }; + + userInfo.setId(user.getId()); + userInfoService.saveUserInfoById(userInfo); + + return Response.success(); + + } catch (JsonMappingException | JsonParseException e) { + return Response.error(ResponseCode.PARAM_WRONG); + + } catch (Exception e) { + return Response.error(ResponseCode.SERVER_INTERNAL_ERROR); + } + } + + @GetMapping(value = "/avatar", produces = "image/png") + public byte[] getAvatarImage(@RequestParam(value = "user", required = false) String user, + @RequestHeader(value = "token", required = false) String token) + throws IOException, ClassNotFoundException { + if (user == null && token == null) { + return Response.error(ResponseCode.PARAM_WRONG).toBytes(); + } + + if (user == null) { + String username = TokenUtil.getUsernameFromToken(token); + User userData = userService.getAccount(username); + if (userData == null) { + return Response.error(ResponseCode.USER_NOT_EXISTS).toBytes(); + } + + return Files.readAllBytes(Paths.get(AVATAR_BASE_DIR, username)); + } else { + return Files.readAllBytes(Paths.get(AVATAR_BASE_DIR, user)); + } + } + + @CheckAuth + @PostMapping(value = "/avatar/upload", produces = "application/json") + public Response uploadAvatar(@RequestHeader(value = "token") String token, @RequestBody String avatarBase64) + throws IOException, ClassNotFoundException { + + String username = TokenUtil.getUsernameFromToken(token); + User user = userService.getAccount(username); + if (user == null) { + return Response.error(ResponseCode.USER_NOT_EXISTS); + } + + try { + File avatarBaseDir = new File(UserInfoController.AVATAR_BASE_DIR); + if (!avatarBaseDir.exists()) { + avatarBaseDir.mkdirs(); + } + + Files.write(Paths.get(UserInfoController.AVATAR_BASE_DIR, username), Base64.decode(avatarBase64), + StandardOpenOption.CREATE, StandardOpenOption.WRITE); + } catch (Exception e) { + return Response.error(ResponseCode.SERVER_INTERNAL_ERROR); + } + + return Response.success(); + } +} diff --git a/disillusion/backend/src/main/java/net/lensfrex/disillusion/web/interceptor/TokenInterceptor.java b/disillusion/backend/src/main/java/net/lensfrex/disillusion/web/interceptor/TokenInterceptor.java new file mode 100644 index 0000000..fc5d314 --- /dev/null +++ b/disillusion/backend/src/main/java/net/lensfrex/disillusion/web/interceptor/TokenInterceptor.java @@ -0,0 +1,52 @@ +package net.lensfrex.disillusion.web.interceptor; + +import com.fasterxml.jackson.databind.ObjectMapper; +import net.lensfrex.disillusion.annotation.CheckAuth; +import net.lensfrex.disillusion.response.general.Response; +import net.lensfrex.disillusion.response.general.ResponseCode; +import net.lensfrex.disillusion.util.TokenUtil; +import org.springframework.stereotype.Service; +import org.springframework.web.method.HandlerMethod; +import org.springframework.web.servlet.HandlerInterceptor; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import java.io.PrintWriter; + +/** + * token验证拦截器 + */ +@Service +public class TokenInterceptor implements HandlerInterceptor { + @Override + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { + if (!needCheckAuth(handler)) { + return true; + } + + String token = request.getHeader("token"); + if (token == null || !TokenUtil.verify(token)) { + response.setCharacterEncoding("utf-8"); + response.setContentType("application/json;charset=utf-8"); + PrintWriter writer = response.getWriter(); + + String result = new ObjectMapper().writeValueAsString( + Response.error(ResponseCode.TOKEN_INVALID)); + writer.print(result); + writer.close(); + + return false; + } + + return true; + } + + private boolean needCheckAuth(Object handler) { + if (handler instanceof HandlerMethod handlerMethod) { + CheckAuth annotation = handlerMethod.getMethod().getAnnotation(CheckAuth.class); + return annotation != null; + } + + return false; + } +} \ No newline at end of file diff --git a/disillusion/backend/src/main/java/net/lensfrex/disillusion/web/request/OfferApplicationRequest.java b/disillusion/backend/src/main/java/net/lensfrex/disillusion/web/request/OfferApplicationRequest.java new file mode 100644 index 0000000..59f0906 --- /dev/null +++ b/disillusion/backend/src/main/java/net/lensfrex/disillusion/web/request/OfferApplicationRequest.java @@ -0,0 +1,32 @@ +package net.lensfrex.disillusion.web.request; + +import lombok.Data; + +@Data +public class OfferApplicationRequest { + /** + * 申请的offerId + */ + private String offerId; + + /** + * 简历id + */ + private String resumeId; + + public String offerId() { + return offerId; + } + + public void setOfferId(String offerId) { + this.offerId = offerId; + } + + public String resumeId() { + return resumeId; + } + + public void setResumeId(String resumeId) { + this.resumeId = resumeId; + } +} diff --git a/disillusion/backend/src/main/java/net/lensfrex/disillusion/web/request/OfferPublishRequest.java b/disillusion/backend/src/main/java/net/lensfrex/disillusion/web/request/OfferPublishRequest.java new file mode 100644 index 0000000..d15a185 --- /dev/null +++ b/disillusion/backend/src/main/java/net/lensfrex/disillusion/web/request/OfferPublishRequest.java @@ -0,0 +1,36 @@ +package net.lensfrex.disillusion.web.request; + +import lombok.Data; + +@Data +public class OfferPublishRequest { + /** + * 岗位名称 + */ + private String jobName; + + /** + * 工作地点 + */ + private String place; + + /** + * 要求 + */ + private String requirement; + + /** + * 预期薪资 + */ + private Integer salary; + + /** + * 目标人数 + */ + private Integer targetNumber; + + /** + * 详细信息 + */ + private String detail; +} diff --git a/disillusion/backend/src/main/java/net/lensfrex/disillusion/web/request/ResumeRequest.java b/disillusion/backend/src/main/java/net/lensfrex/disillusion/web/request/ResumeRequest.java new file mode 100644 index 0000000..2811cd9 --- /dev/null +++ b/disillusion/backend/src/main/java/net/lensfrex/disillusion/web/request/ResumeRequest.java @@ -0,0 +1,27 @@ +package net.lensfrex.disillusion.web.request; + +import lombok.Data; +import net.lensfrex.disillusion.model.StoreData; + +import java.io.Serializable; + +/** + * 简历 + */ +@Data +public class ResumeRequest { + /** + * 简历标题(名称) + */ + private String title; + + /** + * 简历备注信息 + */ + private String remark; + + /** + * 简历文件数据,base64 + */ + private String resumeFileBase64; +} diff --git a/disillusion/backend/src/main/java/net/lensfrex/disillusion/web/response/ApplicationResponse.java b/disillusion/backend/src/main/java/net/lensfrex/disillusion/web/response/ApplicationResponse.java new file mode 100644 index 0000000..eaefab1 --- /dev/null +++ b/disillusion/backend/src/main/java/net/lensfrex/disillusion/web/response/ApplicationResponse.java @@ -0,0 +1,37 @@ +package net.lensfrex.disillusion.web.response; + +import lombok.Data; +import net.lensfrex.disillusion.model.OfferApplication; + +/** + * 求职申请 + */ +@Data +public class ApplicationResponse { + private String id; + + /** + * 发布者id + */ + private String publisherId; + + /** + * 目标公司id + */ + private String targetId; + + /** + * 申请的offerId + */ + private String offerId; + + /** + * 简历id + */ + private String resumeId; + + /** + * 申请状态 + */ + private OfferApplication.Status status; +} \ No newline at end of file diff --git a/disillusion/backend/src/main/java/net/lensfrex/disillusion/web/response/EmployerApplicationResponse.java b/disillusion/backend/src/main/java/net/lensfrex/disillusion/web/response/EmployerApplicationResponse.java new file mode 100644 index 0000000..c196b28 --- /dev/null +++ b/disillusion/backend/src/main/java/net/lensfrex/disillusion/web/response/EmployerApplicationResponse.java @@ -0,0 +1,28 @@ +package net.lensfrex.disillusion.web.response; + +import lombok.Data; +import net.lensfrex.disillusion.model.OfferApplication; + +/** + * 求职申请 + */ +@Data +public class EmployerApplicationResponse { + private String id; + + /** + * 发布者id + */ + private String publisherId; + private String publisherName; + + /** + * 简历id + */ + private String resumeId; + + /** + * 申请状态 + */ + private OfferApplication.Status status; +} \ No newline at end of file diff --git a/disillusion/backend/src/main/java/net/lensfrex/disillusion/web/response/OfferListResponse.java b/disillusion/backend/src/main/java/net/lensfrex/disillusion/web/response/OfferListResponse.java new file mode 100644 index 0000000..1c6cfa9 --- /dev/null +++ b/disillusion/backend/src/main/java/net/lensfrex/disillusion/web/response/OfferListResponse.java @@ -0,0 +1,51 @@ +package net.lensfrex.disillusion.web.response; + +import lombok.Data; + +@Data +public class OfferListResponse { + private int total; + private int page; + private int size; + private OfferResponse[] result; + + @Data + public static class OfferResponse { + private String id; + + /** + * 发布公司 + */ + private String publisherId; + + /** + * 岗位名称 + */ + private String jobName; + + /** + * 工作地点 + */ + private String place; + + /** + * 要求 + */ + private String requirement; + + /** + * 预期薪资 + */ + private Integer salary; + + /** + * 目标人数 + */ + private Integer targetNumber; + + /** + * 详细信息 + */ + private String detail; + } +} diff --git a/disillusion/backend/src/main/java/net/lensfrex/disillusion/web/response/user/PublicEmployerInfoResponse.java b/disillusion/backend/src/main/java/net/lensfrex/disillusion/web/response/user/PublicEmployerInfoResponse.java new file mode 100644 index 0000000..3944978 --- /dev/null +++ b/disillusion/backend/src/main/java/net/lensfrex/disillusion/web/response/user/PublicEmployerInfoResponse.java @@ -0,0 +1,40 @@ +package net.lensfrex.disillusion.web.response.user; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +@Data +@EqualsAndHashCode(callSuper = false) +public class PublicEmployerInfoResponse extends UserInfoResponseBase { + private String id; + + /** + * 公司名称 + */ + private String name; + + /** + * 总部地址 + */ + private String place; + + /** + * 联系方式 + */ + private String contract; + + /** + * 公司简要介绍 + */ + private String shortDescribe; + + /** + * 公司介绍 + */ + private String describe; + + /** + * 所属行业 + */ + private String industry; +} diff --git a/disillusion/backend/src/main/java/net/lensfrex/disillusion/web/response/user/PublicStudentInfoResponse.java b/disillusion/backend/src/main/java/net/lensfrex/disillusion/web/response/user/PublicStudentInfoResponse.java new file mode 100644 index 0000000..d86f759 --- /dev/null +++ b/disillusion/backend/src/main/java/net/lensfrex/disillusion/web/response/user/PublicStudentInfoResponse.java @@ -0,0 +1,40 @@ +package net.lensfrex.disillusion.web.response.user; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +@Data +@EqualsAndHashCode(callSuper = false) +public class PublicStudentInfoResponse extends UserInfoResponseBase { + private String id; + + /** + * 真名 + */ + private String realName; + + /** + * 简介 + */ + private String shortDescribe; + + /** + * 介绍 + */ + private String describe; + + /** + * 电话号 + */ + private String phoneNumber; + + /** + * 年龄 + */ + private int age; + + /** + * 性别,0:男;1:女 + */ + private int sex; +} diff --git a/disillusion/backend/src/main/java/net/lensfrex/disillusion/web/response/user/UserInfoResponseBase.java b/disillusion/backend/src/main/java/net/lensfrex/disillusion/web/response/user/UserInfoResponseBase.java new file mode 100644 index 0000000..2881041 --- /dev/null +++ b/disillusion/backend/src/main/java/net/lensfrex/disillusion/web/response/user/UserInfoResponseBase.java @@ -0,0 +1,4 @@ +package net.lensfrex.disillusion.web.response.user; + +public class UserInfoResponseBase { +} \ No newline at end of file diff --git a/disillusion/backend/src/main/java/net/lensfrex/disillusion/web/service/employer/OfferService.java b/disillusion/backend/src/main/java/net/lensfrex/disillusion/web/service/employer/OfferService.java new file mode 100644 index 0000000..a830bfd --- /dev/null +++ b/disillusion/backend/src/main/java/net/lensfrex/disillusion/web/service/employer/OfferService.java @@ -0,0 +1,191 @@ +package net.lensfrex.disillusion.web.service.employer; + +import cn.hutool.core.lang.Validator; +import net.lensfrex.disillusion.dao.OfferApplicationTable; +import net.lensfrex.disillusion.dao.OfferTable; +import net.lensfrex.disillusion.dao.index.EmployerOfferIndex; +import net.lensfrex.disillusion.dao.index.application.OfferApplicationIndex; +import net.lensfrex.disillusion.data.ArrayList; +import net.lensfrex.disillusion.data.List; +import net.lensfrex.disillusion.model.MultiIndex; +import net.lensfrex.disillusion.model.employer.Employer; +import net.lensfrex.disillusion.model.employer.Offer; +import net.lensfrex.disillusion.web.request.OfferPublishRequest; +import net.lensfrex.disillusion.web.service.user.UserInfoService; +import org.apache.commons.lang.RandomStringUtils; +import org.springframework.beans.BeanUtils; +import org.springframework.stereotype.Service; + +import java.io.IOException; +import java.util.Arrays; + +@Service +public class OfferService { + private final EmployerOfferIndex employerOfferIndex; + private final OfferTable offerTable; + private final UserInfoService userInfoService; + + private final OfferApplicationIndex offerApplicationIndex; + private final OfferApplicationTable offerApplicationTable; + + public OfferService(EmployerOfferIndex EmployerOfferIndex, + OfferTable offerTable, UserInfoService userInfoService, + OfferApplicationIndex offerApplicationIndex, + OfferApplicationTable offerApplicationTable) { + + this.employerOfferIndex = EmployerOfferIndex; + this.offerTable = offerTable; + this.userInfoService = userInfoService; + this.offerApplicationIndex = offerApplicationIndex; + this.offerApplicationTable = offerApplicationTable; + } + + public void addOffer(String user, OfferPublishRequest request) throws IOException, ClassNotFoundException { + Offer offer = packageOffer(user, RandomStringUtils.randomAlphanumeric(8), request); + + MultiIndex multiIndex = employerOfferIndex.select(user); + if (multiIndex == null) { + multiIndex = new MultiIndex(user); + multiIndex.addValue(offer.getId()); + } else { + multiIndex.addValue(offer.getId()); + } + + offerTable.insertOrUpdate(offer); + employerOfferIndex.insertOrUpdate(multiIndex); + } + + public void updateOffer(String user, String offerId, OfferPublishRequest request) throws IOException, ClassNotFoundException { + MultiIndex multiIndex = employerOfferIndex.select(user); + if (multiIndex == null || !multiIndex.hasValue(offerId)) { + return; + } + + offerTable.insertOrUpdate(packageOffer(user, offerId, request)); + } + + private Offer packageOffer(String user, String id, OfferPublishRequest request) { + Offer offer = new Offer(); + offer.setPublisherId(user); + offer.setId(id); + + BeanUtils.copyProperties(request, offer); + return offer; + } + + public Offer[] getAllOfferByUser(String user) throws IOException, ClassNotFoundException { + MultiIndex index = employerOfferIndex.select(user); + if (index == null) { + return new Offer[0]; + } + + String[] ids = index.getAllValues(); + List offers = new ArrayList<>(ids.length); + for (String id : ids) { + if (id == null) { + continue; + } + + offers.add(offerTable.select(id)); + } + + return offers.toArray(new Offer[offers.size()]); + } + + public Offer[] searchOfferByUser(String keyWord) throws IOException, ClassNotFoundException { + List offers = new ArrayList<>(32); + List employers = userInfoService.getAllEmployerUserInfo(); + + for (int i = 0; i < employers.size(); i++) { + Employer employer = employers.get(i); + if (employer.getName().matches(".*?" + keyWord + ".*")) { + MultiIndex index = employerOfferIndex.select(employer.getId()); + if (index == null) { + return new Offer[0]; + } + + String[] ids = index.getAllValues(); + for (String id : ids) { + offers.add(offerTable.select(id)); + } + } + } + + return offers.toArray(new Offer[offers.size()]); + } + + public Offer[] getAllOfferBySalary(String minSalaryText) throws IOException, ClassNotFoundException { + if (!Validator.isNumber(minSalaryText)) { + return new Offer[0]; + } + + int minSalary = Integer.parseInt(minSalaryText); + List result = offerTable.getAll(); + List filterResult = new ArrayList<>(result.size()); + + for (int i = 0; i < result.size(); i++) { + Offer offer = result.get(i); + if (offer.getSalary() > minSalary) { + filterResult.add(offer); + } + } + + return filterResult.toArray(new Offer[0]); + } + + public Offer[] searchOfferByName(String keyWord) throws IOException, ClassNotFoundException { + List result = offerTable.getAll(); + List filterResult = new ArrayList<>(result.size()); + + for (int i = 0; i < result.size(); i++) { + Offer offer = result.get(i); + if (offer.getJobName().matches(".*?" + keyWord + ".*")) { + filterResult.add(offer); + } + } + + return filterResult.toArray(new Offer[0]); + } + + public void deleteOffer(String user, String id) throws IOException, ClassNotFoundException { + MultiIndex index = employerOfferIndex.select(user); + if (index == null || !index.hasValue(id)) { + return; + } + + index.removeValue(id); + employerOfferIndex.insertOrUpdate(index); + + MultiIndex offerApplication = offerApplicationIndex.select(id); + if (offerApplication == null) { + return; + } + + String[] applications = offerApplication.getAllValues(); + for (String application : applications) { + offerApplicationTable.delete(application); + } + + offerApplicationIndex.insertOrUpdate(offerApplication); + offerTable.delete(id); + } + + public Offer getOffer(String id) throws IOException, ClassNotFoundException { + return offerTable.select(id); + } + + public Offer[] getOffer(int page, int size) throws IOException, ClassNotFoundException { + List offerList = offerTable.getAll(); + Offer[] allOffer = new Offer[size]; + offerList.toArray(allOffer); + + return Arrays.copyOfRange(allOffer, page * size, size); + } + + public Offer[] getAllOffer() throws IOException, ClassNotFoundException { + List offerList = offerTable.getAll(); + Offer[] allOffer = new Offer[offerList.size()]; + + return offerList.toArray(allOffer); + } +} diff --git a/disillusion/backend/src/main/java/net/lensfrex/disillusion/web/service/student/ApplicationService.java b/disillusion/backend/src/main/java/net/lensfrex/disillusion/web/service/student/ApplicationService.java new file mode 100644 index 0000000..2e875da --- /dev/null +++ b/disillusion/backend/src/main/java/net/lensfrex/disillusion/web/service/student/ApplicationService.java @@ -0,0 +1,169 @@ +package net.lensfrex.disillusion.web.service.student; + +import net.lensfrex.disillusion.dao.DaoBase; +import net.lensfrex.disillusion.dao.OfferApplicationTable; +import net.lensfrex.disillusion.dao.OfferTable; +import net.lensfrex.disillusion.dao.index.application.EmployerApplicationIndex; +import net.lensfrex.disillusion.dao.index.application.OfferApplicationIndex; +import net.lensfrex.disillusion.dao.index.application.StudentApplicationIndex; +import net.lensfrex.disillusion.data.ArrayList; +import net.lensfrex.disillusion.data.List; +import net.lensfrex.disillusion.model.OfferApplication; +import net.lensfrex.disillusion.model.MultiIndex; +import net.lensfrex.disillusion.model.User; +import net.lensfrex.disillusion.model.employer.Offer; +import net.lensfrex.disillusion.web.request.OfferApplicationRequest; +import net.lensfrex.disillusion.web.service.user.UserService; +import org.apache.commons.lang.RandomStringUtils; +import org.springframework.stereotype.Service; + +import java.io.IOException; + +@Service +public class ApplicationService { + private final OfferTable offerTable; + + private final OfferApplicationIndex offerApplicationIndex; + private final StudentApplicationIndex studentApplicationIndex; + private final EmployerApplicationIndex employerApplicationIndex; + + private final OfferApplicationTable offerApplicationTable; + + private final UserService userService; + + public ApplicationService(OfferTable offerTable, + OfferApplicationIndex offerApplicationIndex, + StudentApplicationIndex studentApplicationIndex, + EmployerApplicationIndex employerApplicationIndex, + OfferApplicationTable offerApplicationTable, + UserService userService) { + + this.offerTable = offerTable; + + this.offerApplicationIndex = offerApplicationIndex; + this.studentApplicationIndex = studentApplicationIndex; + this.employerApplicationIndex = employerApplicationIndex; + this.offerApplicationTable = offerApplicationTable; + this.userService = userService; + } + + public void addApplication(String user, OfferApplicationRequest request) throws IOException, ClassNotFoundException { + Offer offer = offerTable.select(request.getOfferId()); + if (offer == null) { + return; + } + + String employer = offer.getPublisherId(); + User employerUser = userService.getAccount(employer); + if (employerUser == null) { + return; + } + + OfferApplication application = packageApplication(user, RandomStringUtils.randomAlphanumeric(8), employer, request); + + offerApplicationTable.insertOrUpdate(application); + + addRelationIndex(offerApplicationIndex, offer.getId(), application.getId()); + addRelationIndex(studentApplicationIndex, user, application.getId()); + addRelationIndex(employerApplicationIndex, employer, offer.getId()); + } + + private OfferApplication packageApplication(String user, String applicationId, String target, + OfferApplicationRequest request) { + + OfferApplication application = new OfferApplication(); + + application.setStatus(OfferApplication.Status.WAITING); + + application.setOfferId(request.getOfferId()); + application.setResumeId(request.getResumeId()); + + application.setPublisherId(user); + application.setId(applicationId); + application.setTargetId(target); + + return application; + } + + private void addRelationIndex(DaoBase index, String id, String value) + throws IOException, ClassNotFoundException { + + MultiIndex multiIndex = index.select(id); + if (multiIndex == null) { + multiIndex = new MultiIndex(id); + multiIndex.addValue(value); + } else { + multiIndex.addValue(value); + } + + index.insertOrUpdate(multiIndex); + } + + private void removeRelationIndex(DaoBase index, String id, String value) + throws IOException, ClassNotFoundException { + + MultiIndex multiIndex = index.select(id); + if (multiIndex == null || !multiIndex.hasValue(id)) { + return; + } + + multiIndex.removeValue(value); + index.insertOrUpdate(multiIndex); + } + + public OfferApplication[] getAllApplicationByUser(String user) throws IOException, ClassNotFoundException { + return getFromIndex(studentApplicationIndex, user); + } + + public OfferApplication[] getAllApplicationByTarget(String employer) throws IOException, ClassNotFoundException { + return getFromIndex(employerApplicationIndex, employer); + } + + public OfferApplication[] getAllApplicationByOfferId(String offerId) throws IOException, ClassNotFoundException { + return getFromIndex(offerApplicationIndex, offerId); + } + + private OfferApplication[] getFromIndex(DaoBase indexDao, String queryKey) + throws IOException, ClassNotFoundException { + + MultiIndex index = indexDao.select(queryKey); + if (index == null) { + return new OfferApplication[0]; + } + + String[] ids = index.getAllValues(); + List applications = new ArrayList<>(ids.length); + for (String id : ids) { + applications.add(offerApplicationTable.select(id)); + } + + return applications.toArray(new OfferApplication[applications.size()]); + } + + public void deleteOfferApplication(String user, String id) + throws IOException, ClassNotFoundException { + + removeRelationIndex(offerApplicationIndex, user, id); + removeRelationIndex(employerApplicationIndex, user, id); + removeRelationIndex(studentApplicationIndex, user, id); + + offerApplicationTable.delete(id); + } + + public OfferApplication getOfferApplication(String id) throws IOException, ClassNotFoundException { + return offerApplicationTable.select(id); + } + + public void changeApplicationStatus(String id, OfferApplication.Status status) + throws IOException, ClassNotFoundException { + + OfferApplication application = getOfferApplication(id); + if (application == null) { + return; + } + + application.setStatus(status); + + offerApplicationTable.insertOrUpdate(application); + } +} diff --git a/disillusion/backend/src/main/java/net/lensfrex/disillusion/web/service/student/ResumeService.java b/disillusion/backend/src/main/java/net/lensfrex/disillusion/web/service/student/ResumeService.java new file mode 100644 index 0000000..3e80749 --- /dev/null +++ b/disillusion/backend/src/main/java/net/lensfrex/disillusion/web/service/student/ResumeService.java @@ -0,0 +1,131 @@ +package net.lensfrex.disillusion.web.service.student; + +import cn.hutool.core.codec.Base64; +import cn.hutool.core.io.FileUtil; +import net.lensfrex.disillusion.dao.ResumeTable; +import net.lensfrex.disillusion.dao.index.StudentResumeIndex; +import net.lensfrex.disillusion.data.ArrayList; +import net.lensfrex.disillusion.data.List; +import net.lensfrex.disillusion.model.MultiIndex; +import net.lensfrex.disillusion.model.student.Resume; +import net.lensfrex.disillusion.web.request.ResumeRequest; +import org.apache.commons.lang.RandomStringUtils; +import org.springframework.stereotype.Service; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardOpenOption; + +@Service +public class ResumeService { + + private final StudentResumeIndex studentResumeIndex; + private final ResumeTable resumeTable; + + private static final String resumeFileLocationBase = "database/files/resume"; + private static final File baseDir = new File(resumeFileLocationBase); + + public ResumeService(StudentResumeIndex studentResumeIndex, + ResumeTable resumeTable) { + + this.studentResumeIndex = studentResumeIndex; + this.resumeTable = resumeTable; + } + + public void addResume(String user, ResumeRequest request) throws IOException, ClassNotFoundException { + String resumeId = RandomStringUtils.randomAlphanumeric(8); + + Resume resume = new Resume(); + resume.setOwnerId(user); + resume.setId(resumeId); + resume.setTitle(request.getTitle()); + resume.setRemark(request.getRemark()); + resume.setResumeFileId(resumeId); + + resumeTable.insertOrUpdate(resume); + + if (!baseDir.exists()) { + baseDir.mkdirs(); + } + + Files.writeString(Paths.get(resumeFileLocationBase, resumeId), + request.getResumeFileBase64(), + StandardOpenOption.CREATE, StandardOpenOption.WRITE); + + MultiIndex multiIndex = studentResumeIndex.select(user); + if (multiIndex == null) { + multiIndex = new MultiIndex(user); + multiIndex.addValue(resume.getId()); + } else { + multiIndex.addValue(resume.getId()); + } + + studentResumeIndex.insertOrUpdate(multiIndex); + } + + public void updateResume(String user, String resumeId, ResumeRequest request) throws IOException, ClassNotFoundException { + MultiIndex multiIndex = studentResumeIndex.select(user); + if (multiIndex == null || !multiIndex.hasValue(resumeId)) { + return; + } + + Resume resume = new Resume(); + resume.setOwnerId(user); + resume.setId(resumeId); + resume.setTitle(request.getTitle()); + resume.setRemark(request.getRemark()); + resume.setResumeFileId(resumeId); + + updateResume(resume, request.getResumeFileBase64()); + } + + public void updateResume(Resume resume, String resumeFileData) throws IOException, ClassNotFoundException { + if (resumeFileData != null) { + Files.writeString(Paths.get(resumeFileLocationBase, resume.getId()), resumeFileData, StandardOpenOption.WRITE); + } + + resumeTable.insertOrUpdate(resume); + } + + public Resume[] getAllResume(String user) throws IOException, ClassNotFoundException { + MultiIndex index = studentResumeIndex.select(user); + if (index == null) { + return new Resume[0]; + } + + String[] ids = index.getAllValues(); + List resumes = new ArrayList<>(ids.length); + for (String id : ids) { + resumes.add(resumeTable.select(id)); + } + + return resumes.toArray(new Resume[resumes.size()]); + } + + public void deleteResume(String user, String id) throws IOException, ClassNotFoundException { + MultiIndex index = studentResumeIndex.select(user); + if (index == null || !index.hasValue(id)) { + return; + } + + index.removeValue(id); + studentResumeIndex.insertOrUpdate(index); + resumeTable.delete(id); + } + + public Resume getResume(String id) throws IOException, ClassNotFoundException { + return resumeTable.select(id); + } + + public String getResumeFileData(String fileId) throws IOException { + Path file = Paths.get(resumeFileLocationBase, fileId); + if (FileUtil.exists(file, true)) { + return Files.readString(file); + } else { + return Base64.encode("文件不存在"); + } + } +} diff --git a/disillusion/backend/src/main/java/net/lensfrex/disillusion/web/service/user/UserInfoService.java b/disillusion/backend/src/main/java/net/lensfrex/disillusion/web/service/user/UserInfoService.java new file mode 100644 index 0000000..6d88f9e --- /dev/null +++ b/disillusion/backend/src/main/java/net/lensfrex/disillusion/web/service/user/UserInfoService.java @@ -0,0 +1,41 @@ +package net.lensfrex.disillusion.web.service.user; + +import net.lensfrex.disillusion.dao.UserInfoTable; +import net.lensfrex.disillusion.data.ArrayList; +import net.lensfrex.disillusion.data.List; +import net.lensfrex.disillusion.model.UserInfo; +import net.lensfrex.disillusion.model.employer.Employer; +import org.springframework.stereotype.Service; + +import java.io.IOException; + +@Service +public class UserInfoService { + private final UserInfoTable userInfoTable; + + public UserInfoService(UserInfoTable userInfoTable) { + this.userInfoTable = userInfoTable; + } + + public UserInfo getUserInfoById(String id) throws IOException, ClassNotFoundException { + return userInfoTable.select(id); + } + + public List getAllEmployerUserInfo() throws IOException, ClassNotFoundException { + List allUserInfo = userInfoTable.getAll(); + List employers = new ArrayList<>(allUserInfo.size()); + + for (int i = 0; i < allUserInfo.size(); i++) { + UserInfo userInfo = allUserInfo.get(i); + if (userInfo instanceof Employer employer) { + employers.add(employer); + } + } + + return employers; + } + + public void saveUserInfoById(UserInfo userInfo) throws IOException, ClassNotFoundException { + userInfoTable.insertOrUpdate(userInfo); + } +} diff --git a/disillusion/backend/src/main/java/net/lensfrex/disillusion/web/service/user/UserService.java b/disillusion/backend/src/main/java/net/lensfrex/disillusion/web/service/user/UserService.java new file mode 100644 index 0000000..940d1da --- /dev/null +++ b/disillusion/backend/src/main/java/net/lensfrex/disillusion/web/service/user/UserService.java @@ -0,0 +1,36 @@ +package net.lensfrex.disillusion.web.service.user; + +import net.lensfrex.disillusion.dao.UserTable; +import net.lensfrex.disillusion.data.List; +import net.lensfrex.disillusion.model.User; +import org.mindrot.jbcrypt.BCrypt; +import org.springframework.stereotype.Service; + +import java.io.IOException; + +@Service +public class UserService { + private final UserTable userTable; + + public UserService(UserTable userTable) { + this.userTable = userTable; + } + + public User getAccount(String username) throws IOException, ClassNotFoundException { + return userTable.select(username); + } + + public List getAllUser() throws IOException, ClassNotFoundException { + return userTable.getAll(); + } + + public void registerNewUser(String username, String password, User.UserType userType) throws IOException, ClassNotFoundException { + String hashedPassword = BCrypt.hashpw(password, BCrypt.gensalt()); + User newUser = new User(userType, username, hashedPassword); + userTable.insertOrUpdate(newUser); + } + + public void updateUser(User user) throws IOException, ClassNotFoundException { + userTable.insertOrUpdate(user); + } +} diff --git a/disillusion/backend/src/main/resources/application.properties b/disillusion/backend/src/main/resources/application.properties new file mode 100644 index 0000000..c959e3c --- /dev/null +++ b/disillusion/backend/src/main/resources/application.properties @@ -0,0 +1,6 @@ +disillusion.database-dir=./database +disillusion.tokenSecretKey=&&xenoblade&&test&&disillusion&& +spring.jackson.default-property-inclusion=non_null +springdoc.swagger-ui.path=/api.html +server.servlet.context-path=/api +server.port=8888 \ No newline at end of file diff --git a/disillusion/backend/src/test/java/net/lensfrex/disillusion/BackendApplicationTests.java b/disillusion/backend/src/test/java/net/lensfrex/disillusion/BackendApplicationTests.java new file mode 100644 index 0000000..9370d1e --- /dev/null +++ b/disillusion/backend/src/test/java/net/lensfrex/disillusion/BackendApplicationTests.java @@ -0,0 +1,13 @@ +package net.lensfrex.disillusion; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class BackendApplicationTests { + + @Test + void contextLoads() { + } + +} diff --git a/disillusion/common/.flattened-pom.xml b/disillusion/common/.flattened-pom.xml new file mode 100644 index 0000000..4a9a60f --- /dev/null +++ b/disillusion/common/.flattened-pom.xml @@ -0,0 +1,40 @@ + + + 4.0.0 + net.lensfrex + common + 0.0.1-SNAPSHOT + + + cn.hutool + hutool-json + 5.8.12 + compile + + + jakarta.annotation + jakarta.annotation-api + 2.1.1 + compile + + + javax.annotation + javax.annotation-api + 1.3.2 + compile + + + org.projectlombok + lombok + 1.18.22 + compile + + + org.mapstruct + mapstruct + 1.5.3.Final + compile + + + diff --git a/disillusion/common/pom.xml b/disillusion/common/pom.xml new file mode 100644 index 0000000..5568f1b --- /dev/null +++ b/disillusion/common/pom.xml @@ -0,0 +1,27 @@ + + + 4.0.0 + + net.lensfrex + disillusion + ${revision} + + + common + + + 17 + 17 + UTF-8 + + + + + cn.hutool + hutool-json + 5.8.12 + + + \ No newline at end of file diff --git a/disillusion/common/src/main/java/net/lensfrex/disillusion/data/ArrayList.java b/disillusion/common/src/main/java/net/lensfrex/disillusion/data/ArrayList.java new file mode 100644 index 0000000..67285e3 --- /dev/null +++ b/disillusion/common/src/main/java/net/lensfrex/disillusion/data/ArrayList.java @@ -0,0 +1,178 @@ +package net.lensfrex.disillusion.data; + +import java.io.Serial; +import java.util.Arrays; +import java.util.Objects; + +/** + * 简略版的顺序表(ArrayList),参考了Java内置的ArrayList实现 + * + * @param + */ +public class ArrayList implements List { + @Serial + private static final long serialVersionUID = 114514L; + + private static final int DEFAULT_MAX_SIZE = 10; + + private static final Object[] EMPTY_ARRAY = {}; + + private int size = 0; + + private Object[] data; + + public ArrayList(int initialSize) { + data = new Object[initialSize]; + } + + public ArrayList() { + data = EMPTY_ARRAY; + } + + @Override + @SuppressWarnings("unchecked") + public T get(int index) { + if (index >= size) { + throw new IndexOutOfBoundsException(String.format("index %s is out of size %s", index, size)); + } + + return (T) data[index]; + } + + @Override + public int size() { + return this.size; + } + + @Override + public boolean isEmpty() { + return size == 0; + } + + @Override + public void add(T value) { + if (value == null) { + return; + } + + if (size == data.length) { + this.resize(); + } + + this.data[size++] = value; + } + + @Override + public void add(T value, int index) { + if (value == null) { + return; + } + + checkIndex(index); + if (data.length == size) { + this.resize(); + } + + System.arraycopy(this.data, index, + this.data, index + 1, + size - index); + + this.data[index] = value; + size++; + } + + @Override + public void set(int index, T newValue) { + if (newValue == null) { + return; + } + + checkIndex(index); + + this.data[index] = newValue; + } + + @Override + public void remove(int index) { + checkIndex(index); + + int numMoved = size - index - 1; + if (numMoved > 0) { + System.arraycopy(this.data, index + 1, + this.data, index, numMoved); + } + this.data[--size] = null; + this.trim(); + } + + @Override + public void remove(Object value) { + int index = indexOf(value); + if (index >= 0) { + remove(index); + } + } + + public void trim() { + if (size < data.length) { + data = (size == 0) ? EMPTY_ARRAY : Arrays.copyOf(data, size); + } + } + + @Override + public int indexOf(Object value) { + for (int i = 0; i < size; i++) { + if (Objects.equals(value, data[i])) { + return i; + } + } + + return -1; + } + + @Override + public Object[] toArray() { + return Arrays.copyOf(data, data.length); + } + + @SuppressWarnings("unchecked") + public T[] toArray(T[] array) { + if (array.length < size) { + return (T[]) Arrays.copyOf(data, size, array.getClass()); + } + System.arraycopy(data, 0, array, 0, size); + + return array; + } + + @Override + public boolean contains(Object obj) { + for (int i = 0; i < size; i++) { + if (data[i].equals(obj)) { + return true; + } + } + + return false; + } + + private void checkIndex(int index) { + if (index < 0 || index > size) { + throw new IndexOutOfBoundsException(indexOutOfBoundsMessage(index)); + } + } + + private String indexOutOfBoundsMessage(int index) { + return String.format("Index %s out of %s", index, size); + } + + private void resize() { + int newSize = (this.size + 1) * 2; + + if (this.data != EMPTY_ARRAY) { + this.data = Arrays.copyOf(data, newSize); + } else { + this.data = new Object[Math.max(newSize, DEFAULT_MAX_SIZE)]; + } + } +} diff --git a/disillusion/common/src/main/java/net/lensfrex/disillusion/data/Hash.java b/disillusion/common/src/main/java/net/lensfrex/disillusion/data/Hash.java new file mode 100644 index 0000000..28bc65b --- /dev/null +++ b/disillusion/common/src/main/java/net/lensfrex/disillusion/data/Hash.java @@ -0,0 +1,120 @@ +package net.lensfrex.disillusion.data; + +import java.io.Serializable; + +// 极其简陋的hashMap实现 +public class Hash implements Serializable { + private static final int TABLE_SIZE = 32; + + private final Node[] table; + + private int size = 0; + + @SuppressWarnings({"unchecked"}) + public Hash() { + this.table = (Node[]) new Node[TABLE_SIZE]; + } + + public void put(K key, V value) { + int index = Math.abs(key.hashCode()) % TABLE_SIZE; + + if (table[index] == null) { + table[index] = new Node<>(key, value, null); + + } else { + Node node = table[index]; + // key中第一个节点 + if (node.key.equals(key)) { + node.value = value; + return; + } + + while (node.next != null) { + // key对应的值已存在,直接替换 + if (node.next.key.equals(key)) { + node.next.value = value; + size++; + + return; + } + node = node.next; + } + + // key对应值不存在,插入 + node.next = new Node<>(key, value, null); + } + } + + public V get(K key) { + int index = Math.abs(key.hashCode()) % TABLE_SIZE; + + Node node = table[index]; + while (node != null) { + if (node.key.equals(key)) { + return node.value; + } + node = node.next; + } + + return null; + } + + public void delete(K key) { + int index = Math.abs(key.hashCode()) % TABLE_SIZE; + + Node node = table[index]; + while (node != null) { + if (node.key.equals(key)) { + table[index] = node.next; + + node.value = null; + node.next = null; + + size--; + + return; + } + + node = node.next; + } + } + + public List allValues() { + List result = new ArrayList<>(); + + for (Node node : table) { + while (node != null) { + result.add(node.value); + node = node.next; + } + } + + return result; + } + + public List allKeys() { + List result = new ArrayList<>(); + + for (Node node : table) { + while (node != null && !result.contains(node.key)) { + result.add(node.key); + node = node.next; + } + } + + return result; + } + + private static class Node implements Serializable { + private final K key; + + private V value; + private Node next; + + public Node(K key, V value, Node next) { + this.key = key; + this.value = value; + this.next = next; + } + } +} diff --git a/disillusion/common/src/main/java/net/lensfrex/disillusion/data/LinkedList.java b/disillusion/common/src/main/java/net/lensfrex/disillusion/data/LinkedList.java new file mode 100644 index 0000000..00180d7 --- /dev/null +++ b/disillusion/common/src/main/java/net/lensfrex/disillusion/data/LinkedList.java @@ -0,0 +1,217 @@ +package net.lensfrex.disillusion.data; + +import java.util.Arrays; +import java.util.Objects; + +public class LinkedList implements List { + private int size = 0; + + private Node first; + private Node last; + + public LinkedList() { + } + + @Override + public T get(int index) { + return node(index).data; + } + + private Node node(int index) { + if (index < size / 2) { + return getNodeForward(index); + } else { + return getNodeBackward(index); + } + } + + private Node getNodeForward(int index) { + Node node = first; + for (int i = 0; i < index; i++) { + node = node.next; + } + + return node; + } + + private Node getNodeBackward(int index) { + Node node = last; + for (int i = size - 1; i > index; i--) { + node = node.prev; + } + + return node; + } + + @Override + public int size() { + return this.size; + } + + @Override + public boolean isEmpty() { + return this.size == 0; + } + + @Override + public void add(T value) { + Node newNode = new Node<>(this.last, value, null); + // 空表 + if (this.first == null) { + this.first = newNode; + } else { + this.last.next = newNode; + } + + this.last = newNode; + size++; + } + + @Override + public void add(T value, int index) { + checkPositionIndex(index); + if (index == size) { + add(value); + } + + Node nativeNode = node(index); + Node prev = nativeNode.prev; + + Node newNode = new Node<>(prev, value, nativeNode); + nativeNode.prev = newNode; + + // 表头位置 + if (prev == null) { + first = newNode; + } else { + prev.next = newNode; + } + + size++; + } + + @Override + public void set(int index, T newValue) { + Node node = node(index); + node.data = newValue; + } + + @Override + public void remove(int index) { + checkElementIndex(index); + + unlink(node(index)); + size--; + } + + @Override + public void remove(Object value) { + Node node = first; + while (node.next != null) { + if (Objects.equals(value, node.data)) { + unlink(node); + return; + } + + node = node.next; + } + } + + private void unlink(Node node) { + Node prev = node.prev; + Node next = node.next; + + // 表头位置 + if (prev == null) { + this.first = next; + } else { + prev.next = next; + node.next = null; + } + + // 表尾位置 + if (next == null) { + this.last = prev; + } else { + next.prev = prev; + node.prev = null; + } + + node.data = null; + } + + @Override + public int indexOf(Object value) { + Node node = first; + if (node == null) { + return -1; + } + + for (int i = 0; node.next != null; i++) { + if (Objects.equals(value, node.data)) { + return i; + } + + node = node.next; + } + + return -1; + } + + @Override + @SuppressWarnings("unchecked") + public T[] toArray() { + Object[] data = new Object[size]; + + Node node = first; + for (int i = 0; i < size; i++) { + data[i] = node; + node = node.next; + } + + return (T[]) Arrays.copyOf(data, data.length); + } + + @Override + public T[] toArray(T[] array) { + return null; + } + + @Override + public boolean contains(Object obj) { + Node node = first; + for (int i = 0; i < size; i++) { + if (obj.equals(node.data)){ + return true; + } + + node = node.next; + } + + return false; + } + + private void checkPositionIndex(int index) { + if (index > size) { + throw new IndexOutOfBoundsException(String.format("Index %s out of %s", index, size)); + } + } + + private void checkElementIndex(int index) { + if (index >= size) { + throw new IndexOutOfBoundsException(String.format("Index %s out of %s", index, size)); + } + } + + private static class Node { + Node prev; + T data; + Node next; + + public Node(Node prev, T data, Node next) { + this.prev = prev; + this.data = data; + this.next = next; + } + } +} diff --git a/disillusion/common/src/main/java/net/lensfrex/disillusion/data/List.java b/disillusion/common/src/main/java/net/lensfrex/disillusion/data/List.java new file mode 100644 index 0000000..c2ae77c --- /dev/null +++ b/disillusion/common/src/main/java/net/lensfrex/disillusion/data/List.java @@ -0,0 +1,29 @@ +package net.lensfrex.disillusion.data; + +import java.io.Serializable; + +public interface List extends Serializable { + T get(int index); + + int size(); + + boolean isEmpty(); + + void add(T value); + + void add(T value, int index); + + void set(int index, T newValue); + + void remove(int index); + + void remove(Object value); + + int indexOf(Object value); + + boolean contains(Object obj); + + Object[] toArray(); + + T[] toArray(T[] array); +} diff --git a/disillusion/common/src/main/java/net/lensfrex/disillusion/data/Map.java b/disillusion/common/src/main/java/net/lensfrex/disillusion/data/Map.java new file mode 100644 index 0000000..caa4e0e --- /dev/null +++ b/disillusion/common/src/main/java/net/lensfrex/disillusion/data/Map.java @@ -0,0 +1,6 @@ +package net.lensfrex.disillusion.data; + +import java.io.Serializable; + +public interface Map extends Serializable { +} diff --git a/disillusion/common/src/main/java/net/lensfrex/disillusion/data/db/Table.java b/disillusion/common/src/main/java/net/lensfrex/disillusion/data/db/Table.java new file mode 100644 index 0000000..f0e0a02 --- /dev/null +++ b/disillusion/common/src/main/java/net/lensfrex/disillusion/data/db/Table.java @@ -0,0 +1,40 @@ +package net.lensfrex.disillusion.data.db; + +import net.lensfrex.disillusion.data.ArrayList; +import net.lensfrex.disillusion.data.Hash; + +public class Table { + private final ArrayList> page; + private final ArrayList keys; + + private final Hash primaryKeyIndex = new Hash<>(); + + private final Hash index = new Hash<>(); + private final ArrayList indexKeys; + + public Table(String[] fields, String... indexFields) { + this.page = new ArrayList<>(); + this.keys = new ArrayList<>(); + for (String field : fields) { + keys.add(field); + } + + indexKeys= new ArrayList<>(indexFields.length); + for (String indexField : indexFields) { + indexKeys.add(indexField); + } + } + + public void insert(String[] fields, String[] value) { + Hash record = new Hash<>(); + for (int i = 0; i < fields.length; i++) { + if (!keys.contains(fields[i])) { + throw new IllegalArgumentException("Field '%s' doesn't exist."); + } + + record.put(fields[i], value[i]); + } + + primaryKeyIndex.put(fields[1], page.size()); + } +} diff --git a/disillusion/common/src/main/java/net/lensfrex/disillusion/model/MultiIndex.java b/disillusion/common/src/main/java/net/lensfrex/disillusion/model/MultiIndex.java new file mode 100644 index 0000000..1e3d325 --- /dev/null +++ b/disillusion/common/src/main/java/net/lensfrex/disillusion/model/MultiIndex.java @@ -0,0 +1,44 @@ +package net.lensfrex.disillusion.model; + +import lombok.Data; +import net.lensfrex.disillusion.data.ArrayList; + +/** + * 哈希一对多索引,用于对取值不唯一的字段的查询 + */ +@Data +public class MultiIndex implements StoreData { + private final String key; + private final ArrayList values; + + public MultiIndex(String key) { + this.key = key; + this.values = new ArrayList<>(8); + } + + public void addValue(String value) { + values.add(value); + } + + public String[] getAllValues() { + return values.toArray(new String[values.size()]); + } + + public void removeValue(Object target) { + values.remove(target); + } + + public boolean hasValue(Object obj) { + return values.contains(obj); + } + + @Override + public String getId() { + return key; + } + + @Override + public void setId(String id) { + + } +} \ No newline at end of file diff --git a/disillusion/common/src/main/java/net/lensfrex/disillusion/model/OfferApplication.java b/disillusion/common/src/main/java/net/lensfrex/disillusion/model/OfferApplication.java new file mode 100644 index 0000000..53c9f26 --- /dev/null +++ b/disillusion/common/src/main/java/net/lensfrex/disillusion/model/OfferApplication.java @@ -0,0 +1,42 @@ +package net.lensfrex.disillusion.model; + +import lombok.Data; + +import java.io.Serializable; + +/** + * 求职申请 + */ +@Data +public class OfferApplication implements Serializable, StoreData { + private String id; + + /** + * 目标公司id + */ + private String targetId; + + /** + * 求职发起者id + */ + private String publisherId; + + /** + * 申请的offerId + */ + private String offerId; + + /** + * 简历id + */ + private String resumeId; + + /** + * 申请状态 + */ + private Status status; + + public enum Status { + ACCEPTED, WAITING, REJECTED + } +} \ No newline at end of file diff --git a/disillusion/common/src/main/java/net/lensfrex/disillusion/model/OfferStatus.java b/disillusion/common/src/main/java/net/lensfrex/disillusion/model/OfferStatus.java new file mode 100644 index 0000000..2c7e2d8 --- /dev/null +++ b/disillusion/common/src/main/java/net/lensfrex/disillusion/model/OfferStatus.java @@ -0,0 +1,8 @@ +package net.lensfrex.disillusion.model; + +import lombok.Data; + +@Data +public class OfferStatus implements StoreData { + private String id; +} diff --git a/disillusion/common/src/main/java/net/lensfrex/disillusion/model/StoreData.java b/disillusion/common/src/main/java/net/lensfrex/disillusion/model/StoreData.java new file mode 100644 index 0000000..4facfbd --- /dev/null +++ b/disillusion/common/src/main/java/net/lensfrex/disillusion/model/StoreData.java @@ -0,0 +1,14 @@ +package net.lensfrex.disillusion.model; + +import java.io.Serializable; + +public interface StoreData extends Serializable { + String id = ""; + + default String getId() { + return id; + } + + void setId(String id); + boolean equals(Object o); +} diff --git a/disillusion/common/src/main/java/net/lensfrex/disillusion/model/User.java b/disillusion/common/src/main/java/net/lensfrex/disillusion/model/User.java new file mode 100644 index 0000000..24d6270 --- /dev/null +++ b/disillusion/common/src/main/java/net/lensfrex/disillusion/model/User.java @@ -0,0 +1,61 @@ +package net.lensfrex.disillusion.model; + +import lombok.Data; + +import java.io.Serializable; +import java.util.Objects; + +@Data +public class User implements Serializable, StoreData { + private UserType userType; + + private final String username; + private final String password; + + public enum UserType { + STUDENT(0, "学生"), + EMPLOYER(1, "用人单位"), + ; + + public final int code; + public final String name; + + UserType(int code, String name) { + this.code = code; + this.name = name; + } + } + + public User(UserType userType, String username, String password) { + if (username == null) { + throw new NullPointerException("Field 'username' can't be null"); + } + + this.userType = userType; + this.username = username; + this.password = password; + } + + @Override + public String getId() { + return username; + } + + @Override + public void setId(String id) { + + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + User user = (User) o; + return username.equals(user.username); + } + + @Override + public int hashCode() { + return Objects.hash(username); + } +} \ No newline at end of file diff --git a/disillusion/common/src/main/java/net/lensfrex/disillusion/model/UserInfo.java b/disillusion/common/src/main/java/net/lensfrex/disillusion/model/UserInfo.java new file mode 100644 index 0000000..ba8e2b6 --- /dev/null +++ b/disillusion/common/src/main/java/net/lensfrex/disillusion/model/UserInfo.java @@ -0,0 +1,5 @@ +package net.lensfrex.disillusion.model; + +public interface UserInfo extends StoreData { + String getId(); +} diff --git a/disillusion/common/src/main/java/net/lensfrex/disillusion/model/employer/Employer.java b/disillusion/common/src/main/java/net/lensfrex/disillusion/model/employer/Employer.java new file mode 100644 index 0000000..d5ababa --- /dev/null +++ b/disillusion/common/src/main/java/net/lensfrex/disillusion/model/employer/Employer.java @@ -0,0 +1,60 @@ +package net.lensfrex.disillusion.model.employer; + +import lombok.Data; +import net.lensfrex.disillusion.model.UserInfo; +import net.lensfrex.disillusion.model.StoreData; +import net.lensfrex.disillusion.model.student.Student; + +import java.io.Serializable; +import java.util.Objects; + +/** + * 用人单位信息 + */ +@Data +public class Employer implements UserInfo, Serializable, StoreData { + private String id; + + /** + * 公司名称 + */ + private String name; + + /** + * 总部地址 + */ + private String place; + + /** + * 联系方式 + */ + private String contract; + + /** + * 公司简要介绍 + */ + private String shortDescribe; + + /** + * 公司介绍 + */ + private String describe; + + /** + * 所属行业 + */ + private String industry; + + public static Employer getDefaultInfo(String id) { + Employer employer = new Employer(); + employer.setId(id); + employer.setName("不知道是什么公司"); + employer.setPlace("不知道公司在哪里"); + employer.setShortDescribe("简短的介绍"); + employer.setDescribe("还没有什么描述呢"); + employer.setContract("400-1010101010"); + employer.setIndustry("不知道是什么行业"); + + return employer; + } +} diff --git a/disillusion/common/src/main/java/net/lensfrex/disillusion/model/employer/Offer.java b/disillusion/common/src/main/java/net/lensfrex/disillusion/model/employer/Offer.java new file mode 100644 index 0000000..80d4f27 --- /dev/null +++ b/disillusion/common/src/main/java/net/lensfrex/disillusion/model/employer/Offer.java @@ -0,0 +1,63 @@ +package net.lensfrex.disillusion.model.employer; + +import lombok.Data; +import net.lensfrex.disillusion.model.StoreData; + +import java.io.Serializable; +import java.util.Objects; + +/** + * 招聘信息 + */ +@Data +public class Offer implements Serializable, StoreData { + private String id; + + /** + * 发布公司 + */ + private String publisherId; + + /** + * 岗位名称 + */ + private String jobName; + + /** + * 工作地点 + */ + private String place; + + /** + * 要求 + */ + private String requirement; + + /** + * 预期薪资 + */ + private Integer salary; + + /** + * 目标人数 + */ + private Integer targetNumber; + + /** + * 详细信息 + */ + private String detail; + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Offer offer = (Offer) o; + return id.equals(offer.id); + } + + @Override + public int hashCode() { + return Objects.hash(id); + } +} diff --git a/disillusion/common/src/main/java/net/lensfrex/disillusion/model/student/Resume.java b/disillusion/common/src/main/java/net/lensfrex/disillusion/model/student/Resume.java new file mode 100644 index 0000000..dece53c --- /dev/null +++ b/disillusion/common/src/main/java/net/lensfrex/disillusion/model/student/Resume.java @@ -0,0 +1,34 @@ +package net.lensfrex.disillusion.model.student; + +import lombok.Data; +import net.lensfrex.disillusion.model.StoreData; + +import java.io.Serializable; + +/** + * 简历 + */ +@Data +public class Resume implements Serializable, StoreData { + private String id; + + /** + * 学生id + */ + private String ownerId; + + /** + * 简历标题(名称) + */ + private String title; + + /** + * 简历备注信息 + */ + private String remark; + + /** + * 简历文件id,一般来说等同于简历id + */ + private String resumeFileId; +} diff --git a/disillusion/common/src/main/java/net/lensfrex/disillusion/model/student/Student.java b/disillusion/common/src/main/java/net/lensfrex/disillusion/model/student/Student.java new file mode 100644 index 0000000..4d7a640 --- /dev/null +++ b/disillusion/common/src/main/java/net/lensfrex/disillusion/model/student/Student.java @@ -0,0 +1,66 @@ +package net.lensfrex.disillusion.model.student; + +import lombok.Data; +import net.lensfrex.disillusion.model.UserInfo; +import net.lensfrex.disillusion.model.StoreData; +import net.lensfrex.disillusion.util.IOUtil; + +import java.io.Serializable; +import java.nio.charset.StandardCharsets; + +@Data +public class Student implements UserInfo, Serializable, StoreData { + private String id; + + /** + * 真名 + */ + private String realName; + + /** + * 简介 + */ + private String shortDescribe; + + /** + * 介绍 + */ + private String describe; + + /** + * 电话号 + */ + private String phoneNumber; + + /** + * 身份证号 + */ + private String idNumber; + + /** + * 年龄 + */ + private int age; + + /** + * 性别,0:男;1:女 + */ + private int sex; + + public static final byte[] DEFAULT_AVATAR = IOUtil.readDataFromInputStream( + Student.class.getResourceAsStream("/61212844_p0_compress.png")); + + public static Student getDefaultInfo(String username) { + Student student = new Student(); + student.setId(username); + student.setRealName(username); + student.setShortDescribe("这个人很懒什么都没写"); + student.setDescribe("没有简介..."); + student.setPhoneNumber("没有填写"); + student.setIdNumber("没有提供"); + student.setAge(22); + student.setSex(0); + + return student; + } +} diff --git a/disillusion/common/src/main/java/net/lensfrex/disillusion/response/general/Response.java b/disillusion/common/src/main/java/net/lensfrex/disillusion/response/general/Response.java new file mode 100644 index 0000000..b0b2ffe --- /dev/null +++ b/disillusion/common/src/main/java/net/lensfrex/disillusion/response/general/Response.java @@ -0,0 +1,32 @@ +/* + * Class created by lensfrex. + */ + +package net.lensfrex.disillusion.response.general; + +import cn.hutool.json.JSONUtil; + +import java.nio.charset.StandardCharsets; + +public record Response(int code, String message, T data) { + + public static Response success(T data) { + return new Response<>(ResponseCode.SUCCESS.getCode(), "success", data); + } + + public static Response success() { + return success(null); + } + + public static Response error(ResponseCode code, String message) { + return new Response<>(code.getCode(), message, null); + } + + public static Response error(ResponseCode code) { + return error(code, code.getMessage()); + } + + public byte[] toBytes() { + return JSONUtil.toJsonStr(this).getBytes(StandardCharsets.UTF_8); + } +} \ No newline at end of file diff --git a/disillusion/common/src/main/java/net/lensfrex/disillusion/response/general/ResponseCode.java b/disillusion/common/src/main/java/net/lensfrex/disillusion/response/general/ResponseCode.java new file mode 100644 index 0000000..54c7c02 --- /dev/null +++ b/disillusion/common/src/main/java/net/lensfrex/disillusion/response/general/ResponseCode.java @@ -0,0 +1,45 @@ +/* + * Class created by lensfrex. + */ + +package net.lensfrex.disillusion.response.general; + +public enum ResponseCode { + SUCCESS(20000, "成功"), + REQUEST_TOO_FAST(20001, "技能冷却中..."), + + INVALID_REQUEST(30000, "非法请求"), + PARAM_WRONG(30001, "参数错误"), + + PERMISSION_DENIED(40000, "权限不足"), + TOKEN_EXPIRED(40001, "token过期"), + TOKEN_INVALID(40002, "token无效"), + + IDENTIFY_ERROR(40100, "用户不存在或密码错误"), + USER_ALREADY_EXISTS(40101, "用户名已经被注册"), + + USER_NOT_EXISTS(40103, "用户不存在"), + + SERVER_INTERNAL_ERROR(50000, "服务器内部错误"), + + API_NOT_IMPLEMENT(0, "接口未实现"), + + REQUEST_FILE_DOES_NOT_EXIST(60701, "请求的文件不存在"); + + private final int code; + + private final String message; + + ResponseCode(int code, String message) { + this.code = code; + this.message = message; + } + + public int getCode() { + return code; + } + + public String getMessage() { + return message; + } +} diff --git a/disillusion/common/src/main/java/net/lensfrex/disillusion/util/IOUtil.java b/disillusion/common/src/main/java/net/lensfrex/disillusion/util/IOUtil.java new file mode 100644 index 0000000..379872e --- /dev/null +++ b/disillusion/common/src/main/java/net/lensfrex/disillusion/util/IOUtil.java @@ -0,0 +1,48 @@ +package net.lensfrex.disillusion.util; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.InputStream; +import java.nio.charset.Charset; + +public class IOUtil { + public static String inputStreamToString(InputStream inputStream, Charset charSet) { + byte[] bytes = readDataFromInputStream(inputStream); + if (bytes != null) { + return new String(bytes, charSet); + } else { + return null; + } + } + + public static byte[] readDataFromInputStream(InputStream inputStream) { + return readDataFromInputStream(inputStream, 5);// read every 5kb in default + } + + public static byte[] readDataFromInputStream(InputStream inputStream, int byteAllocation) { + try { + ByteArrayOutputStream byteArrayInputStream = new ByteArrayOutputStream(); + byte[] bytes = new byte[1024 * byteAllocation]; + + for (int length; (length = inputStream.read(bytes)) != -1; ) { + byteArrayInputStream.write(bytes, 0, length); + } + + byteArrayInputStream.flush(); + + inputStream.close(); + byteArrayInputStream.close(); + return byteArrayInputStream.toByteArray(); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + public static void writeFile(byte[] bytes, File file) throws Exception { + FileOutputStream fileOutputStream = new FileOutputStream(file); + fileOutputStream.write(bytes); + fileOutputStream.close(); + } +} diff --git a/disillusion/common/src/main/resources/61212844_p0_compress.png b/disillusion/common/src/main/resources/61212844_p0_compress.png new file mode 100644 index 0000000000000000000000000000000000000000..1ba397bd2f02fd8b5b48ddd4c7af0c46596332d3 GIT binary patch literal 25882 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%0Dk>1@GAOyCZ%`cDQK5e zUBnT#K1mPAZpV6QWg>Sfe9faePLc7v+rFvi&QgrRa$}p?r&qGADGrs#%QKXU-4|jA zr&JvRqh^=MP?am@y)@8Rl8%hR>^rgU^4g6)BqNIRjHspEE{S=~<%}RdIIK?8i74^D z1P9~6IiN8#D-LmPfBGqqOwCUY>y&gS_pR6K8O^9zmz$hsBOvBIE^aoWIL~XEyFmR` z(*6EngK)*`4eA|TB9eYZcdT5B13b?&{gPAZTzo>GxI#kn}8zuUj0z+ zxRLaDKVv>QXXVKZi?e1{Q8kU?Je%`c<=`Q7n1Y}9zjAe#Zik1TABv2PmkQwL#B&Ww z>+-1-Gu1~;oN(lNZU4AJKiFpNZ^PEmoy*_c^@a0ybq6W#%(6jLA) z-|uKo*6F?C-|46D-8Q}tt|{+Ui*r)>x$Jukvqo`H*Y7SB#Y6N!vDrIptAQ%6c+V~r zXNM+~=A|(OB5~%6_GFu}ar(@21rUFC0l2nvZbn!rT4UE%vz||&WIWdH8H;|eTF*Dy zf?wx(&n^@vobF4=uZc=V#~Gg4+@3VN@{#}j0kzNnJWb9ya3LdTHj{g2*z$KQ^-FP_ z4F%f=7HmL0NjGBTwJUM!c*9a1?rl~JhC@)<^Z$(i`Movs7j%7jsadrdWm;@n!!oj3 znNwoM4{K}_-)UPAVa+TrE}}SL?<;Xfbwf}HH#Lg|U=Yz&w1#<(vTjm($@+_V8Jyv6U=UHqXa7TEYN!0_^7^U@ zYu&QgZ-0$2e{xY6*09T;j&+G%Ttsl5M>tDcE7E2}N{uv!)4(92KfZ@5$y4wRm|Xib zW0Teu&0zw6kmm}zYnb`xzU{Y0$NH{$?%PKaoH$Kk2$WI`%Skxh9Bu*;9l@GU2a!E& zqtbo#Nu4dhP(17OB2VPXf9shW729ueMsNUi-4T@FL|Z(RknIh>vyL+r>I=^Vl5VU{ zEUti!`oY+w?+zY}hDP;Rq-x0zh7r6{kf)j5| zQerrVK7ppXn}x67vndhDOP~=e`pfYHT@#Oidse4LV~&^ZVwqt^GrotVdrJ-p+}WT) zh072nII&frsPUa&beCDUyANfi$>?s|C#&ITT|E8h5r{KpogZ6MyfGrT4BAgv?&@^h z#2KM(Lo{OKr!I8@y=USS;oxl1oq6X@ybn9?^l!)NpxL(0GWfRfjZv|bH5=TJMWlVe ziGXn~9d;X{5hLGtsT0c@I$wwD`$Wju%{mcn8~Y8&dsc>43_U|l!;a8<{Yh?BCoR!g z3Y*{%NEVS;FD=fjPBokzdnwK{MCk~DQi`sbq4rGm%9XmQGCBkH;#raIwg(%~RczGS zYL_KOQkeZ3)`*wW+v@DUAkS5<^X! z26(hMjjDt607XP{%3jsdk zqMlpe;H=Vnp7*zQDH35Z@u%vU!&=P`50{P|%pz+?Y)^H)^v;Jrtim`I)*A0MQ$Nz6 zg(R!n~@AqHwL=f%A-Rn?xp}DZ%YTEfnWMPzAj- z$I$TH-uEMrSLM}t70;esBr{4&!DxbP!K=EsxPQ86S(29Uy1|;QLBv^&&?fL!EnbFY{Dy<4o+R$&@GtK|k)aUTwSFO-zLe=3`fAp`wc+STyE24B%%G>Zi`ZwhUN~<_ zIuFXdVu3A01o7n&8stIGv3(ceJf-7A5>adLq{hTV6#iFJEIR;}0_B)?p#Z?`O+UMR3Q&l4)$+3}nfuPHOXc@bIHzP(?765LRvU>*w}>n#sAB?{!+uCm+o2viZA zX3$fkDctePu*&+G6s|^Q6xrB2O0(rY>*+ZxrUtrsI2RT`UvL8oj-w49>>Ukvf6RFg z0#G)hOxI+KWM_$FSsE&~dXcIuV#)y%Qfw6kig7YQK;MFn;5⪼*HQJ_V%?Woxu!} zMh8NRK2}<@#Z>yfFf)sf7V%hiQkV;SdbmKbZ^w>vMzSHeB^Uc15Y8L2V;fVbQE2Be zEF-uJ`e=*T78}f!v2gqq`6#RL+iU&&gvJ!89cO!Yt11Q+iMC;!Cx7a@XZV+t4avkaj}Ugyx~7nW;*E>8WN(U=NVDM zDDu0;+ip>=+xp~8QTC5OoT)`UJ)S_XTjv#67Bve&JFbi-7P0o%KV}|v{6YxF;%SzUppf<624Q;D9tX~vOTo7?!yBcxFNyw#vySuM>4q zzGf3x&{AFx=q44q6<$}QyrvepTGRRqkzQX!gr<# zCqv|nVM71Wqry&A3xCyC@b&ODU;4vo*!wIE8Ug_dUQdS-kIOc@XzW)Vr=*KGM&Qpr z50V*1=B$nW76m45`opQ)Z>=Se7K+1gRV0)qthrqONW)HDz;C0=;|aq6@S|6SnyBS! zd@b9E`0TfaJ>F87+@L-lg>Y)P#g^S*ZdB0DGsH|{xHC-)x<`XT1{YBt$6W2k{}y|= zG~g3w3lC=?yPu4@gqA|@Eq8vIG|uCM*TkM+I0KpZrXM|Uk_Clm51Cot6g(fU2tIL+ z@^A(da#1vfMq+A|%o6%u@&u%*5S+5HH4w9*hr1Zih)m6@sMTEF^6ct`B60pzQ~lxW z;NlF#L}-ADvrupAYZS<8toF{Tz zoPj(LEeNM!1-L!^Vnqe<%|&w1oUYV2w(C4}j(r##HE((KAUMl6`NMgNk5dd1GC!=T zn#x*Q@DZZWTmCp+B0+ zYiq8cSGPqJ{LHTmj)FIilP?enMXfmYCV+Q@?fJ~0b-E3D-Itd)Mfy_t{#OONW44B~ zzzlCPp!t5d-+s#%6i|cC@Q+hL>vO|vt8-Th%vltEF|7R)&NWTYCweiQUpT4C_7F}Y z?om8<`o`J0$tTVZ7o28BHXo${?Fc*j^L2DOAq5$yM}^ddm3W=IeEV>?-SI3lo0k4@ za;b&uHQ#&EDb=rh;&hong+Rfo5fp3!DQD?AbyGoq1?XE*+Qo$_W3S8mPA+h8Zi6;Z zFIDRi%SB6D&ZnHfvk>X!Ti-Zkw8bx$@qRx(wuNMrse|fh+pn3$_m3E)^1vA&Qu~9o zqH44lbvWf$)$db%%6+aA3oJ{LzJW@xsi<#EUU^BF_43GJ6s4IN$*^JW5l+C*`~q7~ zB8Ob+N_eKf$41ru9-lbfCKgnl1K*_%ey)PvC`(^gVlIvMCoMUD61>1B%a->IBz9=j z=dQ>d;-Fugd<~QoZ-cVELD~2Td1n&-zwxH-(WalDhqH$9o66NBLZZQ63c;8f{@FLq z7w$NP0K4~`gF28psgK`gyRV`mjp8oz+H0T$=5+ZUZvz`rL9KYfdjiRHc^^iTDV z(~F3_E$8+IRa05Zf`jr9l>1l{OoMCBA*6fS_$WRS>)o#sv;{o@aCUeM&>+$unnUy1 zSugeMD#+k>PFuoUvD*DPNC0H^p=tcO*wZ4=CtciE#^OBTH9)<6Th>yi?OO^JliYk_ zjzp_nM=dzZJ)X1nE=ZWceDPwj-DnF=@kUP&oaZ&Kj$7kjSdhY6dfT6Gt;=VxeYEr8 z2U7}2jPr|Ao%hWIChTs0k!Q8Rw(-S6%zhnJUO0nDf5Y0%*#Ds5cM;mc#N2zx1|qVz zcX~ZXwB*h|TjWtMkQcot7|v0z0UAVDP(oE_dBH-PjP%Y6~)%d6CksmzF6XE#jj^Mq+le>vRlA^Jf9(&yya zW8Lq{8)pDx?u!8+9n`d1tb + + 4.0.0 + + net.lensfrex + disillusion + ${revision} + pom + + + backend + common + virtual-database + + + + 17 + 17 + UTF-8 + + 0.0.1-SNAPSHOT + + 1.18.22 + + 1.5.3.Final + + + + + + jakarta.annotation + jakarta.annotation-api + 2.1.1 + + + + javax.annotation + javax.annotation-api + 1.3.2 + + + org.projectlombok + lombok + ${lombok.version} + + + + org.mapstruct + mapstruct + ${mapstruct.version} + + + + + + + + org.codehaus.mojo + flatten-maven-plugin + 1.3.0 + + + + + flatten + process-resources + + flatten + + + + flatten.clean + clean + + clean + + + + + + + \ No newline at end of file diff --git a/disillusion/virtual-database/.flattened-pom.xml b/disillusion/virtual-database/.flattened-pom.xml new file mode 100644 index 0000000..5b4000e --- /dev/null +++ b/disillusion/virtual-database/.flattened-pom.xml @@ -0,0 +1,46 @@ + + + 4.0.0 + net.lensfrex + virtual-database + 0.0.1-SNAPSHOT + + + com.fasterxml.jackson.core + jackson-databind + 2.14.1 + compile + + + net.lensfrex + common + 0.0.1-SNAPSHOT + compile + + + jakarta.annotation + jakarta.annotation-api + 2.1.1 + compile + + + javax.annotation + javax.annotation-api + 1.3.2 + compile + + + org.projectlombok + lombok + 1.18.22 + compile + + + org.mapstruct + mapstruct + 1.5.3.Final + compile + + + diff --git a/disillusion/virtual-database/pom.xml b/disillusion/virtual-database/pom.xml new file mode 100644 index 0000000..fedb970 --- /dev/null +++ b/disillusion/virtual-database/pom.xml @@ -0,0 +1,33 @@ + + + 4.0.0 + + net.lensfrex + disillusion + ${revision} + + + virtual-database + + + 17 + 17 + UTF-8 + + + + com.fasterxml.jackson.core + jackson-databind + 2.14.1 + + + + net.lensfrex + common + ${revision} + + + + \ No newline at end of file diff --git a/disillusion/virtual-database/src/main/java/net/lensfrex/disillusion/dao/database/DatabaseOperator.java b/disillusion/virtual-database/src/main/java/net/lensfrex/disillusion/dao/database/DatabaseOperator.java new file mode 100644 index 0000000..11706d3 --- /dev/null +++ b/disillusion/virtual-database/src/main/java/net/lensfrex/disillusion/dao/database/DatabaseOperator.java @@ -0,0 +1,79 @@ +package net.lensfrex.disillusion.dao.database; + +import java.io.EOFException; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * 虚拟数据库底层 + */ +class DatabaseOperator { + private static String baseDir; + + private static boolean initial = false; + + private static final Map paths = new ConcurrentHashMap<>(); + + private static final Map tableLocks = new ConcurrentHashMap<>(); + + public static void write(String table, T data) throws IOException { + ObjectOutputStream outputStream = new ObjectOutputStream(Files.newOutputStream(Path.of(baseDir, table))); + outputStream.writeObject(data); + outputStream.flush(); + outputStream.close(); + + tableLocks.remove(table); + } + + public static T read(String table, Class type) throws IOException { + if (!tableLocks.containsKey(table)) { + tableLocks.put(table, new Object()); + } + + Path path = getPath(table); + + if (!Files.exists(path)) { + Files.createFile(path); + return null; + } + + try { + ObjectInputStream inputStream = new ObjectInputStream(Files.newInputStream(getPath(table))); + @SuppressWarnings("unchecked") T result = (T) inputStream.readObject(); + inputStream.close(); + + tableLocks.remove(table); + + return result; + } catch (EOFException e) { + return null; + } catch (Exception e) { + e.printStackTrace(); + throw new IOException("IOException"); + } + } + + private static Path getPath(String table) { + if (!paths.containsKey(table)) { + paths.put(table, Path.of(baseDir, table)); + } + + return paths.get(table); + } + + protected static void init(String baseDir) { + if (initial) { + return; + } + + DatabaseOperator.baseDir = baseDir; + Paths.get(baseDir).toFile().mkdirs(); + initial = true; + } +} diff --git a/disillusion/virtual-database/src/main/java/net/lensfrex/disillusion/dao/database/Table.java b/disillusion/virtual-database/src/main/java/net/lensfrex/disillusion/dao/database/Table.java new file mode 100644 index 0000000..17b0346 --- /dev/null +++ b/disillusion/virtual-database/src/main/java/net/lensfrex/disillusion/dao/database/Table.java @@ -0,0 +1,11 @@ +package net.lensfrex.disillusion.dao.database; + +import java.io.IOException; + +public interface Table { + T select(String id) throws IOException, ClassNotFoundException; + void delete(T record) throws IOException, ClassNotFoundException; + void delete(String id) throws IOException, ClassNotFoundException; + void insertOrUpdate(T record) throws IOException, ClassNotFoundException; + void update(T record) throws IOException, ClassNotFoundException; +} diff --git a/disillusion/virtual-database/src/main/java/net/lensfrex/disillusion/dao/database/VirtualDatabase.java b/disillusion/virtual-database/src/main/java/net/lensfrex/disillusion/dao/database/VirtualDatabase.java new file mode 100644 index 0000000..47ec621 --- /dev/null +++ b/disillusion/virtual-database/src/main/java/net/lensfrex/disillusion/dao/database/VirtualDatabase.java @@ -0,0 +1,17 @@ +package net.lensfrex.disillusion.dao.database; + +import java.io.IOException; + +public class VirtualDatabase { + public static T getTable(String tableName, Class objectClass) throws IOException, ClassNotFoundException { + return (T) DatabaseOperator.read(tableName, objectClass); + } + + public static void saveTable(String tableName, T tableObj) throws IOException { + DatabaseOperator.write(tableName, tableObj); + } + + public static void init(String baseDir) throws IOException { + DatabaseOperator.init(baseDir); + } +}