Showing preview only (630K chars total). Download the full file or copy to clipboard to get everything.
Repository: liyupi/yudada
Branch: master
Commit: e5eb265817f1
Files: 264
Total size: 513.2 KB
Directory structure:
gitextract_ta69pkyf/
├── .gitignore
├── README.md
├── mbti-test-mini/
│ ├── .editorconfig
│ ├── .eslintrc
│ ├── babel.config.js
│ ├── config/
│ │ ├── dev.ts
│ │ ├── index.ts
│ │ └── prod.ts
│ ├── package.json
│ ├── project.config.json
│ ├── project.private.config.json
│ ├── project.tt.json
│ ├── src/
│ │ ├── app.config.ts
│ │ ├── app.scss
│ │ ├── app.ts
│ │ ├── components/
│ │ │ └── GlobalFooter/
│ │ │ ├── index.scss
│ │ │ └── index.tsx
│ │ ├── data/
│ │ │ ├── question_results.json
│ │ │ └── questions.json
│ │ ├── index.html
│ │ ├── pages/
│ │ │ ├── doQuestion/
│ │ │ │ ├── index.config.ts
│ │ │ │ ├── index.scss
│ │ │ │ └── index.tsx
│ │ │ ├── index/
│ │ │ │ ├── index.config.ts
│ │ │ │ ├── index.scss
│ │ │ │ └── index.tsx
│ │ │ └── result/
│ │ │ ├── index.config.ts
│ │ │ ├── index.scss
│ │ │ └── index.tsx
│ │ └── utils/
│ │ └── bizUtils.ts
│ ├── tsconfig.json
│ └── types/
│ ├── custom.d.ts
│ └── global.d.ts
├── yudada-backend/
│ ├── .gitignore
│ ├── .mvn/
│ │ └── wrapper/
│ │ └── maven-wrapper.properties
│ ├── Dockerfile
│ ├── README.md
│ ├── mvnw
│ ├── mvnw.cmd
│ ├── pom.xml
│ ├── sql/
│ │ ├── create_table.sql
│ │ ├── init_data.sql
│ │ └── post_es_mapping.json
│ └── src/
│ ├── main/
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── yupi/
│ │ │ └── yudada/
│ │ │ ├── MainApplication.java
│ │ │ ├── annotation/
│ │ │ │ └── AuthCheck.java
│ │ │ ├── aop/
│ │ │ │ ├── AuthInterceptor.java
│ │ │ │ └── LogInterceptor.java
│ │ │ ├── common/
│ │ │ │ ├── BaseResponse.java
│ │ │ │ ├── DeleteRequest.java
│ │ │ │ ├── ErrorCode.java
│ │ │ │ ├── PageRequest.java
│ │ │ │ ├── ResultUtils.java
│ │ │ │ └── ReviewRequest.java
│ │ │ ├── config/
│ │ │ │ ├── AiConfig.java
│ │ │ │ ├── CorsConfig.java
│ │ │ │ ├── CosClientConfig.java
│ │ │ │ ├── JsonConfig.java
│ │ │ │ ├── MyBatisPlusConfig.java
│ │ │ │ ├── RedissonConfig.java
│ │ │ │ └── VipSchedulerConfig.java
│ │ │ ├── constant/
│ │ │ │ ├── CommonConstant.java
│ │ │ │ ├── FileConstant.java
│ │ │ │ └── UserConstant.java
│ │ │ ├── controller/
│ │ │ │ ├── AppController.java
│ │ │ │ ├── AppStatisticController.java
│ │ │ │ ├── FileController.java
│ │ │ │ ├── PostController.java
│ │ │ │ ├── PostFavourController.java
│ │ │ │ ├── PostThumbController.java
│ │ │ │ ├── QuestionController.java
│ │ │ │ ├── ScoringResultController.java
│ │ │ │ ├── UserAnswerController.java
│ │ │ │ └── UserController.java
│ │ │ ├── exception/
│ │ │ │ ├── BusinessException.java
│ │ │ │ ├── GlobalExceptionHandler.java
│ │ │ │ └── ThrowUtils.java
│ │ │ ├── generate/
│ │ │ │ └── CodeGenerator.java
│ │ │ ├── manager/
│ │ │ │ ├── AiManager.java
│ │ │ │ └── CosManager.java
│ │ │ ├── mapper/
│ │ │ │ ├── AppMapper.java
│ │ │ │ ├── PostFavourMapper.java
│ │ │ │ ├── PostMapper.java
│ │ │ │ ├── PostThumbMapper.java
│ │ │ │ ├── QuestionMapper.java
│ │ │ │ ├── ScoringResultMapper.java
│ │ │ │ ├── UserAnswerMapper.java
│ │ │ │ └── UserMapper.java
│ │ │ ├── model/
│ │ │ │ ├── dto/
│ │ │ │ │ ├── app/
│ │ │ │ │ │ ├── AppAddRequest.java
│ │ │ │ │ │ ├── AppEditRequest.java
│ │ │ │ │ │ ├── AppQueryRequest.java
│ │ │ │ │ │ └── AppUpdateRequest.java
│ │ │ │ │ ├── file/
│ │ │ │ │ │ └── UploadFileRequest.java
│ │ │ │ │ ├── post/
│ │ │ │ │ │ ├── PostAddRequest.java
│ │ │ │ │ │ ├── PostEditRequest.java
│ │ │ │ │ │ ├── PostQueryRequest.java
│ │ │ │ │ │ └── PostUpdateRequest.java
│ │ │ │ │ ├── postfavour/
│ │ │ │ │ │ ├── PostFavourAddRequest.java
│ │ │ │ │ │ └── PostFavourQueryRequest.java
│ │ │ │ │ ├── postthumb/
│ │ │ │ │ │ └── PostThumbAddRequest.java
│ │ │ │ │ ├── question/
│ │ │ │ │ │ ├── AiGenerateQuestionRequest.java
│ │ │ │ │ │ ├── QuestionAddRequest.java
│ │ │ │ │ │ ├── QuestionAnswerDTO.java
│ │ │ │ │ │ ├── QuestionContentDTO.java
│ │ │ │ │ │ ├── QuestionEditRequest.java
│ │ │ │ │ │ ├── QuestionQueryRequest.java
│ │ │ │ │ │ └── QuestionUpdateRequest.java
│ │ │ │ │ ├── scoringResult/
│ │ │ │ │ │ ├── ScoringResultAddRequest.java
│ │ │ │ │ │ ├── ScoringResultEditRequest.java
│ │ │ │ │ │ ├── ScoringResultQueryRequest.java
│ │ │ │ │ │ └── ScoringResultUpdateRequest.java
│ │ │ │ │ ├── statistic/
│ │ │ │ │ │ ├── AppAnswerCountDTO.java
│ │ │ │ │ │ └── AppAnswerResultCountDTO.java
│ │ │ │ │ ├── user/
│ │ │ │ │ │ ├── UserAddRequest.java
│ │ │ │ │ │ ├── UserLoginRequest.java
│ │ │ │ │ │ ├── UserQueryRequest.java
│ │ │ │ │ │ ├── UserRegisterRequest.java
│ │ │ │ │ │ ├── UserUpdateMyRequest.java
│ │ │ │ │ │ └── UserUpdateRequest.java
│ │ │ │ │ └── userAnswer/
│ │ │ │ │ ├── UserAnswerAddRequest.java
│ │ │ │ │ ├── UserAnswerEditRequest.java
│ │ │ │ │ ├── UserAnswerQueryRequest.java
│ │ │ │ │ └── UserAnswerUpdateRequest.java
│ │ │ │ ├── entity/
│ │ │ │ │ ├── App.java
│ │ │ │ │ ├── Post.java
│ │ │ │ │ ├── PostFavour.java
│ │ │ │ │ ├── PostThumb.java
│ │ │ │ │ ├── Question.java
│ │ │ │ │ ├── ScoringResult.java
│ │ │ │ │ ├── User.java
│ │ │ │ │ └── UserAnswer.java
│ │ │ │ ├── enums/
│ │ │ │ │ ├── AppScoringStrategyEnum.java
│ │ │ │ │ ├── AppTypeEnum.java
│ │ │ │ │ ├── FileUploadBizEnum.java
│ │ │ │ │ ├── ReviewStatusEnum.java
│ │ │ │ │ └── UserRoleEnum.java
│ │ │ │ └── vo/
│ │ │ │ ├── AppVO.java
│ │ │ │ ├── LoginUserVO.java
│ │ │ │ ├── PostVO.java
│ │ │ │ ├── QuestionVO.java
│ │ │ │ ├── ScoringResultVO.java
│ │ │ │ ├── UserAnswerVO.java
│ │ │ │ └── UserVO.java
│ │ │ ├── scoring/
│ │ │ │ ├── AiTestScoringStrategy.java
│ │ │ │ ├── CustomScoreScoringStrategy.java
│ │ │ │ ├── CustomTestScoringStrategy.java
│ │ │ │ ├── ScoringStrategy.java
│ │ │ │ ├── ScoringStrategyConfig.java
│ │ │ │ ├── ScoringStrategyContext.java
│ │ │ │ └── ScoringStrategyExecutor.java
│ │ │ ├── service/
│ │ │ │ ├── AppService.java
│ │ │ │ ├── PostFavourService.java
│ │ │ │ ├── PostService.java
│ │ │ │ ├── PostThumbService.java
│ │ │ │ ├── QuestionService.java
│ │ │ │ ├── ScoringResultService.java
│ │ │ │ ├── UserAnswerService.java
│ │ │ │ ├── UserService.java
│ │ │ │ └── impl/
│ │ │ │ ├── AppServiceImpl.java
│ │ │ │ ├── PostFavourServiceImpl.java
│ │ │ │ ├── PostServiceImpl.java
│ │ │ │ ├── PostThumbServiceImpl.java
│ │ │ │ ├── QuestionServiceImpl.java
│ │ │ │ ├── ScoringResultServiceImpl.java
│ │ │ │ ├── UserAnswerServiceImpl.java
│ │ │ │ └── UserServiceImpl.java
│ │ │ └── utils/
│ │ │ ├── NetUtils.java
│ │ │ ├── SpringContextUtils.java
│ │ │ └── SqlUtils.java
│ │ └── resources/
│ │ ├── META-INF/
│ │ │ └── additional-spring-configuration-metadata.json
│ │ ├── application-prod.yml
│ │ ├── application-test.yml
│ │ ├── application.yml
│ │ ├── banner.txt
│ │ ├── mapper/
│ │ │ ├── AppMapper.xml
│ │ │ ├── PostFavourMapper.xml
│ │ │ ├── PostMapper.xml
│ │ │ ├── PostThumbMapper.xml
│ │ │ ├── QuestionMapper.xml
│ │ │ ├── ScoringResultMapper.xml
│ │ │ ├── UserAnswerMapper.xml
│ │ │ └── UserMapper.xml
│ │ ├── templates/
│ │ │ ├── TemplateController.java.ftl
│ │ │ ├── TemplateService.java.ftl
│ │ │ ├── TemplateServiceImpl.java.ftl
│ │ │ └── model/
│ │ │ ├── TemplateAddRequest.java.ftl
│ │ │ ├── TemplateEditRequest.java.ftl
│ │ │ ├── TemplateQueryRequest.java.ftl
│ │ │ ├── TemplateUpdateRequest.java.ftl
│ │ │ └── TemplateVO.java.ftl
│ │ └── test_excel.xlsx
│ └── test/
│ └── java/
│ └── com/
│ └── yupi/
│ └── yudada/
│ ├── MainApplicationTests.java
│ ├── QuestionControllerTest.java
│ ├── RxJavaTest.java
│ ├── UserAnswerShardingTest.java
│ ├── ZhiPuAiTest.java
│ ├── manager/
│ │ └── CosManagerTest.java
│ ├── mapper/
│ │ ├── PostFavourMapperTest.java
│ │ └── PostMapperTest.java
│ ├── service/
│ │ ├── PostFavourServiceTest.java
│ │ ├── PostThumbServiceTest.java
│ │ └── UserServiceTest.java
│ └── utils/
│ └── EasyExcelTest.java
└── yudada-frontend/
├── .browserslistrc
├── .eslintrc.js
├── .gitignore
├── Dockerfile
├── README.md
├── babel.config.js
├── docker/
│ └── nginx.conf
├── openapi.config.ts
├── package.json
├── public/
│ └── index.html
├── src/
│ ├── App.vue
│ ├── access/
│ │ ├── accessEnum.ts
│ │ ├── checkAccess.ts
│ │ └── index.ts
│ ├── api/
│ │ ├── appController.ts
│ │ ├── appStatisticController.ts
│ │ ├── fileController.ts
│ │ ├── index.ts
│ │ ├── postController.ts
│ │ ├── postFavourController.ts
│ │ ├── postThumbController.ts
│ │ ├── questionController.ts
│ │ ├── scoringResultController.ts
│ │ ├── typings.d.ts
│ │ ├── userAnswerController.ts
│ │ └── userController.ts
│ ├── components/
│ │ ├── AppCard.vue
│ │ ├── GlobalHeader.vue
│ │ ├── MdEditor.vue
│ │ ├── MdViewer.vue
│ │ ├── PictureUploader.vue
│ │ └── ShareModal.vue
│ ├── constant/
│ │ └── app.ts
│ ├── layouts/
│ │ ├── BasicLayout.vue
│ │ └── UserLayout.vue
│ ├── main.ts
│ ├── request.ts
│ ├── router/
│ │ ├── index.ts
│ │ └── routes.ts
│ ├── shims-vue.d.ts
│ ├── store/
│ │ └── userStore.ts
│ └── views/
│ ├── HomePage.vue
│ ├── NoAuthPage.vue
│ ├── add/
│ │ ├── AddAppPage.vue
│ │ ├── AddQuestionPage.vue
│ │ ├── AddScoringResultPage.vue
│ │ └── components/
│ │ ├── AiGenerateQuestionDrawer.vue
│ │ └── ScoringResultTable.vue
│ ├── admin/
│ │ ├── AdminAppPage.vue
│ │ ├── AdminQuestionPage.vue
│ │ ├── AdminScoringResultPage.vue
│ │ ├── AdminUserAnswerPage.vue
│ │ └── AdminUserPage.vue
│ ├── answer/
│ │ ├── AnswerResultPage.vue
│ │ ├── DoAnswerPage.vue
│ │ └── MyAnswerPage.vue
│ ├── app/
│ │ └── AppDetailPage.vue
│ ├── statistic/
│ │ └── AppStatisticPage.vue
│ └── user/
│ ├── UserLoginPage.vue
│ └── UserRegisterPage.vue
├── tsconfig.json
└── vue.config.js
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
### Intellij template
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
# User-specific stuff
.idea/**/workspace.xml
.idea/**/tasks.xml
.idea/**/usage.statistics.xml
.idea/**/dictionaries
.idea/**/shelf
# AWS User-specific
.idea/**/aws.xml
# Generated files
.idea/**/contentModel.xml
# Sensitive or high-churn files
.idea/**/dataSources/
.idea/**/dataSources.ids
.idea/**/dataSources.local.xml
.idea/**/sqlDataSources.xml
.idea/**/dynamic.xml
.idea/**/uiDesigner.xml
.idea/**/dbnavigator.xml
# Gradle
.idea/**/gradle.xml
.idea/**/libraries
# Gradle and Maven with auto-import
# When using Gradle or Maven with auto-import, you should exclude module files,
# since they will be recreated, and may cause churn. Uncomment if using
# auto-import.
# .idea/artifacts
# .idea/compiler.xml
# .idea/jarRepositories.xml
# .idea/modules.xml
# .idea/*.iml
# .idea/modules
# *.iml
# *.ipr
# CMake
cmake-build-*/
# Mongo Explorer plugin
.idea/**/mongoSettings.xml
# File-based project format
*.iws
# IntelliJ
out/
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Cursive Clojure plugin
.idea/replstate.xml
# SonarLint plugin
.idea/sonarlint/
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties
# Editor-based Rest Client
.idea/httpRequests
# Android studio 3.1+ serialized cache file
.idea/caches/build_file_checksums.ser
### WebStorm template
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
# User-specific stuff
.idea/**/workspace.xml
.idea/**/tasks.xml
.idea/**/usage.statistics.xml
.idea/**/dictionaries
.idea/**/shelf
# AWS User-specific
.idea/**/aws.xml
# Generated files
.idea/**/contentModel.xml
# Sensitive or high-churn files
.idea/**/dataSources/
.idea/**/dataSources.ids
.idea/**/dataSources.local.xml
.idea/**/sqlDataSources.xml
.idea/**/dynamic.xml
.idea/**/uiDesigner.xml
.idea/**/dbnavigator.xml
# Gradle
.idea/**/gradle.xml
.idea/**/libraries
# Gradle and Maven with auto-import
# When using Gradle or Maven with auto-import, you should exclude module files,
# since they will be recreated, and may cause churn. Uncomment if using
# auto-import.
# .idea/artifacts
# .idea/compiler.xml
# .idea/jarRepositories.xml
# .idea/modules.xml
# .idea/*.iml
# .idea/modules
# *.iml
# *.ipr
# CMake
cmake-build-*/
# Mongo Explorer plugin
.idea/**/mongoSettings.xml
# File-based project format
*.iws
# IntelliJ
out/
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Cursive Clojure plugin
.idea/replstate.xml
# SonarLint plugin
.idea/sonarlint/
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties
# Editor-based Rest Client
.idea/httpRequests
# Android studio 3.1+ serialized cache file
.idea/caches/build_file_checksums.ser
================================================
FILE: README.md
================================================
# 鱼答答 - AI 答题应用平台
> 作者:[程序员鱼皮](https://yuyuanweb.feishu.cn/wiki/Abldw5WkjidySxkKxU2cQdAtnah)
>
> ⭐️ 加入项目系列学习:[加入编程导航](https://yuyuanweb.feishu.cn/wiki/SDtMwjR1DituVpkz5MLc3fZLnzb)
## 项目简介
### 项目介绍
深入业务场景的企业级实战项目,基于 Vue 3 + Spring Boot + Redis + ChatGLM AI + RxJava + SSE 的 **AI 答题应用平台。**
用户可以基于 AI 快速制作并发布多种答题应用,支持检索和分享应用、在线答题并基于评分算法或 AI 得到回答总结;管理员可以审核应用、集中管理整站内容,并进行统计分析。
> 视频介绍:https://www.bilibili.com/video/BV1m142197hg
### 项目四大阶段
该项目选题新颖、业务完整、技术亮点多,为了帮大家循序渐进地学习,鱼皮将项目设计为 4 个阶段,通俗易懂:
1)第一阶段,开发本地的 `MBTI 性格测试小程序`。带大家熟悉答题应用的开发流程和原理,从 0 到 1 实战 Taro 跨端微信小程序开发,并分享小程序开发经验。

2)第二阶段,上升一个层次,开发 `答题应用平台`。用户可以通过上传题目和自定义评分规则,创建答题应用,供其他用户检索和使用。该阶段涉及 Vue 3 + Spring Boot 前后端全栈项目从 0 到 1 的开发。

3)第三阶段,让 AI 为平台赋能,开发 `AI 智能答题应用平台`。用户只需设定主题,就能通过 AI 快速生成题目、让 AI 分析用户答案,极大降低创建答题应用的成本、提高回答多样性。是从 0 到 1 的 AI 应用开发教程,封装 AI 通用模块并教你成为 Prompt 大师!

4)第四阶段,通过多种企业开发技术手段进行 `项目优化`。包括 RxJava + SSE 优化 AI 生成体验、通过缓存和分库分表优化性能、通过幂等设计和线程池隔离提高系统安全性、通过统计分析和应用分享功能来将应用 “产品化” 等等,涉及大量干货!
在这个项目中,鱼皮还会带大家用 AI 工具 `CodeGeeX 智能编程助手` 提高开发效率,是不是已经迫不及待了呢?
### 项目展示
本项目涉及 10 多个页面,前面已经展示部分页面。
应用详情页:

用户答题页面:

创建应用页:

创建题目页,涉及复杂动态嵌套表单的开发:

应用管理页面:

统计分析页面:

应用分享功能:

### 免费试看
感兴趣的同学可以 **免费试看** 第一期项目回放:https://www.bilibili.com/video/BV1m142197hg
## 项目特点
鱼皮 **从 0 到 1 全程直播无剪辑** 地带大家开发完成项目,包括 **视频教程** 和 **文字教程**!从需求分析、技术选型、项目设计、项目初始化、Demo 编写、前后端开发实现、项目优化、部署上线等,每个环节我都 **从理论到实践** 给大家讲的明明白白、每个细节都不放过!
细致入微的教程:

满满的项目正反馈:

### 为什么要带做这个项目?
1)**业务真实新颖**:别人做答题应用,你做 AI 应用平台。需求实用价值更高,可以扩展出很多新奇有趣的热门应用。
2)**技术主流新颖**:基于企业主流前后端技术实现,再结合当下最热门的 AI 技术,比传统项目更有亮点。
3)**能学到东西**:不再是增删改查的项目,而是包含了大量的实际业务场景、系统设计优化、企业级解决方案。
4)**教程资料少**:市面上虽然有 AI 应用平台,但几乎没有从 0 到 1 的实战教程,鱼皮将提供细致入微的讲解。
5)**增加竞争力**:区别于各种管理平台项目,本项目涉及响应式编程、分库分表、设计模式、性能优化、多角度系统优化、产品优化的实战,给你的简历增加竞争力。
### 项目收获
鱼皮给大家讲的都是 **通用的项目开发方法和架构设计套路**,从这个项目中你可以学到:
- 如何拆解复杂业务,从 0 开始设计实现系统?
- 如何快速开发小程序、响应式网站和后端项目?
- 如何自己制作一套 Vue 3 万用前端模板?
- 如何巧用 JSON 实现复杂评分策略?
- 如何巧妙利用设计模式来优化代码?
- 如何利用 AI 工具 `CodeGeeX 智能编程助手` 提高开发效率?
- 如何利用 SSE 技术实时推送通知?
- 如何利用 Redis + Caffeine + 分布式锁实现稳定高效的缓存?
- 如何通过 RxJava 反应式编程 + 分库分表提升服务性能?
- 如何通过幂等设计、线程池隔离提升系统安全稳定性?
此外,还能学会很多思考问题、对比方案、产品设计的方法,提升排查问题、自主解决 Bug、产品理解的能力,成为一个项目负责人。
### 鱼皮系列项目优势
鱼皮原创项目系列以 **实战** 为主,用 **全程直播** 的方式,**从 0 到 1** 带大家学习技术知识,并立即实践运用到项目中,做到学以致用。
此外,还提供如下服务:
- 详细的直播笔记(本项目有全套文字教程)
- 完整的项目源码(分节的代码,更易学习)
- 答疑解惑
- 专属项目交流群
- ⭐️ 现成的简历写法(直接写满简历)
- ⭐️ 项目的扩展思路(拉开和其他人的差距)
- ⭐️ 项目相关面试题、题解和真实面经(提前准备,面试不懵逼)
- ⭐️ 前端 + Java 后端万用项目模板(快速创建项目)
比起看网上的教程学习,鱼皮项目系列的优势:
> 从学知识 => 实践项目 => 复习笔记 => 项目答疑 => 简历写法 => 面试题解的一条龙服务
从需求分析、技术选型、项目设计、项目初始化、Demo 编写、前后端开发实现、项目优化、部署上线等,每个环节我都 **从理论到实践** 给大家讲的明明白白、每个细节都不放过!
编程导航已有 **10 多套项目教程!** 每个项目的学习重点不同,几乎全都是前端 + 后端的 **全栈** 项目 。
详细请见:https://yuyuanweb.feishu.cn/wiki/SePYwTc9tipQiCktw7Uc7kujnCd
## 架构设计
### 1、核心业务流程图

### 2、时序图

### 3、架构设计图

## 技术选型
### 后端
- Java Spring Boot 开发框架(万用后端模板)
- 存储层:MySQL 数据库 + Redis 缓存 + 腾讯云 COS 对象存储
- MyBatis-Plus 及 MyBatis X 自动生成
- Redisson 分布式锁
- Caffeine 本地缓存
- ⭐️ 基于 ChatGLM 大模型的通用 AI 能力
- ⭐️ RxJava 响应式框架 + 线程池隔离实战
- ⭐️ SSE 服务端推送
- ⭐️ Shardingsphere 分库分表
- ⭐️ 幂等设计 + 分布式 ID 雪花算法
- ⭐️ 多种设计模式
- ⭐️ 多角度项目优化:性能、稳定性、成本优化、产品优化等
### 前端
#### Web 网页开发
- Vue 3
- Vue-CLI 脚手架
- Axios 请求库
- Arco Design 组件库
- 前端工程化:ESLint + Prettier + TypeScript
- 富文本编辑器
- QRCode.js 二维码生成
- ⭐️ Pinia 状态管理
- ⭐️ OpenAPI 前端代码生成
#### 小程序开发
- React
- Taro 跨端开发框架
- Taro UI 组件库
### 开发工具
- 前端 IDE:JetBrains WebStorm
- 后端 IDE:JetBrains IDEA
- [CodeGeeX 智能编程助手](https://codegeex.cn/)
## 项目大纲
### 第一阶段:MBTI 性格测试小程序
1. 项目介绍 | 项目背景和优势
2. 项目介绍 | 核心业务流程
3. 项目介绍 | 项目功能梳理
4. 项目介绍 | 技术选型和架构设计
5. MBTI 小程序 | 性格测试应用介绍
6. MBTI 小程序 | 实现方案和评分原理
7. MBTI 小程序 | Taro + React 小程序入门
8. MBTI 小程序 | 小程序开发实战
9. MBTI 小程序 | 小程序开发常用解决方案
### 第二阶段:Web 答题应用平台
1. 平台开发 | 需求分析
2. 平台开发 | 库表设计
3. 平台开发 | 后端初始化
4. 平台开发 | 后端基础开发
5. 平台开发 | 后端核心业务流程开发
6. 平台开发 | 前端技术选型
7. 平台开发 | 前端项目初始化
8. 平台开发 | 前端 Vue 3万用模板开发
9. 平台开发 | 前端基础页面开发(管理页面)
10. 平台开发 | 前端应用主页开发
11. 平台开发 | 前端应用详情页开发
12. 平台开发 | 前端创建模块开发 - 创建应用
13. 平台开发 | 前端创建模块开发 - 创建题目
14. 平台开发 | 前端创建模块开发 - 创建评分
15. 平台开发 | 前端答题模块开发 - 应用答题
16. 平台开发 | 前端答题模块开发 - 答题结果
17. 平台开发 | 前端答题模块开发 - 我的回答
### 第三阶段:AI 智能答题应用平台
1. 平台智能化 | 智谱 AI 大模型介绍
2. 平台智能化 | 智谱 AI SDK 接入
3. 平台智能化 | 通用 AI 模块封装
4. 平台智能化 | AI 生成题目 - 方案设计(Prompt)
5. 平台智能化 | AI 生成题目 - 后端开发
6. 平台智能化 | AI 生成题目 - 前端开发
7. 平台智能化 | AI 智能评分 - 方案设计(Prompt)
8. 平台智能化 | AI 智能评分 - 后端开发
9. 平台智能化 | AI 智能评分 - 前端开发
10. 扩展知识 | Spring AI
11. 扩展知识 | 智谱 AI + Spring AI 整合应用
### 第四阶段:多角度项目优化
1. 性能优化 | RxJava 响应式编程 - 核心概念
2. 性能优化 | RxJava 响应式编程 - Demo 实操
3. 性能优化 | AI 生成题目优化 - 需求分析
4. 性能优化 | AI 生成题目优化 - 前后端实时通讯(SSE 技术)
5. 性能优化 | AI 生成题目优化 - 后端开发
6. 性能优化 | AI 生成题目优化 - 前端开发
7. 性能优化 | AI 评分优化 - 需求分析
8. 性能优化 | AI 评分优化 - 方案设计(缓存设计)
9. 性能优化 | AI 评分优化 - 后端本地缓存开发
10. 性能优化 | AI 评分优化 - Redisson 解决缓存击穿
11. 性能优化 | 分库分表 - 核心概念
12. 性能优化 | 分库分表 - 技术选型
13. 性能优化 | 分库分表 - Sharding JDBC 实战
14. 系统优化 | 幂等设计 - 主流方案
15. 系统优化 | 幂等设计 - 分布式唯一 id 生成
16. 系统优化 | 幂等设计 - 后端开发
17. 系统优化 | 幂等设计 - 前端开发
18. 系统优化 | 线程池隔离 - 方案设计
19. 系统优化 | 线程池隔离 - 开发实现
20. 系统优化 | 统计分析 - 方案选型
21. 系统优化 | 统计分析 - 自定义 SQL
22. 系统优化 | 统计分析 - 后端开发
23. 系统优化 | 统计分析 - 前端可视化
24. 系统优化 | 应用分享 - 移动端扫码分享
25. 系统优化 | 应用分享 - 通用分享组件
## 项目资料
包括:
- 学习计划、视频教程、文字教程、项目源码
- 项目答疑、项目交流群、学员笔记
- 简历写法、面试题解、扩展思路
以上资料均可在编程导航网站获取:https://www.code-nav.cn/course/1790274408835506178
点击 [加入编程导航](https://yuyuanweb.feishu.cn/wiki/SDtMwjR1DituVpkz5MLc3fZLnzb) 后,可以按照帖子 https://t.zsxq.com/eJxjY 的引导认证并解锁项目资料的权限。如图:

## 更多项目
请见:[项目实战 - 鱼皮原创项目教程系列](https://yuyuanweb.feishu.cn/wiki/SePYwTc9tipQiCktw7Uc7kujnCd)
## 加入学习
欢迎 [点此加入编程导航](https://yuyuanweb.feishu.cn/wiki/SDtMwjR1DituVpkz5MLc3fZLnzb) ,学习大量优质原创项目,享受更多原创资料,开启你的编程起飞之旅~
================================================
FILE: mbti-test-mini/.editorconfig
================================================
# http://editorconfig.org
root = true
[*]
indent_style = space
indent_size = 2
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.md]
trim_trailing_whitespace = false
================================================
FILE: mbti-test-mini/.eslintrc
================================================
{
"extends": ["taro/react"],
"rules": {
"react/jsx-uses-react": "off",
"react/react-in-jsx-scope": "off",
"jsx-quotes": "off"
}
}
================================================
FILE: mbti-test-mini/babel.config.js
================================================
// babel-preset-taro 更多选项和默认值:
// https://github.com/NervJS/taro/blob/next/packages/babel-preset-taro/README.md
module.exports = {
presets: [
['taro', {
framework: 'react',
ts: true
}]
]
}
================================================
FILE: mbti-test-mini/config/dev.ts
================================================
module.exports = {
env: {
NODE_ENV: '"development"'
},
defineConstants: {
},
mini: {},
h5: {
esnextModules: ['taro-ui']
}
}
================================================
FILE: mbti-test-mini/config/index.ts
================================================
const config = {
projectName: 'mbti-test-mini',
date: '2024-5-7',
designWidth: 750,
deviceRatio: {
640: 2.34 / 2,
750: 1,
828: 1.81 / 2
},
sourceRoot: 'src',
outputRoot: 'dist',
plugins: [],
defineConstants: {
},
copy: {
patterns: [
],
options: {
}
},
framework: 'react',
compiler: 'webpack5',
cache: {
enable: false // Webpack 持久化缓存配置,建议开启。默认配置请参考:https://docs.taro.zone/docs/config-detail#cache
},
mini: {
postcss: {
pxtransform: {
enable: true,
config: {
}
},
url: {
enable: true,
config: {
limit: 1024 // 设定转换尺寸上限
}
},
cssModules: {
enable: false, // 默认为 false,如需使用 css modules 功能,则设为 true
config: {
namingPattern: 'module', // 转换模式,取值为 global/module
generateScopedName: '[name]__[local]___[hash:base64:5]'
}
}
}
},
h5: {
publicPath: '/',
staticDirectory: 'static',
postcss: {
autoprefixer: {
enable: true,
config: {
}
},
cssModules: {
enable: false, // 默认为 false,如需使用 css modules 功能,则设为 true
config: {
namingPattern: 'module', // 转换模式,取值为 global/module
generateScopedName: '[name]__[local]___[hash:base64:5]'
}
}
}
}
}
module.exports = function (merge) {
if (process.env.NODE_ENV === 'development') {
return merge({}, config, require('./dev'))
}
return merge({}, config, require('./prod'))
}
================================================
FILE: mbti-test-mini/config/prod.ts
================================================
module.exports = {
env: {
NODE_ENV: '"production"'
},
defineConstants: {
},
mini: {},
h5: {
/**
* WebpackChain 插件配置
* @docs https://github.com/neutrinojs/webpack-chain
*/
// webpackChain (chain) {
// /**
// * 如果 h5 端编译后体积过大,可以使用 webpack-bundle-analyzer 插件对打包体积进行分析。
// * @docs https://github.com/webpack-contrib/webpack-bundle-analyzer
// */
// chain.plugin('analyzer')
// .use(require('webpack-bundle-analyzer').BundleAnalyzerPlugin, [])
// /**
// * 如果 h5 端首屏加载时间过长,可以使用 prerender-spa-plugin 插件预加载首页。
// * @docs https://github.com/chrisvfritz/prerender-spa-plugin
// */
// const path = require('path')
// const Prerender = require('prerender-spa-plugin')
// const staticDir = path.join(__dirname, '..', 'dist')
// chain
// .plugin('prerender')
// .use(new Prerender({
// staticDir,
// routes: [ '/pages/index/index' ],
// postProcess: (context) => ({ ...context, outputPath: path.join(staticDir, 'index.html') })
// }))
// }
}
}
================================================
FILE: mbti-test-mini/package.json
================================================
{
"name": "mbti-test-mini",
"version": "1.0.0",
"private": true,
"description": "",
"templateInfo": {
"name": "taro-ui",
"typescript": true,
"css": "Sass",
"framework": "React"
},
"scripts": {
"build:weapp": "taro build --type weapp",
"build:swan": "taro build --type swan",
"build:alipay": "taro build --type alipay",
"build:tt": "taro build --type tt",
"build:h5": "taro build --type h5",
"build:rn": "taro build --type rn",
"build:qq": "taro build --type qq",
"build:jd": "taro build --type jd",
"build:quickapp": "taro build --type quickapp",
"dev:weapp": "npm run build:weapp -- --watch",
"dev:swan": "npm run build:swan -- --watch",
"dev:alipay": "npm run build:alipay -- --watch",
"dev:tt": "npm run build:tt -- --watch",
"dev:h5": "npm run build:h5 -- --watch",
"dev:rn": "npm run build:rn -- --watch",
"dev:qq": "npm run build:qq -- --watch",
"dev:jd": "npm run build:jd -- --watch",
"dev:quickapp": "npm run build:quickapp -- --watch"
},
"browserslist": [
"last 3 versions",
"Android >= 4.1",
"ios >= 8"
],
"author": "",
"dependencies": {
"@babel/runtime": "^7.7.7",
"@tarojs/components": "3.6.28",
"@tarojs/helper": "3.6.28",
"@tarojs/plugin-platform-weapp": "3.6.28",
"@tarojs/plugin-platform-alipay": "3.6.28",
"@tarojs/plugin-platform-tt": "3.6.28",
"@tarojs/plugin-platform-swan": "3.6.28",
"@tarojs/plugin-platform-jd": "3.6.28",
"@tarojs/plugin-platform-qq": "3.6.28",
"@tarojs/plugin-platform-h5": "3.6.28",
"@tarojs/runtime": "3.6.28",
"@tarojs/shared": "3.6.28",
"@tarojs/taro": "3.6.28",
"@tarojs/plugin-framework-react": "3.6.28",
"lodash": "4.17.15",
"taro-ui": "^3.2.1",
"@tarojs/react": "3.6.28",
"react": "^18.0.0",
"react-dom": "^18.0.0"
},
"devDependencies": {
"@babel/core": "^7.8.0",
"@tarojs/cli": "3.6.28",
"postcss": "^8.4.18",
"webpack": "^5.78.0",
"@tarojs/taro-loader": "3.6.28",
"@tarojs/webpack5-runner": "3.6.28",
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.5",
"react-refresh": "^0.11.0",
"@types/webpack-env": "^1.13.6",
"babel-preset-taro": "3.6.28",
"eslint": "^8.12.0",
"eslint-config-taro": "3.6.28",
"stylelint": "9.3.0",
"@typescript-eslint/parser": "^5.20.0",
"@typescript-eslint/eslint-plugin": "^5.20.0",
"typescript": "^4.1.0",
"@types/react": "^18.0.0",
"eslint-plugin-react": "^7.8.2",
"eslint-plugin-import": "^2.12.0",
"eslint-plugin-react-hooks": "^4.2.0",
"ts-node": "^10.9.1",
"@types/node": "^18.15.11"
}
}
================================================
FILE: mbti-test-mini/project.config.json
================================================
{
"miniprogramRoot": "dist/",
"projectname": "mbti-test-mini",
"description": "",
"appid": "wx370d9458c983e21d",
"setting": {
"urlCheck": true,
"es6": false,
"enhance": false,
"compileHotReLoad": false,
"postcss": false,
"preloadBackgroundData": false,
"minified": false,
"newFeature": true,
"autoAudits": false,
"coverView": true,
"showShadowRootInWxmlPanel": false,
"scopeDataCheck": false,
"useCompilerModule": false,
"babelSetting": {
"ignore": [],
"disablePlugins": [],
"outputPath": ""
}
},
"compileType": "miniprogram",
"simulatorType": "wechat",
"simulatorPluginLibVersion": {},
"condition": {},
"libVersion": "3.4.3",
"srcMiniprogramRoot": "dist/",
"packOptions": {
"ignore": [],
"include": []
},
"editorSetting": {
"tabIndent": "insertSpaces",
"tabSize": 2
}
}
================================================
FILE: mbti-test-mini/project.private.config.json
================================================
{
"description": "项目私有配置文件。此文件中的内容将覆盖 project.config.json 中的相同字段。项目的改动优先同步到此文件中。详见文档:https://developers.weixin.qq.com/miniprogram/dev/devtools/projectconfig.html",
"projectname": "mbti-test-mini",
"setting": {
"compileHotReLoad": true
}
}
================================================
FILE: mbti-test-mini/project.tt.json
================================================
{
"miniprogramRoot": "./",
"projectname": "mbti-test-mini",
"description": "",
"appid": "touristappid",
"setting": {
"urlCheck": true,
"es6": false,
"postcss": false,
"minified": false
},
"compileType": "miniprogram"
}
================================================
FILE: mbti-test-mini/src/app.config.ts
================================================
export default defineAppConfig({
pages: ["pages/index/index", "pages/result/index", "pages/doQuestion/index"],
window: {
backgroundTextStyle: "light",
navigationBarBackgroundColor: "#fff",
navigationBarTitleText: "鱼皮 MBTI 性格测试",
navigationBarTextStyle: "black",
},
});
================================================
FILE: mbti-test-mini/src/app.scss
================================================
.at-button--primary {
background: #806497;
border-color: #806497;
}
================================================
FILE: mbti-test-mini/src/app.ts
================================================
import Taro, { useLaunch } from "@tarojs/taro";
import { PropsWithChildren } from "react";
import "taro-ui/dist/style/index.scss"; // 引入组件样式 - 方式一
import "./app.scss";
function App({ children }: PropsWithChildren) {
useLaunch(async () => {
const res = await Taro.login();
console.log(res);
// todo 拿到 res.code 后,调用后端登录
});
return children;
}
export default App;
================================================
FILE: mbti-test-mini/src/components/GlobalFooter/index.scss
================================================
.globalFooter {
position: fixed;
bottom: 16px;
left: 0;
right: 0;
text-align: center;
}
================================================
FILE: mbti-test-mini/src/components/GlobalFooter/index.tsx
================================================
import { View } from "@tarojs/components";
import "./index.scss";
/**
* 全局底部栏组件
* @author <a href="https://github.com/liyupi">程序员鱼皮</a>
* @from <a href="https://www.code-nav.cn">编程导航学习圈</a>
*/
export default () => {
return (
<View className="globalFooter">
作者:程序员鱼皮
</View>
);
};
================================================
FILE: mbti-test-mini/src/data/question_results.json
================================================
[
{
"resultProp": [
"I",
"S",
"T",
"J"
],
"resultDesc": "忠诚可靠,被公认为务实,注重细节。",
"resultPicture": "icon_url_istj",
"resultName": "ISTJ(物流师)"
},
{
"resultProp": [
"I",
"S",
"F",
"J"
],
"resultDesc": "善良贴心,以同情心和责任为特点。",
"resultPicture": "icon_url_isfj",
"resultName": "ISFJ(守护者)"
},
{
"resultProp": [
"I",
"N",
"F",
"J"
],
"resultDesc": "理想主义者,有着深刻的洞察力,善于理解他人。",
"resultPicture": "icon_url_infj",
"resultName": "INFJ(占有者)"
},
{
"resultProp": [
"I",
"N",
"T",
"J"
],
"resultDesc": "独立思考者,善于规划和实现目标,理性而果断。",
"resultPicture": "icon_url_intj",
"resultName": "INTJ(设计师)"
},
{
"resultProp": [
"I",
"S",
"T",
"P"
],
"resultDesc": "冷静自持,善于解决问题,擅长实践技能。",
"resultPicture": "icon_url_istp",
"resultName": "ISTP(运动员)"
},
{
"resultProp": [
"I",
"S",
"F",
"P"
],
"resultDesc": "具有艺术感和敏感性,珍视个人空间和自由。",
"resultPicture": "icon_url_isfp",
"resultName": "ISFP(艺术家)"
},
{
"resultProp": [
"I",
"N",
"F",
"P"
],
"resultDesc": "理想主义者,富有创造力,以同情心和理解他人著称。",
"resultPicture": "icon_url_infp",
"resultName": "INFP(治愈者)"
},
{
"resultProp": [
"I",
"N",
"T",
"P"
],
"resultDesc": "思维清晰,探索精神,独立思考且理性。",
"resultPicture": "icon_url_intp",
"resultName": "INTP(学者)"
},
{
"resultProp": [
"E",
"S",
"T",
"P"
],
"resultDesc": "敢于冒险,乐于冒险,思维敏捷,行动果断。",
"resultPicture": "icon_url_estp",
"resultName": "ESTP(拓荒者)"
},
{
"resultProp": [
"E",
"S",
"F",
"P"
],
"resultDesc": "热情开朗,善于社交,热爱生活,乐于助人。",
"resultPicture": "icon_url_esfp",
"resultName": "ESFP(表演者)"
},
{
"resultProp": [
"E",
"N",
"F",
"P"
],
"resultDesc": "富有想象力,充满热情,善于激发他人的活力和潜力。",
"resultPicture": "icon_url_enfp",
"resultName": "ENFP(倡导者)"
},
{
"resultProp": [
"E",
"N",
"T",
"P"
],
"resultDesc": "充满创造力,善于辩论,挑战传统,喜欢探索新领域。",
"resultPicture": "icon_url_entp",
"resultName": "ENTP(发明家)"
},
{
"resultProp": [
"E",
"S",
"T",
"J"
],
"resultDesc": "务实果断,善于组织和管理,重视效率和目标。",
"resultPicture": "icon_url_estj",
"resultName": "ESTJ(主管)"
},
{
"resultProp": [
"E",
"S",
"F",
"J"
],
"resultDesc": "友善热心,以协调、耐心和关怀为特点,善于团队合作。",
"resultPicture": "icon_url_esfj",
"resultName": "ESFJ(尽责者)"
},
{
"resultProp": [
"E",
"N",
"F",
"J"
],
"resultDesc": "热情关爱,善于帮助他人,具有领导力和社交能力。",
"resultPicture": "icon_url_enfj",
"resultName": "ENFJ(教导着)"
},
{
"resultProp": [
"E",
"N",
"T",
"J"
],
"resultDesc": "果断自信,具有领导才能,善于规划和执行目标。",
"resultPicture": "icon_url_entj",
"resultName": "ENTJ(统帅)"
}
]
================================================
FILE: mbti-test-mini/src/data/questions.json
================================================
[
{
"options": [
{
"result": "I",
"value": "独自工作",
"key": "A"
},
{
"result": "E",
"value": "与他人合作",
"key": "B"
}
],
"title": "你通常更喜欢"
},
{
"options": [
{
"result": "J",
"value": "喜欢有明确的计划",
"key": "A"
},
{
"result": "P",
"value": "更愿意随机应变",
"key": "B"
}
],
"title": "当安排活动时"
},
{
"options": [
{
"result": "T",
"value": "认为应该严格遵守",
"key": "A"
},
{
"result": "F",
"value": "认为应灵活运用",
"key": "B"
}
],
"title": "你如何看待规则"
},
{
"options": [
{
"result": "E",
"value": "经常是说话的人",
"key": "A"
},
{
"result": "I",
"value": "更倾向于倾听",
"key": "B"
}
],
"title": "在社交场合中"
},
{
"options": [
{
"result": "J",
"value": "先研究再行动",
"key": "A"
},
{
"result": "P",
"value": "边做边学习",
"key": "B"
}
],
"title": "面对新的挑战"
},
{
"options": [
{
"result": "S",
"value": "注重细节和事实",
"key": "A"
},
{
"result": "N",
"value": "注重概念和想象",
"key": "B"
}
],
"title": "在日常生活中"
},
{
"options": [
{
"result": "T",
"value": "更多基于逻辑分析",
"key": "A"
},
{
"result": "F",
"value": "更多基于个人情感",
"key": "B"
}
],
"title": "做决定时"
},
{
"options": [
{
"result": "S",
"value": "喜欢有结构和常规",
"key": "A"
},
{
"result": "N",
"value": "喜欢自由和灵活性",
"key": "B"
}
],
"title": "对于日常安排"
},
{
"options": [
{
"result": "P",
"value": "首先考虑可能性",
"key": "A"
},
{
"result": "J",
"value": "首先考虑后果",
"key": "B"
}
],
"title": "当遇到问题时"
},
{
"options": [
{
"result": "T",
"value": "时间是一种宝贵的资源",
"key": "A"
},
{
"result": "F",
"value": "时间是相对灵活的概念",
"key": "B"
}
],
"title": "你如何看待时间"
}
]
================================================
FILE: mbti-test-mini/src/index.html
================================================
<!DOCTYPE html>
<html>
<head>
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
<meta content="width=device-width,initial-scale=1,user-scalable=no" name="viewport">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-touch-fullscreen" content="yes">
<meta name="format-detection" content="telephone=no,address=no">
<meta name="apple-mobile-web-app-status-bar-style" content="white">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" >
<title>mbti-test-mini</title>
<script><%= htmlWebpackPlugin.options.script %></script>
</head>
<body>
<div id="app"></div>
</body>
</html>
================================================
FILE: mbti-test-mini/src/pages/doQuestion/index.config.ts
================================================
export default definePageConfig({
// navigationBarTitleText: ''
})
================================================
FILE: mbti-test-mini/src/pages/doQuestion/index.scss
================================================
.doQuestionPage {
.title {
margin-bottom: 48px;
}
.options-wrapper {
margin-bottom: 48px;
}
.controlBtn {
margin: 24px 48px;
}
}
================================================
FILE: mbti-test-mini/src/pages/doQuestion/index.tsx
================================================
import {View} from "@tarojs/components";
import Taro from "@tarojs/taro";
import {AtButton, AtRadio} from "taro-ui";
import {useEffect, useState} from "react";
import GlobalFooter from "../../components/GlobalFooter";
import questions from "../../data/questions.json";
import "./index.scss";
/**
* 做题页面
* @author <a href="https://github.com/liyupi">程序员鱼皮</a>
* @from <a href="https://www.code-nav.cn">编程导航学习圈</a>
*/
export default () => {
// 当前题目序号(从 1 开始)
const [current, setCurrent] = useState<number>(1);
// 当前题目
const [currentQuestion, setCurrentQuestion] = useState<Question>(questions[0]);
const questionOptions = currentQuestion.options.map((option) => {
return {label: `${option.key}. ${option.value}`, value: option.key};
});
// 当前答案
const [currentAnswer, setCurrentAnswer] = useState<string>();
// 回答列表
const [answerList] = useState<string[]>([]);
// 序号变化时,切换当前题目和当前回答
useEffect(() => {
setCurrentQuestion(questions[current - 1]);
setCurrentAnswer(answerList[current - 1]);
}, [current]);
return (
<View className="doQuestionPage">
<View className="at-article__h2 title">
{current}、{currentQuestion.title}
</View>
<View className="options-wrapper">
<AtRadio
options={questionOptions}
value={currentAnswer}
onClick={(value) => {
setCurrentAnswer(value);
// 记录回答
answerList[current - 1] = value;
}}
/>
</View>
{current < questions.length && (
<AtButton
type="primary"
circle
className="controlBtn"
disabled={!currentAnswer}
onClick={() => setCurrent(current + 1)}
>
下一题
</AtButton>
)}
{current == questions.length && (
<AtButton
type="primary"
circle
className="controlBtn"
disabled={!currentAnswer}
onClick={() => {
// 传递答案
Taro.setStorageSync("answerList", answerList);
// 跳转到结果页面
Taro.navigateTo({
url: "/pages/result/index",
});
}}
>
查看结果
</AtButton>
)}
{current > 1 && (
<AtButton
circle
className="controlBtn"
onClick={() => setCurrent(current - 1)}
>
上一题
</AtButton>
)}
<GlobalFooter/>
</View>
);
};
================================================
FILE: mbti-test-mini/src/pages/index/index.config.ts
================================================
export default definePageConfig({
// navigationBarTitleText: ''
})
================================================
FILE: mbti-test-mini/src/pages/index/index.scss
================================================
.indexPage {
background: #A2C7D7;
.title {
color: white;
padding-top: 48px;
text-align: center;
}
.subTitle {
color: white;
margin-bottom: 48px;
}
.enterBtn {
width: 60vw;
}
}
================================================
FILE: mbti-test-mini/src/pages/index/index.tsx
================================================
import {View, Image} from "@tarojs/components";
import {AtButton} from "taro-ui";
import Taro from "@tarojs/taro";
import headerBg from "../../assets/headerBg.jpg";
import GlobalFooter from "../../components/GlobalFooter";
import "./index.scss";
/**
* 主页
* @author <a href="https://github.com/liyupi">程序员鱼皮</a>
* @from <a href="https://www.code-nav.cn">编程导航学习圈</a>
*/
export default () => {
return (
<View className="indexPage">
<View className="at-article__h1 title">MBTI 性格测试</View>
<View className="at-article__h2 subTitle">
只需 2 分钟,就能非常准确地描述出你是谁,以及你的性格特点
</View>
<AtButton
type="primary"
circle
className="enterBtn"
onClick={() => {
Taro.navigateTo({
url: "/pages/doQuestion/index",
});
}}
>
开始测试
</AtButton>
<Image className="headerBg" src={headerBg}/>
<GlobalFooter/>
</View>
);
};
================================================
FILE: mbti-test-mini/src/pages/result/index.config.ts
================================================
export default definePageConfig({
navigationBarTitleText: '查看结果'
})
================================================
FILE: mbti-test-mini/src/pages/result/index.scss
================================================
.resultPage {
background: #A2C7D7;
.title {
color: white;
padding-top: 48px;
text-align: center;
}
.subTitle {
color: white;
margin-bottom: 48px;
}
.enterBtn {
width: 60vw;
}
}
================================================
FILE: mbti-test-mini/src/pages/result/index.tsx
================================================
import {View, Image} from "@tarojs/components";
import {AtButton} from "taro-ui";
import Taro from "@tarojs/taro";
import headerBg from "../../assets/headerBg.jpg";
import GlobalFooter from "../../components/GlobalFooter";
import {getBestQuestionResult} from "../../utils/bizUtils";
import questions from "../../data/questions.json";
import questionResults from "../../data/question_results.json";
import "./index.scss";
/**
* 测试结果页面
* @author <a href="https://github.com/liyupi">程序员鱼皮</a>
* @from <a href="https://www.code-nav.cn">编程导航学习圈</a>
*/
export default () => {
// 获取答案
const answerList = Taro.getStorageSync("answerList");
if (!answerList || answerList.length < 1) {
Taro.showToast({
title: "答案为空",
icon: "error",
duration: 3000,
});
}
// 获取测试结果
const result = getBestQuestionResult(answerList, questions, questionResults);
return (
<View className="resultPage">
<View className="at-article__h1 title">{result.resultName}</View>
<View className="at-article__h2 subTitle">{result.resultDesc}</View>
<AtButton
type="primary"
circle
className="enterBtn"
onClick={() => {
Taro.reLaunch({
url: "/pages/index/index",
});
}}
>
返回主页
</AtButton>
<Image className="headerBg" src={headerBg}/>
<GlobalFooter/>
</View>
);
};
================================================
FILE: mbti-test-mini/src/utils/bizUtils.ts
================================================
/**
* 获取最佳题目评分结果
* @param answerList
* @param questions
* @param question_results
*/
export function getBestQuestionResult(answerList, questions, question_results) {
// 初始化一个对象,用于存储每个选项的计数
const optionCount = {};
// 用户选择 A, B, C
// 对应 result:I, I, J
// optionCount[I] = 2; optionCount[J] = 1
// 遍历题目列表
for (const question of questions) {
// 遍历答案列表
for (const answer of answerList) {
// 遍历题目中的选项
for (const option of question.options) {
// 如果答案和选项的key匹配
if (option.key === answer) {
// 获取选项的result属性
const result = option.result;
// 如果result属性不在optionCount中,初始化为0
if (!optionCount[result]) {
optionCount[result] = 0;
}
// 在optionCount中增加计数
optionCount[result]++;
}
}
}
}
// 初始化最高分数和最高分数对应的评分结果
let maxScore = 0;
let maxScoreResult = question_results[0];
// 遍历评分结果列表
for (const result of question_results) {
// 计算当前评分结果的分数
const score = result.resultProp.reduce((count, prop) => {
return count + (optionCount[prop] || 0);
}, 0);
// 如果分数高于当前最高分数,更新最高分数和最高分数对应的评分结果
if (score > maxScore) {
maxScore = score;
maxScoreResult = result;
}
}
// 返回最高分数和最高分数对应的评分结果
return maxScoreResult;
}
// 示例数据
const answerList = ["B","B","B","A"];
const questions = [
{
title: "你通常更喜欢",
options: [
{
result: "I",
value: "独自工作",
key: "A",
},
{
result: "E",
value: "与他人合作",
key: "B",
},
],
},
{
options: [
{
result: "S",
value: "喜欢有结构和常规",
key: "A",
},
{
result: "N",
value: "喜欢自由和灵活性",
key: "B",
},
],
title: "对于日常安排",
},
{
options: [
{
result: "P",
value: "首先考虑可能性",
key: "A",
},
{
result: "J",
value: "首先考虑后果",
key: "B",
},
],
title: "当遇到问题时",
},
{
options: [
{
result: "T",
value: "时间是一种宝贵的资源",
key: "A",
},
{
result: "F",
value: "时间是相对灵活的概念",
key: "B",
},
],
title: "你如何看待时间",
},
];
const question_results = [
{
resultProp: ["I", "S", "T", "J"],
resultDesc: "忠诚可靠,被公认为务实,注重细节。",
resultPicture: "icon_url_istj",
resultName: "ISTJ(物流师)",
},
{
resultProp: ["I", "S", "F", "J"],
resultDesc: "善良贴心,以同情心和责任为特点。",
resultPicture: "icon_url_isfj",
resultName: "ISFJ(守护者)",
},
];
console.log(getBestQuestionResult(answerList, questions, question_results));
================================================
FILE: mbti-test-mini/tsconfig.json
================================================
{
"compilerOptions": {
"target": "es2017",
"module": "commonjs",
"removeComments": false,
"preserveConstEnums": true,
"moduleResolution": "node",
"experimentalDecorators": true,
"noImplicitAny": false,
"allowSyntheticDefaultImports": true,
"outDir": "lib",
"noUnusedLocals": true,
"noUnusedParameters": true,
"strictNullChecks": true,
"sourceMap": true,
"baseUrl": ".",
"rootDir": ".",
"jsx": "react-jsx",
"allowJs": true,
"resolveJsonModule": true,
"typeRoots": [
"node_modules/@types"
]
},
"include": ["./src", "./types"],
"compileOnSave": false
}
================================================
FILE: mbti-test-mini/types/custom.d.ts
================================================
interface QuestionOption<T extends string = string> {
result: T;
value: string;
key: T;
}
interface Question<T extends string = string> {
title: string;
options: QuestionOption<T>[];
}
================================================
FILE: mbti-test-mini/types/global.d.ts
================================================
/// <reference types="@tarojs/taro" />
declare module '*.png';
declare module '*.gif';
declare module '*.jpg';
declare module '*.jpeg';
declare module '*.svg';
declare module '*.css';
declare module '*.less';
declare module '*.scss';
declare module '*.sass';
declare module '*.styl';
declare namespace NodeJS {
interface ProcessEnv {
TARO_ENV: 'weapp' | 'swan' | 'alipay' | 'h5' | 'rn' | 'tt' | 'quickapp' | 'qq' | 'jd'
}
}
================================================
FILE: yudada-backend/.gitignore
================================================
### @author <a href="https://github.com/liyupi">程序员鱼皮</a> ###
### @from <a href="https://yupi.icu">编程导航知识星球</a> ###
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
### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
build/
!**/src/main/**/build/
!**/src/test/**/build/
### VS Code ###
.vscode/
### Java template
# Compiled class file
*.class
# Log file
*.log
# BlueJ files
*.ctxt
# Mobile Tools for Java (J2ME)
.mtj.tmp/
# Package Files #
*.jar
*.war
*.nar
*.ear
*.zip
*.tar.gz
*.rar
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
### Maven template
target/
pom.xml.tag
pom.xml.releaseBackup
pom.xml.versionsBackup
pom.xml.next
release.properties
dependency-reduced-pom.xml
buildNumber.properties
.mvn/timing.properties
# https://github.com/takari/maven-wrapper#usage-without-binary-jar
.mvn/wrapper/maven-wrapper.jar
### JetBrains template
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
# User-specific stuff
.idea/**/workspace.xml
.idea/**/tasks.xml
.idea/**/usage.statistics.xml
.idea/**/dictionaries
.idea/**/shelf
# Generated files
.idea/**/contentModel.xml
# Sensitive or high-churn files
.idea/**/dataSources/
.idea/**/dataSources.ids
.idea/**/dataSources.local.xml
.idea/**/sqlDataSources.xml
.idea/**/dynamic.xml
.idea/**/uiDesigner.xml
.idea/**/dbnavigator.xml
# Gradle
.idea/**/gradle.xml
.idea/**/libraries
# Gradle and Maven with auto-import
# When using Gradle or Maven with auto-import, you should exclude module files,
# since they will be recreated, and may cause churn. Uncomment if using
# auto-import.
# .idea/artifacts
# .idea/compiler.xml
# .idea/jarRepositories.xml
# .idea/modules.xml
# .idea/*.iml
# .idea/modules
# *.iml
# *.ipr
# CMake
cmake-build-*/
# Mongo Explorer plugin
.idea/**/mongoSettings.xml
# File-based project format
*.iws
# IntelliJ
out/
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Cursive Clojure plugin
.idea/replstate.xml
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties
# Editor-based Rest Client
.idea/httpRequests
# Android studio 3.1+ serialized cache file
.idea/caches/build_file_checksums.ser
================================================
FILE: yudada-backend/.mvn/wrapper/maven-wrapper.properties
================================================
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.1/apache-maven-3.8.1-bin.zip
wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar
================================================
FILE: yudada-backend/Dockerfile
================================================
# Docker 镜像构建
# @author <a href="https://github.com/liyupi">程序员鱼皮</a>
# @from <a href="https://yupi.icu">编程导航知识星球</a>
# 选择基础镜像
FROM maven:3.8.1-jdk-8-slim as builder
# 解决容器时期与真实时间相差 8 小时的问题
RUN ln -snf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && echo Asia/Shanghai > /etc/timezone
# 复制代码到容器内
WORKDIR /app
COPY pom.xml .
COPY src ./src
# 打包构建
RUN mvn package -DskipTests
# 容器启动时运行 jar 包
CMD ["java","-jar","/app/target/yudada-backend-0.0.1-SNAPSHOT.jar","--spring.profiles.active=prod"]
================================================
FILE: yudada-backend/README.md
================================================
# SpringBoot 项目初始模板
> 作者:[程序员鱼皮](https://github.com/liyupi)
> 仅分享于 [编程导航知识星球](https://yupi.icu)
基于 Java SpringBoot 的项目初始模板,整合了常用框架和主流业务的示例代码。
只需 1 分钟即可完成内容网站的后端!!!大家还可以在此基础上快速开发自己的项目。
[toc]
## 模板特点
### 主流框架 & 特性
- Spring Boot 2.7.x(贼新)
- Spring MVC
- MyBatis + MyBatis Plus 数据访问(开启分页)
- Spring Boot 调试工具和项目处理器
- Spring AOP 切面编程
- Spring Scheduler 定时任务
- Spring 事务注解
### 数据存储
- MySQL 数据库
- Redis 内存数据库
- Elasticsearch 搜索引擎
- 腾讯云 COS 对象存储
### 工具类
- Easy Excel 表格处理
- Hutool 工具库
- Apache Commons Lang3 工具类
- Lombok 注解
### 业务特性
- 业务代码生成器(支持自动生成 Service、Controller、数据模型代码)
- Spring Session Redis 分布式登录
- 全局请求响应拦截器(记录日志)
- 全局异常处理器
- 自定义错误码
- 封装通用响应类
- Swagger + Knife4j 接口文档
- 自定义权限注解 + 全局校验
- 全局跨域处理
- 长整数丢失精度解决
- 多环境配置
## 业务功能
- 提供示例 SQL(用户、帖子、帖子点赞、帖子收藏表)
- 用户登录、注册、注销、更新、检索、权限管理
- 帖子创建、删除、编辑、更新、数据库检索、ES 灵活检索
- 帖子点赞、取消点赞
- 帖子收藏、取消收藏、检索已收藏帖子
- 帖子全量同步 ES、增量同步 ES 定时任务
- 支持微信开放平台登录
- 支持微信公众号订阅、收发消息、设置菜单
- 支持分业务的文件上传
### 单元测试
- JUnit5 单元测试
- 示例单元测试类
### 架构设计
- 合理分层
## 快速上手
> 所有需要修改的地方鱼皮都标记了 `todo`,便于大家找到修改的位置~
### MySQL 数据库
1)修改 `application.yml` 的数据库配置为你自己的:
```yml
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/yudada
username: root
password: 123456
```
2)执行 `sql/create_table.sql` 中的数据库语句,自动创建库表
3)启动项目,访问 `http://localhost:8101/api/doc.html` 即可打开接口文档,不需要写前端就能在线调试接口了~

### Redis 分布式登录
1)修改 `application.yml` 的 Redis 配置为你自己的:
```yml
spring:
redis:
database: 1
host: localhost
port: 6379
timeout: 5000
password: 123456
```
2)修改 `application.yml` 中的 session 存储方式:
```yml
spring:
session:
store-type: redis
```
3)移除 `MainApplication` 类开头 `@SpringBootApplication` 注解内的 exclude 参数:
修改前:
```java
@SpringBootApplication(exclude = {RedisAutoConfiguration.class})
```
修改后:
```java
@SpringBootApplication
```
### Elasticsearch 搜索引擎
1)修改 `application.yml` 的 Elasticsearch 配置为你自己的:
```yml
spring:
elasticsearch:
uris: http://localhost:9200
username: root
password: 123456
```
2)复制 `sql/post_es_mapping.json` 文件中的内容,通过调用 Elasticsearch 的接口或者 Kibana Dev Tools 来创建索引(相当于数据库建表)
```
PUT post_v1
{
参数见 sql/post_es_mapping.json 文件
}
```
这步不会操作的话需要补充下 Elasticsearch 的知识,或者自行百度一下~
3)开启同步任务,将数据库的帖子同步到 Elasticsearch
找到 job 目录下的 `FullSyncPostToEs` 和 `IncSyncPostToEs` 文件,取消掉 `@Component` 注解的注释,再次执行程序即可触发同步:
```java
// todo 取消注释开启任务
//@Component
```
### 业务代码生成器
支持自动生成 Service、Controller、数据模型代码,配合 MyBatisX 插件,可以快速开发增删改查等实用基础功能。
找到 `generate.CodeGenerator` 类,修改生成参数和生成路径,并且支持注释掉不需要的生成逻辑,然后运行即可。
```
// 指定生成参数
String packageName = "com.yupi.yudada";
String dataName = "用户评论";
String dataKey = "userComment";
String upperDataKey = "UserComment";
```
生成代码后,可以移动到实际项目中,并且按照 `// todo` 注释的提示来针对自己的业务需求进行修改。
================================================
FILE: yudada-backend/mvnw
================================================
#!/bin/sh
# ----------------------------------------------------------------------------
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
# ----------------------------------------------------------------------------
# ----------------------------------------------------------------------------
# Maven Start Up Batch script
#
# Required ENV vars:
# ------------------
# JAVA_HOME - location of a JDK home dir
#
# Optional ENV vars
# -----------------
# M2_HOME - location of maven2's installed home dir
# MAVEN_OPTS - parameters passed to the Java VM when running Maven
# e.g. to debug Maven itself, use
# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
# MAVEN_SKIP_RC - flag to disable loading of mavenrc files
# ----------------------------------------------------------------------------
if [ -z "$MAVEN_SKIP_RC" ] ; then
if [ -f /usr/local/etc/mavenrc ] ; then
. /usr/local/etc/mavenrc
fi
if [ -f /etc/mavenrc ] ; then
. /etc/mavenrc
fi
if [ -f "$HOME/.mavenrc" ] ; then
. "$HOME/.mavenrc"
fi
fi
# OS specific support. $var _must_ be set to either true or false.
cygwin=false;
darwin=false;
mingw=false
case "`uname`" in
CYGWIN*) cygwin=true ;;
MINGW*) mingw=true;;
Darwin*) darwin=true
# Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home
# See https://developer.apple.com/library/mac/qa/qa1170/_index.html
if [ -z "$JAVA_HOME" ]; then
if [ -x "/usr/libexec/java_home" ]; then
export JAVA_HOME="`/usr/libexec/java_home`"
else
export JAVA_HOME="/Library/Java/Home"
fi
fi
;;
esac
if [ -z "$JAVA_HOME" ] ; then
if [ -r /etc/gentoo-release ] ; then
JAVA_HOME=`java-config --jre-home`
fi
fi
if [ -z "$M2_HOME" ] ; then
## resolve links - $0 may be a link to maven's home
PRG="$0"
# need this for relative symlinks
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG="`dirname "$PRG"`/$link"
fi
done
saveddir=`pwd`
M2_HOME=`dirname "$PRG"`/..
# make it fully qualified
M2_HOME=`cd "$M2_HOME" && pwd`
cd "$saveddir"
# echo Using m2 at $M2_HOME
fi
# For Cygwin, ensure paths are in UNIX format before anything is touched
if $cygwin ; then
[ -n "$M2_HOME" ] &&
M2_HOME=`cygpath --unix "$M2_HOME"`
[ -n "$JAVA_HOME" ] &&
JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
[ -n "$CLASSPATH" ] &&
CLASSPATH=`cygpath --path --unix "$CLASSPATH"`
fi
# For Mingw, ensure paths are in UNIX format before anything is touched
if $mingw ; then
[ -n "$M2_HOME" ] &&
M2_HOME="`(cd "$M2_HOME"; pwd)`"
[ -n "$JAVA_HOME" ] &&
JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`"
fi
if [ -z "$JAVA_HOME" ]; then
javaExecutable="`which javac`"
if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then
# readlink(1) is not available as standard on Solaris 10.
readLink=`which readlink`
if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then
if $darwin ; then
javaHome="`dirname \"$javaExecutable\"`"
javaExecutable="`cd \"$javaHome\" && pwd -P`/javac"
else
javaExecutable="`readlink -f \"$javaExecutable\"`"
fi
javaHome="`dirname \"$javaExecutable\"`"
javaHome=`expr "$javaHome" : '\(.*\)/bin'`
JAVA_HOME="$javaHome"
export JAVA_HOME
fi
fi
fi
if [ -z "$JAVACMD" ] ; then
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
else
JAVACMD="`\\unset -f command; \\command -v java`"
fi
fi
if [ ! -x "$JAVACMD" ] ; then
echo "Error: JAVA_HOME is not defined correctly." >&2
echo " We cannot execute $JAVACMD" >&2
exit 1
fi
if [ -z "$JAVA_HOME" ] ; then
echo "Warning: JAVA_HOME environment variable is not set."
fi
CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher
# traverses directory structure from process work directory to filesystem root
# first directory with .mvn subdirectory is considered project base directory
find_maven_basedir() {
if [ -z "$1" ]
then
echo "Path not specified to find_maven_basedir"
return 1
fi
basedir="$1"
wdir="$1"
while [ "$wdir" != '/' ] ; do
if [ -d "$wdir"/.mvn ] ; then
basedir=$wdir
break
fi
# workaround for JBEAP-8937 (on Solaris 10/Sparc)
if [ -d "${wdir}" ]; then
wdir=`cd "$wdir/.."; pwd`
fi
# end of workaround
done
echo "${basedir}"
}
# concatenates all lines of a file
concat_lines() {
if [ -f "$1" ]; then
echo "$(tr -s '\n' ' ' < "$1")"
fi
}
BASE_DIR=`find_maven_basedir "$(pwd)"`
if [ -z "$BASE_DIR" ]; then
exit 1;
fi
##########################################################################################
# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
# This allows using the maven wrapper in projects that prohibit checking in binary data.
##########################################################################################
if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then
if [ "$MVNW_VERBOSE" = true ]; then
echo "Found .mvn/wrapper/maven-wrapper.jar"
fi
else
if [ "$MVNW_VERBOSE" = true ]; then
echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..."
fi
if [ -n "$MVNW_REPOURL" ]; then
jarUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar"
else
jarUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar"
fi
while IFS="=" read key value; do
case "$key" in (wrapperUrl) jarUrl="$value"; break ;;
esac
done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties"
if [ "$MVNW_VERBOSE" = true ]; then
echo "Downloading from: $jarUrl"
fi
wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar"
if $cygwin; then
wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"`
fi
if command -v wget > /dev/null; then
if [ "$MVNW_VERBOSE" = true ]; then
echo "Found wget ... using wget"
fi
if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
wget "$jarUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath"
else
wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath"
fi
elif command -v curl > /dev/null; then
if [ "$MVNW_VERBOSE" = true ]; then
echo "Found curl ... using curl"
fi
if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
curl -o "$wrapperJarPath" "$jarUrl" -f
else
curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f
fi
else
if [ "$MVNW_VERBOSE" = true ]; then
echo "Falling back to using Java to download"
fi
javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java"
# For Cygwin, switch paths to Windows format before running javac
if $cygwin; then
javaClass=`cygpath --path --windows "$javaClass"`
fi
if [ -e "$javaClass" ]; then
if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
if [ "$MVNW_VERBOSE" = true ]; then
echo " - Compiling MavenWrapperDownloader.java ..."
fi
# Compiling the Java class
("$JAVA_HOME/bin/javac" "$javaClass")
fi
if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
# Running the downloader
if [ "$MVNW_VERBOSE" = true ]; then
echo " - Running MavenWrapperDownloader.java ..."
fi
("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR")
fi
fi
fi
fi
##########################################################################################
# End of extension
##########################################################################################
export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}
if [ "$MVNW_VERBOSE" = true ]; then
echo $MAVEN_PROJECTBASEDIR
fi
MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
# For Cygwin, switch paths to Windows format before running java
if $cygwin; then
[ -n "$M2_HOME" ] &&
M2_HOME=`cygpath --path --windows "$M2_HOME"`
[ -n "$JAVA_HOME" ] &&
JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"`
[ -n "$CLASSPATH" ] &&
CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
[ -n "$MAVEN_PROJECTBASEDIR" ] &&
MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"`
fi
# Provide a "standardized" way to retrieve the CLI args that will
# work with both Windows and non-Windows executions.
MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@"
export MAVEN_CMD_LINE_ARGS
WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
exec "$JAVACMD" \
$MAVEN_OPTS \
$MAVEN_DEBUG_OPTS \
-classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
"-Dmaven.home=${M2_HOME}" \
"-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"
================================================
FILE: yudada-backend/mvnw.cmd
================================================
@REM ----------------------------------------------------------------------------
@REM Licensed to the Apache Software Foundation (ASF) under one
@REM or more contributor license agreements. See the NOTICE file
@REM distributed with this work for additional information
@REM regarding copyright ownership. The ASF licenses this file
@REM to you under the Apache License, Version 2.0 (the
@REM "License"); you may not use this file except in compliance
@REM with the License. You may obtain a copy of the License at
@REM
@REM https://www.apache.org/licenses/LICENSE-2.0
@REM
@REM Unless required by applicable law or agreed to in writing,
@REM software distributed under the License is distributed on an
@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@REM KIND, either express or implied. See the License for the
@REM specific language governing permissions and limitations
@REM under the License.
@REM ----------------------------------------------------------------------------
@REM ----------------------------------------------------------------------------
@REM Maven Start Up Batch script
@REM
@REM Required ENV vars:
@REM JAVA_HOME - location of a JDK home dir
@REM
@REM Optional ENV vars
@REM M2_HOME - location of maven2's installed home dir
@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending
@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
@REM e.g. to debug Maven itself, use
@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
@REM ----------------------------------------------------------------------------
@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
@echo off
@REM set title of command window
title %0
@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on'
@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
@REM set %HOME% to equivalent of $HOME
if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
@REM Execute a user defined script before this one
if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
@REM check for pre script, once with legacy .bat ending and once with .cmd ending
if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %*
if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %*
:skipRcPre
@setlocal
set ERROR_CODE=0
@REM To isolate internal variables from possible post scripts, we use another setlocal
@setlocal
@REM ==== START VALIDATION ====
if not "%JAVA_HOME%" == "" goto OkJHome
echo.
echo Error: JAVA_HOME not found in your environment. >&2
echo Please set the JAVA_HOME variable in your environment to match the >&2
echo location of your Java installation. >&2
echo.
goto error
:OkJHome
if exist "%JAVA_HOME%\bin\java.exe" goto init
echo.
echo Error: JAVA_HOME is set to an invalid directory. >&2
echo JAVA_HOME = "%JAVA_HOME%" >&2
echo Please set the JAVA_HOME variable in your environment to match the >&2
echo location of your Java installation. >&2
echo.
goto error
@REM ==== END VALIDATION ====
:init
@REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
@REM Fallback to current working directory if not found.
set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
set EXEC_DIR=%CD%
set WDIR=%EXEC_DIR%
:findBaseDir
IF EXIST "%WDIR%"\.mvn goto baseDirFound
cd ..
IF "%WDIR%"=="%CD%" goto baseDirNotFound
set WDIR=%CD%
goto findBaseDir
:baseDirFound
set MAVEN_PROJECTBASEDIR=%WDIR%
cd "%EXEC_DIR%"
goto endDetectBaseDir
:baseDirNotFound
set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
cd "%EXEC_DIR%"
:endDetectBaseDir
IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
@setlocal EnableExtensions EnableDelayedExpansion
for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
:endReadAdditionalConfig
SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar"
FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B
)
@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
@REM This allows using the maven wrapper in projects that prohibit checking in binary data.
if exist %WRAPPER_JAR% (
if "%MVNW_VERBOSE%" == "true" (
echo Found %WRAPPER_JAR%
)
) else (
if not "%MVNW_REPOURL%" == "" (
SET DOWNLOAD_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar"
)
if "%MVNW_VERBOSE%" == "true" (
echo Couldn't find %WRAPPER_JAR%, downloading it ...
echo Downloading from: %DOWNLOAD_URL%
)
powershell -Command "&{"^
"$webclient = new-object System.Net.WebClient;"^
"if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^
"$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^
"}"^
"[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^
"}"
if "%MVNW_VERBOSE%" == "true" (
echo Finished downloading %WRAPPER_JAR%
)
)
@REM End of extension
@REM Provide a "standardized" way to retrieve the CLI args that will
@REM work with both Windows and non-Windows executions.
set MAVEN_CMD_LINE_ARGS=%*
%MAVEN_JAVA_EXE% ^
%JVM_CONFIG_MAVEN_PROPS% ^
%MAVEN_OPTS% ^
%MAVEN_DEBUG_OPTS% ^
-classpath %WRAPPER_JAR% ^
"-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^
%WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
if ERRORLEVEL 1 goto error
goto end
:error
set ERROR_CODE=1
:end
@endlocal & set ERROR_CODE=%ERROR_CODE%
if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost
@REM check for post script, once with legacy .bat ending and once with .cmd ending
if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat"
if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd"
:skipRcPost
@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
if "%MAVEN_BATCH_PAUSE%"=="on" pause
if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE%
cmd /C exit /B %ERROR_CODE%
================================================
FILE: yudada-backend/pom.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!-- @author <a href="https://github.com/liyupi">程序员鱼皮</a> -->
<!-- @from <a href="https://yupi.icu">编程导航知识星球</a> -->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.2</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.yupi</groupId>
<artifactId>yudada-backend</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>yudada-backend</name>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.baomidou/mybatis-plus-boot-starter -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.2</version>
</dependency>
<!-- redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>
<!-- https://github.com/redisson/redisson -->
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.21.0</version>
</dependency>
<!-- https://github.com/ben-manes/caffeine -->
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
<version>2.9.2</version>
</dependency>
<!-- https://github.com/apache/shardingsphere -->
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId>
<version>5.2.0</version>
</dependency>
<!-- https://doc.xiaominfo.com/docs/quick-start#openapi2 -->
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-openapi2-spring-boot-starter</artifactId>
<version>4.4.0</version>
</dependency>
<!-- https://cloud.tencent.com/document/product/436/10199-->
<dependency>
<groupId>com.qcloud</groupId>
<artifactId>cos_api</artifactId>
<version>5.6.89</version>
</dependency>
<!-- https://open.bigmodel.cn/dev/api#sdk_install -->
<dependency>
<groupId>cn.bigmodel.openapi</groupId>
<artifactId>oapi-java-sdk</artifactId>
<version>release-V4-2.0.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<!-- https://github.com/alibaba/easyexcel -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>3.1.1</version>
</dependency>
<!-- https://hutool.cn/docs/index.html#/-->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.8</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
================================================
FILE: yudada-backend/sql/create_table.sql
================================================
# 数据库初始化
# @author <a href="https://github.com/liyupi">程序员鱼皮</a>
# @from <a href="https://yupi.icu">编程导航知识星球</a>
-- 创建库
create database if not exists yudada;
-- 切换库
use yudada;
-- 用户表
create table if not exists user
(
id bigint auto_increment comment 'id' primary key,
userAccount varchar(256) not null comment '账号',
userPassword varchar(512) not null comment '密码',
unionId varchar(256) null comment '微信开放平台id',
mpOpenId varchar(256) null comment '公众号openId',
userName varchar(256) null comment '用户昵称',
userAvatar varchar(1024) null comment '用户头像',
userProfile varchar(512) null comment '用户简介',
userRole varchar(256) default 'user' not null comment '用户角色:user/admin/ban',
createTime datetime default CURRENT_TIMESTAMP not null comment '创建时间',
updateTime datetime default CURRENT_TIMESTAMP not null on update CURRENT_TIMESTAMP comment '更新时间',
isDelete tinyint default 0 not null comment '是否删除',
index idx_unionId (unionId)
) comment '用户' collate = utf8mb4_unicode_ci;
-- 应用表
create table if not exists app
(
id bigint auto_increment comment 'id' primary key,
appName varchar(128) not null comment '应用名',
appDesc varchar(2048) null comment '应用描述',
appIcon varchar(1024) null comment '应用图标',
appType tinyint default 0 not null comment '应用类型(0-得分类,1-测评类)',
scoringStrategy tinyint default 0 not null comment '评分策略(0-自定义,1-AI)',
reviewStatus int default 0 not null comment '审核状态:0-待审核, 1-通过, 2-拒绝',
reviewMessage varchar(512) null comment '审核信息',
reviewerId bigint null comment '审核人 id',
reviewTime datetime null comment '审核时间',
userId bigint not null comment '创建用户 id',
createTime datetime default CURRENT_TIMESTAMP not null comment '创建时间',
updateTime datetime default CURRENT_TIMESTAMP not null on update CURRENT_TIMESTAMP comment '更新时间',
isDelete tinyint default 0 not null comment '是否删除',
index idx_appName (appName)
) comment '应用' collate = utf8mb4_unicode_ci;
-- 题目表
create table if not exists question
(
id bigint auto_increment comment 'id' primary key,
questionContent text null comment '题目内容(json格式)',
appId bigint not null comment '应用 id',
userId bigint not null comment '创建用户 id',
createTime datetime default CURRENT_TIMESTAMP not null comment '创建时间',
updateTime datetime default CURRENT_TIMESTAMP not null on update CURRENT_TIMESTAMP comment '更新时间',
isDelete tinyint default 0 not null comment '是否删除',
index idx_appId (appId)
) comment '题目' collate = utf8mb4_unicode_ci;
-- 评分结果表
create table if not exists scoring_result
(
id bigint auto_increment comment 'id' primary key,
resultName varchar(128) not null comment '结果名称,如物流师',
resultDesc text null comment '结果描述',
resultPicture varchar(1024) null comment '结果图片',
resultProp varchar(128) null comment '结果属性集合 JSON,如 [I,S,T,J]',
resultScoreRange int null comment '结果得分范围,如 80,表示 80及以上的分数命中此结果',
appId bigint not null comment '应用 id',
userId bigint not null comment '创建用户 id',
createTime datetime default CURRENT_TIMESTAMP not null comment '创建时间',
updateTime datetime default CURRENT_TIMESTAMP not null on update CURRENT_TIMESTAMP comment '更新时间',
isDelete tinyint default 0 not null comment '是否删除',
index idx_appId (appId)
) comment '评分结果' collate = utf8mb4_unicode_ci;
-- 用户答题记录表
create table if not exists user_answer
(
id bigint auto_increment primary key,
appId bigint not null comment '应用 id',
appType tinyint default 0 not null comment '应用类型(0-得分类,1-角色测评类)',
scoringStrategy tinyint default 0 not null comment '评分策略(0-自定义,1-AI)',
choices text null comment '用户答案(JSON 数组)',
resultId bigint null comment '评分结果 id',
resultName varchar(128) null comment '结果名称,如物流师',
resultDesc text null comment '结果描述',
resultPicture varchar(1024) null comment '结果图标',
resultScore int null comment '得分',
userId bigint not null comment '用户 id',
createTime datetime default CURRENT_TIMESTAMP not null comment '创建时间',
updateTime datetime default CURRENT_TIMESTAMP not null on update CURRENT_TIMESTAMP comment '更新时间',
isDelete tinyint default 0 not null comment '是否删除',
index idx_appId (appId),
index idx_userId (userId)
) comment '用户答题记录' collate = utf8mb4_unicode_ci;
-- 用户答题记录表(分表 0)
create table if not exists user_answer_0
(
id bigint auto_increment primary key,
appId bigint not null comment '应用 id',
appType tinyint default 0 not null comment '应用类型(0-得分类,1-角色测评类)',
scoringStrategy tinyint default 0 not null comment '评分策略(0-自定义,1-AI)',
choices text null comment '用户答案(JSON 数组)',
resultId bigint null comment '评分结果 id',
resultName varchar(128) null comment '结果名称,如物流师',
resultDesc text null comment '结果描述',
resultPicture varchar(1024) null comment '结果图标',
resultScore int null comment '得分',
userId bigint not null comment '用户 id',
createTime datetime default CURRENT_TIMESTAMP not null comment '创建时间',
updateTime datetime default CURRENT_TIMESTAMP not null on update CURRENT_TIMESTAMP comment '更新时间',
isDelete tinyint default 0 not null comment '是否删除',
index idx_appId (appId),
index idx_userId (userId)
) comment '用户答题记录分表 0' collate = utf8mb4_unicode_ci;
-- 用户答题记录表(分表 1)
create table if not exists user_answer_1
(
id bigint auto_increment primary key,
appId bigint not null comment '应用 id',
appType tinyint default 0 not null comment '应用类型(0-得分类,1-角色测评类)',
scoringStrategy tinyint default 0 not null comment '评分策略(0-自定义,1-AI)',
choices text null comment '用户答案(JSON 数组)',
resultId bigint null comment '评分结果 id',
resultName varchar(128) null comment '结果名称,如物流师',
resultDesc text null comment '结果描述',
resultPicture varchar(1024) null comment '结果图标',
resultScore int null comment '得分',
userId bigint not null comment '用户 id',
createTime datetime default CURRENT_TIMESTAMP not null comment '创建时间',
updateTime datetime default CURRENT_TIMESTAMP not null on update CURRENT_TIMESTAMP comment '更新时间',
isDelete tinyint default 0 not null comment '是否删除',
index idx_appId (appId),
index idx_userId (userId)
) comment '用户答题记录分表 1' collate = utf8mb4_unicode_ci;
================================================
FILE: yudada-backend/sql/init_data.sql
================================================
-- 切换库
use yudada;
-- 用户表初始数据
INSERT INTO user (id, userAccount, userPassword, unionId, mpOpenId, userName, userAvatar, userProfile, userRole,
createTime, updateTime, isDelete)
VALUES (1, 'yupi', 'b0dd3697a192885d7c055db46155b26a', null, null, '鱼皮',
'https://k.sinaimg.cn/n/sinakd20110/560/w1080h1080/20230930/915d-f3d7b580c33632b191e19afa0a858d31.jpg/w700d1q75cms.jpg',
'欢迎来编程导航学习', 'admin', '2024-05-09 11:13:13', '2024-05-09 15:07:48', 0);
-- 应用表初始数据
INSERT INTO app (id, appName, appDesc, appIcon, appType, scoringStrategy, reviewStatus, reviewMessage, reviewerId,
reviewTime, userId, createTime, updateTime, isDelete)
VALUES (1, '自定义MBTI性格测试', '测试性格', '11', 1, 0, 1, null, null, null, 1, '2024-04-24 15:58:05', '2024-05-09 15:09:53', 0);
INSERT INTO app (id, appName, appDesc, appIcon, appType, scoringStrategy, reviewStatus, reviewMessage, reviewerId,
reviewTime, userId, createTime, updateTime, isDelete)
VALUES (2, '自定义得分测试', '测试得分', '22', 0, 0, 1, null, null, null, 1, '2024-04-25 11:39:30', '2024-05-09 15:09:53', 0);
INSERT INTO app (id, appName, appDesc, appIcon, appType, scoringStrategy, reviewStatus, reviewMessage, reviewerId,
reviewTime, userId, createTime, updateTime, isDelete)
VALUES (3, 'AI MBTI 性格测试', '快来测测你的 MBTI', '11', 1, 1, 1, null, null, null, 1, '2024-04-26 16:38:12',
'2024-05-09 15:09:53', 0);
INSERT INTO app (id, appName, appDesc, appIcon, appType, scoringStrategy, reviewStatus, reviewMessage, reviewerId,
reviewTime, userId, createTime, updateTime, isDelete)
VALUES (4, 'AI 得分测试', '看看你熟悉多少首都', '22', 0, 1, 1, null, null, null, 1, '2024-04-26 16:38:56', '2024-05-09 15:09:53', 0);
-- 题目表初始数据
INSERT INTO question (id, questionContent, appId, userId, createTime, updateTime, isDelete)
VALUES (1,
'[{"options":[{"result":"I","value":"独自工作","key":"A"},{"result":"E","value":"与他人合作","key":"B"}],"title":"1. 你通常更喜欢"},{"options":[{"result":"J","value":"喜欢有明确的计划","key":"A"},{"result":"P","value":"更愿意随机应变","key":"B"}],"title":"2. 当安排活动时"},{"options":[{"result":"T","value":"认为应该严格遵守","key":"A"},{"result":"F","value":"认为应灵活运用","key":"B"}],"title":"3. 你如何看待规则"},{"options":[{"result":"E","value":"经常是说话的人","key":"A"},{"result":"I","value":"更倾向于倾听","key":"B"}],"title":"4. 在社交场合中"},{"options":[{"result":"J","value":"先研究再行动","key":"A"},{"result":"P","value":"边做边学习","key":"B"}],"title":"5. 面对新的挑战"},{"options":[{"result":"S","value":"注重细节和事实","key":"A"},{"result":"N","value":"注重概念和想象","key":"B"}],"title":"6. 在日常生活中"},{"options":[{"result":"T","value":"更多基于逻辑分析","key":"A"},{"result":"F","value":"更多基于个人情感","key":"B"}],"title":"7. 做决定时"},{"options":[{"result":"S","value":"喜欢有结构和常规","key":"A"},{"result":"N","value":"喜欢自由和灵活性","key":"B"}],"title":"8. 对于日常安排"},{"options":[{"result":"P","value":"首先考虑可能性","key":"A"},{"result":"J","value":"首先考虑后果","key":"B"}],"title":"9. 当遇到问题时"},{"options":[{"result":"T","value":"时间是一种宝贵的资源","key":"A"},{"result":"F","value":"时间是相对灵活的概念","key":"B"}],"title":"10. 你如何看待时间"}]',
1, 1, '2024-04-24 16:41:53', '2024-05-09 12:28:58', 0);
INSERT INTO question (id, questionContent, appId, userId, createTime, updateTime, isDelete)
VALUES (2,
'[{"options":[{"score":0,"value":"利马","key":"A"},{"score":0,"value":"圣多明各","key":"B"},{"score":0,"value":"圣萨尔瓦多","key":"C"},{"score":1,"value":"波哥大","key":"D"}],"title":"哥伦比亚的首都是?"},{"options":[{"score":0,"value":"蒙特利尔","key":"A"},{"score":0,"value":"多伦多","key":"B"},{"score":1,"value":"渥太华","key":"C"},{"score":0,"value":"温哥华","key":"D"}],"title":"加拿大的首都是?"},{"options":[{"score":0,"value":"大阪","key":"A"},{"score":1,"value":"东京","key":"B"},{"score":0,"value":"京都","key":"C"},{"score":0,"value":"名古屋","key":"D"}],"title":"日本的首都是?"},{"options":[{"score":0,"value":"墨尔本","key":"A"},{"score":0,"value":"悉尼","key":"B"},{"score":0,"value":"布里斯班","key":"C"},{"score":1,"value":"堪培拉","key":"D"}],"title":"澳大利亚的首都是?"},{"options":[{"score":1,"value":"雅加达","key":"A"},{"score":0,"value":"曼谷","key":"B"},{"score":0,"value":"胡志明市","key":"C"},{"score":0,"value":"吉隆坡","key":"D"}],"title":"印度尼西亚的首都是?"},{"options":[{"score":0,"value":"上海","key":"A"},{"score":0,"value":"杭州","key":"B"},{"score":1,"value":"北京","key":"C"},{"score":0,"value":"广州","key":"D"}],"title":"中国的首都是?"},{"options":[{"score":0,"value":"汉堡","key":"A"},{"score":0,"value":"慕尼黑","key":"B"},{"score":1,"value":"柏林","key":"C"},{"score":0,"value":"科隆","key":"D"}],"title":"德国的首都是?"},{"options":[{"score":0,"value":"釜山","key":"A"},{"score":1,"value":"首尔","key":"B"},{"score":0,"value":"大田","key":"C"},{"score":0,"value":"仁川","key":"D"}],"title":"韩国的首都是?"},{"options":[{"score":0,"value":"瓜达拉哈拉","key":"A"},{"score":0,"value":"蒙特雷","key":"B"},{"score":1,"value":"墨西哥城","key":"C"},{"score":0,"value":"坎昆","key":"D"}],"title":"墨西哥的首都是?"},{"options":[{"score":1,"value":"开罗","key":"A"},{"score":0,"value":"亚历山大","key":"B"},{"score":0,"value":"卢克索","key":"C"},{"score":0,"value":"卡利乌比亚","key":"D"}],"title":"埃及的首都是?"}]',
2, 1, '2024-04-25 15:03:07', '2024-05-09 12:28:58', 0);
INSERT INTO question (id, questionContent, appId, userId, createTime, updateTime, isDelete)
VALUES (3,
'[{"options":[{"result":"I","value":"独自工作","key":"A"},{"result":"E","value":"与他人合作","key":"B"}],"title":"1. 你通常更喜欢"},{"options":[{"result":"J","value":"喜欢有明确的计划","key":"A"},{"result":"P","value":"更愿意随机应变","key":"B"}],"title":"2. 当安排活动时"},{"options":[{"result":"T","value":"认为应该严格遵守","key":"A"},{"result":"F","value":"认为应灵活运用","key":"B"}],"title":"3. 你如何看待规则"},{"options":[{"result":"E","value":"经常是说话的人","key":"A"},{"result":"I","value":"更倾向于倾听","key":"B"}],"title":"4. 在社交场合中"},{"options":[{"result":"J","value":"先研究再行动","key":"A"},{"result":"P","value":"边做边学习","key":"B"}],"title":"5. 面对新的挑战"},{"options":[{"result":"S","value":"注重细节和事实","key":"A"},{"result":"N","value":"注重概念和想象","key":"B"}],"title":"6. 在日常生活中"},{"options":[{"result":"T","value":"更多基于逻辑分析","key":"A"},{"result":"F","value":"更多基于个人情感","key":"B"}],"title":"7. 做决定时"},{"options":[{"result":"S","value":"喜欢有结构和常规","key":"A"},{"result":"N","value":"喜欢自由和灵活性","key":"B"}],"title":"8. 对于日常安排"},{"options":[{"result":"P","value":"首先考虑可能性","key":"A"},{"result":"J","value":"首先考虑后果","key":"B"}],"title":"9. 当遇到问题时"},{"options":[{"result":"T","value":"时间是一种宝贵的资源","key":"A"},{"result":"F","value":"时间是相对灵活的概念","key":"B"}],"title":"10. 你如何看待时间"}]',
3, 1, '2024-04-26 16:39:29', '2024-05-09 12:28:58', 0);
INSERT INTO question (id, questionContent, appId, userId, createTime, updateTime, isDelete)
VALUES (4,
'[{"options":[{"score":0,"value":"利马","key":"A"},{"score":0,"value":"圣多明各","key":"B"},{"score":0,"value":"圣萨尔瓦多","key":"C"},{"score":1,"value":"波哥大","key":"D"}],"title":"哥伦比亚的首都是?"},{"options":[{"score":0,"value":"蒙特利尔","key":"A"},{"score":0,"value":"多伦多","key":"B"},{"score":1,"value":"渥太华","key":"C"},{"score":0,"value":"温哥华","key":"D"}],"title":"加拿大的首都是?"},{"options":[{"score":0,"value":"大阪","key":"A"},{"score":1,"value":"东京","key":"B"},{"score":0,"value":"京都","key":"C"},{"score":0,"value":"名古屋","key":"D"}],"title":"日本的首都是?"},{"options":[{"score":0,"value":"墨尔本","key":"A"},{"score":0,"value":"悉尼","key":"B"},{"score":0,"value":"布里斯班","key":"C"},{"score":1,"value":"堪培拉","key":"D"}],"title":"澳大利亚的首都是?"},{"options":[{"score":1,"value":"雅加达","key":"A"},{"score":0,"value":"曼谷","key":"B"},{"score":0,"value":"胡志明市","key":"C"},{"score":0,"value":"吉隆坡","key":"D"}],"title":"印度尼西亚的首都是?"},{"options":[{"score":0,"value":"上海","key":"A"},{"score":0,"value":"杭州","key":"B"},{"score":1,"value":"北京","key":"C"},{"score":0,"value":"广州","key":"D"}],"title":"中国的首都是?"},{"options":[{"score":0,"value":"汉堡","key":"A"},{"score":0,"value":"慕尼黑","key":"B"},{"score":1,"value":"柏林","key":"C"},{"score":0,"value":"科隆","key":"D"}],"title":"德国的首都是?"},{"options":[{"score":0,"value":"釜山","key":"A"},{"score":1,"value":"首尔","key":"B"},{"score":0,"value":"大田","key":"C"},{"score":0,"value":"仁川","key":"D"}],"title":"韩国的首都是?"},{"options":[{"score":0,"value":"瓜达拉哈拉","key":"A"},{"score":0,"value":"蒙特雷","key":"B"},{"score":1,"value":"墨西哥城","key":"C"},{"score":0,"value":"坎昆","key":"D"}],"title":"墨西哥的首都是?"},{"options":[{"score":1,"value":"开罗","key":"A"},{"score":0,"value":"亚历山大","key":"B"},{"score":0,"value":"卢克索","key":"C"},{"score":0,"value":"卡利乌比亚","key":"D"}],"title":"埃及的首都是?"}]',
4, 1, '2024-04-26 16:39:29', '2024-05-09 12:28:58', 0);
-- 评分结果表初始数据
INSERT INTO scoring_result (id, resultName, resultDesc, resultPicture, resultProp, resultScoreRange, createTime,
updateTime, isDelete, appId, userId)
VALUES (1, 'ISTJ(物流师)', '忠诚可靠,被公认为务实,注重细节。', 'icon_url_istj', '["I","S","T","J"]', null,
'2024-04-24 16:57:02', '2024-05-09 12:28:21', 0, 1, 1);
INSERT INTO scoring_result (id, resultName, resultDesc, resultPicture, resultProp, resultScoreRange, createTime,
updateTime, isDelete, appId, userId)
VALUES (2, 'ISFJ(守护者)', '善良贴心,以同情心和责任为特点。', 'icon_url_isfj', '["I","S","F","J"]', null,
'2024-04-24 16:57:02', '2024-05-09 12:28:21', 0, 1, 1);
INSERT INTO scoring_result (id, resultName, resultDesc, resultPicture, resultProp, resultScoreRange, createTime,
updateTime, isDelete, appId, userId)
VALUES (3, 'INFJ(占有者)', '理想主义者,有着深刻的洞察力,善于理解他人。', 'icon_url_infj', '["I","N","F","J"]', null,
'2024-04-24 16:57:02', '2024-05-09 12:28:21', 0, 1, 1);
INSERT INTO scoring_result (id, resultName, resultDesc, resultPicture, resultProp, resultScoreRange, createTime,
updateTime, isDelete, appId, userId)
VALUES (4, 'INTJ(设计师)', '独立思考者,善于规划和实现目标,理性而果断。', 'icon_url_intj', '["I","N","T","J"]', null,
'2024-04-24 16:57:02', '2024-05-09 12:28:21', 0, 1, 1);
INSERT INTO scoring_result (id, resultName, resultDesc, resultPicture, resultProp, resultScoreRange, createTime,
updateTime, isDelete, appId, userId)
VALUES (5, 'ISTP(运动员)', '冷静自持,善于解决问题,擅长实践技能。', 'icon_url_istp', '["I","S","T","P"]', null,
'2024-04-24 16:57:02', '2024-05-09 12:28:21', 0, 1, 1);
INSERT INTO scoring_result (id, resultName, resultDesc, resultPicture, resultProp, resultScoreRange, createTime,
updateTime, isDelete, appId, userId)
VALUES (6, 'ISFP(艺术家)', '具有艺术感和敏感性,珍视个人空间和自由。', 'icon_url_isfp', '["I","S","F","P"]', null,
'2024-04-24 16:57:02', '2024-05-09 12:28:21', 0, 1, 1);
INSERT INTO scoring_result (id, resultName, resultDesc, resultPicture, resultProp, resultScoreRange, createTime,
updateTime, isDelete, appId, userId)
VALUES (7, 'INFP(治愈者)', '理想主义者,富有创造力,以同情心和理解他人著称。', 'icon_url_infp', '["I","N","F","P"]', null,
'2024-04-24 16:57:02', '2024-05-09 12:28:21', 0, 1, 1);
INSERT INTO scoring_result (id, resultName, resultDesc, resultPicture, resultProp, resultScoreRange, createTime,
updateTime, isDelete, appId, userId)
VALUES (8, 'INTP(学者)', '思维清晰,探索精神,独立思考且理性。', 'icon_url_intp', '["I","N","T","P"]', null,
'2024-04-24 16:57:02', '2024-05-09 12:28:21', 0, 1, 1);
INSERT INTO scoring_result (id, resultName, resultDesc, resultPicture, resultProp, resultScoreRange, createTime,
updateTime, isDelete, appId, userId)
VALUES (9, 'ESTP(拓荒者)', '敢于冒险,乐于冒险,思维敏捷,行动果断。', 'icon_url_estp', '["E","S","T","P"]', null,
'2024-04-24 16:57:02', '2024-05-09 12:28:21', 0, 1, 1);
INSERT INTO scoring_result (id, resultName, resultDesc, resultPicture, resultProp, resultScoreRange, createTime,
updateTime, isDelete, appId, userId)
VALUES (10, 'ESFP(表演者)', '热情开朗,善于社交,热爱生活,乐于助人。', 'icon_url_esfp', '["E","S","F","P"]', null,
'2024-04-24 16:57:02', '2024-05-09 12:28:21', 0, 1, 1);
INSERT INTO scoring_result (id, resultName, resultDesc, resultPicture, resultProp, resultScoreRange, createTime,
updateTime, isDelete, appId, userId)
VALUES (11, 'ENFP(倡导者)', '富有想象力,充满热情,善于激发他人的活力和潜力。', 'icon_url_enfp', '["E","N","F","P"]', null,
'2024-04-24 16:57:02', '2024-05-09 12:28:21', 0, 1, 1);
INSERT INTO scoring_result (id, resultName, resultDesc, resultPicture, resultProp, resultScoreRange, createTime,
updateTime, isDelete, appId, userId)
VALUES (12, 'ENTP(发明家)', '充满创造力,善于辩论,挑战传统,喜欢探索新领域。', 'icon_url_entp', '["E","N","T","P"]', null,
'2024-04-24 16:57:02', '2024-05-09 12:28:21', 0, 1, 1);
INSERT INTO scoring_result (id, resultName, resultDesc, resultPicture, resultProp, resultScoreRange, createTime,
updateTime, isDelete, appId, userId)
VALUES (13, 'ESTJ(主管)', '务实果断,善于组织和管理,重视效率和目标。', 'icon_url_estj', '["E","S","T","J"]', null,
'2024-04-24 16:57:02', '2024-05-09 12:28:21', 0, 1, 1);
INSERT INTO scoring_result (id, resultName, resultDesc, resultPicture, resultProp, resultScoreRange, createTime,
updateTime, isDelete, appId, userId)
VALUES (14, 'ESFJ(尽责者)', '友善热心,以协调、耐心和关怀为特点,善于团队合作。', 'icon_url_esfj', '["E","S","F","J"]',
null, '2024-04-24 16:57:02', '2024-05-09 12:28:21', 0, 1, 1);
INSERT INTO scoring_result (id, resultName, resultDesc, resultPicture, resultProp, resultScoreRange, createTime,
updateTime, isDelete, appId, userId)
VALUES (15, 'ENFJ(教导着)', '热情关爱,善于帮助他人,具有领导力和社交能力。', 'icon_url_enfj', '["E","N","F","J"]', null,
'2024-04-24 16:57:02', '2024-05-09 12:28:21', 0, 1, 1);
INSERT INTO scoring_result (id, resultName, resultDesc, resultPicture, resultProp, resultScoreRange, createTime,
updateTime, isDelete, appId, userId)
VALUES (16, 'ENTJ(统帅)', '果断自信,具有领导才能,善于规划和执行目标。', 'icon_url_entj', '["E","N","T","J"]', null,
'2024-04-24 16:57:02', '2024-05-09 12:28:21', 0, 1, 1);
INSERT INTO scoring_result (id, resultName, resultDesc, resultPicture, resultProp, resultScoreRange, createTime,
updateTime, isDelete, appId, userId)
VALUES (17, '首都知识大师', '你真棒棒哦,首都知识非常出色!', null, null, 9, '2024-04-25 15:05:44', '2024-05-09 12:28:21',
0, 2, 1);
INSERT INTO scoring_result (id, resultName, resultDesc, resultPicture, resultProp, resultScoreRange, createTime,
updateTime, isDelete, appId, userId)
VALUES (18, '地理小能手!', '你对于世界各国的首都了解得相当不错,但还有一些小地方需要加强哦!', null, null, 7,
'2024-04-25 15:05:44', '2024-05-09 12:28:21', 0, 2, 1);
INSERT INTO scoring_result (id, resultName, resultDesc, resultPicture, resultProp, resultScoreRange, createTime,
updateTime, isDelete, appId, userId)
VALUES (19, '继续加油!', '还需努力哦', null, null, 0, '2024-04-25 15:05:44', '2024-05-09 12:28:21', 0, 2, 1);
-- 用户回答表初始数据
INSERT INTO user_answer (id, appId, appType, choices, resultId, resultName, resultDesc, resultPicture, resultScore,
scoringStrategy, userId, createTime, updateTime, isDelete)
VALUES (1, 1, 1, '["A","A","A","B","A","A","A","B","B","A"]', 1, 'ISTJ(物流师)', '忠诚可靠,被公认为务实,注重细节。', 'icon_url_istj',
null, 0, 1, '2024-05-09 15:08:22', '2024-05-09 15:10:13', 0);
INSERT INTO user_answer (id, appId, appType, choices, resultId, resultName, resultDesc, resultPicture, resultScore,
scoringStrategy, userId, createTime, updateTime, isDelete)
VALUES (2, 2, 0, '["D","C","B","D","A","C","C","B","C","A"]', 17, '首都知识大师', '你真棒棒哦,首都知识非常出色!', null, 10, 0, 1,
'2024-05-09 15:08:36', '2024-05-09 15:10:13', 0);
================================================
FILE: yudada-backend/sql/post_es_mapping.json
================================================
{
"aliases": {
"post": {}
},
"mappings": {
"properties": {
"title": {
"type": "text",
"analyzer": "ik_max_word",
"search_analyzer": "ik_smart",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"content": {
"type": "text",
"analyzer": "ik_max_word",
"search_analyzer": "ik_smart",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"tags": {
"type": "keyword"
},
"thumbNum": {
"type": "long"
},
"favourNum": {
"type": "long"
},
"userId": {
"type": "keyword"
},
"createTime": {
"type": "date"
},
"updateTime": {
"type": "date"
},
"isDelete": {
"type": "keyword"
}
}
}
}
================================================
FILE: yudada-backend/src/main/java/com/yupi/yudada/MainApplication.java
================================================
package com.yupi.yudada;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.scheduling.annotation.EnableScheduling;
/**
* 主类(项目启动入口)
*
* @author <a href="https://github.com/liyupi">程序员鱼皮</a>
* @from <a href="https://yupi.icu">编程导航知识星球</a>
*/
// todo 如需开启 Redis,须移除 exclude 中的内容
@SpringBootApplication
@MapperScan("com.yupi.yudada.mapper")
@EnableScheduling
@EnableAspectJAutoProxy(proxyTargetClass = true, exposeProxy = true)
public class MainApplication {
public static void main(String[] args) {
SpringApplication.run(MainApplication.class, args);
}
}
================================================
FILE: yudada-backend/src/main/java/com/yupi/yudada/annotation/AuthCheck.java
================================================
package com.yupi.yudada.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 权限校验
*
* @author <a href="https://github.com/liyupi">程序员鱼皮</a>
* @from <a href="https://yupi.icu">编程导航知识星球</a>
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AuthCheck {
/**
* 必须有某个角色
*
* @return
*/
String mustRole() default "";
}
================================================
FILE: yudada-backend/src/main/java/com/yupi/yudada/aop/AuthInterceptor.java
================================================
package com.yupi.yudada.aop;
import com.yupi.yudada.annotation.AuthCheck;
import com.yupi.yudada.common.ErrorCode;
import com.yupi.yudada.exception.BusinessException;
import com.yupi.yudada.model.entity.User;
import com.yupi.yudada.model.enums.UserRoleEnum;
import com.yupi.yudada.service.UserService;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
/**
* 权限校验 AOP
*
* @author <a href="https://github.com/liyupi">程序员鱼皮</a>
* @from <a href="https://yupi.icu">编程导航知识星球</a>
*/
@Aspect
@Component
public class AuthInterceptor {
@Resource
private UserService userService;
/**
* 执行拦截
*
* @param joinPoint
* @param authCheck
* @return
*/
@Around("@annotation(authCheck)")
public Object doInterceptor(ProceedingJoinPoint joinPoint, AuthCheck authCheck) throws Throwable {
String mustRole = authCheck.mustRole();
RequestAttributes requestAttributes = RequestContextHolder.currentRequestAttributes();
HttpServletRequest request = ((ServletRequestAttributes) requestAttributes).getRequest();
// 当前登录用户
User loginUser = userService.getLoginUser(request);
UserRoleEnum mustRoleEnum = UserRoleEnum.getEnumByValue(mustRole);
// 不需要权限,放行
if (mustRoleEnum == null) {
return joinPoint.proceed();
}
// 必须有该权限才通过
UserRoleEnum userRoleEnum = UserRoleEnum.getEnumByValue(loginUser.getUserRole());
if (userRoleEnum == null) {
throw new BusinessException(ErrorCode.NO_AUTH_ERROR);
}
// 如果被封号,直接拒绝
if (UserRoleEnum.BAN.equals(userRoleEnum)) {
throw new BusinessException(ErrorCode.NO_AUTH_ERROR);
}
// 必须有管理员权限
if (UserRoleEnum.ADMIN.equals(mustRoleEnum)) {
// 用户没有管理员权限,拒绝
if (!UserRoleEnum.ADMIN.equals(userRoleEnum)) {
throw new BusinessException(ErrorCode.NO_AUTH_ERROR);
}
}
// 通过权限校验,放行
return joinPoint.proceed();
}
}
================================================
FILE: yudada-backend/src/main/java/com/yupi/yudada/aop/LogInterceptor.java
================================================
package com.yupi.yudada.aop;
import java.util.UUID;
import javax.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
import org.springframework.util.StopWatch;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
/**
* 请求响应日志 AOP
*
* @author <a href="https://github.com/liyupi">程序员鱼皮</a>
* @from <a href="https://yupi.icu">编程导航知识星球</a>
**/
@Aspect
@Component
@Slf4j
public class LogInterceptor {
/**
* 执行拦截
*/
@Around("execution(* com.yupi.yudada.controller.*.*(..))")
public Object doInterceptor(ProceedingJoinPoint point) throws Throwable {
// 计时
StopWatch stopWatch = new StopWatch();
stopWatch.start();
// 获取请求路径
RequestAttributes requestAttributes = RequestContextHolder.currentRequestAttributes();
HttpServletRequest httpServletRequest = ((ServletRequestAttributes) requestAttributes).getRequest();
// 生成请求唯一 id
String requestId = UUID.randomUUID().toString();
String url = httpServletRequest.getRequestURI();
// 获取请求参数
Object[] args = point.getArgs();
String reqParam = "[" + StringUtils.join(args, ", ") + "]";
// 输出请求日志
log.info("request start,id: {}, path: {}, ip: {}, params: {}", requestId, url,
httpServletRequest.getRemoteHost(), reqParam);
// 执行原方法
Object result = point.proceed();
// 输出响应日志
stopWatch.stop();
long totalTimeMillis = stopWatch.getTotalTimeMillis();
log.info("request end, id: {}, cost: {}ms", requestId, totalTimeMillis);
return result;
}
}
================================================
FILE: yudada-backend/src/main/java/com/yupi/yudada/common/BaseResponse.java
================================================
package com.yupi.yudada.common;
import java.io.Serializable;
import lombok.Data;
/**
* 通用返回类
*
* @param <T>
* @author <a href="https://github.com/liyupi">程序员鱼皮</a>
* @from <a href="https://yupi.icu">编程导航知识星球</a>
*/
@Data
public class BaseResponse<T> implements Serializable {
private int code;
private T data;
private String message;
public BaseResponse(int code, T data, String message) {
this.code = code;
this.data = data;
this.message = message;
}
public BaseResponse(int code, T data) {
this(code, data, "");
}
public BaseResponse(ErrorCode errorCode) {
this(errorCode.getCode(), null, errorCode.getMessage());
}
}
================================================
FILE: yudada-backend/src/main/java/com/yupi/yudada/common/DeleteRequest.java
================================================
package com.yupi.yudada.common;
import java.io.Serializable;
import lombok.Data;
/**
* 删除请求
*
* @author <a href="https://github.com/liyupi">程序员鱼皮</a>
* @from <a href="https://yupi.icu">编程导航知识星球</a>
*/
@Data
public class DeleteRequest implements Serializable {
/**
* id
*/
private Long id;
private static final long serialVersionUID = 1L;
}
================================================
FILE: yudada-backend/src/main/java/com/yupi/yudada/common/ErrorCode.java
================================================
package com.yupi.yudada.common;
/**
* 自定义错误码
*
* @author <a href="https://github.com/liyupi">程序员鱼皮</a>
* @from <a href="https://yupi.icu">编程导航知识星球</a>
*/
public enum ErrorCode {
SUCCESS(0, "ok"),
PARAMS_ERROR(40000, "请求参数错误"),
NOT_LOGIN_ERROR(40100, "未登录"),
NO_AUTH_ERROR(40101, "无权限"),
NOT_FOUND_ERROR(40400, "请求数据不存在"),
FORBIDDEN_ERROR(40300, "禁止访问"),
SYSTEM_ERROR(50000, "系统内部异常"),
OPERATION_ERROR(50001, "操作失败");
/**
* 状态码
*/
private final int code;
/**
* 信息
*/
private final String message;
ErrorCode(int code, String message) {
this.code = code;
this.message = message;
}
public int getCode() {
return code;
}
public String getMessage() {
return message;
}
}
================================================
FILE: yudada-backend/src/main/java/com/yupi/yudada/common/PageRequest.java
================================================
package com.yupi.yudada.common;
import com.yupi.yudada.constant.CommonConstant;
import lombok.Data;
/**
* 分页请求
*
* @author <a href="https://github.com/liyupi">程序员鱼皮</a>
* @from <a href="https://yupi.icu">编程导航知识星球</a>
*/
@Data
public class PageRequest {
/**
* 当前页号
*/
private int current = 1;
/**
* 页面大小
*/
private int pageSize = 10;
/**
* 排序字段
*/
private String sortField;
/**
* 排序顺序(默认升序)
*/
private String sortOrder = CommonConstant.SORT_ORDER_ASC;
}
================================================
FILE: yudada-backend/src/main/java/com/yupi/yudada/common/ResultUtils.java
================================================
package com.yupi.yudada.common;
/**
* 返回工具类
*
* @author <a href="https://github.com/liyupi">程序员鱼皮</a>
* @from <a href="https://yupi.icu">编程导航知识星球</a>
*/
public class ResultUtils {
/**
* 成功
*
* @param data
* @param <T>
* @return
*/
public static <T> BaseResponse<T> success(T data) {
return new BaseResponse<>(0, data, "ok");
}
/**
* 失败
*
* @param errorCode
* @return
*/
public static BaseResponse error(ErrorCode errorCode) {
return new BaseResponse<>(errorCode);
}
/**
* 失败
*
* @param code
* @param message
* @return
*/
public static BaseResponse error(int code, String message) {
return new BaseResponse(code, null, message);
}
/**
* 失败
*
* @param errorCode
* @return
*/
public static BaseResponse error(ErrorCode errorCode, String message) {
return new BaseResponse(errorCode.getCode(), null, message);
}
}
================================================
FILE: yudada-backend/src/main/java/com/yupi/yudada/common/ReviewRequest.java
================================================
package com.yupi.yudada.common;
import lombok.Data;
import java.io.Serializable;
/**
* 审核请求
*/
@Data
public class ReviewRequest implements Serializable {
/**
* id
*/
private Long id;
/**
* 状态:0-待审核, 1-通过, 2-拒绝
*/
private Integer reviewStatus;
/**
* 审核信息
*/
private String reviewMessage;
private static final long serialVersionUID = 1L;
}
================================================
FILE: yudada-backend/src/main/java/com/yupi/yudada/config/AiConfig.java
================================================
package com.yupi.yudada.config;
import com.zhipu.oapi.ClientV4;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
@ConfigurationProperties(prefix = "ai")
@Data
public class AiConfig {
/**
* apiKey,需要从开放平台获取
*/
private String apiKey;
@Bean
public ClientV4 getClientV4() {
return new ClientV4.Builder(apiKey).build();
}
}
================================================
FILE: yudada-backend/src/main/java/com/yupi/yudada/config/CorsConfig.java
================================================
package com.yupi.yudada.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* 全局跨域配置
*
* @author <a href="https://github.com/liyupi">程序员鱼皮</a>
* @from <a href="https://yupi.icu">编程导航知识星球</a>
*/
@Configuration
public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
// 覆盖所有请求
registry.addMapping("/**")
// 允许发送 Cookie
.allowCredentials(true)
// 放行哪些域名(必须用 patterns,否则 * 会和 allowCredentials 冲突)
.allowedOriginPatterns("*")
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
.allowedHeaders("*")
.exposedHeaders("*");
}
}
================================================
FILE: yudada-backend/src/main/java/com/yupi/yudada/config/CosClientConfig.java
================================================
package com.yupi.yudada.config;
import com.qcloud.cos.COSClient;
import com.qcloud.cos.ClientConfig;
import com.qcloud.cos.auth.BasicCOSCredentials;
import com.qcloud.cos.auth.COSCredentials;
import com.qcloud.cos.region.Region;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* 腾讯云对象存储客户端
*
* @author <a href="https://github.com/liyupi">程序员鱼皮</a>
* @from <a href="https://yupi.icu">编程导航知识星球</a>
*/
@Configuration
@ConfigurationProperties(prefix = "cos.client")
@Data
public class CosClientConfig {
/**
* accessKey
*/
private String accessKey;
/**
* secretKey
*/
private String secretKey;
/**
* 区域
*/
private String region;
/**
* 桶名
*/
private String bucket;
@Bean
public COSClient cosClient() {
// 初始化用户身份信息(secretId, secretKey)
COSCredentials cred = new BasicCOSCredentials(accessKey, secretKey);
// 设置bucket的区域, COS地域的简称请参照 https://www.qcloud.com/document/product/436/6224
ClientConfig clientConfig = new ClientConfig(new Region(region));
// 生成cos客户端
return new COSClient(cred, clientConfig);
}
}
================================================
FILE: yudada-backend/src/main/java/com/yupi/yudada/config/JsonConfig.java
================================================
package com.yupi.yudada.config;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import org.springframework.boot.jackson.JsonComponent;
import org.springframework.context.annotation.Bean;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
/**
* Spring MVC Json 配置
*
* @author <a href="https://github.com/liyupi">程序员鱼皮</a>
* @from <a href="https://yupi.icu">编程导航知识星球</a>
*/
@JsonComponent
public class JsonConfig {
/**
* 添加 Long 转 json 精度丢失的配置
*/
@Bean
public ObjectMapper jacksonObjectMapper(Jackson2ObjectMapperBuilder builder) {
ObjectMapper objectMapper = builder.createXmlMapper(false).build();
SimpleModule module = new SimpleModule();
module.addSerializer(Long.class, ToStringSerializer.instance);
module.addSerializer(Long.TYPE, ToStringSerializer.instance);
objectMapper.registerModule(module);
return objectMapper;
}
}
================================================
FILE: yudada-backend/src/main/java/com/yupi/yudada/config/MyBatisPlusConfig.java
================================================
package com.yupi.yudada.config;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* MyBatis Plus 配置
*
* @author https://github.com/liyupi
*/
@Configuration
@MapperScan("com.yupi.yudada.mapper")
public class MyBatisPlusConfig {
/**
* 拦截器配置
*
* @return
*/
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 分页插件
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
}
================================================
FILE: yudada-backend/src/main/java/com/yupi/yudada/config/RedissonConfig.java
================================================
package com.yupi.yudada.config;
import com.zhipu.oapi.ClientV4;
import lombok.Data;
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
@ConfigurationProperties(prefix = "spring.redis")
@Data
public class RedissonConfig {
private String host;
private Integer port;
private Integer database;
private String password;
@Bean
public RedissonClient redissonClient() {
Config config = new Config();
config.useSingleServer()
.setAddress("redis://" + host + ":" + port)
.setDatabase(database)
.setPassword(password);
return Redisson.create(config);
}
}
================================================
FILE: yudada-backend/src/main/java/com/yupi/yudada/config/VipSchedulerConfig.java
================================================
package com.yupi.yudada.config;
import io.reactivex.Scheduler;
import io.reactivex.schedulers.Schedulers;
import lombok.Data;
import org.jetbrains.annotations.NotNull;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicInteger;
@Configuration
@Data
public class VipSchedulerConfig {
@Bean
public Scheduler vipScheduler() {
ThreadFactory threadFactory = new ThreadFactory() {
private final AtomicInteger threadNumber = new AtomicInteger(1);
@Override
public Thread newThread(@NotNull Runnable r) {
Thread thread = new Thread(r, "VIPThreadPool-" + threadNumber.getAndIncrement());
// 非守护线程
thread.setDaemon(false);
return thread;
}
};
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(10, threadFactory);
return Schedulers.from(scheduledExecutorService);
}
}
================================================
FILE: yudada-backend/src/main/java/com/yupi/yudada/constant/CommonConstant.java
================================================
package com.yupi.yudada.constant;
/**
* 通用常量
*
* @author <a href="https://github.com/liyupi">程序员鱼皮</a>
* @from <a href="https://yupi.icu">编程导航知识星球</a>
*/
public interface CommonConstant {
/**
* 升序
*/
String SORT_ORDER_ASC = "ascend";
/**
* 降序
*/
String SORT_ORDER_DESC = " descend";
}
================================================
FILE: yudada-backend/src/main/java/com/yupi/yudada/constant/FileConstant.java
================================================
package com.yupi.yudada.constant;
/**
* 文件常量
*
* @author <a href="https://github.com/liyupi">程序员鱼皮</a>
* @from <a href="https://yupi.icu">编程导航知识星球</a>
*/
public interface FileConstant {
/**
* COS 访问地址
* todo 需替换配置
*/
String COS_HOST = "https://yupi.icu";
}
================================================
FILE: yudada-backend/src/main/java/com/yupi/yudada/constant/UserConstant.java
================================================
package com.yupi.yudada.constant;
/**
* 用户常量
*
* @author <a href="https://github.com/liyupi">程序员鱼皮</a>
* @from <a href="https://yupi.icu">编程导航知识星球</a>
*/
public interface UserConstant {
/**
* 用户登录态键
*/
String USER_LOGIN_STATE = "user_login";
// region 权限
/**
* 默认角色
*/
String DEFAULT_ROLE = "user";
/**
* 管理员角色
*/
String ADMIN_ROLE = "admin";
/**
* 被封号
*/
String BAN_ROLE = "ban";
// endregion
}
================================================
FILE: yudada-backend/src/main/java/com/yupi/yudada/controller/AppController.java
================================================
package com.yupi.yudada.controller;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.yupi.yudada.annotation.AuthCheck;
import com.yupi.yudada.common.*;
import com.yupi.yudada.constant.UserConstant;
import com.yupi.yudada.exception.BusinessException;
import com.yupi.yudada.exception.ThrowUtils;
import com.yupi.yudada.model.dto.app.AppAddRequest;
import com.yupi.yudada.model.dto.app.AppEditRequest;
import com.yupi.yudada.model.dto.app.AppQueryRequest;
import com.yupi.yudada.model.dto.app.AppUpdateRequest;
import com.yupi.yudada.model.entity.App;
import com.yupi.yudada.model.entity.User;
import com.yupi.yudada.model.enums.ReviewStatusEnum;
import com.yupi.yudada.model.vo.AppVO;
import com.yupi.yudada.service.AppService;
import com.yupi.yudada.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.Date;
/**
* 应用接口
*
* @author <a href="https://github.com/liyupi">程序员鱼皮</a>
* @from <a href="https://www.code-nav.cn">编程导航学习圈</a>
*/
@RestController
@RequestMapping("/app")
@Slf4j
public class AppController {
@Resource
private AppService appService;
@Resource
private UserService userService;
// region 增删改查
/**
* 创建应用
*
* @param appAddRequest
* @param request
* @return
*/
@PostMapping("/add")
public BaseResponse<Long> addApp(@RequestBody AppAddRequest appAddRequest, HttpServletRequest request) {
ThrowUtils.throwIf(appAddRequest == null, ErrorCode.PARAMS_ERROR);
// 在此处将实体类和 DTO 进行转换
App app = new App();
BeanUtils.copyProperties(appAddRequest, app);
// 数据校验
appService.validApp(app, true);
// 填充默认值
User loginUser = userService.getLoginUser(request);
app.setUserId(loginUser.getId());
app.setReviewStatus(ReviewStatusEnum.REVIEWING.getValue());
// 写入数据库
boolean result = appService.save(app);
ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR);
// 返回新写入的数据 id
long newAppId = app.getId();
return ResultUtils.success(newAppId);
}
/**
* 删除应用
*
* @param deleteRequest
* @param request
* @return
*/
@PostMapping("/delete")
public BaseResponse<Boolean> deleteApp(@RequestBody DeleteRequest deleteRequest, HttpServletRequest request) {
if (deleteRequest == null || deleteRequest.getId() <= 0) {
throw new BusinessException(ErrorCode.PARAMS_ERROR);
}
User user = userService.getLoginUser(request);
long id = deleteRequest.getId();
// 判断是否存在
App oldApp = appService.getById(id);
ThrowUtils.throwIf(oldApp == null, ErrorCode.NOT_FOUND_ERROR);
// 仅本人或管理员可删除
if (!oldApp.getUserId().equals(user.getId()) && !userService.isAdmin(request)) {
throw new BusinessException(ErrorCode.NO_AUTH_ERROR);
}
// 操作数据库
boolean result = appService.removeById(id);
ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR);
return ResultUtils.success(true);
}
/**
* 更新应用(仅管理员可用)
*
* @param appUpdateRequest
* @return
*/
@PostMapping("/update")
@AuthCheck(mustRole = UserConstant.ADMIN_ROLE)
public BaseResponse<Boolean> updateApp(@RequestBody AppUpdateRequest appUpdateRequest) {
if (appUpdateRequest == null || appUpdateRequest.getId() <= 0) {
throw new BusinessException(ErrorCode.PARAMS_ERROR);
}
// 在此处将实体类和 DTO 进行转换
App app = new App();
BeanUtils.copyProperties(appUpdateRequest, app);
// 数据校验
appService.validApp(app, false);
// 判断是否存在
long id = appUpdateRequest.getId();
App oldApp = appService.getById(id);
ThrowUtils.throwIf(oldApp == null, ErrorCode.NOT_FOUND_ERROR);
// 操作数据库
boolean result = appService.updateById(app);
ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR);
return ResultUtils.success(true);
}
/**
* 根据 id 获取应用(封装类)
*
* @param id
* @return
*/
@GetMapping("/get/vo")
public BaseResponse<AppVO> getAppVOById(long id, HttpServletRequest request) {
ThrowUtils.throwIf(id <= 0, ErrorCode.PARAMS_ERROR);
// 查询数据库
App app = appService.getById(id);
ThrowUtils.throwIf(app == null, ErrorCode.NOT_FOUND_ERROR);
// 获取封装类
return ResultUtils.success(appService.getAppVO(app, request));
}
/**
* 分页获取应用列表(仅管理员可用)
*
* @param appQueryRequest
* @return
*/
@PostMapping("/list/page")
@AuthCheck(mustRole = UserConstant.ADMIN_ROLE)
public BaseResponse<Page<App>> listAppByPage(@RequestBody AppQueryRequest appQueryRequest) {
long current = appQueryRequest.getCurrent();
long size = appQueryRequest.getPageSize();
// 查询数据库
Page<App> appPage = appService.page(new Page<>(current, size),
appService.getQueryWrapper(appQueryRequest));
return ResultUtils.success(appPage);
}
/**
* 分页获取应用列表(封装类)
*
* @param appQueryRequest
* @param request
* @return
*/
@PostMapping("/list/page/vo")
public BaseResponse<Page<AppVO>> listAppVOByPage(@RequestBody AppQueryRequest appQueryRequest,
HttpServletRequest request) {
long current = appQueryRequest.getCurrent();
long size = appQueryRequest.getPageSize();
// 限制爬虫
ThrowUtils.throwIf(size > 20, ErrorCode.PARAMS_ERROR);
// 只能看到已过审的应用
appQueryRequest.setReviewStatus(ReviewStatusEnum.PASS.getValue());
// 查询数据库
Page<App> appPage = appService.page(new Page<>(current, size),
appService.getQueryWrapper(appQueryRequest));
// 获取封装类
return ResultUtils.success(appService.getAppVOPage(appPage, request));
}
/**
* 分页获取当前登录用户创建的应用列表
*
* @param appQueryRequest
* @param request
* @return
*/
@PostMapping("/my/list/page/vo")
public BaseResponse<Page<AppVO>> listMyAppVOByPage(@RequestBody AppQueryRequest appQueryRequest,
HttpServletRequest request) {
ThrowUtils.throwIf(appQueryRequest == null, ErrorCode.PARAMS_ERROR);
// 补充查询条件,只查询当前登录用户的数据
User loginUser = userService.getLoginUser(request);
appQueryRequest.setUserId(loginUser.getId());
long current = appQueryRequest.getCurrent();
long size = appQueryRequest.getPageSize();
// 限制爬虫
ThrowUtils.throwIf(size > 20, ErrorCode.PARAMS_ERROR);
// 查询数据库
Page<App> appPage = appService.page(new Page<>(current, size),
appService.getQueryWrapper(appQueryRequest));
// 获取封装类
return ResultUtils.success(appService.getAppVOPage(appPage, request));
}
/**
* 编辑应用(给用户使用)
*
* @param appEditRequest
* @param request
* @return
*/
@PostMapping("/edit")
public BaseResponse<Boolean> editApp(@RequestBody AppEditRequest appEditRequest, HttpServletRequest request) {
if (appEditRequest == null || appEditRequest.getId() <= 0) {
throw new BusinessException(ErrorCode.PARAMS_ERROR);
}
// 在此处将实体类和 DTO 进行转换
App app = new App();
BeanUtils.copyProperties(appEditRequest, app);
// 数据校验
appService.validApp(app, false);
User loginUser = userService.getLoginUser(request);
// 判断是否存在
long id = appEditRequest.getId();
App oldApp = appService.getById(id);
ThrowUtils.throwIf(oldApp == null, ErrorCode.NOT_FOUND_ERROR);
// 仅本人或管理员可编辑
if (!oldApp.getUserId().equals(loginUser.getId()) && !userService.isAdmin(loginUser)) {
throw new BusinessException(ErrorCode.NO_AUTH_ERROR);
}
// 重置审核状态
app.setReviewStatus(ReviewStatusEnum.REVIEWING.getValue());
// 操作数据库
boolean result = appService.updateById(app);
ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR);
return ResultUtils.success(true);
}
// endregion
/**
* 应用审核
*
* @param reviewRequest
* @param request
* @return
*/
@PostMapping("/review")
@AuthCheck(mustRole = UserConstant.ADMIN_ROLE)
public BaseResponse<Boolean> doAppReview(@RequestBody ReviewRequest reviewRequest, HttpServletRequest request) {
ThrowUtils.throwIf(reviewRequest == null, ErrorCode.PARAMS_ERROR);
Long id = reviewRequest.getId();
Integer reviewStatus = reviewRequest.getReviewStatus();
// 校验
ReviewStatusEnum reviewStatusEnum = ReviewStatusEnum.getEnumByValue(reviewStatus);
if (id == null || reviewStatusEnum == null) {
throw new BusinessException(ErrorCode.PARAMS_ERROR);
}
// 判断是否存在
App oldApp = appService.getById(id);
ThrowUtils.throwIf(oldApp == null, ErrorCode.NOT_FOUND_ERROR);
// 已是该状态
if (oldApp.getReviewStatus().equals(reviewStatus)) {
throw new BusinessException(ErrorCode.PARAMS_ERROR, "请勿重复审核");
}
// 更新审核状态
User loginUser = userService.getLoginUser(request);
App app = new App();
app.setId(id);
app.setReviewStatus(reviewStatus);
app.setReviewMessage(reviewRequest.getReviewMessage());
app.setReviewerId(loginUser.getId());
app.setReviewTime(new Date());
boolean result = appService.updateById(app);
ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR);
return ResultUtils.success(true);
}
}
================================================
FILE: yudada-backend/src/main/java/com/yupi/yudada/controller/AppStatisticController.java
================================================
package com.yupi.yudada.controller;
import cn.hutool.core.io.FileUtil;
import com.yupi.yudada.common.BaseResponse;
import com.yupi.yudada.common.ErrorCode;
import com.yupi.yudada.common.ResultUtils;
import com.yupi.yudada.constant.FileConstant;
import com.yupi.yudada.exception.BusinessException;
import com.yupi.yudada.exception.ThrowUtils;
import com.yupi.yudada.manager.CosManager;
import com.yupi.yudada.mapper.UserAnswerMapper;
import com.yupi.yudada.model.dto.file.UploadFileRequest;
import com.yupi.yudada.model.dto.statistic.AppAnswerCountDTO;
import com.yupi.yudada.model.dto.statistic.AppAnswerResultCountDTO;
import com.yupi.yudada.model.entity.User;
import com.yupi.yudada.model.enums.FileUploadBizEnum;
import com.yupi.yudada.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomStringUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.util.Arrays;
import java.util.List;
/**
* App 统计分析接口
*
* @author <a href="https://github.com/liyupi">程序员鱼皮</a>
* @from <a href="https://yupi.icu">编程导航知识星球</a>
*/
@RestController
@RequestMapping("/app/statistic")
@Slf4j
public class AppStatisticController {
@Resource
private UserAnswerMapper userAnswerMapper;
/**
* 热门应用及回答数统计(top 10)
*
* @return
*/
@GetMapping("/answer_count")
public BaseResponse<List<AppAnswerCountDTO>> getAppAnswerCount() {
return ResultUtils.success(userAnswerMapper.doAppAnswerCount());
}
/**
* 某应用回答结果分布统计
*
* @param appId
* @return
*/
@GetMapping("/answer_result_count")
public BaseResponse<List<AppAnswerResultCountDTO>> getAppAnswerResultCount(Long appId) {
ThrowUtils.throwIf(appId == null || appId <= 0, ErrorCode.PARAMS_ERROR);
return ResultUtils.success(userAnswerMapper.doAppAnswerResultCount(appId));
}
}
================================================
FILE: yudada-backend/src/main/java/com/yupi/yudada/controller/FileController.java
================================================
package com.yupi.yudada.controller;
import cn.hutool.core.io.FileUtil;
import com.yupi.yudada.common.BaseResponse;
import com.yupi.yudada.common.ErrorCode;
import com.yupi.yudada.common.ResultUtils;
import com.yupi.yudada.constant.FileConstant;
import com.yupi.yudada.exception.BusinessException;
import com.yupi.yudada.manager.CosManager;
import com.yupi.yudada.model.dto.file.UploadFileRequest;
import com.yupi.yudada.model.entity.User;
import com.yupi.yudada.model.enums.FileUploadBizEnum;
import com.yupi.yudada.service.UserService;
import java.io.File;
import java.util.Arrays;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomStringUtils;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
/**
* 文件接口
*
* @author <a href="https://github.com/liyupi">程序员鱼皮</a>
* @from <a href="https://yupi.icu">编程导航知识星球</a>
*/
@RestController
@RequestMapping("/file")
@Slf4j
public class FileController {
@Resource
private UserService userService;
@Resource
private CosManager cosManager;
/**
* 文件上传
*
* @param multipartFile
* @param uploadFileRequest
* @param request
* @return
*/
@PostMapping("/upload")
public BaseResponse<String> uploadFile(@RequestPart("file") MultipartFile multipartFile,
UploadFileRequest uploadFileRequest, HttpServletRequest request) {
String biz = uploadFileRequest.getBiz();
FileUploadBizEnum fileUploadBizEnum = FileUploadBizEnum.getEnumByValue(biz);
if (fileUploadBizEnum == null) {
throw new BusinessException(ErrorCode.PARAMS_ERROR);
}
validFile(multipartFile, fileUploadBizEnum);
User loginUser = userService.getLoginUser(request);
// 文件目录:根据业务、用户来划分
String uuid = RandomStringUtils.randomAlphanumeric(8);
String filename = uuid + "-" + multipartFile.getOriginalFilename();
String filepath = String.format("/%s/%s/%s", fileUploadBizEnum.getValue(), loginUser.getId(), filename);
File file = null;
try {
// 上传文件
file = File.createTempFile(filepath, null);
multipartFile.transferTo(file);
cosManager.putObject(filepath, file);
// 返回可访问地址
return ResultUtils.success(FileConstant.COS_HOST + filepath);
} catch (Exception e) {
log.error("file upload error, filepath = " + filepath, e);
throw new BusinessException(ErrorCode.SYSTEM_ERROR, "上传失败");
} finally {
if (file != null) {
// 删除临时文件
boolean delete = file.delete();
if (!delete) {
log.error("file delete error, filepath = {}", filepath);
}
}
}
}
/**
* 校验文件
*
* @param multipartFile
* @param fileUploadBizEnum 业务类型
*/
private void validFile(MultipartFile multipartFile, FileUploadBizEnum fileUploadBizEnum) {
// 文件大小
long fileSize = multipartFile.getSize();
// 文件后缀
String fileSuffix = FileUtil.getSuffix(multipartFile.getOriginalFilename());
final long ONE_M = 1024 * 1024L;
if (FileUploadBizEnum.USER_AVATAR.equals(fileUploadBizEnum)) {
if (fileSize > ONE_M) {
throw new BusinessException(ErrorCode.PARAMS_ERROR, "文件大小不能超过 1M");
}
if (!Arrays.asList("jpeg", "jpg", "svg", "png", "webp").contains(fileSuffix)) {
throw new BusinessException(ErrorCode.PARAMS_ERROR, "文件类型错误");
}
}
}
}
================================================
FILE: yudada-backend/src/main/java/com/yupi/yudada/controller/PostController.java
================================================
package com.yupi.yudada.controller;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.yupi.yudada.annotation.AuthCheck;
import com.yupi.yudada.common.BaseResponse;
import com.yupi.yudada.common.DeleteRequest;
import com.yupi.yudada.common.ErrorCode;
import com.yupi.yudada.common.ResultUtils;
import com.yupi.yudada.constant.UserConstant;
import com.yupi.yudada.exception.BusinessException;
import com.yupi.yudada.exception.ThrowUtils;
import com.yupi.yudada.model.dto.post.PostAddRequest;
import com.yupi.yudada.model.dto.post.PostEditRequest;
import com.yupi.yudada.model.dto.post.PostQueryRequest;
import com.yupi.yudada.model.dto.post.PostUpdateRequest;
import com.yupi.yudada.model.entity.Post;
import com.yupi.yudada.model.entity.User;
import com.yupi.yudada.model.vo.PostVO;
import com.yupi.yudada.service.PostService;
import com.yupi.yudada.service.UserService;
import java.util.List;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* 帖子接口
*
* @author <a href="https://github.com/liyupi">程序员鱼皮</a>
* @from <a href="https://yupi.icu">编程导航知识星球</a>
*/
@RestController
@RequestMapping("/post")
@Slf4j
public class PostController {
@Resource
private PostService postService;
@Resource
private UserService userService;
// region 增删改查
/**
* 创建
*
* @param postAddRequest
* @param request
* @return
*/
@PostMapping("/add")
public BaseResponse<Long> addPost(@RequestBody PostAddRequest postAddRequest, HttpServletRequest request) {
if (postAddRequest == null) {
throw new BusinessException(ErrorCode.PARAMS_ERROR);
}
Post post = new Post();
BeanUtils.copyProperties(postAddRequest, post);
List<String> tags = postAddRequest.getTags();
if (tags != null) {
post.setTags(JSONUtil.toJsonStr(tags));
}
postService.validPost(post, true);
User loginUser = userService.getLoginUser(request);
post.setUserId(loginUser.getId());
post.setFavourNum(0);
post.setThumbNum(0);
boolean result = postService.save(post);
ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR);
long newPostId = post.getId();
return ResultUtils.success(newPostId);
}
/**
* 删除
*
* @param deleteRequest
* @param request
* @return
*/
@PostMapping("/delete")
public BaseResponse<Boolean> deletePost(@RequestBody DeleteRequest deleteRequest, HttpServletRequest request) {
if (deleteRequest == null || deleteRequest.getId() <= 0) {
throw new BusinessException(ErrorCode.PARAMS_ERROR);
}
User user = userService.getLoginUser(request);
long id = deleteRequest.getId();
// 判断是否存在
Post oldPost = postService.getById(id);
ThrowUtils.throwIf(oldPost == null, ErrorCode.NOT_FOUND_ERROR);
// 仅本人或管理员可删除
if (!oldPost.getUserId().equals(user.getId()) && !userService.isAdmin(request)) {
throw new BusinessException(ErrorCode.NO_AUTH_ERROR);
}
boolean b = postService.removeById(id);
return ResultUtils.success(b);
}
/**
* 更新(仅管理员)
*
* @param postUpdateRequest
* @return
*/
@PostMapping("/update")
@AuthCheck(mustRole = UserConstant.ADMIN_ROLE)
public BaseResponse<Boolean> updatePost(@RequestBody PostUpdateRequest postUpdateRequest) {
if (postUpdateRequest == null || postUpdateRequest.getId() <= 0) {
throw new BusinessException(ErrorCode.PARAMS_ERROR);
}
Post post = new Post();
BeanUtils.copyProperties(postUpdateRequest, post);
List<String> tags = postUpdateRequest.getTags();
if (tags != null) {
post.setTags(JSONUtil.toJsonStr(tags));
}
// 参数校验
postService.validPost(post, false);
long id = postUpdateRequest.getId();
// 判断是否存在
Post oldPost = postService.getById(id);
ThrowUtils.throwIf(oldPost == null, ErrorCode.NOT_FOUND_ERROR);
boolean result = postService.updateById(post);
return ResultUtils.success(result);
}
/**
* 根据 id 获取
*
* @param id
* @return
*/
@GetMapping("/get/vo")
public BaseResponse<PostVO> getPostVOById(long id, HttpServletRequest request) {
if (id <= 0) {
throw new BusinessException(ErrorCode.PARAMS_ERROR);
}
Post post = postService.getById(id);
if (post == null) {
throw new BusinessException(ErrorCode.NOT_FOUND_ERROR);
}
return ResultUtils.success(postService.getPostVO(post, request));
}
/**
* 分页获取列表(仅管理员)
*
* @param postQueryRequest
* @return
*/
@PostMapping("/list/page")
@AuthCheck(mustRole = UserConstant.ADMIN_ROLE)
public BaseResponse<Page<Post>> listPostByPage(@RequestBody PostQueryRequest postQueryRequest) {
long current = postQueryRequest.getCurrent();
long size = postQueryRequest.getPageSize();
Page<Post> postPage = postService.page(new Page<>(current, size),
postService.getQueryWrapper(postQueryRequest));
return ResultUtils.success(postPage);
}
/**
* 分页获取列表(封装类)
*
* @param postQueryRequest
* @param request
* @return
*/
@PostMapping("/list/page/vo")
public BaseResponse<Page<PostVO>> listPostVOByPage(@RequestBody PostQueryRequest postQueryRequest,
HttpServletRequest request) {
long current = postQueryRequest.getCurrent();
long size = postQueryRequest.getPageSize();
// 限制爬虫
ThrowUtils.throwIf(size > 20, ErrorCode.PARAMS_ERROR);
Page<Post> postPage = postService.page(new Page<>(current, size),
postService.getQueryWrapper(postQueryRequest));
return ResultUtils.success(postService.getPostVOPage(postPage, request));
}
/**
* 分页获取当前用户创建的资源列表
*
* @param postQueryRequest
* @param request
* @return
*/
@PostMapping("/my/list/page/vo")
public BaseResponse<Page<PostVO>> listMyPostVOByPage(@RequestBody PostQueryRequest postQueryRequest,
HttpServletRequest request) {
if (postQueryRequest == null) {
throw new BusinessException(ErrorCode.PARAMS_ERROR);
}
User loginUser = userService.getLoginUser(request);
postQueryRequest.setUserId(loginUser.getId());
long current = postQueryRequest.getCurrent();
long size = postQueryRequest.getPageSize();
// 限制爬虫
ThrowUtils.throwIf(size > 20, ErrorCode.PARAMS_ERROR);
Page<Post> postPage = postService.page(new Page<>(current, size),
postService.getQueryWrapper(postQueryRequest));
return ResultUtils.success(postService.getPostVOPage(postPage, request));
}
// endregion
/**
* 编辑(用户)
*
* @param postEditRequest
* @param request
* @return
*/
@PostMapping("/edit")
public BaseResponse<Boolean> editPost(@RequestBody PostEditRequest postEditRequest, HttpServletRequest request) {
if (postEditRequest == null || postEditRequest.getId() <= 0) {
throw new BusinessException(ErrorCode.PARAMS_ERROR);
}
Post post = new Post();
BeanUtils.copyProperties(postEditRequest, post);
List<String> tags = postEditRequest.getTags();
if (tags != null) {
post.setTags(JSONUtil.toJsonStr(tags));
}
// 参数校验
postService.validPost(post, false);
User loginUser = userService.getLoginUser(request);
long id = postEditRequest.getId();
// 判断是否存在
Post oldPost = postService.getById(id);
ThrowUtils.throwIf(oldPost == null, ErrorCode.NOT_FOUND_ERROR);
// 仅本人或管理员可编辑
if (!oldPost.getUserId().equals(loginUser.getId()) && !userService.isAdmin(loginUser)) {
throw new BusinessException(ErrorCode.NO_AUTH_ERROR);
}
boolean result = postService.updateById(post);
return ResultUtils.success(result);
}
}
================================================
FILE: yudada-backend/src/main/java/com/yupi/yudada/controller/PostFavourController.java
================================================
package com.yupi.yudada.controller;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.yupi.yudada.common.BaseResponse;
import com.yupi.yudada.common.ErrorCode;
import com.yupi.yudada.common.ResultUtils;
import com.yupi.yudada.exception.BusinessException;
import com.yupi.yudada.exception.ThrowUtils;
import com.yupi.yudada.model.dto.post.PostQueryRequest;
import com.yupi.yudada.model.dto.postfavour.PostFavourAddRequest;
import com.yupi.yudada.model.dto.postfavour.PostFavourQueryRequest;
import com.yupi.yudada.model.entity.Post;
import com.yupi.yudada.model.entity.User;
import com.yupi.yudada.model.vo.PostVO;
import com.yupi.yudada.service.PostFavourService;
import com.yupi.yudada.service.PostService;
import com.yupi.yudada.service.UserService;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* 帖子收藏接口
*
* @author <a href="https://github.com/liyupi">程序员鱼皮</a>
* @from <a href="https://yupi.icu">编程导航知识星球</a>
*/
@RestController
@RequestMapping("/post_favour")
@Slf4j
public class PostFavourController {
@Resource
private PostFavourService postFavourService;
@Resource
private PostService postService;
@Resource
private UserService userService;
/**
* 收藏 / 取消收藏
*
* @param postFavourAddRequest
* @param request
* @return resultNum 收藏变化数
*/
@PostMapping("/")
public BaseResponse<Integer> doPostFavour(@RequestBody PostFavourAddRequest postFavourAddRequest,
HttpServletRequest request) {
if (postFavourAddRequest == null || postFavourAddRequest.getPostId() <= 0) {
throw new BusinessException(ErrorCode.PARAMS_ERROR);
}
// 登录才能操作
final User loginUser = userService.getLoginUser(request);
long postId = postFavourAddRequest.getPostId();
int result = postFavourService.doPostFavour(postId, loginUser);
return ResultUtils.success(result);
}
/**
* 获取我收藏的帖子列表
*
* @param postQueryRequest
* @param request
*/
@PostMapping("/my/list/page")
public BaseResponse<Page<PostVO>> listMyFavourPostByPage(@RequestBody PostQueryRequest postQueryRequest,
HttpServletRequest request) {
if (postQueryRequest == null) {
throw new BusinessException(ErrorCode.PARAMS_ERROR);
}
User loginUser = userService.getLoginUser(request);
long current = postQueryRequest.getCurrent();
long size = postQueryRequest.getPageSize();
// 限制爬虫
ThrowUtils.throwIf(size > 20, ErrorCode.PARAMS_ERROR);
Page<Post> postPage = postFavourService.listFavourPostByPage(new Page<>(current, size),
postService.getQueryWrapper(postQueryRequest), loginUser.getId());
return ResultUtils.success(postService.getPostVOPage(postPage, request));
}
/**
* 获取用户收藏的帖子列表
*
* @param postFavourQueryRequest
* @param request
*/
@PostMapping("/list/page")
public BaseResponse<Page<PostVO>> listFavourPostByPage(@RequestBody PostFavourQueryRequest postFavourQueryRequest,
HttpServletRequest request) {
if (postFavourQueryRequest == null) {
throw new BusinessException(ErrorCode.PARAMS_ERROR);
}
long current = postFavourQueryRequest.getCurrent();
long size = postFavourQueryRequest.getPageSize();
Long userId = postFavourQueryRequest.getUserId();
// 限制爬虫
ThrowUtils.throwIf(size > 20 || userId == null, ErrorCode.PARAMS_ERROR);
Page<Post> postPage = postFavourService.listFavourPostByPage(new Page<>(current, size),
postService.getQueryWrapper(postFavourQueryRequest.getPostQueryRequest()), userId);
return ResultUtils.success(postService.getPostVOPage(postPage, request));
}
}
================================================
FILE: yudada-backend/src/main/java/com/yupi/yudada/controller/PostThumbController.java
================================================
package com.yupi.yudada.controller;
import com.yupi.yudada.common.BaseResponse;
import com.yupi.yudada.common.ErrorCode;
import com.yupi.yudada.common.ResultUtils;
import com.yupi.yudada.exception.BusinessException;
import com.yupi.yudada.model.dto.postthumb.PostThumbAddRequest;
import com.yupi.yudada.model.entity.User;
import com.yupi.yudada.service.PostThumbService;
import com.yupi.yudada.service.UserService;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* 帖子点赞接口
*
* @author <a href="https://github.com/liyupi">程序员鱼皮</a>
* @from <a href="https://yupi.icu">编程导航知识星球</a>
*/
@RestController
@RequestMapping("/post_thumb")
@Slf4j
public class PostThumbController {
@Resource
private PostThumbService postThumbService;
@Resource
private UserService userService;
/**
* 点赞 / 取消点赞
*
* @param postThumbAddRequest
* @param request
* @return resultNum 本次点赞变化数
*/
@PostMapping("/")
public BaseResponse<Integer> doThumb(@RequestBody PostThumbAddRequest postThumbAddRequest,
HttpServletRequest request) {
if (postThumbAddRequest == null || postThumbAddRequest.getPostId() <= 0) {
throw new BusinessException(ErrorCode.PARAMS_ERROR);
}
// 登录才能点赞
final User loginUser = userService.getLoginUser(request);
long postId = postThumbAddRequest.getPostId();
int result = postThumbService.doPostThumb(postId, loginUser);
return ResultUtils.success(result);
}
}
================================================
FILE: yudada-backend/src/main/java/com/yupi/yudada/controller/QuestionController.java
================================================
package com.yupi.yudada.controller;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.yupi.yudada.annotation.AuthCheck;
import com.yupi.yudada.common.BaseResponse;
import com.yupi.yudada.common.DeleteRequest;
import com.yupi.yudada.common.ErrorCode;
import com.yupi.yudada.common.ResultUtils;
import com.yupi.yudada.constant.UserConstant;
import com.yupi.yudada.exception.BusinessException;
import com.yupi.yudada.exception.ThrowUtils;
import com.yupi.yudada.manager.AiManager;
import com.yupi.yudada.model.dto.question.*;
import com.yupi.yudada.model.entity.App;
import com.yupi.yudada.model.entity.Question;
import com.yupi.yudada.model.entity.User;
import com.yupi.yudada.model.enums.AppTypeEnum;
import com.yupi.yudada.model.vo.QuestionVO;
import com.yupi.yudada.service.AppService;
import com.yupi.yudada.service.QuestionService;
import com.yupi.yudada.service.UserService;
import com.zhipu.oapi.service.v4.model.ModelData;
import io.reactivex.Flowable;
import io.reactivex.Scheduler;
import io.reactivex.schedulers.Schedulers;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Stream;
/**
* 题目接口
*
* @author <a href="https://github.com/liyupi">程序员鱼皮</a>
* @from <a href="https://www.code-nav.cn">编程导航学习圈</a>
*/
@RestController
@RequestMapping("/question")
@Slf4j
public class QuestionController {
@Resource
private QuestionService questionService;
@Resource
private UserService userService;
@Resource
private AppService appService;
@Resource
private AiManager aiManager;
@Resource
private Scheduler vipScheduler;
// region 增删改查
/**
* 创建题目
*
* @param questionAddRequest
* @param request
* @return
*/
@PostMapping("/add")
public BaseResponse<Long> addQuestion(@RequestBody QuestionAddRequest questionAddRequest, HttpServletRequest request) {
ThrowUtils.throwIf(questionAddRequest == null, ErrorCode.PARAMS_ERROR);
// 在此处将实体类和 DTO 进行转换
Question question = new Question();
BeanUtils.copyProperties(questionAddRequest, question);
List<QuestionContentDTO> questionContentDTO = questionAddRequest.getQuestionContent();
question.setQuestionContent(JSONUtil.toJsonStr(questionContentDTO));
// 数据校验
questionService.validQuestion(question, true);
// 填充默认值
User loginUser = userService.getLoginUser(request);
question.setUserId(loginUser.getId());
// 写入数据库
boolean result = questionService.save(question);
ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR);
// 返回新写入的数据 id
long newQuestionId = question.getId();
return ResultUtils.success(newQuestionId);
}
/**
* 删除题目
*
* @param deleteRequest
* @param request
* @return
*/
@PostMapping("/delete")
public BaseResponse<Boolean> deleteQuestion(@RequestBody DeleteRequest deleteRequest, HttpServletRequest request) {
if (deleteRequest == null || deleteRequest.getId() <= 0) {
throw new BusinessException(ErrorCode.PARAMS_ERROR);
}
User user = userService.getLoginUser(request);
long id = deleteRequest.getId();
// 判断是否存在
Question oldQuestion = questionService.getById(id);
ThrowUtils.throwIf(oldQuestion == null, ErrorCode.NOT_FOUND_ERROR);
// 仅本人或管理员可删除
if (!oldQuestion.getUserId().equals(user.getId()) && !userService.isAdmin(request)) {
throw new BusinessException(ErrorCode.NO_AUTH_ERROR);
}
// 操作数据库
boolean result = questionService.removeById(id);
ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR);
return ResultUtils.success(true);
}
/**
* 更新题目(仅管理员可用)
*
* @param questionUpdateRequest
* @return
*/
@PostMapping("/update")
@AuthCheck(mustRole = UserConstant.ADMIN_ROLE)
public BaseResponse<Boolean> updateQuestion(@RequestBody QuestionUpdateRequest questionUpdateRequest) {
if (questionUpdateRequest == null || questionUpdateRequest.getId() <= 0) {
throw new BusinessException(ErrorCode.PARAMS_ERROR);
}
// 在此处将实体类和 DTO 进行转换
Question question = new Question();
BeanUtils.copyProperties(questionUpdateRequest, question);
List<QuestionContentDTO> questionContentDTO = questionUpdateRequest.getQuestionContent();
question.setQuestionContent(JSONUtil.toJsonStr(questionContentDTO));
// 数据校验
questionService.validQuestion(question, false);
// 判断是否存在
long id = questionUpdateRequest.getId();
Question oldQuestion = questionService.getById(id);
ThrowUtils.throwIf(oldQuestion == null, ErrorCode.NOT_FOUND_ERROR);
// 操作数据库
boolean result = questionService.updateById(question);
ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR);
return ResultUtils.success(true);
}
/**
* 根据 id 获取题目(封装类)
*
* @param id
* @return
*/
@GetMapping("/get/vo")
public BaseResponse<QuestionVO> getQuestionVOById(long id, HttpServletRequest request) {
ThrowUtils.throwIf(id <= 0, ErrorCode.PARAMS_ERROR);
// 查询数据库
Question question = questionService.getById(id);
ThrowUtils.throwIf(question == null, ErrorCode.NOT_FOUND_ERROR);
// 获取封装类
return ResultUtils.success(questionService.getQuestionVO(question, request));
}
/**
* 分页获取题目列表(仅管理员可用)
*
* @param questionQueryRequest
* @return
*/
@PostMapping("/list/page")
@AuthCheck(mustRole = UserConstant.ADMIN_ROLE)
public BaseResponse<Page<Question>> listQuestionByPage(@RequestBody QuestionQueryRequest questionQueryRequest) {
long current = questionQueryRequest.getCurrent();
long size = questionQueryRequest.getPageSize();
// 查询数据库
Page<Question> questionPage = questionService.page(new Page<>(current, size),
questionService.getQueryWrapper(questionQueryRequest));
return ResultUtils.success(questionPage);
}
/**
* 分页获取题目列表(封装类)
*
* @param questionQueryRequest
* @param request
* @return
*/
@PostMapping("/list/page/vo")
public BaseResponse<Page<QuestionVO>> listQuestionVOByPage(@RequestBody QuestionQueryRequest questionQueryRequest,
HttpServletRequest request) {
long current = questionQueryRequest.getCurrent();
long size = questionQueryRequest.getPageSize();
// 限制爬虫
ThrowUtils.throwIf(size > 20, ErrorCode.PARAMS_ERROR);
// 查询数据库
Page<Question> questionPage = questionService.page(new Page<>(current, size),
questionService.getQueryWrapper(questionQueryRequest));
// 获取封装类
return ResultUtils.success(questionService.getQuestionVOPage(questionPage, request));
}
/**
* 分页获取当前登录用户创建的题目列表
*
* @param questionQueryRequest
* @param request
* @return
*/
@PostMapping("/my/list/page/vo")
public BaseResponse<Page<QuestionVO>> listMyQuestionVOByPage(@RequestBody QuestionQueryRequest questionQueryRequest,
HttpServletRequest request) {
ThrowUtils.throwIf(questionQueryRequest == null, ErrorCode.PARAMS_ERROR);
// 补充查询条件,只查询当前登录用户的数据
User loginUser = userService.getLoginUser(request);
questionQueryRequest.setUserId(loginUser.getId());
long current = questionQueryRequest.getCurrent();
long size = questionQueryRequest.getPageSize();
// 限制爬虫
ThrowUtils.throwIf(size > 20, ErrorCode.PARAMS_ERROR);
// 查询数据库
Page<Question> questionPage = questionService.page(new Page<>(current, size),
questionService.getQueryWrapper(questionQueryRequest));
// 获取封装类
return ResultUtils.success(questionService.getQuestionVOPage(questionPage, request));
}
/**
* 编辑题目(给用户使用)
*
* @param questionEditRequest
* @param request
* @return
*/
@PostMapping("/edit")
public BaseResponse<Boolean> editQuestion(@RequestBody QuestionEditRequest questionEditRequest, HttpServletRequest request) {
if (questionEditRequest == null || questionEditRequest.getId() <= 0) {
throw new BusinessException(ErrorCode.PARAMS_ERROR);
}
// 在此处将实体类和 DTO 进行转换
Question question = new Question();
BeanUtils.copyProperties(questionEditRequest, question);
List<QuestionContentDTO> questionContentDTO = questionEditRequest.getQuestionContent();
question.setQuestionContent(JSONUtil.toJsonStr(questionContentDTO));
// 数据校验
questionService.validQuestion(question, false);
User loginUser = userService.getLoginUser(request);
// 判断是否存在
long id = questionEditRequest.getId();
Question oldQuestion = questionService.getById(id);
ThrowUtils.throwIf(oldQuestion == null, ErrorCode.NOT_FOUND_ERROR);
// 仅本人或管理员可编辑
if (!oldQuestion.getUserId().equals(loginUser.getId()) && !userService.isAdmin(loginUser)) {
throw new BusinessException(ErrorCode.NO_AUTH_ERROR);
}
// 操作数据库
boolean result = questionService.updateById(question);
ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR);
return ResultUtils.success(true);
}
// endregion
// region AI 生成题目功能
private static final String GENERATE_QUESTION_SYSTEM_MESSAGE = "你是一位严谨的出题专家,我会给你如下信息:\n" +
"```\n" +
"应用名称,\n" +
"【【【应用描述】】】,\n" +
"应用类别,\n" +
"要生成的题目数,\n" +
"每个题目的选项数\n" +
"```\n" +
"\n" +
"请你根据上述信息,按照以下步骤来出题:\n" +
"1. 要求:题目和选项尽可能地短,题目不要包含序号,每题的选项数以我提供的为主,题目不能重复\n" +
"2. 严格按照下面的 json 格式输出题目和选项\n" +
"```\n" +
"[{\"options\":[{\"value\":\"选项内容\",\"key\":\"A\"},{\"value\":\"\",\"key\":\"B\"}],\"title\":\"题目标题\"}]\n" +
"```\n" +
"title 是题目,options 是选项,每个选项的 key 按照英文字母序(比如 A、B、C、D)以此类推,value 是选项内容\n" +
"3. 检查题目是否包含序号,若包含序号则去除序号\n" +
"4. 返回的题目列表格式必须为 JSON 数组";
/**
* 生成题目的用户消息
*
* @param app
* @param questionNumber
* @param optionNumber
* @return
*/
private String getGenerateQuestionUserMessage(App app, int questionNumber, int optionNumber) {
StringBuilder userMessage = new StringBuilder();
userMessage.append(app.getAppName()).append("\n");
userMessage.append(app.getAppDesc()).append("\n");
userMessage.append(AppTypeEnum.getEnumByValue(app.getAppType()).getText() + "类").append("\n");
userMessage.append(questionNumber).append("\n");
userMessage.append(optionNumber);
return userMessage.toString();
}
@PostMapping("/ai_generate")
public BaseResponse<List<QuestionContentDTO>> aiGenerateQuestion(
@RequestBody AiGenerateQuestionRequest aiGenerateQuestionRequest) {
ThrowUtils.throwIf(aiGenerateQuestionRequest == null, ErrorCode.PARAMS_ERROR);
// 获取参数
Long appId = aiGenerateQuestionRequest.getAppId();
int questionNumber = aiGenerateQuestionRequest.getQuestionNumber();
int optionNumber = aiGenerateQuestionRequest.getOptionNumber();
// 获取应用信息
App app = appService.getById(appId);
ThrowUtils.throwIf(app == null, ErrorCode.NOT_FOUND_ERROR);
// 封装 Prompt
String userMessage = getGenerateQuestionUserMessage(app, questionNumber, optionNumber);
// AI 生成
String result = aiManager.doSyncRequest(GENERATE_QUESTION_SYSTEM_MESSAGE, userMessage, null);
// 截取需要的 JSON 信息
int start = result.indexOf("[");
int end = result.lastIndexOf("]");
String json = result.substring(start, end + 1);
List<QuestionContentDTO> questionContentDTOList = JSONUtil.toList(json, QuestionContentDTO.class);
return ResultUtils.success(questionContentDTOList);
}
@GetMapping("/ai_generate/sse")
public SseEmitter aiGenerateQuestionSSE(AiGenerateQuestionRequest aiGenerateQuestionRequest, HttpServletRequest request) {
ThrowUtils.throwIf(aiGenerateQuestionRequest == null, ErrorCode.PARAMS_ERROR);
// 获取参数
Long appId = aiGenerateQuestionRequest.getAppId();
int questionNumber = aiGenerateQuestionRequest.getQuestionNumber();
int optionNumber = aiGenerateQuestionRequest.getOptionNumber();
// 获取应用信息
App app = appService.getById(appId);
ThrowUtils.throwIf(app == null, ErrorCode.NOT_FOUND_ERROR);
// 封装 Prompt
String userMessage = getGenerateQuestionUserMessage(app, questionNumber, optionNumber);
// 建立 SSE 连接对象,0 表示永不超时
SseEmitter sseEmitter = new SseEmitter(0L);
// AI 生成,SSE 流式返回
Flowable<ModelData> modelDataFlowable = aiManager.doStreamRequest(GENERATE_QUESTION_SYSTEM_MESSAGE, userMessage, null);
// 左括号计数器,除了默认值外,当回归为 0 时,表示左括号等于右括号,可以截取
AtomicInteger counter = new AtomicInteger(0);
// 拼接完整题目
StringBuilder stringBuilder = new StringBuilder();
// 获取登录用户
User loginUser = userService.getLoginUser(request);
// 默认全局线程池
Scheduler scheduler = Schedulers.io();
if ("vip".equals(loginUser.getUserRole())) {
scheduler = vipScheduler;
}
modelDataFlowable
.observeOn(scheduler)
.map(modelData -> modelData.getChoices().get(0).getDelta().getContent())
.map(message -> message.replaceAll("\\s", ""))
.filter(StrUtil::isNotBlank)
.flatMap(message -> {
List<Character> characterList = new ArrayList<>();
for (char c : message.toCharArray()) {
characterList.add(c);
}
return Flowable.fromIterable(characterList);
})
.doOnNext(c -> {
// 如果是 '{',计数器 + 1
if (c == '{') {
counter.addAndGet(1);
}
if (counter.get() > 0) {
stringBuilder.append(c);
}
if (c == '}') {
counter.addAndGet(-1);
if (counter.get() == 0) {
// 可以拼接题目,并且通过 SSE 返回给前端
sseEmitter.send(JSONUtil.toJsonStr(stringBuilder.toString()));
// 重置,准备拼接下一道题
stringBuilder.setLength(0);
}
}
})
.doOnError((e) -> log.error("sse error", e))
.doOnComplete(sseEmitter::complete)
.subscribe();
return sseEmitter;
}
// 仅测试隔离线程池使用
@Deprecated
@GetMapping("/ai_generate/sse/test")
public SseEmitter aiGenerateQuestionSSETest(AiGenerateQuestionRequest aiGenerateQuestionRequest,
boolean isVip) {
ThrowUtils.throwIf(aiGenerateQuestionRequest == null, ErrorCode.PARAMS_ERROR);
// 获取参数
Long appId = aiGenerateQuestionRequest.getAppId();
int questionNumber = aiGenerateQuestionRequest.getQuestionNumber();
int optionNumber = aiGenerateQuestionRequest.getOptionNumber();
// 获取应用信息
App app = appService.getById(appId);
ThrowUtils.throwIf(app == null, ErrorCode.NOT_FOUND_ERROR);
// 封装 Prompt
String userMessage = getGenerateQuestionUserMessage(app, questionNumber, optionNumber);
// 建立 SSE 连接对象,0 表示永不超时
SseEmitter sseEmitter = new SseEmitter(0L);
// AI 生成,SSE 流式返回
Flowable<ModelData> modelDataFlowable = aiManager.doStreamRequest(GENERATE_QUESTION_SYSTEM_MESSAGE, userMessage, null);
// 左括号计数器,除了默认值外,当回归为 0 时,表示左括号等于右括号,可以截取
AtomicInteger counter = new AtomicInteger(0);
// 拼接完整题目
StringBuilder stringBuilder = new StringBuilder();
// 默认全局线程池
Scheduler scheduler = Schedulers.single();
if (isVip) {
scheduler = vipScheduler;
}
modelDataFlowable
.observeOn(scheduler)
.map(modelData -> modelData.getChoices().get(0).getDelta().getContent())
.map(message -> message.replaceAll("\\s", ""))
.filter(StrUtil::isNotBlank)
.flatMap(message -> {
List<Character> characterList = new ArrayList<>();
for (char c : message.toCharArray()) {
characterList.add(c);
}
return Flowable.fromIterable(characterList);
})
.doOnNext(c -> {
// 如果是 '{',计数器 + 1
if (c == '{') {
counter.addAndGet(1);
}
if (counter.get() > 0) {
stringBuilder.append(c);
}
if (c == '}') {
counter.addAndGet(-1);
if (counter.get() == 0) {
// 输出当前线程的名称
System.out.println(Thread.currentThread().getName());
// 模拟普通用户阻塞
if (!isVip) {
Thread.sleep(10000L);
}
// 可以拼接题目,并且通过 SSE 返回给前端
sseEmitter.send(JSONUtil.toJsonStr(stringBuilder.toString()));
// 重置,准备拼接下一道题
stringBuilder.setLength(0);
}
}
})
.doOnError((e) -> log.error("sse error", e))
.doOnComplete(sseEmitter::complete)
.subscribe();
return sseEmitter;
}
// endregion
}
================================================
FILE: yudada-backend/src/main/java/com/yupi/yudada/controller/ScoringResultController.java
================================================
package com.yupi.yudada.controller;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.yupi.yudada.annotation.AuthCheck;
import com.yupi.yudada.common.BaseResponse;
import com.yupi.yudada.common.DeleteRequest;
import com.yupi.yudada.common.ErrorCode;
import com.yupi.yudada.common.ResultUtils;
import com.yupi.yudada.constant.UserConstant;
import com.yupi.yudada.exception.BusinessException;
import com.yupi.yudada.exception.ThrowUtils;
import com.yupi.yudada.model.dto.scoringResult.ScoringResultAddRequest;
import com.yupi.yudada.model.dto.scoringResult.ScoringResultEditRequest;
import com.yupi.yudada.model.dto.scoringResult.ScoringResultQueryRequest;
import com.yupi.yudada.model.dto.scoringResult.ScoringResultUpdateRequest;
import com.yupi.yudada.model.entity.ScoringResult;
import com.yupi.yudada.model.entity.User;
import com.yupi.yudada.model.vo.ScoringResultVO;
import com.yupi.yudada.service.ScoringResultService;
import com.yupi.yudada.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.List;
/**
* 评分结果接口
*
* @author <a href="https://github.com/liyupi">程序员鱼皮</a>
* @from <a href="https://www.code-nav.cn">编程导航学习圈</a>
*/
@RestController
@RequestMapping("/scoringResult")
@Slf4j
public class ScoringResultController {
@Resource
private ScoringResultService scoringResultService;
@Resource
private UserService userService;
// region 增删改查
/**
* 创建评分结果
*
* @param scoringResultAddRequest
* @param request
* @return
*/
@PostMapping("/add")
public BaseResponse<Long> addScoringResult(@RequestBody ScoringResultAddRequest scoringResultAddRequest, HttpServletRequest request) {
ThrowUtils.throwIf(scoringResultAddRequest == null, ErrorCode.PARAMS_ERROR);
// 在此处将实体类和 DTO 进行转换
ScoringResult scoringResult = new ScoringResult();
BeanUtils.copyProperties(scoringResultAddRequest, scoringResult);
List<String> resultProp = scoringResultAddRequest.getResultProp();
scoringResult.setResultProp(JSONUtil.toJsonStr(resultProp));
// 数据校验
scoringResultService.validScoringResult(scoringResult, true);
// 填充默认值
User loginUser = userService.getLoginUser(request);
scoringResult.setUserId(loginUser.getId());
// 写入数据库
boolean result = scoringResultService.save(scoringResult);
ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR);
// 返回新写入的数据 id
long newScoringResultId = scoringResult.getId();
return ResultUtils.success(newScoringResultId);
}
/**
* 删除评分结果
*
* @param deleteRequest
* @param request
* @return
*/
@PostMapping("/delete")
public BaseResponse<Boolean> deleteScoringResult(@RequestBody DeleteRequest deleteRequest, HttpServletRequest request) {
if (deleteRequest == null || deleteRequest.getId() <= 0) {
throw new BusinessException(ErrorCode.PARAMS_ERROR);
}
User user = userService.getLoginUser(request);
long id = deleteRequest.getId();
// 判断是否存在
ScoringResult oldScoringResult = scoringResultService.getById(id);
ThrowUtils.throwIf(oldScoringResult == null, ErrorCode.NOT_FOUND_ERROR);
// 仅本人或管理员可删除
if (!oldScoringResult.getUserId().equals(user.getId()) && !userService.isAdmin(request)) {
throw new BusinessException(ErrorCode.NO_AUTH_ERROR);
}
// 操作数据库
boolean result = scoringResultService.removeById(id);
ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR);
return ResultUtils.success(true);
}
/**
* 更新评分结果(仅管理员可用)
*
* @param scoringResultUpdateRequest
* @return
*/
@PostMapping("/update")
@AuthCheck(mustRole = UserConstant.ADMIN_ROLE)
public BaseResponse<Boolean> updateScoringResult(@RequestBody ScoringResultUpdateRequest scoringResultUpdateRequest) {
if (scoringResultUpdateRequest == null || scoringResultUpdateRequest.getId() <= 0) {
throw new BusinessException(ErrorCode.PARAMS_ERROR);
}
// 在此处将实体类和 DTO 进行转换
ScoringResult scoringResult = new ScoringResult();
BeanUtils.copyProperties(scoringResultUpdateRequest, scoringResult);
List<String> resultProp = scoringResultUpdateRequest.getResultProp();
scoringResult.setResultProp(JSONUtil.toJsonStr(resultProp));
// 数据校验
scoringResultService.validScoringResult(scoringResult, false);
// 判断是否存在
long id = scoringResultUpdateRequest.getId();
ScoringResult oldScoringResult = scoringResultService.getById(id);
ThrowUtils.throwIf(oldScoringResult == null, ErrorCode.NOT_FOUND_ERROR);
// 操作数据库
boolean result = scoringResultService.updateById(scoringResult);
ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR);
return ResultUtils.success(true);
}
/**
* 根据 id 获取评分结果(封装类)
*
* @param id
* @return
*/
@GetMapping("/get/vo")
public BaseResponse<ScoringResultVO> getScoringResultVOById(long id, HttpServletRequest request) {
ThrowUtils.throwIf(id <= 0, ErrorCode.PARAMS_ERROR);
// 查询数据库
ScoringResult scoringResult = scoringResultService.getById(id);
ThrowUtils.throwIf(scoringResult == null, ErrorCode.NOT_FOUND_ERROR);
// 获取封装类
return ResultUtils.success(scoringResultService.getScoringResultVO(scoringResult, request));
}
/**
* 分页获取评分结果列表(仅管理员可用)
*
* @param scoringResultQueryRequest
* @return
*/
@PostMapping("/list/page")
@AuthCheck(mustRole = UserConstant.ADMIN_ROLE)
public BaseResponse<Page<ScoringResult>> listScoringResultByPage(@RequestBody ScoringResultQueryRequest scoringResultQueryRequest) {
long current = scoringResultQueryRequest.getCurrent();
long size = scoringResultQueryRequest.getPageSize();
// 查询数据库
Page<ScoringResult> scoringResultPage = scoringResultService.page(new Page<>(current, size),
scoringResultService.getQueryWrapper(scoringResultQueryRequest));
return ResultUtils.success(scoringResultPage);
}
/**
* 分页获取评分结果列表(封装类)
*
* @param scoringResultQueryRequest
* @param request
* @return
*/
@PostMapping("/list/page/vo")
public BaseResponse<Page<ScoringResultVO>> listScoringResultVOByPage(@RequestBody ScoringResultQueryRequest scoringResultQueryRequest,
HttpServletRequest request) {
long current = scoringResultQueryRequest.getCurrent();
long size = scoringResultQueryRequest.getPageSize();
// 限制爬虫
ThrowUtils.throwIf(size > 20, ErrorCode.PARAMS_ERROR);
// 查询数据库
Page<ScoringResult> scoringResultPage = scoringResultService.page(new Page<>(current, size),
scoringResultService.getQueryWrapper(scoringResultQueryRequest));
// 获取封装类
return ResultUtils.success(scoringResultService.getScoringResultVOPage(scoringResultPage, request));
}
/**
* 分页获取当前登录用户创建的评分结果列表
*
* @param scoringResultQueryRequest
* @param request
* @return
*/
@PostMapping("/my/list/page/vo")
public BaseResponse<Page<ScoringResultVO>> listMyScoringResultVOByPage(@RequestBody ScoringResultQueryRequest scoringResultQueryRequest,
HttpServletRequest request) {
ThrowUtils.throwIf(scoringResultQueryRequest == null, ErrorCode.PARAMS_ERROR);
// 补充查询条件,只查询当前登录用户的数据
User loginUser = userService.getLoginUser(request);
scoringResultQueryRequest.setUserId(loginUser.getId());
long current = scoringResultQueryRequest.getCurrent();
long size = scoringResultQueryRequest.getPageSize();
// 限制爬虫
ThrowUtils.throwIf(size > 20, ErrorCode.PARAMS_ERROR);
// 查询数据库
Page<ScoringResult> scoringResultPage = scoringResultService.page(new Page<>(current, size),
scoringResultService.getQueryWrapper(scoringResultQueryRequest));
// 获取封装类
return ResultUtils.success(scoringResultService.getScoringResultVOPage(scoringResultPage, request));
}
/**
* 编辑评分结果(给用户使用)
*
* @param scoringResultEditRequest
* @param request
* @return
*/
@PostMapping("/edit")
public BaseResponse<Boolean> editScoringResult(@RequestBody ScoringResultEditRequest scoringResultEditRequest, HttpServletRequest request) {
if (scoringResultEditRequest == null || scoringResultEditRequest.getId() <= 0) {
throw new BusinessException(ErrorCode.PARAMS_ERROR);
}
// 在此处将实体类和 DTO 进行转换
ScoringResult scoringResult = new ScoringResult();
BeanUtils.copyProperties(scoringResultEditRequest, scoringResult);
List<String> resultProp = scoringResultEditRequest.getResultProp();
scoringResult.setResultProp(JSONUtil.toJsonStr(resultProp));
// 数据校验
scoringResultService.validScoringResult(scoringResult, false);
User loginUser = userService.getLoginUser(request);
// 判断是否存在
long id = scoringResultEditRequest.getId();
ScoringResult oldScoringResult = scoringResultService.getById(id);
ThrowUtils.throwIf(oldScoringResult == null, ErrorCode.NOT_FOUND_ERROR);
// 仅本人或管理员可编辑
if (!oldScoringResult.getUserId().equals(loginUser.getId()) && !userService.isAdmin(loginUser)) {
throw new BusinessException(ErrorCode.NO_AUTH_ERROR);
}
// 操作数据库
boolean result = scoringResultService.updateById(scoringResult);
ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR);
return ResultUtils.success(true);
}
// endregion
}
================================================
FILE: yudada-backend/src/main/java/com/yupi/yudada/controller/UserAnswerController.java
================================================
package com.yupi.yudada.controller;
import cn.hutool.core.util.IdUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.yupi.yudada.annotation.AuthCheck;
import com.yupi.yudada.common.BaseResponse;
import com.yupi.yudada.common.DeleteRequest;
import com.yupi.yudada.common.ErrorCode;
import com.yupi.yudada.common.ResultUtils;
import com.yupi.yudada.constant.UserConstant;
import com.yupi.yudada.exception.BusinessException;
import com.yupi.yudada.exception.ThrowUtils;
import com.yupi.yudada.model.dto.userAnswer.UserAnswerAddRequest;
import com.yupi.yudada.model.dto.userAnswer.UserAnswerEditRequest;
import com.yupi.yudada.model.dto.userAnswer.UserAnswerQueryRequest;
import com.yupi.yudada.model.dto.userAnswer.UserAnswerUpdateRequest;
import com.yupi.yudada.model.entity.App;
import com.yupi.yudada.model.entity.User;
import com.yupi.yudada.model.entity.UserAnswer;
import com.yupi.yudada.model.enums.ReviewStatusEnum;
import com.yupi.yudada.model.vo.UserAnswerVO;
import com.yupi.yudada.scoring.ScoringStrategyExecutor;
import com.yupi.yudada.service.AppService;
import com.yupi.yudada.service.UserAnswerService;
import com.yupi.yudada.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.List;
/**
* 用户答案接口
*
* @author <a href="https://github.com/liyupi">程序员鱼皮</a>
* @from <a href="https://www.code-nav.cn">编程导航学习圈</a>
*/
@RestController
@RequestMapping("/userAnswer")
@Slf4j
public class UserAnswerController {
@Resource
private UserAnswerService userAnswerService;
@Resource
private AppService appService;
@Resource
private UserService userService;
@Resource
private ScoringStrategyExecutor scoringStrategyExecutor;
// region 增删改查
/**
* 创建用户答案
*
* @param userAnswerAddRequest
* @param request
* @return
*/
@PostMapping("/add")
public BaseResponse<Long> addUserAnswer(@RequestBody UserAnswerAddRequest userAnswerAddRequest, HttpServletRequest request) {
ThrowUtils.throwIf(userAnswerAddRequest == null, ErrorCode.PARAMS_ERROR);
// 在此处将实体类和 DTO 进行转换
UserAnswer userAnswer = new UserAnswer();
BeanUtils.copyProperties(userAnswerAddRequest, userAnswer);
List<String> choices = userAnswerAddRequest.getChoices();
userAnswer.setChoices(JSONUtil.toJsonStr(choices));
// 数据校验
userAnswerService.validUserAnswer(userAnswer, true);
// 判断 app 是否存在
Long appId = userAnswerAddRequest.getAppId();
App app = appService.getById(appId);
ThrowUtils.throwIf(app == null, ErrorCode.NOT_FOUND_ERROR);
if (!ReviewStatusEnum.PASS.equals(ReviewStatusEnum.getEnumByValue(app.getReviewStatus()))) {
throw new BusinessException(ErrorCode.NO_AUTH_ERROR, "应用未通过审核,无法答题");
}
// 填充默认值
User loginUser = userService.getLoginUser(request);
userAnswer.setUserId(loginUser.getId());
// 写入数据库
try {
boolean result = userAnswerService.save(userAnswer);
ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR);
} catch (DuplicateKeyException e) {
// ignore error
}
// 返回新写入的数据 id
long newUserAnswerId = userAnswer.getId();
// 调用评分模块
try {
UserAnswer userAnswerWithResult = scoringStrategyExecutor.doScore(choices, app);
userAnswerWithResult.setId(newUserAnswerId);
userAnswerWithResult.setAppId(null);
userAnswerService.updateById(userAnswerWithResult);
} catch (Exception e) {
e.printStackTrace();
throw new BusinessException(ErrorCode.OPERATION_ERROR, "评分错误");
}
return ResultUtils.success(newUserAnswerId);
}
/**
* 删除用户答案
*
* @param deleteRequest
* @param request
* @return
*/
@PostMapping("/delete")
public BaseResponse<Boolean> deleteUserAnswer(@RequestBody DeleteRequest deleteRequest, HttpServletRequest request) {
if (deleteRequest == null || deleteRequest.getId() <= 0) {
throw new BusinessException(ErrorCode.PARAMS_ERROR);
}
User user = userService.getLoginUser(request);
long id = deleteRequest.getId();
// 判断是否存在
UserAnswer oldUserAnswer = userAnswerService.getById(id);
ThrowUtils.throwIf(oldUserAnswer == null, ErrorCode.NOT_FOUND_ERROR);
// 仅本人或管理员可删除
if (!oldUserAnswer.getUserId().equals(user.getId()) && !userService.isAdmin(request)) {
throw new BusinessException(ErrorCode.NO_AUTH_ERROR);
}
// 操作数据库
boolean result = userAnswerService.removeById(id);
ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR);
return ResultUtils.success(true);
}
/**
* 更新用户答案(仅管理员可用)
*
* @param userAnswerUpdateRequest
* @return
*/
@PostMapping("/update")
@AuthCheck(mustRole = UserConstant.ADMIN_ROLE)
public BaseResponse<Boolean> updateUserAnswer(@RequestBody UserAnswerUpdateRequest userAnswerUpdateRequest) {
if (userAnswerUpdateRequest == null || userAnswerUpdateRequest.getId() <= 0) {
throw new BusinessException(ErrorCode.PARAMS_ERROR);
}
// 在此处将实体类和 DTO 进行转换
UserAnswer userAnswer = new UserAnswer();
BeanUtils.copyProperties(userAnswerUpdateRequest, userAnswer);
List<String> choices = userAnswerUpdateRequest.getChoices();
userAnswer.setChoices(JSONUtil.toJsonStr(choices));
// 数据校验
userAnswerService.validUserAnswer(userAnswer, false);
// 判断是否存在
long id = userAnswerUpdateRequest.getId();
UserAnswer oldUserAnswer = userAnswerService.getById(id);
ThrowUtils.throwIf(oldUserAnswer == null, ErrorCode.NOT_FOUND_ERROR);
// 操作数据库
boolean result = userAnswerService.updateById(userAnswer);
ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR);
return ResultUtils.success(true);
}
/**
* 根据 id 获取用户答案(封装类)
*
* @param id
* @return
*/
@GetMapping("/get/vo")
public BaseResponse<UserAnswerVO> getUserAnswerVOById(long id, HttpServletRequest request) {
ThrowUtils.throwIf(id <= 0, ErrorCode.PARAMS_ERROR);
// 查询数据库
UserAnswer userAnswer = userAnswerService.getById(id);
ThrowUtils.throwIf(userAnswer == null, ErrorCode.NOT_FOUND_ERROR);
// 获取封装类
return ResultUtils.success(userAnswerService.getUserAnswerVO(userAnswer, request));
}
/**
* 分页获取用户答案列表(仅管理员可用)
*
* @param userAnswerQueryRequest
* @return
*/
@PostMapping("/list/page")
@AuthCheck(mustRole = UserConstant.ADMIN_ROLE)
public BaseResponse<Page<UserAnswer>> listUserAnswerByPage(@RequestBody UserAnswerQueryRequest userAnswerQueryRequest) {
long current = userAnswerQueryRequest.getCurrent();
long size = userAnswerQueryRequest.getPageSize();
// 查询数据库
Page<UserAnswer> userAnswerPage = userAnswerService.page(new Page<>(current, size),
userAnswerService.getQueryWrapper(userAnswerQueryRequest));
return ResultUtils.success(userAnswerPage);
}
/**
* 分页获取用户答案列表(封装类)
*
* @param userAnswerQueryRequest
* @param request
* @return
*/
@PostMapping("/list/page/vo")
public BaseResponse<Page<UserAnswerVO>> listUserAnswerVOByPage(@RequestBody UserAnswerQueryRequest userAnswerQueryRequest,
HttpServletRequest request) {
long current = userAnswerQueryRequest.getCurrent();
long size = userAnswerQueryRequest.getPageSize();
// 限制爬虫
ThrowUtils.throwIf(size > 20, ErrorCode.PARAMS_ERROR);
// 查询数据库
Page<UserAnswer> userAnswerPage = userAnswerService.page(new Page<>(current, size),
userAnswerService.getQueryWrapper(userAnswerQueryRequest));
// 获取封装类
return ResultUtils.success(userAnswerService.getUserAnswerVOPage(userAnswerPage, request));
}
/**
* 分页获取当前登录用户创建的用户答案列表
*
* @param userAnswerQueryRequest
* @param request
* @return
*/
@PostMapping("/my/list/page/vo")
public BaseResponse<Page<UserAnswerVO>> listMyUserAnswerVOByPage(@RequestBody UserAnswerQueryRequest userAnswerQueryRequest,
HttpServletRequest request) {
ThrowUtils.throwIf(userAnswerQueryRequest == null, ErrorCode.PARAMS_ERROR);
// 补充查询条件,只查询当前登录用户的数据
User loginUser = userService.getLoginUser(request);
userAnswerQueryRequest.setUserId(loginUser.getId());
long current = userAnswerQueryRequest.getCurrent();
long size = userAnswerQueryRequest.getPageSize();
// 限制爬虫
ThrowUtils.throwIf(size > 20, ErrorCode.PARAMS_ERROR);
// 查询数据库
Page<UserAnswer> userAnswerPage = userAnswerService.page(new Page<>(current, size),
userAnswerService.getQueryWrapper(userAnswerQueryRequest));
// 获取封装类
return ResultUtils.success(userAnswerService.getUserAnswerVOPage(userAnswerPage, request));
}
/**
* 编辑用户答案(给用户使用)
*
* @param userAnswerEditRequest
* @param request
* @return
*/
@PostMapping("/edit")
public BaseResponse<Boolean> editUserAnswer(@RequestBody UserAnswerEditRequest userAnswerEditRequest, HttpServletRequest request) {
if (userAnswerEditRequest == null || userAnswerEditRequest.getId() <= 0) {
throw new BusinessException(ErrorCode.PARAMS_ERROR);
}
// 在此处将实体类和 DTO 进行转换
UserAnswer userAnswer = new UserAnswer();
BeanUtils.copyProperties(userAnswerEditRequest, userAnswer);
List<String> choices = userAnswerEditRequest.getChoices();
userAnswer.setChoices(JSONUtil.toJsonStr(choices));
// 数据校验
userAnswerService.validUserAnswer(userAnswer, false);
User loginUser = userService.getLoginUser(request);
// 判断是否存在
long id = userAnswerEditRequest.getId();
UserAnswer oldUserAnswer = userAnswerService.getById(id);
ThrowUtils.throwIf(oldUserAnswer == null, ErrorCode.NOT_FOUND_ERROR);
// 仅本人或管理员可编辑
if (!oldUserAnswer.getUserId().equals(loginUser.getId()) && !userService.isAdmin(loginUser)) {
throw new BusinessException(ErrorCode.NO_AUTH_ERROR);
}
// 操作数据库
boolean result = userAnswerService.updateById(userAnswer);
ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR);
return ResultUtils.success(true);
}
// endregion
@GetMapping("/generate/id")
public BaseResponse<Long> generateUserAnswerId() {
return ResultUtils.success(IdUtil.getSnowflakeNextId());
}
}
================================================
FILE: yudada-backend/src/main/java/com/yupi/yudada/controller/UserController.java
================================================
package com.yupi.yudada.controller;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.yupi.yudada.annotation.AuthCheck;
import com.yupi.yudada.common.BaseResponse;
import com.yupi.yudada.common.DeleteRequest;
import com.yupi.yudada.common.ErrorCode;
import com.yupi.yudada.common.ResultUtils;
import com.yupi.yudada.constant.UserConstant;
import com.yupi.yudada.exception.BusinessException;
import com.yupi.yudada.exception.ThrowUtils;
import com.yupi.yudada.model.dto.user.UserAddRequest;
import com.yupi.yudada.model.dto.user.UserLoginRequest;
import com.yupi.yudada.model.dto.user.UserQueryRequest;
import com.yupi.yudada.model.dto.user.UserRegisterRequest;
import com.yupi.yudada.model.dto.user.UserUpdateMyRequest;
import com.yupi.yudada.model.dto.user.UserUpdateRequest;
import com.yupi.yudada.model.entity.User;
import com.yupi.yudada.model.vo.LoginUserVO;
import com.yupi.yudada.model.vo.UserVO;
import com.yupi.yudada.service.UserService;
import java.util.List;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.util.DigestUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import static com.yupi.yudada.service.impl.UserServiceImpl.SALT;
/**
* 用户接口
*
* @author <a href="https://github.com/liyupi">程序员鱼皮</a>
* @from <a href="https://yupi.icu">编程导航知识星球</a>
*/
@RestController
@RequestMapping("/user")
@Slf4j
public class UserController {
@Resource
private UserService userService;
// region 登录相关
/**
* 用户注册
*
* @param userRegisterRequest
* @return
*/
@PostMapping("/register")
public BaseResponse<Long> userRegister(@RequestBody UserRegisterRequest userRegisterRequest) {
if (userRegisterRequest == null) {
throw new BusinessException(ErrorCode.PARAMS_ERROR);
}
String userAccount = userRegisterRequest.getUserAccount();
String userPassword = userRegisterRequest.getUserPassword();
String checkPassword = userRegisterRequest.getCheckPassword();
if (StringUtils.isAnyBlank(userAccount, userPassword, checkPassword)) {
return null;
}
long result = userService.userRegister(userAccount, userPassword, checkPassword);
return ResultUtils.success(result);
}
/**
* 用户登录
*
* @param userLoginRequest
* @param request
* @return
*/
@PostMapping("/login")
public BaseResponse<LoginUserVO> userLogin(@RequestBody UserLoginRequest userLoginRequest, HttpServletRequest request) {
if (userLoginRequest == null) {
throw new BusinessException(ErrorCode.PARAMS_ERROR);
}
String userAccount = userLoginRequest.getUserAccount();
String userPassword = userLoginRequest.getUserPassword();
if (StringUtils.isAnyBlank(userAccount, userPassword)) {
throw new BusinessException(ErrorCode.PARAMS_ERROR);
}
LoginUserVO loginUserVO = userService.userLogin(userAccount, userPassword, request);
return ResultUtils.success(loginUserVO);
}
/**
* 用户注销
*
* @param request
* @return
*/
@PostMapping("/logout")
public BaseResponse<Boolean> userLogout(HttpServletRequest request) {
if (request == null) {
throw new BusinessException(ErrorCode.PARAMS_ERROR);
}
boolean result = userService.userLogout(request);
return ResultUtils.success(result);
}
/**
* 获取当前登录用户
*
* @param request
* @return
*/
@GetMapping("/get/login")
public BaseResponse<LoginUserVO> getLoginUser(HttpServletRequest request) {
User user = userService.getLoginUser(request);
return ResultUtils.success(userService.getLoginUserVO(user));
}
// endregion
// region 增删改查
/**
* 创建用户
*
* @param userAddRequest
* @param request
* @return
*/
@PostMapping("/add")
@AuthCheck(mustRole = UserConstant.ADMIN_ROLE)
public BaseResponse<Long> addUser(@RequestBody UserAddRequest userAddRequest, HttpServletRequest request) {
if (userAddRequest == null) {
throw new BusinessException(ErrorCode.PARAMS_ERROR);
}
User user = new User();
BeanUtils.copyProperties(userAddRequest, user);
// 默认密码 12345678
String defaultPassword = "12345678";
String encryptPassword = DigestUtils.md5DigestAsHex((SALT + defaultPassword).getBytes());
user.setUserPassword(encryptPassword);
boolean result = userService.save(user);
ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR);
return ResultUtils.success(user.getId());
}
/**
* 删除用户
*
* @param deleteRequest
* @param request
* @return
*/
@PostMapping("/delete")
@AuthCheck(mustRole = UserConstant.ADMIN_ROLE)
public BaseResponse<Boolean> deleteUser(@RequestBody DeleteRequest deleteRequest, HttpServletRequest request) {
if (deleteRequest == null || deleteRequest.getId() <= 0) {
throw new BusinessException(ErrorCode.PARAMS_ERROR);
}
boolean b = userService.removeById(deleteRequest.getId());
return ResultUtils.success(b);
}
/**
* 更新用户
*
* @param userUpdateRequest
* @param request
* @return
*/
@PostMapping("/update")
@AuthCheck(mustRole = UserConstant.ADMIN_ROLE)
public BaseResponse<Boolean> updateUser(@RequestBody UserUpdateRequest userUpdateRequest,
HttpServletRequest request) {
if (userUpdateRequest == null || userUpdateRequest.getId() == null) {
throw new BusinessException(ErrorCode.PARAMS_ERROR);
}
User user = new User();
BeanUtils.copyProperties(userUpdateRequest, user);
boolean result = userService.updateById(user);
ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR);
return ResultUtils.success(true);
}
/**
* 根据 id 获取用户(仅管理员)
*
* @param id
* @param request
* @return
*/
@GetMapping("/get")
@AuthCheck(mustRole = UserConstant.ADMIN_ROLE)
public BaseResponse<User> getUserById(long id, HttpServletRequest request) {
if (id <= 0) {
throw new BusinessException(ErrorCode.PARAMS_ERROR);
}
User user = userService.getById(id);
ThrowUtils.throwIf(user == null, ErrorCode.NOT_FOUND_ERROR);
return ResultUtils.success(user);
}
/**
* 根据 id 获取包装类
*
* @param id
* @param request
* @return
*/
@GetMapping("/get/vo")
public BaseResponse<UserVO> getUserVOById(long id, HttpServletRequest request) {
BaseResponse<User> response = getUserById(id, request);
User user = response.getData();
return ResultUtils.success(userService.getUserVO(user));
}
/**
* 分页获取用户列表(仅管理员)
*
* @param userQueryRequest
* @param request
* @return
*/
@PostMapping("/list/page")
@AuthCheck(mustRole = UserConstant.ADMIN_ROLE)
public BaseResponse<Page<User>> listUserByPage(@RequestBody UserQueryRequest userQueryRequest,
HttpServletRequest request) {
long current = userQueryRequest.getCurrent();
long size = userQueryRequest.getPageSize();
Page<User> userPage = userService.page(new Page<>(current, size),
userService.getQueryWrapper(userQueryRequest));
return ResultUtils.success(userPage);
}
/**
* 分页获取用户封装列表
*
* @param userQueryRequest
* @param request
* @return
*/
@PostMapping("/list/page/vo")
public BaseResponse<Page<UserVO>> listUserVOByPage(@RequestBody UserQueryRequest userQueryRequest,
HttpServletRequest request) {
if (userQueryRequest == null) {
throw new BusinessException(ErrorCode.PARAMS_ERROR);
}
long current = userQueryRequest.getCurrent();
long size = userQueryRequest.getPageSize();
// 限制爬虫
ThrowUtils.throwIf(size > 20, ErrorCode.PARAMS_ERROR);
Page<User> userPage = userService.page(new Page<>(current, size),
userService.getQueryWrapper(userQueryRequest));
Page<UserVO> userVOPage = new Page<>(current, size, userPage.getTotal());
List<UserVO> userVO = userService.getUserVO(userPage.getRecords());
userVOPage.setRecords(userVO);
return ResultUtils.success(userVOPage);
}
// endregion
/**
* 更新个人信息
*
* @param userUpdateMyRequest
* @param request
* @return
*/
@PostMapping("/update/my")
public BaseResponse<Boolean> updateMyUser(@RequestBody UserUpdateMyRequest userUpdateMyRequest,
HttpServletRequest request) {
if (userUpdateMyRequest == null) {
throw new BusinessException(ErrorCode.PARAMS_ERROR);
}
User loginUser = userService.getLoginUser(request);
User user = new User();
BeanUtils.copyProperties(userUpdateMyRequest, user);
user.setId(loginUser.getId());
boolean result = userService.updateById(user);
ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR);
return ResultUtils.success(true);
}
}
================================================
FILE: yudada-backend/src/main/java/com/yupi/yudada/exception/BusinessException.java
================================================
package com.yupi.yudada.exception;
import com.yupi.yudada.common.ErrorCode;
/**
* 自定义异常类
*
* @author <a href="https://github.com/liyupi">程序员鱼皮</a>
* @from <a href="https://yupi.icu">编程导航知识星球</a>
*/
public class BusinessException extends RuntimeException {
/**
* 错误码
*/
private final int code;
public BusinessException(int code, String message) {
super(message);
this.code = code;
}
public BusinessException(ErrorCode errorCode) {
super(errorCode.getMessage());
this.code = errorCode.getCode();
}
public BusinessException(ErrorCode errorCode, String message) {
super(message);
this.code = errorCode.getCode();
}
public int getCode() {
return code;
}
}
================================================
FILE: yudada-backend/src/main/java/com/yupi/yudada/exception/GlobalExceptionHandler.java
================================================
package com.yupi.yudada.exception;
import com.yupi.yudada.common.BaseResponse;
import com.yupi.yudada.common.ErrorCode;
import com.yupi.yudada.common.ResultUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
/**
* 全局异常处理器
*
* @author <a href="https://github.com/liyupi">程序员鱼皮</a>
* @from <a href="https://yupi.icu">编程导航知识星球</a>
*/
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
@ExceptionHandler(BusinessException.class)
public BaseResponse<?> businessExceptionHandler(BusinessException e) {
log.error("BusinessException", e);
return ResultUtils.error(e.getCode(), e.getMessage());
}
@ExceptionHandler(RuntimeException.class)
public BaseResponse<?> runtimeExceptionHandler(RuntimeException e) {
log.error("RuntimeException", e);
return ResultUtils.error(ErrorCode.SYSTEM_ERROR, "系统错误");
}
}
================================================
FILE: yudada-backend/src/main/java/com/yupi/yudada/exception/ThrowUtils.java
================================================
package com.yupi.yudada.exception;
import com.yupi.yudada.common.ErrorCode;
/**
* 抛异常工具类
*
* @author <a href="https://github.com/liyupi">程序员鱼皮</a>
* @from <a href="https://yupi.icu">编程导航知识星球</a>
*/
public class ThrowUtils {
/**
* 条件成立则抛异常
*
* @param condition
* @param runtimeException
*/
public static void throwIf(boolean condition, RuntimeException runtimeException) {
if (condition) {
throw runtimeException;
}
}
/**
* 条件成立则抛异常
*
* @param condition
* @param errorCode
*/
public static void throwIf(boolean condition, ErrorCode errorCode) {
throwIf(condition, new BusinessException(errorCode));
}
/**
* 条件成立则抛异常
*
* @param condition
* @param errorCode
* @param message
*/
public static void throwIf(boolean condition, ErrorCode errorCode, String message) {
throwIf(condition, new BusinessException(errorCode, message));
}
}
================================================
FILE: yudada-backend/src/main/java/com/yupi/yudada/generate/CodeGenerator.java
================================================
package com.yupi.yudada.generate;
i
gitextract_ta69pkyf/
├── .gitignore
├── README.md
├── mbti-test-mini/
│ ├── .editorconfig
│ ├── .eslintrc
│ ├── babel.config.js
│ ├── config/
│ │ ├── dev.ts
│ │ ├── index.ts
│ │ └── prod.ts
│ ├── package.json
│ ├── project.config.json
│ ├── project.private.config.json
│ ├── project.tt.json
│ ├── src/
│ │ ├── app.config.ts
│ │ ├── app.scss
│ │ ├── app.ts
│ │ ├── components/
│ │ │ └── GlobalFooter/
│ │ │ ├── index.scss
│ │ │ └── index.tsx
│ │ ├── data/
│ │ │ ├── question_results.json
│ │ │ └── questions.json
│ │ ├── index.html
│ │ ├── pages/
│ │ │ ├── doQuestion/
│ │ │ │ ├── index.config.ts
│ │ │ │ ├── index.scss
│ │ │ │ └── index.tsx
│ │ │ ├── index/
│ │ │ │ ├── index.config.ts
│ │ │ │ ├── index.scss
│ │ │ │ └── index.tsx
│ │ │ └── result/
│ │ │ ├── index.config.ts
│ │ │ ├── index.scss
│ │ │ └── index.tsx
│ │ └── utils/
│ │ └── bizUtils.ts
│ ├── tsconfig.json
│ └── types/
│ ├── custom.d.ts
│ └── global.d.ts
├── yudada-backend/
│ ├── .gitignore
│ ├── .mvn/
│ │ └── wrapper/
│ │ └── maven-wrapper.properties
│ ├── Dockerfile
│ ├── README.md
│ ├── mvnw
│ ├── mvnw.cmd
│ ├── pom.xml
│ ├── sql/
│ │ ├── create_table.sql
│ │ ├── init_data.sql
│ │ └── post_es_mapping.json
│ └── src/
│ ├── main/
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── yupi/
│ │ │ └── yudada/
│ │ │ ├── MainApplication.java
│ │ │ ├── annotation/
│ │ │ │ └── AuthCheck.java
│ │ │ ├── aop/
│ │ │ │ ├── AuthInterceptor.java
│ │ │ │ └── LogInterceptor.java
│ │ │ ├── common/
│ │ │ │ ├── BaseResponse.java
│ │ │ │ ├── DeleteRequest.java
│ │ │ │ ├── ErrorCode.java
│ │ │ │ ├── PageRequest.java
│ │ │ │ ├── ResultUtils.java
│ │ │ │ └── ReviewRequest.java
│ │ │ ├── config/
│ │ │ │ ├── AiConfig.java
│ │ │ │ ├── CorsConfig.java
│ │ │ │ ├── CosClientConfig.java
│ │ │ │ ├── JsonConfig.java
│ │ │ │ ├── MyBatisPlusConfig.java
│ │ │ │ ├── RedissonConfig.java
│ │ │ │ └── VipSchedulerConfig.java
│ │ │ ├── constant/
│ │ │ │ ├── CommonConstant.java
│ │ │ │ ├── FileConstant.java
│ │ │ │ └── UserConstant.java
│ │ │ ├── controller/
│ │ │ │ ├── AppController.java
│ │ │ │ ├── AppStatisticController.java
│ │ │ │ ├── FileController.java
│ │ │ │ ├── PostController.java
│ │ │ │ ├── PostFavourController.java
│ │ │ │ ├── PostThumbController.java
│ │ │ │ ├── QuestionController.java
│ │ │ │ ├── ScoringResultController.java
│ │ │ │ ├── UserAnswerController.java
│ │ │ │ └── UserController.java
│ │ │ ├── exception/
│ │ │ │ ├── BusinessException.java
│ │ │ │ ├── GlobalExceptionHandler.java
│ │ │ │ └── ThrowUtils.java
│ │ │ ├── generate/
│ │ │ │ └── CodeGenerator.java
│ │ │ ├── manager/
│ │ │ │ ├── AiManager.java
│ │ │ │ └── CosManager.java
│ │ │ ├── mapper/
│ │ │ │ ├── AppMapper.java
│ │ │ │ ├── PostFavourMapper.java
│ │ │ │ ├── PostMapper.java
│ │ │ │ ├── PostThumbMapper.java
│ │ │ │ ├── QuestionMapper.java
│ │ │ │ ├── ScoringResultMapper.java
│ │ │ │ ├── UserAnswerMapper.java
│ │ │ │ └── UserMapper.java
│ │ │ ├── model/
│ │ │ │ ├── dto/
│ │ │ │ │ ├── app/
│ │ │ │ │ │ ├── AppAddRequest.java
│ │ │ │ │ │ ├── AppEditRequest.java
│ │ │ │ │ │ ├── AppQueryRequest.java
│ │ │ │ │ │ └── AppUpdateRequest.java
│ │ │ │ │ ├── file/
│ │ │ │ │ │ └── UploadFileRequest.java
│ │ │ │ │ ├── post/
│ │ │ │ │ │ ├── PostAddRequest.java
│ │ │ │ │ │ ├── PostEditRequest.java
│ │ │ │ │ │ ├── PostQueryRequest.java
│ │ │ │ │ │ └── PostUpdateRequest.java
│ │ │ │ │ ├── postfavour/
│ │ │ │ │ │ ├── PostFavourAddRequest.java
│ │ │ │ │ │ └── PostFavourQueryRequest.java
│ │ │ │ │ ├── postthumb/
│ │ │ │ │ │ └── PostThumbAddRequest.java
│ │ │ │ │ ├── question/
│ │ │ │ │ │ ├── AiGenerateQuestionRequest.java
│ │ │ │ │ │ ├── QuestionAddRequest.java
│ │ │ │ │ │ ├── QuestionAnswerDTO.java
│ │ │ │ │ │ ├── QuestionContentDTO.java
│ │ │ │ │ │ ├── QuestionEditRequest.java
│ │ │ │ │ │ ├── QuestionQueryRequest.java
│ │ │ │ │ │ └── QuestionUpdateRequest.java
│ │ │ │ │ ├── scoringResult/
│ │ │ │ │ │ ├── ScoringResultAddRequest.java
│ │ │ │ │ │ ├── ScoringResultEditRequest.java
│ │ │ │ │ │ ├── ScoringResultQueryRequest.java
│ │ │ │ │ │ └── ScoringResultUpdateRequest.java
│ │ │ │ │ ├── statistic/
│ │ │ │ │ │ ├── AppAnswerCountDTO.java
│ │ │ │ │ │ └── AppAnswerResultCountDTO.java
│ │ │ │ │ ├── user/
│ │ │ │ │ │ ├── UserAddRequest.java
│ │ │ │ │ │ ├── UserLoginRequest.java
│ │ │ │ │ │ ├── UserQueryRequest.java
│ │ │ │ │ │ ├── UserRegisterRequest.java
│ │ │ │ │ │ ├── UserUpdateMyRequest.java
│ │ │ │ │ │ └── UserUpdateRequest.java
│ │ │ │ │ └── userAnswer/
│ │ │ │ │ ├── UserAnswerAddRequest.java
│ │ │ │ │ ├── UserAnswerEditRequest.java
│ │ │ │ │ ├── UserAnswerQueryRequest.java
│ │ │ │ │ └── UserAnswerUpdateRequest.java
│ │ │ │ ├── entity/
│ │ │ │ │ ├── App.java
│ │ │ │ │ ├── Post.java
│ │ │ │ │ ├── PostFavour.java
│ │ │ │ │ ├── PostThumb.java
│ │ │ │ │ ├── Question.java
│ │ │ │ │ ├── ScoringResult.java
│ │ │ │ │ ├── User.java
│ │ │ │ │ └── UserAnswer.java
│ │ │ │ ├── enums/
│ │ │ │ │ ├── AppScoringStrategyEnum.java
│ │ │ │ │ ├── AppTypeEnum.java
│ │ │ │ │ ├── FileUploadBizEnum.java
│ │ │ │ │ ├── ReviewStatusEnum.java
│ │ │ │ │ └── UserRoleEnum.java
│ │ │ │ └── vo/
│ │ │ │ ├── AppVO.java
│ │ │ │ ├── LoginUserVO.java
│ │ │ │ ├── PostVO.java
│ │ │ │ ├── QuestionVO.java
│ │ │ │ ├── ScoringResultVO.java
│ │ │ │ ├── UserAnswerVO.java
│ │ │ │ └── UserVO.java
│ │ │ ├── scoring/
│ │ │ │ ├── AiTestScoringStrategy.java
│ │ │ │ ├── CustomScoreScoringStrategy.java
│ │ │ │ ├── CustomTestScoringStrategy.java
│ │ │ │ ├── ScoringStrategy.java
│ │ │ │ ├── ScoringStrategyConfig.java
│ │ │ │ ├── ScoringStrategyContext.java
│ │ │ │ └── ScoringStrategyExecutor.java
│ │ │ ├── service/
│ │ │ │ ├── AppService.java
│ │ │ │ ├── PostFavourService.java
│ │ │ │ ├── PostService.java
│ │ │ │ ├── PostThumbService.java
│ │ │ │ ├── QuestionService.java
│ │ │ │ ├── ScoringResultService.java
│ │ │ │ ├── UserAnswerService.java
│ │ │ │ ├── UserService.java
│ │ │ │ └── impl/
│ │ │ │ ├── AppServiceImpl.java
│ │ │ │ ├── PostFavourServiceImpl.java
│ │ │ │ ├── PostServiceImpl.java
│ │ │ │ ├── PostThumbServiceImpl.java
│ │ │ │ ├── QuestionServiceImpl.java
│ │ │ │ ├── ScoringResultServiceImpl.java
│ │ │ │ ├── UserAnswerServiceImpl.java
│ │ │ │ └── UserServiceImpl.java
│ │ │ └── utils/
│ │ │ ├── NetUtils.java
│ │ │ ├── SpringContextUtils.java
│ │ │ └── SqlUtils.java
│ │ └── resources/
│ │ ├── META-INF/
│ │ │ └── additional-spring-configuration-metadata.json
│ │ ├── application-prod.yml
│ │ ├── application-test.yml
│ │ ├── application.yml
│ │ ├── banner.txt
│ │ ├── mapper/
│ │ │ ├── AppMapper.xml
│ │ │ ├── PostFavourMapper.xml
│ │ │ ├── PostMapper.xml
│ │ │ ├── PostThumbMapper.xml
│ │ │ ├── QuestionMapper.xml
│ │ │ ├── ScoringResultMapper.xml
│ │ │ ├── UserAnswerMapper.xml
│ │ │ └── UserMapper.xml
│ │ ├── templates/
│ │ │ ├── TemplateController.java.ftl
│ │ │ ├── TemplateService.java.ftl
│ │ │ ├── TemplateServiceImpl.java.ftl
│ │ │ └── model/
│ │ │ ├── TemplateAddRequest.java.ftl
│ │ │ ├── TemplateEditRequest.java.ftl
│ │ │ ├── TemplateQueryRequest.java.ftl
│ │ │ ├── TemplateUpdateRequest.java.ftl
│ │ │ └── TemplateVO.java.ftl
│ │ └── test_excel.xlsx
│ └── test/
│ └── java/
│ └── com/
│ └── yupi/
│ └── yudada/
│ ├── MainApplicationTests.java
│ ├── QuestionControllerTest.java
│ ├── RxJavaTest.java
│ ├── UserAnswerShardingTest.java
│ ├── ZhiPuAiTest.java
│ ├── manager/
│ │ └── CosManagerTest.java
│ ├── mapper/
│ │ ├── PostFavourMapperTest.java
│ │ └── PostMapperTest.java
│ ├── service/
│ │ ├── PostFavourServiceTest.java
│ │ ├── PostThumbServiceTest.java
│ │ └── UserServiceTest.java
│ └── utils/
│ └── EasyExcelTest.java
└── yudada-frontend/
├── .browserslistrc
├── .eslintrc.js
├── .gitignore
├── Dockerfile
├── README.md
├── babel.config.js
├── docker/
│ └── nginx.conf
├── openapi.config.ts
├── package.json
├── public/
│ └── index.html
├── src/
│ ├── App.vue
│ ├── access/
│ │ ├── accessEnum.ts
│ │ ├── checkAccess.ts
│ │ └── index.ts
│ ├── api/
│ │ ├── appController.ts
│ │ ├── appStatisticController.ts
│ │ ├── fileController.ts
│ │ ├── index.ts
│ │ ├── postController.ts
│ │ ├── postFavourController.ts
│ │ ├── postThumbController.ts
│ │ ├── questionController.ts
│ │ ├── scoringResultController.ts
│ │ ├── typings.d.ts
│ │ ├── userAnswerController.ts
│ │ └── userController.ts
│ ├── components/
│ │ ├── AppCard.vue
│ │ ├── GlobalHeader.vue
│ │ ├── MdEditor.vue
│ │ ├── MdViewer.vue
│ │ ├── PictureUploader.vue
│ │ └── ShareModal.vue
│ ├── constant/
│ │ └── app.ts
│ ├── layouts/
│ │ ├── BasicLayout.vue
│ │ └── UserLayout.vue
│ ├── main.ts
│ ├── request.ts
│ ├── router/
│ │ ├── index.ts
│ │ └── routes.ts
│ ├── shims-vue.d.ts
│ ├── store/
│ │ └── userStore.ts
│ └── views/
│ ├── HomePage.vue
│ ├── NoAuthPage.vue
│ ├── add/
│ │ ├── AddAppPage.vue
│ │ ├── AddQuestionPage.vue
│ │ ├── AddScoringResultPage.vue
│ │ └── components/
│ │ ├── AiGenerateQuestionDrawer.vue
│ │ └── ScoringResultTable.vue
│ ├── admin/
│ │ ├── AdminAppPage.vue
│ │ ├── AdminQuestionPage.vue
│ │ ├── AdminScoringResultPage.vue
│ │ ├── AdminUserAnswerPage.vue
│ │ └── AdminUserPage.vue
│ ├── answer/
│ │ ├── AnswerResultPage.vue
│ │ ├── DoAnswerPage.vue
│ │ └── MyAnswerPage.vue
│ ├── app/
│ │ └── AppDetailPage.vue
│ ├── statistic/
│ │ └── AppStatisticPage.vue
│ └── user/
│ ├── UserLoginPage.vue
│ └── UserRegisterPage.vue
├── tsconfig.json
└── vue.config.js
SYMBOL INDEX (568 symbols across 154 files)
FILE: mbti-test-mini/src/app.ts
function App (line 6) | function App({ children }: PropsWithChildren) {
FILE: mbti-test-mini/src/utils/bizUtils.ts
function getBestQuestionResult (line 8) | function getBestQuestionResult(answerList, questions, question_results) {
FILE: mbti-test-mini/types/custom.d.ts
type QuestionOption (line 1) | interface QuestionOption<T extends string = string> {
type Question (line 7) | interface Question<T extends string = string> {
FILE: mbti-test-mini/types/global.d.ts
type ProcessEnv (line 15) | interface ProcessEnv {
FILE: yudada-backend/sql/create_table.sql
type user (line 12) | create table if not exists user
type app (line 30) | create table if not exists app
type question (line 50) | create table if not exists question
type scoring_result (line 63) | create table if not exists scoring_result
type user_answer (line 80) | create table if not exists user_answer
type user_answer_0 (line 101) | create table if not exists user_answer_0
type user_answer_1 (line 122) | create table if not exists user_answer_1
FILE: yudada-backend/src/main/java/com/yupi/yudada/MainApplication.java
class MainApplication (line 17) | @SpringBootApplication
method main (line 23) | public static void main(String[] args) {
FILE: yudada-backend/src/main/java/com/yupi/yudada/aop/AuthInterceptor.java
class AuthInterceptor (line 26) | @Aspect
method doInterceptor (line 40) | @Around("@annotation(authCheck)")
FILE: yudada-backend/src/main/java/com/yupi/yudada/aop/LogInterceptor.java
class LogInterceptor (line 22) | @Aspect
method doInterceptor (line 30) | @Around("execution(* com.yupi.yudada.controller.*.*(..))")
FILE: yudada-backend/src/main/java/com/yupi/yudada/common/BaseResponse.java
class BaseResponse (line 13) | @Data
method BaseResponse (line 22) | public BaseResponse(int code, T data, String message) {
method BaseResponse (line 28) | public BaseResponse(int code, T data) {
method BaseResponse (line 32) | public BaseResponse(ErrorCode errorCode) {
FILE: yudada-backend/src/main/java/com/yupi/yudada/common/DeleteRequest.java
class DeleteRequest (line 12) | @Data
FILE: yudada-backend/src/main/java/com/yupi/yudada/common/ErrorCode.java
type ErrorCode (line 9) | public enum ErrorCode {
method ErrorCode (line 30) | ErrorCode(int code, String message) {
method getCode (line 35) | public int getCode() {
method getMessage (line 39) | public String getMessage() {
FILE: yudada-backend/src/main/java/com/yupi/yudada/common/PageRequest.java
class PageRequest (line 12) | @Data
FILE: yudada-backend/src/main/java/com/yupi/yudada/common/ResultUtils.java
class ResultUtils (line 9) | public class ResultUtils {
method success (line 18) | public static <T> BaseResponse<T> success(T data) {
method error (line 28) | public static BaseResponse error(ErrorCode errorCode) {
method error (line 39) | public static BaseResponse error(int code, String message) {
method error (line 49) | public static BaseResponse error(ErrorCode errorCode, String message) {
FILE: yudada-backend/src/main/java/com/yupi/yudada/common/ReviewRequest.java
class ReviewRequest (line 10) | @Data
FILE: yudada-backend/src/main/java/com/yupi/yudada/config/AiConfig.java
class AiConfig (line 9) | @Configuration
method getClientV4 (line 20) | @Bean
FILE: yudada-backend/src/main/java/com/yupi/yudada/config/CorsConfig.java
class CorsConfig (line 13) | @Configuration
method addCorsMappings (line 16) | @Override
FILE: yudada-backend/src/main/java/com/yupi/yudada/config/CosClientConfig.java
class CosClientConfig (line 19) | @Configuration
method cosClient (line 44) | @Bean
FILE: yudada-backend/src/main/java/com/yupi/yudada/config/JsonConfig.java
class JsonConfig (line 16) | @JsonComponent
method jacksonObjectMapper (line 22) | @Bean
FILE: yudada-backend/src/main/java/com/yupi/yudada/config/MyBatisPlusConfig.java
class MyBatisPlusConfig (line 15) | @Configuration
method mybatisPlusInterceptor (line 24) | @Bean
FILE: yudada-backend/src/main/java/com/yupi/yudada/config/RedissonConfig.java
class RedissonConfig (line 12) | @Configuration
method redissonClient (line 26) | @Bean
FILE: yudada-backend/src/main/java/com/yupi/yudada/config/VipSchedulerConfig.java
class VipSchedulerConfig (line 15) | @Configuration
method vipScheduler (line 19) | @Bean
FILE: yudada-backend/src/main/java/com/yupi/yudada/constant/CommonConstant.java
type CommonConstant (line 9) | public interface CommonConstant {
FILE: yudada-backend/src/main/java/com/yupi/yudada/constant/FileConstant.java
type FileConstant (line 9) | public interface FileConstant {
FILE: yudada-backend/src/main/java/com/yupi/yudada/constant/UserConstant.java
type UserConstant (line 9) | public interface UserConstant {
FILE: yudada-backend/src/main/java/com/yupi/yudada/controller/AppController.java
class AppController (line 33) | @RestController
method addApp (line 53) | @PostMapping("/add")
method deleteApp (line 80) | @PostMapping("/delete")
method updateApp (line 106) | @PostMapping("/update")
method getAppVOById (line 133) | @GetMapping("/get/vo")
method listAppByPage (line 149) | @PostMapping("/list/page")
method listAppVOByPage (line 167) | @PostMapping("/list/page/vo")
method listMyAppVOByPage (line 190) | @PostMapping("/my/list/page/vo")
method editApp (line 215) | @PostMapping("/edit")
method doAppReview (line 251) | @PostMapping("/review")
FILE: yudada-backend/src/main/java/com/yupi/yudada/controller/AppStatisticController.java
class AppStatisticController (line 35) | @RestController
method getAppAnswerCount (line 48) | @GetMapping("/answer_count")
method getAppAnswerResultCount (line 59) | @GetMapping("/answer_result_count")
FILE: yudada-backend/src/main/java/com/yupi/yudada/controller/FileController.java
class FileController (line 32) | @RestController
method uploadFile (line 51) | @PostMapping("/upload")
method validFile (line 93) | private void validFile(MultipartFile multipartFile, FileUploadBizEnum ...
FILE: yudada-backend/src/main/java/com/yupi/yudada/controller/PostController.java
class PostController (line 39) | @RestController
method addPost (line 59) | @PostMapping("/add")
method deletePost (line 88) | @PostMapping("/delete")
method updatePost (line 112) | @PostMapping("/update")
method getPostVOById (line 140) | @GetMapping("/get/vo")
method listPostByPage (line 158) | @PostMapping("/list/page")
method listPostVOByPage (line 175) | @PostMapping("/list/page/vo")
method listMyPostVOByPage (line 194) | @PostMapping("/my/list/page/vo")
method editPost (line 220) | @PostMapping("/edit")
FILE: yudada-backend/src/main/java/com/yupi/yudada/controller/PostFavourController.java
class PostFavourController (line 32) | @RestController
method doPostFavour (line 53) | @PostMapping("/")
method listMyFavourPostByPage (line 72) | @PostMapping("/my/list/page")
method listFavourPostByPage (line 94) | @PostMapping("/list/page")
FILE: yudada-backend/src/main/java/com/yupi/yudada/controller/PostThumbController.java
class PostThumbController (line 25) | @RestController
method doThumb (line 43) | @PostMapping("/")
FILE: yudada-backend/src/main/java/com/yupi/yudada/controller/QuestionController.java
class QuestionController (line 46) | @RestController
method addQuestion (line 75) | @PostMapping("/add")
method deleteQuestion (line 103) | @PostMapping("/delete")
method updateQuestion (line 129) | @PostMapping("/update")
method getQuestionVOById (line 158) | @GetMapping("/get/vo")
method listQuestionByPage (line 174) | @PostMapping("/list/page")
method listQuestionVOByPage (line 192) | @PostMapping("/list/page/vo")
method listMyQuestionVOByPage (line 213) | @PostMapping("/my/list/page/vo")
method editQuestion (line 238) | @PostMapping("/edit")
method getGenerateQuestionUserMessage (line 295) | private String getGenerateQuestionUserMessage(App app, int questionNum...
method aiGenerateQuestion (line 305) | @PostMapping("/ai_generate")
method aiGenerateQuestionSSE (line 328) | @GetMapping("/ai_generate/sse")
method aiGenerateQuestionSSETest (line 393) | @Deprecated
FILE: yudada-backend/src/main/java/com/yupi/yudada/controller/ScoringResultController.java
class ScoringResultController (line 36) | @RestController
method addScoringResult (line 56) | @PostMapping("/add")
method deleteScoringResult (line 84) | @PostMapping("/delete")
method updateScoringResult (line 110) | @PostMapping("/update")
method getScoringResultVOById (line 139) | @GetMapping("/get/vo")
method listScoringResultByPage (line 155) | @PostMapping("/list/page")
method listScoringResultVOByPage (line 173) | @PostMapping("/list/page/vo")
method listMyScoringResultVOByPage (line 194) | @PostMapping("/my/list/page/vo")
method editScoringResult (line 219) | @PostMapping("/edit")
FILE: yudada-backend/src/main/java/com/yupi/yudada/controller/UserAnswerController.java
class UserAnswerController (line 42) | @RestController
method addUserAnswer (line 68) | @PostMapping("/add")
method deleteUserAnswer (line 117) | @PostMapping("/delete")
method updateUserAnswer (line 143) | @PostMapping("/update")
method getUserAnswerVOById (line 172) | @GetMapping("/get/vo")
method listUserAnswerByPage (line 188) | @PostMapping("/list/page")
method listUserAnswerVOByPage (line 206) | @PostMapping("/list/page/vo")
method listMyUserAnswerVOByPage (line 227) | @PostMapping("/my/list/page/vo")
method editUserAnswer (line 252) | @PostMapping("/edit")
method generateUserAnswerId (line 280) | @GetMapping("/generate/id")
FILE: yudada-backend/src/main/java/com/yupi/yudada/controller/UserController.java
class UserController (line 47) | @RestController
method userRegister (line 63) | @PostMapping("/register")
method userLogin (line 85) | @PostMapping("/login")
method userLogout (line 105) | @PostMapping("/logout")
method getLoginUser (line 120) | @GetMapping("/get/login")
method addUser (line 137) | @PostMapping("/add")
method deleteUser (line 161) | @PostMapping("/delete")
method updateUser (line 178) | @PostMapping("/update")
method getUserById (line 199) | @GetMapping("/get")
method getUserVOById (line 217) | @GetMapping("/get/vo")
method listUserByPage (line 231) | @PostMapping("/list/page")
method listUserVOByPage (line 249) | @PostMapping("/list/page/vo")
method updateMyUser (line 276) | @PostMapping("/update/my")
FILE: yudada-backend/src/main/java/com/yupi/yudada/exception/BusinessException.java
class BusinessException (line 11) | public class BusinessException extends RuntimeException {
method BusinessException (line 18) | public BusinessException(int code, String message) {
method BusinessException (line 23) | public BusinessException(ErrorCode errorCode) {
method BusinessException (line 28) | public BusinessException(ErrorCode errorCode, String message) {
method getCode (line 33) | public int getCode() {
FILE: yudada-backend/src/main/java/com/yupi/yudada/exception/GlobalExceptionHandler.java
class GlobalExceptionHandler (line 16) | @RestControllerAdvice
method businessExceptionHandler (line 20) | @ExceptionHandler(BusinessException.class)
method runtimeExceptionHandler (line 26) | @ExceptionHandler(RuntimeException.class)
FILE: yudada-backend/src/main/java/com/yupi/yudada/exception/ThrowUtils.java
class ThrowUtils (line 11) | public class ThrowUtils {
method throwIf (line 19) | public static void throwIf(boolean condition, RuntimeException runtime...
method throwIf (line 31) | public static void throwIf(boolean condition, ErrorCode errorCode) {
method throwIf (line 42) | public static void throwIf(boolean condition, ErrorCode errorCode, Str...
FILE: yudada-backend/src/main/java/com/yupi/yudada/generate/CodeGenerator.java
class CodeGenerator (line 21) | public class CodeGenerator {
method main (line 30) | public static void main(String[] args) throws TemplateException, IOExc...
method doGenerate (line 101) | public static void doGenerate(String inputPath, String outputPath, Obj...
FILE: yudada-backend/src/main/java/com/yupi/yudada/manager/AiManager.java
class AiManager (line 18) | @Component
method doSyncUnstableRequest (line 37) | public String doSyncUnstableRequest(String systemMessage, String userM...
method doSyncStableRequest (line 48) | public String doSyncStableRequest(String systemMessage, String userMes...
method doSyncRequest (line 60) | public String doSyncRequest(String systemMessage, String userMessage, ...
method doRequest (line 73) | public String doRequest(String systemMessage, String userMessage, Bool...
method doRequest (line 90) | public String doRequest(List<ChatMessage> messages, Boolean stream, Fl...
method doStreamRequest (line 116) | public Flowable<ModelData> doStreamRequest(String systemMessage, Strin...
method doStreamRequest (line 133) | public Flowable<ModelData> doStreamRequest(List<ChatMessage> messages,...
FILE: yudada-backend/src/main/java/com/yupi/yudada/manager/CosManager.java
class CosManager (line 17) | @Component
method putObject (line 33) | public PutObjectResult putObject(String key, String localFilePath) {
method putObject (line 46) | public PutObjectResult putObject(String key, File file) {
FILE: yudada-backend/src/main/java/com/yupi/yudada/mapper/AppMapper.java
type AppMapper (line 12) | public interface AppMapper extends BaseMapper<App> {
FILE: yudada-backend/src/main/java/com/yupi/yudada/mapper/PostFavourMapper.java
type PostFavourMapper (line 18) | public interface PostFavourMapper extends BaseMapper<PostFavour> {
method listFavourPostByPage (line 28) | Page<Post> listFavourPostByPage(IPage<Post> page, @Param(Constants.WRA...
FILE: yudada-backend/src/main/java/com/yupi/yudada/mapper/PostMapper.java
type PostMapper (line 14) | public interface PostMapper extends BaseMapper<Post> {
method listPostWithDelete (line 19) | List<Post> listPostWithDelete(Date minUpdateTime);
FILE: yudada-backend/src/main/java/com/yupi/yudada/mapper/PostThumbMapper.java
type PostThumbMapper (line 12) | public interface PostThumbMapper extends BaseMapper<PostThumb> {
FILE: yudada-backend/src/main/java/com/yupi/yudada/mapper/QuestionMapper.java
type QuestionMapper (line 12) | public interface QuestionMapper extends BaseMapper<Question> {
FILE: yudada-backend/src/main/java/com/yupi/yudada/mapper/ScoringResultMapper.java
type ScoringResultMapper (line 12) | public interface ScoringResultMapper extends BaseMapper<ScoringResult> {
FILE: yudada-backend/src/main/java/com/yupi/yudada/mapper/UserAnswerMapper.java
type UserAnswerMapper (line 17) | public interface UserAnswerMapper extends BaseMapper<UserAnswer> {
method doAppAnswerCount (line 19) | @Select("select appId, count(userId) as answerCount from user_answer\n" +
method doAppAnswerResultCount (line 24) | @Select("select resultName, count(resultName) as resultCount from user...
FILE: yudada-backend/src/main/java/com/yupi/yudada/mapper/UserMapper.java
type UserMapper (line 12) | public interface UserMapper extends BaseMapper<User> {
FILE: yudada-backend/src/main/java/com/yupi/yudada/model/dto/app/AppAddRequest.java
class AppAddRequest (line 13) | @Data
FILE: yudada-backend/src/main/java/com/yupi/yudada/model/dto/app/AppEditRequest.java
class AppEditRequest (line 13) | @Data
FILE: yudada-backend/src/main/java/com/yupi/yudada/model/dto/app/AppQueryRequest.java
class AppQueryRequest (line 15) | @EqualsAndHashCode(callSuper = true)
FILE: yudada-backend/src/main/java/com/yupi/yudada/model/dto/app/AppUpdateRequest.java
class AppUpdateRequest (line 14) | @Data
FILE: yudada-backend/src/main/java/com/yupi/yudada/model/dto/file/UploadFileRequest.java
class UploadFileRequest (line 12) | @Data
FILE: yudada-backend/src/main/java/com/yupi/yudada/model/dto/post/PostAddRequest.java
class PostAddRequest (line 13) | @Data
FILE: yudada-backend/src/main/java/com/yupi/yudada/model/dto/post/PostEditRequest.java
class PostEditRequest (line 13) | @Data
FILE: yudada-backend/src/main/java/com/yupi/yudada/model/dto/post/PostQueryRequest.java
class PostQueryRequest (line 15) | @EqualsAndHashCode(callSuper = true)
FILE: yudada-backend/src/main/java/com/yupi/yudada/model/dto/post/PostUpdateRequest.java
class PostUpdateRequest (line 13) | @Data
FILE: yudada-backend/src/main/java/com/yupi/yudada/model/dto/postfavour/PostFavourAddRequest.java
class PostFavourAddRequest (line 12) | @Data
FILE: yudada-backend/src/main/java/com/yupi/yudada/model/dto/postfavour/PostFavourQueryRequest.java
class PostFavourQueryRequest (line 15) | @Data
FILE: yudada-backend/src/main/java/com/yupi/yudada/model/dto/postthumb/PostThumbAddRequest.java
class PostThumbAddRequest (line 12) | @Data
FILE: yudada-backend/src/main/java/com/yupi/yudada/model/dto/question/AiGenerateQuestionRequest.java
class AiGenerateQuestionRequest (line 13) | @Data
FILE: yudada-backend/src/main/java/com/yupi/yudada/model/dto/question/QuestionAddRequest.java
class QuestionAddRequest (line 17) | @Data
FILE: yudada-backend/src/main/java/com/yupi/yudada/model/dto/question/QuestionAnswerDTO.java
class QuestionAnswerDTO (line 11) | @Data
FILE: yudada-backend/src/main/java/com/yupi/yudada/model/dto/question/QuestionContentDTO.java
class QuestionContentDTO (line 9) | @Data
class Option (line 27) | @Data
FILE: yudada-backend/src/main/java/com/yupi/yudada/model/dto/question/QuestionEditRequest.java
class QuestionEditRequest (line 17) | @Data
FILE: yudada-backend/src/main/java/com/yupi/yudada/model/dto/question/QuestionQueryRequest.java
class QuestionQueryRequest (line 19) | @EqualsAndHashCode(callSuper = true)
FILE: yudada-backend/src/main/java/com/yupi/yudada/model/dto/question/QuestionUpdateRequest.java
class QuestionUpdateRequest (line 14) | @Data
FILE: yudada-backend/src/main/java/com/yupi/yudada/model/dto/scoringResult/ScoringResultAddRequest.java
class ScoringResultAddRequest (line 17) | @Data
FILE: yudada-backend/src/main/java/com/yupi/yudada/model/dto/scoringResult/ScoringResultEditRequest.java
class ScoringResultEditRequest (line 17) | @Data
FILE: yudada-backend/src/main/java/com/yupi/yudada/model/dto/scoringResult/ScoringResultQueryRequest.java
class ScoringResultQueryRequest (line 19) | @EqualsAndHashCode(callSuper = true)
FILE: yudada-backend/src/main/java/com/yupi/yudada/model/dto/scoringResult/ScoringResultUpdateRequest.java
class ScoringResultUpdateRequest (line 14) | @Data
FILE: yudada-backend/src/main/java/com/yupi/yudada/model/dto/statistic/AppAnswerCountDTO.java
class AppAnswerCountDTO (line 8) | @Data
FILE: yudada-backend/src/main/java/com/yupi/yudada/model/dto/statistic/AppAnswerResultCountDTO.java
class AppAnswerResultCountDTO (line 8) | @Data
FILE: yudada-backend/src/main/java/com/yupi/yudada/model/dto/user/UserAddRequest.java
class UserAddRequest (line 12) | @Data
FILE: yudada-backend/src/main/java/com/yupi/yudada/model/dto/user/UserLoginRequest.java
class UserLoginRequest (line 12) | @Data
FILE: yudada-backend/src/main/java/com/yupi/yudada/model/dto/user/UserQueryRequest.java
class UserQueryRequest (line 14) | @EqualsAndHashCode(callSuper = true)
FILE: yudada-backend/src/main/java/com/yupi/yudada/model/dto/user/UserRegisterRequest.java
class UserRegisterRequest (line 12) | @Data
FILE: yudada-backend/src/main/java/com/yupi/yudada/model/dto/user/UserUpdateMyRequest.java
class UserUpdateMyRequest (line 12) | @Data
FILE: yudada-backend/src/main/java/com/yupi/yudada/model/dto/user/UserUpdateRequest.java
class UserUpdateRequest (line 12) | @Data
FILE: yudada-backend/src/main/java/com/yupi/yudada/model/dto/userAnswer/UserAnswerAddRequest.java
class UserAnswerAddRequest (line 14) | @Data
FILE: yudada-backend/src/main/java/com/yupi/yudada/model/dto/userAnswer/UserAnswerEditRequest.java
class UserAnswerEditRequest (line 16) | @Data
FILE: yudada-backend/src/main/java/com/yupi/yudada/model/dto/userAnswer/UserAnswerQueryRequest.java
class UserAnswerQueryRequest (line 18) | @EqualsAndHashCode(callSuper = true)
FILE: yudada-backend/src/main/java/com/yupi/yudada/model/dto/userAnswer/UserAnswerUpdateRequest.java
class UserAnswerUpdateRequest (line 14) | @Data
FILE: yudada-backend/src/main/java/com/yupi/yudada/model/entity/App.java
class App (line 13) | @TableName(value ="app")
FILE: yudada-backend/src/main/java/com/yupi/yudada/model/entity/Post.java
class Post (line 18) | @TableName(value = "post")
FILE: yudada-backend/src/main/java/com/yupi/yudada/model/entity/PostFavour.java
class PostFavour (line 17) | @TableName(value = "post_favour")
FILE: yudada-backend/src/main/java/com/yupi/yudada/model/entity/PostThumb.java
class PostThumb (line 17) | @TableName(value = "post_thumb")
FILE: yudada-backend/src/main/java/com/yupi/yudada/model/entity/Question.java
class Question (line 13) | @TableName(value ="question")
FILE: yudada-backend/src/main/java/com/yupi/yudada/model/entity/ScoringResult.java
class ScoringResult (line 13) | @TableName(value ="scoring_result")
FILE: yudada-backend/src/main/java/com/yupi/yudada/model/entity/User.java
class User (line 18) | @TableName(value = "user")
FILE: yudada-backend/src/main/java/com/yupi/yudada/model/entity/UserAnswer.java
class UserAnswer (line 13) | @TableName(value ="user_answer")
FILE: yudada-backend/src/main/java/com/yupi/yudada/model/enums/AppScoringStrategyEnum.java
type AppScoringStrategyEnum (line 15) | public enum AppScoringStrategyEnum {
method AppScoringStrategyEnum (line 24) | AppScoringStrategyEnum(String text, int value) {
method getEnumByValue (line 35) | public static AppScoringStrategyEnum getEnumByValue(Integer value) {
method getValues (line 52) | public static List<Integer> getValues() {
method getValue (line 56) | public int getValue() {
method getText (line 60) | public String getText() {
FILE: yudada-backend/src/main/java/com/yupi/yudada/model/enums/AppTypeEnum.java
type AppTypeEnum (line 15) | public enum AppTypeEnum {
method AppTypeEnum (line 24) | AppTypeEnum(String text, int value) {
method getEnumByValue (line 35) | public static AppTypeEnum getEnumByValue(Integer value) {
method getValues (line 52) | public static List<Integer> getValues() {
method getValue (line 56) | public int getValue() {
method getText (line 60) | public String getText() {
FILE: yudada-backend/src/main/java/com/yupi/yudada/model/enums/FileUploadBizEnum.java
type FileUploadBizEnum (line 14) | public enum FileUploadBizEnum {
method FileUploadBizEnum (line 24) | FileUploadBizEnum(String text, String value) {
method getValues (line 34) | public static List<String> getValues() {
method getEnumByValue (line 44) | public static FileUploadBizEnum getEnumByValue(String value) {
method getValue (line 56) | public String getValue() {
method getText (line 60) | public String getText() {
FILE: yudada-backend/src/main/java/com/yupi/yudada/model/enums/ReviewStatusEnum.java
type ReviewStatusEnum (line 15) | public enum ReviewStatusEnum {
method ReviewStatusEnum (line 25) | ReviewStatusEnum(String text, int value) {
method getEnumByValue (line 36) | public static ReviewStatusEnum getEnumByValue(Integer value) {
method getValues (line 53) | public static List<Integer> getValues() {
method getValue (line 57) | public int getValue() {
method getText (line 61) | public String getText() {
FILE: yudada-backend/src/main/java/com/yupi/yudada/model/enums/UserRoleEnum.java
type UserRoleEnum (line 14) | public enum UserRoleEnum {
method UserRoleEnum (line 24) | UserRoleEnum(String text, String value) {
method getValues (line 34) | public static List<String> getValues() {
method getEnumByValue (line 44) | public static UserRoleEnum getEnumByValue(String value) {
method getValue (line 56) | public String getValue() {
method getText (line 60) | public String getText() {
FILE: yudada-backend/src/main/java/com/yupi/yudada/model/vo/AppVO.java
class AppVO (line 20) | @Data
method voToObj (line 99) | public static App voToObj(AppVO appVO) {
method objToVo (line 114) | public static AppVO objToVo(App app) {
FILE: yudada-backend/src/main/java/com/yupi/yudada/model/vo/LoginUserVO.java
class LoginUserVO (line 13) | @Data
FILE: yudada-backend/src/main/java/com/yupi/yudada/model/vo/PostVO.java
class PostVO (line 17) | @Data
method voToObj (line 86) | public static Post voToObj(PostVO postVO) {
method objToVo (line 103) | public static PostVO objToVo(Post post) {
FILE: yudada-backend/src/main/java/com/yupi/yudada/model/vo/QuestionVO.java
class QuestionVO (line 21) | @Data
method voToObj (line 64) | public static Question voToObj(QuestionVO questionVO) {
method objToVo (line 81) | public static QuestionVO objToVo(Question question) {
FILE: yudada-backend/src/main/java/com/yupi/yudada/model/vo/ScoringResultVO.java
class ScoringResultVO (line 20) | @Data
method voToObj (line 84) | public static ScoringResult voToObj(ScoringResultVO scoringResultVO) {
method objToVo (line 100) | public static ScoringResultVO objToVo(ScoringResult scoringResult) {
FILE: yudada-backend/src/main/java/com/yupi/yudada/model/vo/UserAnswerVO.java
class UserAnswerVO (line 20) | @Data
method voToObj (line 98) | public static UserAnswer voToObj(UserAnswerVO userAnswerVO) {
method objToVo (line 114) | public static UserAnswerVO objToVo(UserAnswer userAnswer) {
FILE: yudada-backend/src/main/java/com/yupi/yudada/model/vo/UserVO.java
class UserVO (line 13) | @Data
FILE: yudada-backend/src/main/java/com/yupi/yudada/scoring/AiTestScoringStrategy.java
class AiTestScoringStrategy (line 31) | @ScoringStrategyConfig(appType = 1, scoringStrategy = 1)
method doScore (line 73) | @Override
method getAiTestScoringUserMessage (line 145) | private String getAiTestScoringUserMessage(App app, List<QuestionConte...
method buildCacheKey (line 168) | private String buildCacheKey(Long appId, String choices) {
FILE: yudada-backend/src/main/java/com/yupi/yudada/scoring/CustomScoreScoringStrategy.java
class CustomScoreScoringStrategy (line 28) | @ScoringStrategyConfig(appType = 0, scoringStrategy = 0)
method doScore (line 37) | @Override
FILE: yudada-backend/src/main/java/com/yupi/yudada/scoring/CustomTestScoringStrategy.java
class CustomTestScoringStrategy (line 25) | @ScoringStrategyConfig(appType = 1, scoringStrategy = 0)
method doScore (line 34) | @Override
FILE: yudada-backend/src/main/java/com/yupi/yudada/scoring/ScoringStrategy.java
type ScoringStrategy (line 14) | public interface ScoringStrategy {
method doScore (line 24) | UserAnswer doScore(List<String> choices, App app) throws Exception;
FILE: yudada-backend/src/main/java/com/yupi/yudada/scoring/ScoringStrategyContext.java
class ScoringStrategyContext (line 14) | @Service
method doScore (line 32) | public UserAnswer doScore(List<String> choiceList, App app) throws Exc...
FILE: yudada-backend/src/main/java/com/yupi/yudada/scoring/ScoringStrategyExecutor.java
class ScoringStrategyExecutor (line 15) | @Service
method doScore (line 31) | public UserAnswer doScore(List<String> choiceList, App app) throws Exc...
FILE: yudada-backend/src/main/java/com/yupi/yudada/service/AppService.java
type AppService (line 18) | public interface AppService extends IService<App> {
method validApp (line 26) | void validApp(App app, boolean add);
method getQueryWrapper (line 34) | QueryWrapper<App> getQueryWrapper(AppQueryRequest appQueryRequest);
method getAppVO (line 43) | AppVO getAppVO(App app, HttpServletRequest request);
method getAppVOPage (line 52) | Page<AppVO> getAppVOPage(Page<App> appPage, HttpServletRequest request);
FILE: yudada-backend/src/main/java/com/yupi/yudada/service/PostFavourService.java
type PostFavourService (line 17) | public interface PostFavourService extends IService<PostFavour> {
method doPostFavour (line 26) | int doPostFavour(long postId, User loginUser);
method listFavourPostByPage (line 36) | Page<Post> listFavourPostByPage(IPage<Post> page, Wrapper<Post> queryW...
method doPostFavourInner (line 46) | int doPostFavourInner(long userId, long postId);
FILE: yudada-backend/src/main/java/com/yupi/yudada/service/PostService.java
type PostService (line 17) | public interface PostService extends IService<Post> {
method validPost (line 25) | void validPost(Post post, boolean add);
method getQueryWrapper (line 33) | QueryWrapper<Post> getQueryWrapper(PostQueryRequest postQueryRequest);
method getPostVO (line 42) | PostVO getPostVO(Post post, HttpServletRequest request);
method getPostVOPage (line 51) | Page<PostVO> getPostVOPage(Page<Post> postPage, HttpServletRequest req...
FILE: yudada-backend/src/main/java/com/yupi/yudada/service/PostThumbService.java
type PostThumbService (line 13) | public interface PostThumbService extends IService<PostThumb> {
method doPostThumb (line 22) | int doPostThumb(long postId, User loginUser);
method doPostThumbInner (line 31) | int doPostThumbInner(long userId, long postId);
FILE: yudada-backend/src/main/java/com/yupi/yudada/service/QuestionService.java
type QuestionService (line 18) | public interface QuestionService extends IService<Question> {
method validQuestion (line 26) | void validQuestion(Question question, boolean add);
method getQueryWrapper (line 34) | QueryWrapper<Question> getQueryWrapper(QuestionQueryRequest questionQu...
method getQuestionVO (line 43) | QuestionVO getQuestionVO(Question question, HttpServletRequest request);
method getQuestionVOPage (line 52) | Page<QuestionVO> getQuestionVOPage(Page<Question> questionPage, HttpSe...
FILE: yudada-backend/src/main/java/com/yupi/yudada/service/ScoringResultService.java
type ScoringResultService (line 18) | public interface ScoringResultService extends IService<ScoringResult> {
method validScoringResult (line 26) | void validScoringResult(ScoringResult scoringResult, boolean add);
method getQueryWrapper (line 34) | QueryWrapper<ScoringResult> getQueryWrapper(ScoringResultQueryRequest ...
method getScoringResultVO (line 43) | ScoringResultVO getScoringResultVO(ScoringResult scoringResult, HttpSe...
method getScoringResultVOPage (line 52) | Page<ScoringResultVO> getScoringResultVOPage(Page<ScoringResult> scori...
FILE: yudada-backend/src/main/java/com/yupi/yudada/service/UserAnswerService.java
type UserAnswerService (line 18) | public interface UserAnswerService extends IService<UserAnswer> {
method validUserAnswer (line 26) | void validUserAnswer(UserAnswer userAnswer, boolean add);
method getQueryWrapper (line 34) | QueryWrapper<UserAnswer> getQueryWrapper(UserAnswerQueryRequest userAn...
method getUserAnswerVO (line 43) | UserAnswerVO getUserAnswerVO(UserAnswer userAnswer, HttpServletRequest...
method getUserAnswerVOPage (line 52) | Page<UserAnswerVO> getUserAnswerVOPage(Page<UserAnswer> userAnswerPage...
FILE: yudada-backend/src/main/java/com/yupi/yudada/service/UserService.java
type UserService (line 18) | public interface UserService extends IService<User> {
method userRegister (line 28) | long userRegister(String userAccount, String userPassword, String chec...
method userLogin (line 38) | LoginUserVO userLogin(String userAccount, String userPassword, HttpSer...
method getLoginUser (line 46) | User getLoginUser(HttpServletRequest request);
method getLoginUserPermitNull (line 54) | User getLoginUserPermitNull(HttpServletRequest request);
method isAdmin (line 62) | boolean isAdmin(HttpServletRequest request);
method isAdmin (line 70) | boolean isAdmin(User user);
method userLogout (line 78) | boolean userLogout(HttpServletRequest request);
method getLoginUserVO (line 85) | LoginUserVO getLoginUserVO(User user);
method getUserVO (line 93) | UserVO getUserVO(User user);
method getUserVO (line 101) | List<UserVO> getUserVO(List<User> userList);
method getQueryWrapper (line 109) | QueryWrapper<User> getQueryWrapper(UserQueryRequest userQueryRequest);
FILE: yudada-backend/src/main/java/com/yupi/yudada/service/impl/AppServiceImpl.java
class AppServiceImpl (line 40) | @Service
method validApp (line 53) | @Override
method getQueryWrapper (line 90) | @Override
method getAppVO (line 145) | @Override
method getAppVOPage (line 171) | @Override
FILE: yudada-backend/src/main/java/com/yupi/yudada/service/impl/PostFavourServiceImpl.java
class PostFavourServiceImpl (line 27) | @Service
method doPostFavour (line 41) | @Override
method listFavourPostByPage (line 58) | @Override
method doPostFavourInner (line 73) | @Override
FILE: yudada-backend/src/main/java/com/yupi/yudada/service/impl/PostServiceImpl.java
class PostServiceImpl (line 42) | @Service
method validPost (line 55) | @Override
method getQueryWrapper (line 82) | @Override
method getPostVO (line 116) | @Override
method getPostVOPage (line 147) | @Override
FILE: yudada-backend/src/main/java/com/yupi/yudada/service/impl/PostThumbServiceImpl.java
class PostThumbServiceImpl (line 24) | @Service
method doPostThumb (line 38) | @Override
method doPostThumbInner (line 62) | @Override
FILE: yudada-backend/src/main/java/com/yupi/yudada/service/impl/QuestionServiceImpl.java
class QuestionServiceImpl (line 39) | @Service
method validQuestion (line 55) | @Override
method getQueryWrapper (line 81) | @Override
method getQuestionVO (line 118) | @Override
method getQuestionVOPage (line 145) | @Override
FILE: yudada-backend/src/main/java/com/yupi/yudada/service/impl/ScoringResultServiceImpl.java
class ScoringResultServiceImpl (line 39) | @Service
method validScoringResult (line 55) | @Override
method getQueryWrapper (line 85) | @Override
method getScoringResultVO (line 136) | @Override
method getScoringResultVOPage (line 163) | @Override
FILE: yudada-backend/src/main/java/com/yupi/yudada/service/impl/UserAnswerServiceImpl.java
class UserAnswerServiceImpl (line 39) | @Service
method validUserAnswer (line 55) | @Override
method getQueryWrapper (line 81) | @Override
method getUserAnswerVO (line 138) | @Override
method getUserAnswerVOPage (line 165) | @Override
FILE: yudada-backend/src/main/java/com/yupi/yudada/service/impl/UserServiceImpl.java
class UserServiceImpl (line 35) | @Service
method userRegister (line 44) | @Override
method userLogin (line 82) | @Override
method getLoginUser (line 117) | @Override
method getLoginUserPermitNull (line 140) | @Override
method isAdmin (line 159) | @Override
method isAdmin (line 167) | @Override
method userLogout (line 177) | @Override
method getLoginUserVO (line 187) | @Override
method getUserVO (line 197) | @Override
method getUserVO (line 207) | @Override
method getQueryWrapper (line 215) | @Override
FILE: yudada-backend/src/main/java/com/yupi/yudada/utils/NetUtils.java
class NetUtils (line 12) | public class NetUtils {
method getIpAddress (line 20) | public static String getIpAddress(HttpServletRequest request) {
FILE: yudada-backend/src/main/java/com/yupi/yudada/utils/SpringContextUtils.java
class SpringContextUtils (line 15) | @Component
method setApplicationContext (line 20) | @Override
method getBean (line 31) | public static Object getBean(String beanName) {
method getBean (line 42) | public static <T> T getBean(Class<T> beanClass) {
method getBean (line 54) | public static <T> T getBean(String beanName, Class<T> beanClass) {
FILE: yudada-backend/src/main/java/com/yupi/yudada/utils/SqlUtils.java
class SqlUtils (line 11) | public class SqlUtils {
method validSortField (line 19) | public static boolean validSortField(String sortField) {
FILE: yudada-backend/src/test/java/com/yupi/yudada/MainApplicationTests.java
class MainApplicationTests (line 12) | @SpringBootTest
method contextLoads (line 15) | @Test
FILE: yudada-backend/src/test/java/com/yupi/yudada/QuestionControllerTest.java
class QuestionControllerTest (line 11) | @SpringBootTest
method aiGenerateQuestionSSETest (line 17) | @Test
FILE: yudada-backend/src/test/java/com/yupi/yudada/RxJavaTest.java
class RxJavaTest (line 10) | @SpringBootTest
method test (line 13) | @Test
FILE: yudada-backend/src/test/java/com/yupi/yudada/UserAnswerShardingTest.java
class UserAnswerShardingTest (line 12) | @SpringBootTest
method test (line 18) | @Test
FILE: yudada-backend/src/test/java/com/yupi/yudada/ZhiPuAiTest.java
class ZhiPuAiTest (line 16) | @SpringBootTest
method test (line 22) | @Test
FILE: yudada-backend/src/test/java/com/yupi/yudada/manager/CosManagerTest.java
class CosManagerTest (line 13) | @SpringBootTest
method putObject (line 19) | @Test
FILE: yudada-backend/src/test/java/com/yupi/yudada/mapper/PostFavourMapperTest.java
class PostFavourMapperTest (line 18) | @SpringBootTest
method listUserFavourPostByPage (line 24) | @Test
FILE: yudada-backend/src/test/java/com/yupi/yudada/mapper/PostMapperTest.java
class PostMapperTest (line 17) | @SpringBootTest
method listPostWithDelete (line 23) | @Test
FILE: yudada-backend/src/test/java/com/yupi/yudada/service/PostFavourServiceTest.java
class PostFavourServiceTest (line 19) | @SpringBootTest
method setUp (line 27) | @BeforeAll
method doPostFavour (line 32) | @Test
method listFavourPostByPage (line 38) | @Test
FILE: yudada-backend/src/test/java/com/yupi/yudada/service/PostThumbServiceTest.java
class PostThumbServiceTest (line 16) | @SpringBootTest
method setUp (line 24) | @BeforeAll
method doPostThumb (line 29) | @Test
FILE: yudada-backend/src/test/java/com/yupi/yudada/service/UserServiceTest.java
class UserServiceTest (line 14) | @SpringBootTest
method userRegister (line 20) | @Test
FILE: yudada-backend/src/test/java/com/yupi/yudada/utils/EasyExcelTest.java
class EasyExcelTest (line 20) | @SpringBootTest
method doImport (line 23) | @Test
FILE: yudada-frontend/src/access/accessEnum.ts
constant ACCESS_ENUM (line 4) | const ACCESS_ENUM = {
FILE: yudada-frontend/src/api/appController.ts
function addAppUsingPost (line 6) | async function addAppUsingPost(body: API.AppAddRequest, options?: { [key...
function deleteAppUsingPost (line 18) | async function deleteAppUsingPost(
function editAppUsingPost (line 33) | async function editAppUsingPost(body: API.AppEditRequest, options?: { [k...
function getAppVoByIdUsingGet (line 45) | async function getAppVoByIdUsingGet(
function listAppByPageUsingPost (line 60) | async function listAppByPageUsingPost(
function listAppVoByPageUsingPost (line 75) | async function listAppVoByPageUsingPost(
function listMyAppVoByPageUsingPost (line 90) | async function listMyAppVoByPageUsingPost(
function doAppReviewUsingPost (line 105) | async function doAppReviewUsingPost(
function updateAppUsingPost (line 120) | async function updateAppUsingPost(
FILE: yudada-frontend/src/api/appStatisticController.ts
function getAppAnswerCountUsingGet (line 6) | async function getAppAnswerCountUsingGet(options?: { [key: string]: any ...
function getAppAnswerResultCountUsingGet (line 14) | async function getAppAnswerResultCountUsingGet(
FILE: yudada-frontend/src/api/fileController.ts
function uploadFileUsingPost (line 6) | async function uploadFileUsingPost(
FILE: yudada-frontend/src/api/postController.ts
function addPostUsingPost (line 6) | async function addPostUsingPost(body: API.PostAddRequest, options?: { [k...
function deletePostUsingPost (line 18) | async function deletePostUsingPost(
function editPostUsingPost (line 33) | async function editPostUsingPost(
function getPostVoByIdUsingGet (line 48) | async function getPostVoByIdUsingGet(
function listPostByPageUsingPost (line 63) | async function listPostByPageUsingPost(
function listPostVoByPageUsingPost (line 78) | async function listPostVoByPageUsingPost(
function listMyPostVoByPageUsingPost (line 93) | async function listMyPostVoByPageUsingPost(
function updatePostUsingPost (line 108) | async function updatePostUsingPost(
FILE: yudada-frontend/src/api/postFavourController.ts
function doPostFavourUsingPost (line 6) | async function doPostFavourUsingPost(
function listFavourPostByPageUsingPost (line 21) | async function listFavourPostByPageUsingPost(
function listMyFavourPostByPageUsingPost (line 36) | async function listMyFavourPostByPageUsingPost(
FILE: yudada-frontend/src/api/postThumbController.ts
function doThumbUsingPost (line 6) | async function doThumbUsingPost(
FILE: yudada-frontend/src/api/questionController.ts
function addQuestionUsingPost (line 6) | async function addQuestionUsingPost(
function aiGenerateQuestionUsingPost (line 21) | async function aiGenerateQuestionUsingPost(
function aiGenerateQuestionSseUsingGet (line 36) | async function aiGenerateQuestionSseUsingGet(
function aiGenerateQuestionSseTestUsingGet (line 51) | async function aiGenerateQuestionSseTestUsingGet(
function deleteQuestionUsingPost (line 66) | async function deleteQuestionUsingPost(
function editQuestionUsingPost (line 81) | async function editQuestionUsingPost(
function getQuestionVoByIdUsingGet (line 96) | async function getQuestionVoByIdUsingGet(
function listQuestionByPageUsingPost (line 111) | async function listQuestionByPageUsingPost(
function listQuestionVoByPageUsingPost (line 126) | async function listQuestionVoByPageUsingPost(
function listMyQuestionVoByPageUsingPost (line 141) | async function listMyQuestionVoByPageUsingPost(
function updateQuestionUsingPost (line 156) | async function updateQuestionUsingPost(
FILE: yudada-frontend/src/api/scoringResultController.ts
function addScoringResultUsingPost (line 6) | async function addScoringResultUsingPost(
function deleteScoringResultUsingPost (line 21) | async function deleteScoringResultUsingPost(
function editScoringResultUsingPost (line 36) | async function editScoringResultUsingPost(
function getScoringResultVoByIdUsingGet (line 51) | async function getScoringResultVoByIdUsingGet(
function listScoringResultByPageUsingPost (line 66) | async function listScoringResultByPageUsingPost(
function listScoringResultVoByPageUsingPost (line 81) | async function listScoringResultVoByPageUsingPost(
function listMyScoringResultVoByPageUsingPost (line 96) | async function listMyScoringResultVoByPageUsingPost(
function updateScoringResultUsingPost (line 111) | async function updateScoringResultUsingPost(
FILE: yudada-frontend/src/api/typings.d.ts
type AiGenerateQuestionRequest (line 2) | type AiGenerateQuestionRequest = {
type aiGenerateQuestionSSETestUsingGETParams (line 8) | type aiGenerateQuestionSSETestUsingGETParams = {
type aiGenerateQuestionSSEUsingGETParams (line 16) | type aiGenerateQuestionSSEUsingGETParams = {
type App (line 22) | type App = {
type AppAddRequest (line 39) | type AppAddRequest = {
type AppAnswerCountDTO (line 47) | type AppAnswerCountDTO = {
type AppAnswerResultCountDTO (line 52) | type AppAnswerResultCountDTO = {
type AppEditRequest (line 57) | type AppEditRequest = {
type AppQueryRequest (line 66) | type AppQueryRequest = {
type AppUpdateRequest (line 85) | type AppUpdateRequest = {
type AppVO (line 98) | type AppVO = {
type BaseResponseAppVO_ (line 115) | type BaseResponseAppVO_ = {
type BaseResponseBoolean_ (line 121) | type BaseResponseBoolean_ = {
type BaseResponseInt_ (line 127) | type BaseResponseInt_ = {
type BaseResponseListAppAnswerCountDTO_ (line 133) | type BaseResponseListAppAnswerCountDTO_ = {
type BaseResponseListAppAnswerResultCountDTO_ (line 139) | type BaseResponseListAppAnswerResultCountDTO_ = {
type BaseResponseListQuestionContentDTO_ (line 145) | type BaseResponseListQuestionContentDTO_ = {
type BaseResponseLoginUserVO_ (line 151) | type BaseResponseLoginUserVO_ = {
type BaseResponseLong_ (line 157) | type BaseResponseLong_ = {
type BaseResponsePageApp_ (line 163) | type BaseResponsePageApp_ = {
type BaseResponsePageAppVO_ (line 169) | type BaseResponsePageAppVO_ = {
type BaseResponsePagePost_ (line 175) | type BaseResponsePagePost_ = {
type BaseResponsePagePostVO_ (line 181) | type BaseResponsePagePostVO_ = {
type BaseResponsePageQuestion_ (line 187) | type BaseResponsePageQuestion_ = {
type BaseResponsePageQuestionVO_ (line 193) | type BaseResponsePageQuestionVO_ = {
type BaseResponsePageScoringResult_ (line 199) | type BaseResponsePageScoringResult_ = {
type BaseResponsePageScoringResultVO_ (line 205) | type BaseResponsePageScoringResultVO_ = {
type BaseResponsePageUser_ (line 211) | type BaseResponsePageUser_ = {
type BaseResponsePageUserAnswer_ (line 217) | type BaseResponsePageUserAnswer_ = {
type BaseResponsePageUserAnswerVO_ (line 223) | type BaseResponsePageUserAnswerVO_ = {
type BaseResponsePageUserVO_ (line 229) | type BaseResponsePageUserVO_ = {
type BaseResponsePostVO_ (line 235) | type BaseResponsePostVO_ = {
type BaseResponseQuestionVO_ (line 241) | type BaseResponseQuestionVO_ = {
type BaseResponseScoringResultVO_ (line 247) | type BaseResponseScoringResultVO_ = {
type BaseResponseString_ (line 253) | type BaseResponseString_ = {
type BaseResponseUser_ (line 259) | type BaseResponseUser_ = {
type BaseResponseUserAnswerVO_ (line 265) | type BaseResponseUserAnswerVO_ = {
type BaseResponseUserVO_ (line 271) | type BaseResponseUserVO_ = {
type DeleteRequest (line 277) | type DeleteRequest = {
type getAppAnswerResultCountUsingGETParams (line 281) | type getAppAnswerResultCountUsingGETParams = {
type getAppVOByIdUsingGETParams (line 286) | type getAppVOByIdUsingGETParams = {
type getPostVOByIdUsingGETParams (line 291) | type getPostVOByIdUsingGETParams = {
type getQuestionVOByIdUsingGETParams (line 296) | type getQuestionVOByIdUsingGETParams = {
type getScoringResultVOByIdUsingGETParams (line 301) | type getScoringResultVOByIdUsingGETParams = {
type getUserAnswerVOByIdUsingGETParams (line 306) | type getUserAnswerVOByIdUsingGETParams = {
type getUserByIdUsingGETParams (line 311) | type getUserByIdUsingGETParams = {
type getUserVOByIdUsingGETParams (line 316) | type getUserVOByIdUsingGETParams = {
type LoginUserVO (line 321) | type LoginUserVO = {
type Option (line 331) | type Option = {
type OrderItem (line 338) | type OrderItem = {
type PageApp_ (line 343) | type PageApp_ = {
type PageAppVO_ (line 356) | type PageAppVO_ = {
type PagePost_ (line 369) | type PagePost_ = {
type PagePostVO_ (line 382) | type PagePostVO_ = {
type PageQuestion_ (line 395) | type PageQuestion_ = {
type PageQuestionVO_ (line 408) | type PageQuestionVO_ = {
type PageScoringResult_ (line 421) | type PageScoringResult_ = {
type PageScoringResultVO_ (line 434) | type PageScoringResultVO_ = {
type PageUser_ (line 447) | type PageUser_ = {
type PageUserAnswer_ (line 460) | type PageUserAnswer_ = {
type PageUserAnswerVO_ (line 473) | type PageUserAnswerVO_ = {
type PageUserVO_ (line 486) | type PageUserVO_ = {
type Post (line 499) | type Post = {
type PostAddRequest (line 512) | type PostAddRequest = {
type PostEditRequest (line 518) | type PostEditRequest = {
type PostFavourAddRequest (line 525) | type PostFavourAddRequest = {
type PostFavourQueryRequest (line 529) | type PostFavourQueryRequest = {
type PostQueryRequest (line 538) | type PostQueryRequest = {
type PostThumbAddRequest (line 554) | type PostThumbAddRequest = {
type PostUpdateRequest (line 558) | type PostUpdateRequest = {
type PostVO (line 565) | type PostVO = {
type Question (line 580) | type Question = {
type QuestionAddRequest (line 590) | type QuestionAddRequest = {
type QuestionContentDTO (line 595) | type QuestionContentDTO = {
type QuestionEditRequest (line 600) | type QuestionEditRequest = {
type QuestionQueryRequest (line 605) | type QuestionQueryRequest = {
type QuestionUpdateRequest (line 617) | type QuestionUpdateRequest = {
type QuestionVO (line 622) | type QuestionVO = {
type ReviewRequest (line 632) | type ReviewRequest = {
type ScoringResult (line 638) | type ScoringResult = {
type ScoringResultAddRequest (line 652) | type ScoringResultAddRequest = {
type ScoringResultEditRequest (line 661) | type ScoringResultEditRequest = {
type ScoringResultQueryRequest (line 670) | type ScoringResultQueryRequest = {
type ScoringResultUpdateRequest (line 687) | type ScoringResultUpdateRequest = {
type ScoringResultVO (line 696) | type ScoringResultVO = {
type SseEmitter (line 710) | type SseEmitter = {
type uploadFileUsingPOSTParams (line 714) | type uploadFileUsingPOSTParams = {
type User (line 718) | type User = {
type UserAddRequest (line 733) | type UserAddRequest = {
type UserAnswer (line 740) | type UserAnswer = {
type UserAnswerAddRequest (line 757) | type UserAnswerAddRequest = {
type UserAnswerEditRequest (line 763) | type UserAnswerEditRequest = {
type UserAnswerQueryRequest (line 769) | type UserAnswerQueryRequest = {
type UserAnswerUpdateRequest (line 789) | type UserAnswerUpdateRequest = {
type UserAnswerVO (line 795) | type UserAnswerVO = {
type UserLoginRequest (line 812) | type UserLoginRequest = {
type UserQueryRequest (line 817) | type UserQueryRequest = {
type UserRegisterRequest (line 830) | type UserRegisterRequest = {
type UserUpdateMyRequest (line 836) | type UserUpdateMyRequest = {
type UserUpdateRequest (line 842) | type UserUpdateRequest = {
type UserVO (line 850) | type UserVO = {
FILE: yudada-frontend/src/api/userAnswerController.ts
function addUserAnswerUsingPost (line 6) | async function addUserAnswerUsingPost(
function deleteUserAnswerUsingPost (line 21) | async function deleteUserAnswerUsingPost(
function editUserAnswerUsingPost (line 36) | async function editUserAnswerUsingPost(
function generateUserAnswerIdUsingGet (line 51) | async function generateUserAnswerIdUsingGet(options?: { [key: string]: a...
function getUserAnswerVoByIdUsingGet (line 59) | async function getUserAnswerVoByIdUsingGet(
function listUserAnswerByPageUsingPost (line 74) | async function listUserAnswerByPageUsingPost(
function listUserAnswerVoByPageUsingPost (line 89) | async function listUserAnswerVoByPageUsingPost(
function listMyUserAnswerVoByPageUsingPost (line 104) | async function listMyUserAnswerVoByPageUsingPost(
function updateUserAnswerUsingPost (line 119) | async function updateUserAnswerUsingPost(
FILE: yudada-frontend/src/api/userController.ts
function addUserUsingPost (line 6) | async function addUserUsingPost(body: API.UserAddRequest, options?: { [k...
function deleteUserUsingPost (line 18) | async function deleteUserUsingPost(
function getUserByIdUsingGet (line 33) | async function getUserByIdUsingGet(
function getLoginUserUsingGet (line 48) | async function getLoginUserUsingGet(options?: { [key: string]: any }) {
function getUserVoByIdUsingGet (line 56) | async function getUserVoByIdUsingGet(
function listUserByPageUsingPost (line 71) | async function listUserByPageUsingPost(
function listUserVoByPageUsingPost (line 86) | async function listUserVoByPageUsingPost(
function userLoginUsingPost (line 101) | async function userLoginUsingPost(
function userLogoutUsingPost (line 116) | async function userLogoutUsingPost(options?: { [key: string]: any }) {
function userRegisterUsingPost (line 124) | async function userRegisterUsingPost(
function updateUserUsingPost (line 139) | async function updateUserUsingPost(
function updateMyUserUsingPost (line 154) | async function updateMyUserUsingPost(
FILE: yudada-frontend/src/constant/app.ts
constant REVIEW_STATUS_ENUM (line 2) | const REVIEW_STATUS_ENUM = {
constant REVIEW_STATUS_MAP (line 12) | const REVIEW_STATUS_MAP = {
constant APP_TYPE_ENUM (line 19) | const APP_TYPE_ENUM = {
constant APP_TYPE_MAP (line 27) | const APP_TYPE_MAP = {
constant APP_SCORING_STRATEGY_ENUM (line 33) | const APP_SCORING_STRATEGY_ENUM = {
constant APP_SCORING_STRATEGY_MAP (line 41) | const APP_SCORING_STRATEGY_MAP = {
FILE: yudada-frontend/src/store/userStore.ts
function setLoginUser (line 14) | function setLoginUser(newLoginUser: API.LoginUserVO) {
function fetchLoginUser (line 18) | async function fetchLoginUser() {
Condensed preview — 264 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (618K chars).
[
{
"path": ".gitignore",
"chars": 3230,
"preview": "### Intellij template\n# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, We"
},
{
"path": "README.md",
"chars": 6364,
"preview": "# 鱼答答 - AI 答题应用平台\n\n> 作者:[程序员鱼皮](https://yuyuanweb.feishu.cn/wiki/Abldw5WkjidySxkKxU2cQdAtnah)\n>\n> ⭐️ 加入项目系列学习:[加入编程导航](h"
},
{
"path": "mbti-test-mini/.editorconfig",
"chars": 197,
"preview": "# http://editorconfig.org\nroot = true\n\n[*]\nindent_style = space\nindent_size = 2\ncharset = utf-8\ntrim_trailing_whitespace"
},
{
"path": "mbti-test-mini/.eslintrc",
"chars": 148,
"preview": "{\n \"extends\": [\"taro/react\"],\n \"rules\": {\n \"react/jsx-uses-react\": \"off\",\n \"react/react-in-jsx-scope\": \"off\",\n "
},
{
"path": "mbti-test-mini/babel.config.js",
"chars": 213,
"preview": "// babel-preset-taro 更多选项和默认值:\n// https://github.com/NervJS/taro/blob/next/packages/babel-preset-taro/README.md\nmodule.e"
},
{
"path": "mbti-test-mini/config/dev.ts",
"chars": 146,
"preview": "module.exports = {\n env: {\n NODE_ENV: '\"development\"'\n },\n defineConstants: {\n },\n mini: {},\n h5: {\n esnextM"
},
{
"path": "mbti-test-mini/config/index.ts",
"chars": 1535,
"preview": "const config = {\n projectName: 'mbti-test-mini',\n date: '2024-5-7',\n designWidth: 750,\n deviceRatio: {\n 640: 2.34"
},
{
"path": "mbti-test-mini/config/prod.ts",
"chars": 1122,
"preview": "module.exports = {\n env: {\n NODE_ENV: '\"production\"'\n },\n defineConstants: {\n },\n mini: {},\n h5: {\n /**\n "
},
{
"path": "mbti-test-mini/package.json",
"chars": 2662,
"preview": "{\n \"name\": \"mbti-test-mini\",\n \"version\": \"1.0.0\",\n \"private\": true,\n \"description\": \"\",\n \"templateInfo\": {\n \"nam"
},
{
"path": "mbti-test-mini/project.config.json",
"chars": 896,
"preview": "{\n \"miniprogramRoot\": \"dist/\",\n \"projectname\": \"mbti-test-mini\",\n \"description\": \"\",\n \"appid\": \"wx370d9458c983e21d\","
},
{
"path": "mbti-test-mini/project.private.config.json",
"chars": 250,
"preview": "{\n \"description\": \"项目私有配置文件。此文件中的内容将覆盖 project.config.json 中的相同字段。项目的改动优先同步到此文件中。详见文档:https://developers.weixin.qq.com/"
},
{
"path": "mbti-test-mini/project.tt.json",
"chars": 249,
"preview": "{\n \"miniprogramRoot\": \"./\",\n \"projectname\": \"mbti-test-mini\",\n \"description\": \"\",\n \"appid\": \"touristappid\",\n \"setti"
},
{
"path": "mbti-test-mini/src/app.config.ts",
"chars": 291,
"preview": "export default defineAppConfig({\n pages: [\"pages/index/index\", \"pages/result/index\", \"pages/doQuestion/index\"],\n windo"
},
{
"path": "mbti-test-mini/src/app.scss",
"chars": 72,
"preview": ".at-button--primary {\n background: #806497;\n border-color: #806497;\n}\n"
},
{
"path": "mbti-test-mini/src/app.ts",
"chars": 383,
"preview": "import Taro, { useLaunch } from \"@tarojs/taro\";\nimport { PropsWithChildren } from \"react\";\nimport \"taro-ui/dist/style/in"
},
{
"path": "mbti-test-mini/src/components/GlobalFooter/index.scss",
"chars": 98,
"preview": ".globalFooter {\n position: fixed;\n bottom: 16px;\n left: 0;\n right: 0;\n text-align: center;\n}\n"
},
{
"path": "mbti-test-mini/src/components/GlobalFooter/index.tsx",
"chars": 303,
"preview": "import { View } from \"@tarojs/components\";\nimport \"./index.scss\";\n\n/**\n * 全局底部栏组件\n * @author <a href=\"https://github.com"
},
{
"path": "mbti-test-mini/src/data/question_results.json",
"chars": 3041,
"preview": "[\n {\n \"resultProp\": [\n \"I\",\n \"S\",\n \"T\",\n \"J\"\n ],\n \"resultDesc\": \"忠诚可靠,被公认为务实,注重细节。\",\n \""
},
{
"path": "mbti-test-mini/src/data/questions.json",
"chars": 2285,
"preview": "[\n {\n \"options\": [\n {\n \"result\": \"I\",\n \"value\": \"独自工作\",\n \"key\": \"A\"\n },\n {\n "
},
{
"path": "mbti-test-mini/src/index.html",
"chars": 647,
"preview": "<!DOCTYPE html>\n<html>\n<head>\n <meta content=\"text/html; charset=utf-8\" http-equiv=\"Content-Type\">\n <meta content=\"wid"
},
{
"path": "mbti-test-mini/src/pages/doQuestion/index.config.ts",
"chars": 69,
"preview": "export default definePageConfig({\n // navigationBarTitleText: ''\n})\n"
},
{
"path": "mbti-test-mini/src/pages/doQuestion/index.scss",
"chars": 156,
"preview": ".doQuestionPage {\n\n .title {\n margin-bottom: 48px;\n }\n\n .options-wrapper {\n margin-bottom: 48px;\n }\n\n .contro"
},
{
"path": "mbti-test-mini/src/pages/doQuestion/index.tsx",
"chars": 2454,
"preview": "import {View} from \"@tarojs/components\";\nimport Taro from \"@tarojs/taro\";\nimport {AtButton, AtRadio} from \"taro-ui\";\nimp"
},
{
"path": "mbti-test-mini/src/pages/index/index.config.ts",
"chars": 69,
"preview": "export default definePageConfig({\n // navigationBarTitleText: ''\n})\n"
},
{
"path": "mbti-test-mini/src/pages/index/index.scss",
"chars": 217,
"preview": ".indexPage {\n background: #A2C7D7;\n\n .title {\n color: white;\n padding-top: 48px;\n text-align: center;\n }\n\n "
},
{
"path": "mbti-test-mini/src/pages/index/index.tsx",
"chars": 941,
"preview": "import {View, Image} from \"@tarojs/components\";\nimport {AtButton} from \"taro-ui\";\nimport Taro from \"@tarojs/taro\";\nimpor"
},
{
"path": "mbti-test-mini/src/pages/result/index.config.ts",
"chars": 70,
"preview": "export default definePageConfig({\n navigationBarTitleText: '查看结果'\n})\n"
},
{
"path": "mbti-test-mini/src/pages/result/index.scss",
"chars": 218,
"preview": ".resultPage {\n background: #A2C7D7;\n\n .title {\n color: white;\n padding-top: 48px;\n text-align: center;\n }\n\n "
},
{
"path": "mbti-test-mini/src/pages/result/index.tsx",
"chars": 1399,
"preview": "import {View, Image} from \"@tarojs/components\";\nimport {AtButton} from \"taro-ui\";\nimport Taro from \"@tarojs/taro\";\nimpor"
},
{
"path": "mbti-test-mini/src/utils/bizUtils.ts",
"chars": 2646,
"preview": "/**\n * 获取最佳题目评分结果\n * @param answerList\n * @param questions\n * @param question_results\n */\n\nexport function getBestQuesti"
},
{
"path": "mbti-test-mini/tsconfig.json",
"chars": 644,
"preview": "{\n \"compilerOptions\": {\n \"target\": \"es2017\",\n \"module\": \"commonjs\",\n \"removeComments\": false,\n \"preserveCon"
},
{
"path": "mbti-test-mini/types/custom.d.ts",
"chars": 196,
"preview": "interface QuestionOption<T extends string = string> {\n result: T;\n value: string;\n key: T;\n}\n\ninterface Question<T ex"
},
{
"path": "mbti-test-mini/types/global.d.ts",
"chars": 436,
"preview": "/// <reference types=\"@tarojs/taro\" />\n\ndeclare module '*.png';\ndeclare module '*.gif';\ndeclare module '*.jpg';\ndeclare "
},
{
"path": "yudada-backend/.gitignore",
"chars": 2638,
"preview": "### @author <a href=\"https://github.com/liyupi\">程序员鱼皮</a> ###\n### @from <a href=\"https://yupi.icu\">编程导航知识星球</a> ###\n\nHEL"
},
{
"path": "yudada-backend/.mvn/wrapper/maven-wrapper.properties",
"chars": 233,
"preview": "distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.1/apache-maven-3.8.1-bin.zip\nwrap"
},
{
"path": "yudada-backend/Dockerfile",
"chars": 497,
"preview": "# Docker 镜像构建\n# @author <a href=\"https://github.com/liyupi\">程序员鱼皮</a>\n# @from <a href=\"https://yupi.icu\">编程导航知识星球</a>\n# "
},
{
"path": "yudada-backend/README.md",
"chars": 2762,
"preview": "# SpringBoot 项目初始模板\n\n> 作者:[程序员鱼皮](https://github.com/liyupi)\n> 仅分享于 [编程导航知识星球](https://yupi.icu)\n\n基于 Java SpringBoot 的项目"
},
{
"path": "yudada-backend/mvnw",
"chars": 10284,
"preview": "#!/bin/sh\n# ----------------------------------------------------------------------------\n# Licensed to the Apache Softwa"
},
{
"path": "yudada-backend/mvnw.cmd",
"chars": 6734,
"preview": "@REM ----------------------------------------------------------------------------\n@REM Licensed to the Apache Software F"
},
{
"path": "yudada-backend/pom.xml",
"chars": 5964,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!-- @author <a href=\"https://github.com/liyupi\">程序员鱼皮</a> -->\n<!-- @from <a href"
},
{
"path": "yudada-backend/sql/create_table.sql",
"chars": 8273,
"preview": "# 数据库初始化\n# @author <a href=\"https://github.com/liyupi\">程序员鱼皮</a>\n# @from <a href=\"https://yupi.icu\">编程导航知识星球</a>\n\n-- 创建库"
},
{
"path": "yudada-backend/sql/init_data.sql",
"chars": 15440,
"preview": "-- 切换库\nuse yudada;\n\n-- 用户表初始数据\nINSERT INTO user (id, userAccount, userPassword, unionId, mpOpenId, userName, userAvatar,"
},
{
"path": "yudada-backend/sql/post_es_mapping.json",
"chars": 961,
"preview": "{\n \"aliases\": {\n \"post\": {}\n },\n \"mappings\": {\n \"properties\": {\n \"title\": {\n \"type\": \"text\",\n "
},
{
"path": "yudada-backend/src/main/java/com/yupi/yudada/MainApplication.java",
"chars": 875,
"preview": "package com.yupi.yudada;\n\nimport org.mybatis.spring.annotation.MapperScan;\nimport org.springframework.boot.SpringApplica"
},
{
"path": "yudada-backend/src/main/java/com/yupi/yudada/annotation/AuthCheck.java",
"chars": 510,
"preview": "package com.yupi.yudada.annotation;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimp"
},
{
"path": "yudada-backend/src/main/java/com/yupi/yudada/aop/AuthInterceptor.java",
"chars": 2441,
"preview": "package com.yupi.yudada.aop;\n\nimport com.yupi.yudada.annotation.AuthCheck;\nimport com.yupi.yudada.common.ErrorCode;\nimpo"
},
{
"path": "yudada-backend/src/main/java/com/yupi/yudada/aop/LogInterceptor.java",
"chars": 1986,
"preview": "package com.yupi.yudada.aop;\n\nimport java.util.UUID;\nimport javax.servlet.http.HttpServletRequest;\nimport lombok.extern."
},
{
"path": "yudada-backend/src/main/java/com/yupi/yudada/common/BaseResponse.java",
"chars": 710,
"preview": "package com.yupi.yudada.common;\n\nimport java.io.Serializable;\nimport lombok.Data;\n\n/**\n * 通用返回类\n *\n * @param <T>\n * @aut"
},
{
"path": "yudada-backend/src/main/java/com/yupi/yudada/common/DeleteRequest.java",
"chars": 370,
"preview": "package com.yupi.yudada.common;\n\nimport java.io.Serializable;\nimport lombok.Data;\n\n/**\n * 删除请求\n *\n * @author <a href=\"ht"
},
{
"path": "yudada-backend/src/main/java/com/yupi/yudada/common/ErrorCode.java",
"chars": 802,
"preview": "package com.yupi.yudada.common;\n\n/**\n * 自定义错误码\n *\n * @author <a href=\"https://github.com/liyupi\">程序员鱼皮</a>\n * @from <a h"
},
{
"path": "yudada-backend/src/main/java/com/yupi/yudada/common/PageRequest.java",
"chars": 536,
"preview": "package com.yupi.yudada.common;\n\nimport com.yupi.yudada.constant.CommonConstant;\nimport lombok.Data;\n\n/**\n * 分页请求\n *\n * "
},
{
"path": "yudada-backend/src/main/java/com/yupi/yudada/common/ResultUtils.java",
"chars": 1010,
"preview": "package com.yupi.yudada.common;\n\n/**\n * 返回工具类\n *\n * @author <a href=\"https://github.com/liyupi\">程序员鱼皮</a>\n * @from <a hr"
},
{
"path": "yudada-backend/src/main/java/com/yupi/yudada/common/ReviewRequest.java",
"chars": 405,
"preview": "package com.yupi.yudada.common;\n\nimport lombok.Data;\n\nimport java.io.Serializable;\n\n/**\n * 审核请求\n */\n@Data\npublic class R"
},
{
"path": "yudada-backend/src/main/java/com/yupi/yudada/config/AiConfig.java",
"chars": 537,
"preview": "package com.yupi.yudada.config;\n\nimport com.zhipu.oapi.ClientV4;\nimport lombok.Data;\nimport org.springframework.boot.con"
},
{
"path": "yudada-backend/src/main/java/com/yupi/yudada/config/CorsConfig.java",
"chars": 903,
"preview": "package com.yupi.yudada.config;\n\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework"
},
{
"path": "yudada-backend/src/main/java/com/yupi/yudada/config/CosClientConfig.java",
"chars": 1308,
"preview": "package com.yupi.yudada.config;\n\nimport com.qcloud.cos.COSClient;\nimport com.qcloud.cos.ClientConfig;\nimport com.qcloud."
},
{
"path": "yudada-backend/src/main/java/com/yupi/yudada/config/JsonConfig.java",
"chars": 1062,
"preview": "package com.yupi.yudada.config;\n\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport com.fasterxml.jackson.databi"
},
{
"path": "yudada-backend/src/main/java/com/yupi/yudada/config/MyBatisPlusConfig.java",
"chars": 894,
"preview": "package com.yupi.yudada.config;\n\nimport com.baomidou.mybatisplus.annotation.DbType;\nimport com.baomidou.mybatisplus.exte"
},
{
"path": "yudada-backend/src/main/java/com/yupi/yudada/config/RedissonConfig.java",
"chars": 910,
"preview": "package com.yupi.yudada.config;\n\nimport com.zhipu.oapi.ClientV4;\nimport lombok.Data;\nimport org.redisson.Redisson;\nimpor"
},
{
"path": "yudada-backend/src/main/java/com/yupi/yudada/config/VipSchedulerConfig.java",
"chars": 1192,
"preview": "package com.yupi.yudada.config;\n\nimport io.reactivex.Scheduler;\nimport io.reactivex.schedulers.Schedulers;\nimport lombok"
},
{
"path": "yudada-backend/src/main/java/com/yupi/yudada/constant/CommonConstant.java",
"chars": 334,
"preview": "package com.yupi.yudada.constant;\n\n/**\n * 通用常量\n *\n * @author <a href=\"https://github.com/liyupi\">程序员鱼皮</a>\n * @from <a h"
},
{
"path": "yudada-backend/src/main/java/com/yupi/yudada/constant/FileConstant.java",
"chars": 287,
"preview": "package com.yupi.yudada.constant;\n\n/**\n * 文件常量\n *\n * @author <a href=\"https://github.com/liyupi\">程序员鱼皮</a>\n * @from <a h"
},
{
"path": "yudada-backend/src/main/java/com/yupi/yudada/constant/UserConstant.java",
"chars": 489,
"preview": "package com.yupi.yudada.constant;\n\n/**\n * 用户常量\n *\n * @author <a href=\"https://github.com/liyupi\">程序员鱼皮</a>\n * @from <a h"
},
{
"path": "yudada-backend/src/main/java/com/yupi/yudada/controller/AppController.java",
"chars": 9875,
"preview": "package com.yupi.yudada.controller;\n\nimport com.baomidou.mybatisplus.extension.plugins.pagination.Page;\nimport com.yupi."
},
{
"path": "yudada-backend/src/main/java/com/yupi/yudada/controller/AppStatisticController.java",
"chars": 2023,
"preview": "package com.yupi.yudada.controller;\n\nimport cn.hutool.core.io.FileUtil;\nimport com.yupi.yudada.common.BaseResponse;\nimpo"
},
{
"path": "yudada-backend/src/main/java/com/yupi/yudada/controller/FileController.java",
"chars": 3908,
"preview": "package com.yupi.yudada.controller;\n\nimport cn.hutool.core.io.FileUtil;\nimport com.yupi.yudada.common.BaseResponse;\nimpo"
},
{
"path": "yudada-backend/src/main/java/com/yupi/yudada/controller/PostController.java",
"chars": 8609,
"preview": "package com.yupi.yudada.controller;\n\nimport cn.hutool.json.JSONUtil;\nimport com.baomidou.mybatisplus.extension.plugins.p"
},
{
"path": "yudada-backend/src/main/java/com/yupi/yudada/controller/PostFavourController.java",
"chars": 4126,
"preview": "package com.yupi.yudada.controller;\n\nimport com.baomidou.mybatisplus.extension.plugins.pagination.Page;\nimport com.yupi."
},
{
"path": "yudada-backend/src/main/java/com/yupi/yudada/controller/PostThumbController.java",
"chars": 1816,
"preview": "package com.yupi.yudada.controller;\n\nimport com.yupi.yudada.common.BaseResponse;\nimport com.yupi.yudada.common.ErrorCode"
},
{
"path": "yudada-backend/src/main/java/com/yupi/yudada/controller/QuestionController.java",
"chars": 18663,
"preview": "package com.yupi.yudada.controller;\n\nimport cn.hutool.core.util.StrUtil;\nimport cn.hutool.json.JSONUtil;\nimport com.baom"
},
{
"path": "yudada-backend/src/main/java/com/yupi/yudada/controller/ScoringResultController.java",
"chars": 10102,
"preview": "package com.yupi.yudada.controller;\n\nimport cn.hutool.json.JSONUtil;\nimport com.baomidou.mybatisplus.extension.plugins.p"
},
{
"path": "yudada-backend/src/main/java/com/yupi/yudada/controller/UserAnswerController.java",
"chars": 11143,
"preview": "package com.yupi.yudada.controller;\n\nimport cn.hutool.core.util.IdUtil;\nimport cn.hutool.json.JSONUtil;\nimport com.baomi"
},
{
"path": "yudada-backend/src/main/java/com/yupi/yudada/controller/UserController.java",
"chars": 9781,
"preview": "package com.yupi.yudada.controller;\n\nimport com.baomidou.mybatisplus.extension.plugins.pagination.Page;\nimport com.yupi."
},
{
"path": "yudada-backend/src/main/java/com/yupi/yudada/exception/BusinessException.java",
"chars": 769,
"preview": "package com.yupi.yudada.exception;\n\nimport com.yupi.yudada.common.ErrorCode;\n\n/**\n * 自定义异常类\n *\n * @author <a href=\"https"
},
{
"path": "yudada-backend/src/main/java/com/yupi/yudada/exception/GlobalExceptionHandler.java",
"chars": 999,
"preview": "package com.yupi.yudada.exception;\n\nimport com.yupi.yudada.common.BaseResponse;\nimport com.yupi.yudada.common.ErrorCode;"
},
{
"path": "yudada-backend/src/main/java/com/yupi/yudada/exception/ThrowUtils.java",
"chars": 997,
"preview": "package com.yupi.yudada.exception;\n\nimport com.yupi.yudada.common.ErrorCode;\n\n/**\n * 抛异常工具类\n *\n * @author <a href=\"https"
},
{
"path": "yudada-backend/src/main/java/com/yupi/yudada/generate/CodeGenerator.java",
"chars": 5361,
"preview": "package com.yupi.yudada.generate;\n\nimport cn.hutool.core.io.FileUtil;\nimport freemarker.template.Configuration;\nimport f"
},
{
"path": "yudada-backend/src/main/java/com/yupi/yudada/manager/AiManager.java",
"chars": 4707,
"preview": "package com.yupi.yudada.manager;\n\nimport com.yupi.yudada.common.ErrorCode;\nimport com.yupi.yudada.exception.BusinessExce"
},
{
"path": "yudada-backend/src/main/java/com/yupi/yudada/manager/CosManager.java",
"chars": 1309,
"preview": "package com.yupi.yudada.manager;\n\nimport com.qcloud.cos.COSClient;\nimport com.qcloud.cos.model.PutObjectRequest;\nimport "
},
{
"path": "yudada-backend/src/main/java/com/yupi/yudada/mapper/AppMapper.java",
"chars": 329,
"preview": "package com.yupi.yudada.mapper;\n\nimport com.yupi.yudada.model.entity.App;\nimport com.baomidou.mybatisplus.core.mapper.Ba"
},
{
"path": "yudada-backend/src/main/java/com/yupi/yudada/mapper/PostFavourMapper.java",
"chars": 931,
"preview": "package com.yupi.yudada.mapper;\n\nimport com.baomidou.mybatisplus.core.conditions.Wrapper;\nimport com.baomidou.mybatisplu"
},
{
"path": "yudada-backend/src/main/java/com/yupi/yudada/mapper/PostMapper.java",
"chars": 465,
"preview": "package com.yupi.yudada.mapper;\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport com.yupi.yudada.model.en"
},
{
"path": "yudada-backend/src/main/java/com/yupi/yudada/mapper/PostThumbMapper.java",
"chars": 339,
"preview": "package com.yupi.yudada.mapper;\n\nimport com.yupi.yudada.model.entity.PostThumb;\nimport com.baomidou.mybatisplus.core.map"
},
{
"path": "yudada-backend/src/main/java/com/yupi/yudada/mapper/QuestionMapper.java",
"chars": 354,
"preview": "package com.yupi.yudada.mapper;\n\nimport com.yupi.yudada.model.entity.Question;\nimport com.baomidou.mybatisplus.core.mapp"
},
{
"path": "yudada-backend/src/main/java/com/yupi/yudada/mapper/ScoringResultMapper.java",
"chars": 382,
"preview": "package com.yupi.yudada.mapper;\n\nimport com.yupi.yudada.model.entity.ScoringResult;\nimport com.baomidou.mybatisplus.core"
},
{
"path": "yudada-backend/src/main/java/com/yupi/yudada/mapper/UserAnswerMapper.java",
"chars": 1030,
"preview": "package com.yupi.yudada.mapper;\n\nimport com.yupi.yudada.model.dto.statistic.AppAnswerCountDTO;\nimport com.yupi.yudada.mo"
},
{
"path": "yudada-backend/src/main/java/com/yupi/yudada/mapper/UserMapper.java",
"chars": 322,
"preview": "package com.yupi.yudada.mapper;\n\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport com.yupi.yudada.model.en"
},
{
"path": "yudada-backend/src/main/java/com/yupi/yudada/model/dto/app/AppAddRequest.java",
"chars": 657,
"preview": "package com.yupi.yudada.model.dto.app;\n\nimport lombok.Data;\n\nimport java.io.Serializable;\n\n/**\n * 创建应用请求\n *\n * @author <"
},
{
"path": "yudada-backend/src/main/java/com/yupi/yudada/model/dto/app/AppEditRequest.java",
"chars": 705,
"preview": "package com.yupi.yudada.model.dto.app;\n\nimport lombok.Data;\n\nimport java.io.Serializable;\n\n/**\n * 编辑应用请求\n *\n * @author <"
},
{
"path": "yudada-backend/src/main/java/com/yupi/yudada/model/dto/app/AppQueryRequest.java",
"chars": 1211,
"preview": "package com.yupi.yudada.model.dto.app;\n\nimport com.yupi.yudada.common.PageRequest;\nimport lombok.Data;\nimport lombok.Equ"
},
{
"path": "yudada-backend/src/main/java/com/yupi/yudada/model/dto/app/AppUpdateRequest.java",
"chars": 993,
"preview": "package com.yupi.yudada.model.dto.app;\n\nimport lombok.Data;\n\nimport java.io.Serializable;\nimport java.util.Date;\n\n/**\n *"
},
{
"path": "yudada-backend/src/main/java/com/yupi/yudada/model/dto/file/UploadFileRequest.java",
"chars": 387,
"preview": "package com.yupi.yudada.model.dto.file;\n\nimport java.io.Serializable;\nimport lombok.Data;\n\n/**\n * 文件上传请求\n *\n * @author <"
},
{
"path": "yudada-backend/src/main/java/com/yupi/yudada/model/dto/post/PostAddRequest.java",
"chars": 522,
"preview": "package com.yupi.yudada.model.dto.post;\n\nimport java.io.Serializable;\nimport java.util.List;\nimport lombok.Data;\n\n/**\n *"
},
{
"path": "yudada-backend/src/main/java/com/yupi/yudada/model/dto/post/PostEditRequest.java",
"chars": 571,
"preview": "package com.yupi.yudada.model.dto.post;\n\nimport java.io.Serializable;\nimport java.util.List;\nimport lombok.Data;\n\n/**\n *"
},
{
"path": "yudada-backend/src/main/java/com/yupi/yudada/model/dto/post/PostQueryRequest.java",
"chars": 1000,
"preview": "package com.yupi.yudada.model.dto.post;\n\nimport com.yupi.yudada.common.PageRequest;\nimport java.io.Serializable;\nimport "
},
{
"path": "yudada-backend/src/main/java/com/yupi/yudada/model/dto/post/PostUpdateRequest.java",
"chars": 573,
"preview": "package com.yupi.yudada.model.dto.post;\n\nimport java.io.Serializable;\nimport java.util.List;\nimport lombok.Data;\n\n/**\n *"
},
{
"path": "yudada-backend/src/main/java/com/yupi/yudada/model/dto/postfavour/PostFavourAddRequest.java",
"chars": 407,
"preview": "package com.yupi.yudada.model.dto.postfavour;\n\nimport java.io.Serializable;\nimport lombok.Data;\n\n/**\n * 帖子收藏 / 取消收藏请求\n *"
},
{
"path": "yudada-backend/src/main/java/com/yupi/yudada/model/dto/postfavour/PostFavourQueryRequest.java",
"chars": 671,
"preview": "package com.yupi.yudada.model.dto.postfavour;\n\nimport com.yupi.yudada.common.PageRequest;\nimport com.yupi.yudada.model.d"
},
{
"path": "yudada-backend/src/main/java/com/yupi/yudada/model/dto/postthumb/PostThumbAddRequest.java",
"chars": 398,
"preview": "package com.yupi.yudada.model.dto.postthumb;\n\nimport java.io.Serializable;\nimport lombok.Data;\n\n/**\n * 帖子点赞请求\n *\n * @aut"
},
{
"path": "yudada-backend/src/main/java/com/yupi/yudada/model/dto/question/AiGenerateQuestionRequest.java",
"chars": 524,
"preview": "package com.yupi.yudada.model.dto.question;\n\nimport lombok.Data;\n\nimport java.io.Serializable;\n\n/**\n * AI 生成题目请求\n *\n * @"
},
{
"path": "yudada-backend/src/main/java/com/yupi/yudada/model/dto/question/QuestionAddRequest.java",
"chars": 642,
"preview": "package com.yupi.yudada.model.dto.question;\n\nimport com.baomidou.mybatisplus.annotation.IdType;\nimport com.baomidou.myba"
},
{
"path": "yudada-backend/src/main/java/com/yupi/yudada/model/dto/question/QuestionAnswerDTO.java",
"chars": 364,
"preview": "package com.yupi.yudada.model.dto.question;\n\nimport lombok.Data;\n\n/**\n * 题目答案封装类(用于 AI 评分)\n *\n * @author <a href=\"https:"
},
{
"path": "yudada-backend/src/main/java/com/yupi/yudada/model/dto/question/QuestionContentDTO.java",
"chars": 601,
"preview": "package com.yupi.yudada.model.dto.question;\n\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\nimport lombok.AllArgsC"
},
{
"path": "yudada-backend/src/main/java/com/yupi/yudada/model/dto/question/QuestionEditRequest.java",
"chars": 637,
"preview": "package com.yupi.yudada.model.dto.question;\n\nimport com.baomidou.mybatisplus.annotation.IdType;\nimport com.baomidou.myba"
},
{
"path": "yudada-backend/src/main/java/com/yupi/yudada/model/dto/question/QuestionQueryRequest.java",
"chars": 915,
"preview": "package com.yupi.yudada.model.dto.question;\n\nimport com.baomidou.mybatisplus.annotation.IdType;\nimport com.baomidou.myba"
},
{
"path": "yudada-backend/src/main/java/com/yupi/yudada/model/dto/question/QuestionUpdateRequest.java",
"chars": 513,
"preview": "package com.yupi.yudada.model.dto.question;\n\nimport lombok.Data;\n\nimport java.io.Serializable;\nimport java.util.List;\n\n/"
},
{
"path": "yudada-backend/src/main/java/com/yupi/yudada/model/dto/scoringResult/ScoringResultAddRequest.java",
"chars": 927,
"preview": "package com.yupi.yudada.model.dto.scoringResult;\n\nimport com.baomidou.mybatisplus.annotation.IdType;\nimport com.baomidou"
},
{
"path": "yudada-backend/src/main/java/com/yupi/yudada/model/dto/scoringResult/ScoringResultEditRequest.java",
"chars": 922,
"preview": "package com.yupi.yudada.model.dto.scoringResult;\n\nimport com.baomidou.mybatisplus.annotation.IdType;\nimport com.baomidou"
},
{
"path": "yudada-backend/src/main/java/com/yupi/yudada/model/dto/scoringResult/ScoringResultQueryRequest.java",
"chars": 1271,
"preview": "package com.yupi.yudada.model.dto.scoringResult;\n\nimport com.baomidou.mybatisplus.annotation.IdType;\nimport com.baomidou"
},
{
"path": "yudada-backend/src/main/java/com/yupi/yudada/model/dto/scoringResult/ScoringResultUpdateRequest.java",
"chars": 798,
"preview": "package com.yupi.yudada.model.dto.scoringResult;\n\nimport lombok.Data;\n\nimport java.io.Serializable;\nimport java.util.Lis"
},
{
"path": "yudada-backend/src/main/java/com/yupi/yudada/model/dto/statistic/AppAnswerCountDTO.java",
"chars": 219,
"preview": "package com.yupi.yudada.model.dto.statistic;\n\nimport lombok.Data;\n\n/**\n * App 用户提交答案书统计\n */\n@Data\npublic class AppAnswer"
},
{
"path": "yudada-backend/src/main/java/com/yupi/yudada/model/dto/statistic/AppAnswerResultCountDTO.java",
"chars": 222,
"preview": "package com.yupi.yudada.model.dto.statistic;\n\nimport lombok.Data;\n\n/**\n * App 答案结果统计\n */\n@Data\npublic class AppAnswerRes"
},
{
"path": "yudada-backend/src/main/java/com/yupi/yudada/model/dto/user/UserAddRequest.java",
"chars": 581,
"preview": "package com.yupi.yudada.model.dto.user;\n\nimport java.io.Serializable;\nimport lombok.Data;\n\n/**\n * 用户创建请求\n *\n * @author <"
},
{
"path": "yudada-backend/src/main/java/com/yupi/yudada/model/dto/user/UserLoginRequest.java",
"chars": 421,
"preview": "package com.yupi.yudada.model.dto.user;\n\nimport java.io.Serializable;\nimport lombok.Data;\n\n/**\n * 用户登录请求\n *\n * @author <"
},
{
"path": "yudada-backend/src/main/java/com/yupi/yudada/model/dto/user/UserQueryRequest.java",
"chars": 827,
"preview": "package com.yupi.yudada.model.dto.user;\n\nimport com.yupi.yudada.common.PageRequest;\nimport java.io.Serializable;\nimport "
},
{
"path": "yudada-backend/src/main/java/com/yupi/yudada/model/dto/user/UserRegisterRequest.java",
"chars": 460,
"preview": "package com.yupi.yudada.model.dto.user;\n\nimport java.io.Serializable;\nimport lombok.Data;\n\n/**\n * 用户注册请求体\n *\n * @author "
},
{
"path": "yudada-backend/src/main/java/com/yupi/yudada/model/dto/user/UserUpdateMyRequest.java",
"chars": 519,
"preview": "package com.yupi.yudada.model.dto.user;\n\nimport java.io.Serializable;\nimport lombok.Data;\n\n/**\n * 用户更新个人信息请求\n *\n * @auth"
},
{
"path": "yudada-backend/src/main/java/com/yupi/yudada/model/dto/user/UserUpdateRequest.java",
"chars": 633,
"preview": "package com.yupi.yudada.model.dto.user;\n\nimport java.io.Serializable;\nimport lombok.Data;\n\n/**\n * 用户更新请求\n *\n * @author <"
},
{
"path": "yudada-backend/src/main/java/com/yupi/yudada/model/dto/userAnswer/UserAnswerAddRequest.java",
"chars": 573,
"preview": "package com.yupi.yudada.model.dto.userAnswer;\n\nimport lombok.Data;\n\nimport java.io.Serializable;\nimport java.util.List;\n"
},
{
"path": "yudada-backend/src/main/java/com/yupi/yudada/model/dto/userAnswer/UserAnswerEditRequest.java",
"chars": 656,
"preview": "package com.yupi.yudada.model.dto.userAnswer;\n\nimport com.baomidou.mybatisplus.annotation.IdType;\nimport com.baomidou.my"
},
{
"path": "yudada-backend/src/main/java/com/yupi/yudada/model/dto/userAnswer/UserAnswerQueryRequest.java",
"chars": 1403,
"preview": "package com.yupi.yudada.model.dto.userAnswer;\n\nimport com.baomidou.mybatisplus.annotation.IdType;\nimport com.baomidou.my"
},
{
"path": "yudada-backend/src/main/java/com/yupi/yudada/model/dto/userAnswer/UserAnswerUpdateRequest.java",
"chars": 554,
"preview": "package com.yupi.yudada.model.dto.userAnswer;\n\nimport lombok.Data;\n\nimport java.io.Serializable;\nimport java.util.List;\n"
},
{
"path": "yudada-backend/src/main/java/com/yupi/yudada/model/entity/App.java",
"chars": 1265,
"preview": "package com.yupi.yudada.model.entity;\n\nimport com.baomidou.mybatisplus.annotation.*;\n\nimport java.io.Serializable;\nimpor"
},
{
"path": "yudada-backend/src/main/java/com/yupi/yudada/model/entity/Post.java",
"chars": 1283,
"preview": "package com.yupi.yudada.model.entity;\n\nimport com.baomidou.mybatisplus.annotation.IdType;\nimport com.baomidou.mybatisplu"
},
{
"path": "yudada-backend/src/main/java/com/yupi/yudada/model/entity/PostFavour.java",
"chars": 935,
"preview": "package com.yupi.yudada.model.entity;\n\nimport com.baomidou.mybatisplus.annotation.IdType;\nimport com.baomidou.mybatisplu"
},
{
"path": "yudada-backend/src/main/java/com/yupi/yudada/model/entity/PostThumb.java",
"chars": 932,
"preview": "package com.yupi.yudada.model.entity;\n\nimport com.baomidou.mybatisplus.annotation.IdType;\nimport com.baomidou.mybatisplu"
},
{
"path": "yudada-backend/src/main/java/com/yupi/yudada/model/entity/Question.java",
"chars": 826,
"preview": "package com.yupi.yudada.model.entity;\n\nimport com.baomidou.mybatisplus.annotation.*;\n\nimport java.io.Serializable;\nimpor"
},
{
"path": "yudada-backend/src/main/java/com/yupi/yudada/model/entity/ScoringResult.java",
"chars": 1130,
"preview": "package com.yupi.yudada.model.entity;\n\nimport com.baomidou.mybatisplus.annotation.*;\n\nimport java.io.Serializable;\nimpor"
},
{
"path": "yudada-backend/src/main/java/com/yupi/yudada/model/entity/User.java",
"chars": 1439,
"preview": "package com.yupi.yudada.model.entity;\n\nimport com.baomidou.mybatisplus.annotation.IdType;\nimport com.baomidou.mybatisplu"
},
{
"path": "yudada-backend/src/main/java/com/yupi/yudada/model/entity/UserAnswer.java",
"chars": 1285,
"preview": "package com.yupi.yudada.model.entity;\n\nimport com.baomidou.mybatisplus.annotation.*;\n\nimport java.io.Serializable;\nimpor"
},
{
"path": "yudada-backend/src/main/java/com/yupi/yudada/model/enums/AppScoringStrategyEnum.java",
"chars": 1299,
"preview": "package com.yupi.yudada.model.enums;\n\nimport cn.hutool.core.util.ObjectUtil;\n\nimport java.util.Arrays;\nimport java.util."
},
{
"path": "yudada-backend/src/main/java/com/yupi/yudada/model/enums/AppTypeEnum.java",
"chars": 1246,
"preview": "package com.yupi.yudada.model.enums;\n\nimport cn.hutool.core.util.ObjectUtil;\n\nimport java.util.Arrays;\nimport java.util."
},
{
"path": "yudada-backend/src/main/java/com/yupi/yudada/model/enums/FileUploadBizEnum.java",
"chars": 1385,
"preview": "package com.yupi.yudada.model.enums;\n\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.stream.Collectors"
},
{
"path": "yudada-backend/src/main/java/com/yupi/yudada/model/enums/ReviewStatusEnum.java",
"chars": 1291,
"preview": "package com.yupi.yudada.model.enums;\n\nimport cn.hutool.core.util.ObjectUtil;\n\nimport java.util.Arrays;\nimport java.util."
},
{
"path": "yudada-backend/src/main/java/com/yupi/yudada/model/enums/UserRoleEnum.java",
"chars": 1292,
"preview": "package com.yupi.yudada.model.enums;\n\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.stream.Collectors"
},
{
"path": "yudada-backend/src/main/java/com/yupi/yudada/model/vo/AppVO.java",
"chars": 1962,
"preview": "package com.yupi.yudada.model.vo;\n\nimport cn.hutool.json.JSONUtil;\nimport com.baomidou.mybatisplus.annotation.IdType;\nim"
},
{
"path": "yudada-backend/src/main/java/com/yupi/yudada/model/vo/LoginUserVO.java",
"chars": 772,
"preview": "package com.yupi.yudada.model.vo;\n\nimport java.io.Serializable;\nimport java.util.Date;\nimport lombok.Data;\n\n/**\n * 已登录用户"
},
{
"path": "yudada-backend/src/main/java/com/yupi/yudada/model/vo/PostVO.java",
"chars": 1880,
"preview": "package com.yupi.yudada.model.vo;\n\nimport cn.hutool.json.JSONUtil;\nimport com.yupi.yudada.model.entity.Post;\nimport java"
},
{
"path": "yudada-backend/src/main/java/com/yupi/yudada/model/vo/QuestionVO.java",
"chars": 2087,
"preview": "package com.yupi.yudada.model.vo;\n\nimport cn.hutool.json.JSONUtil;\nimport com.baomidou.mybatisplus.annotation.IdType;\nim"
},
{
"path": "yudada-backend/src/main/java/com/yupi/yudada/model/vo/ScoringResultVO.java",
"chars": 2232,
"preview": "package com.yupi.yudada.model.vo;\n\nimport cn.hutool.json.JSONUtil;\nimport com.baomidou.mybatisplus.annotation.IdType;\nim"
},
{
"path": "yudada-backend/src/main/java/com/yupi/yudada/model/vo/UserAnswerVO.java",
"chars": 2296,
"preview": "package com.yupi.yudada.model.vo;\n\nimport cn.hutool.json.JSONUtil;\nimport com.baomidou.mybatisplus.annotation.IdType;\nim"
},
{
"path": "yudada-backend/src/main/java/com/yupi/yudada/model/vo/UserVO.java",
"chars": 702,
"preview": "package com.yupi.yudada.model.vo;\n\nimport java.io.Serializable;\nimport java.util.Date;\nimport lombok.Data;\n\n/**\n * 用户视图("
},
{
"path": "yudada-backend/src/main/java/com/yupi/yudada/scoring/AiTestScoringStrategy.java",
"chars": 5977,
"preview": "package com.yupi.yudada.scoring;\n\nimport cn.hutool.core.util.StrUtil;\nimport cn.hutool.crypto.digest.DigestUtil;\nimport "
},
{
"path": "yudada-backend/src/main/java/com/yupi/yudada/scoring/CustomScoreScoringStrategy.java",
"chars": 3507,
"preview": "package com.yupi.yudada.scoring;\n\nimport cn.hutool.json.JSONUtil;\nimport com.baomidou.mybatisplus.core.toolkit.Wrappers;"
},
{
"path": "yudada-backend/src/main/java/com/yupi/yudada/scoring/CustomTestScoringStrategy.java",
"chars": 4008,
"preview": "package com.yupi.yudada.scoring;\n\nimport cn.hutool.json.JSONUtil;\nimport com.baomidou.mybatisplus.core.toolkit.Wrappers;"
},
{
"path": "yudada-backend/src/main/java/com/yupi/yudada/scoring/ScoringStrategy.java",
"chars": 503,
"preview": "package com.yupi.yudada.scoring;\n\nimport com.yupi.yudada.model.entity.App;\nimport com.yupi.yudada.model.entity.UserAnswe"
},
{
"path": "yudada-backend/src/main/java/com/yupi/yudada/scoring/ScoringStrategyConfig.java",
"chars": 496,
"preview": "package com.yupi.yudada.scoring;\n\nimport org.springframework.stereotype.Component;\n\nimport java.lang.annotation.ElementT"
},
{
"path": "yudada-backend/src/main/java/com/yupi/yudada/scoring/ScoringStrategyContext.java",
"chars": 1961,
"preview": "package com.yupi.yudada.scoring;\n\nimport com.yupi.yudada.common.ErrorCode;\nimport com.yupi.yudada.exception.BusinessExce"
},
{
"path": "yudada-backend/src/main/java/com/yupi/yudada/scoring/ScoringStrategyExecutor.java",
"chars": 1554,
"preview": "package com.yupi.yudada.scoring;\n\nimport com.yupi.yudada.common.ErrorCode;\nimport com.yupi.yudada.exception.BusinessExce"
},
{
"path": "yudada-backend/src/main/java/com/yupi/yudada/service/AppService.java",
"chars": 1205,
"preview": "package com.yupi.yudada.service;\n\nimport com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;\nimport com.baomido"
},
{
"path": "yudada-backend/src/main/java/com/yupi/yudada/service/PostFavourService.java",
"chars": 1145,
"preview": "package com.yupi.yudada.service;\n\nimport com.baomidou.mybatisplus.core.conditions.Wrapper;\nimport com.baomidou.mybatispl"
},
{
"path": "yudada-backend/src/main/java/com/yupi/yudada/service/PostService.java",
"chars": 1205,
"preview": "package com.yupi.yudada.service;\n\nimport com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;\nimport com.baomido"
},
{
"path": "yudada-backend/src/main/java/com/yupi/yudada/service/PostThumbService.java",
"chars": 672,
"preview": "package com.yupi.yudada.service;\n\nimport com.yupi.yudada.model.entity.PostThumb;\nimport com.baomidou.mybatisplus.extensi"
},
{
"path": "yudada-backend/src/main/java/com/yupi/yudada/service/QuestionService.java",
"chars": 1325,
"preview": "package com.yupi.yudada.service;\n\nimport com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;\nimport com.baomido"
},
{
"path": "yudada-backend/src/main/java/com/yupi/yudada/service/ScoringResultService.java",
"chars": 1451,
"preview": "package com.yupi.yudada.service;\n\nimport com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;\nimport com.baomido"
},
{
"path": "yudada-backend/src/main/java/com/yupi/yudada/service/UserAnswerService.java",
"chars": 1379,
"preview": "package com.yupi.yudada.service;\n\nimport com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;\nimport com.baomido"
},
{
"path": "yudada-backend/src/main/java/com/yupi/yudada/service/UserService.java",
"chars": 2205,
"preview": "package com.yupi.yudada.service;\n\nimport com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;\nimport com.baomido"
},
{
"path": "yudada-backend/src/main/java/com/yupi/yudada/service/impl/AppServiceImpl.java",
"chars": 7646,
"preview": "package com.yupi.yudada.service.impl;\n\nimport cn.hutool.core.collection.CollUtil;\nimport com.baomidou.mybatisplus.core.c"
},
{
"path": "yudada-backend/src/main/java/com/yupi/yudada/service/impl/PostFavourServiceImpl.java",
"chars": 3765,
"preview": "package com.yupi.yudada.service.impl;\n\nimport com.baomidou.mybatisplus.core.conditions.Wrapper;\nimport com.baomidou.myba"
},
{
"path": "yudada-backend/src/main/java/com/yupi/yudada/service/impl/PostServiceImpl.java",
"chars": 8317,
"preview": "package com.yupi.yudada.service.impl;\n\nimport com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;\nimport com.ba"
},
{
"path": "yudada-backend/src/main/java/com/yupi/yudada/service/impl/PostThumbServiceImpl.java",
"chars": 3253,
"preview": "package com.yupi.yudada.service.impl;\n\nimport com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;\nimport com.ba"
},
{
"path": "yudada-backend/src/main/java/com/yupi/yudada/service/impl/QuestionServiceImpl.java",
"chars": 6105,
"preview": "package com.yupi.yudada.service.impl;\n\nimport cn.hutool.core.collection.CollUtil;\nimport com.baomidou.mybatisplus.core.c"
},
{
"path": "yudada-backend/src/main/java/com/yupi/yudada/service/impl/ScoringResultServiceImpl.java",
"chars": 7576,
"preview": "package com.yupi.yudada.service.impl;\n\nimport cn.hutool.core.collection.CollUtil;\nimport com.baomidou.mybatisplus.core.c"
},
{
"path": "yudada-backend/src/main/java/com/yupi/yudada/service/impl/UserAnswerServiceImpl.java",
"chars": 7561,
"preview": "package com.yupi.yudada.service.impl;\n\nimport cn.hutool.core.collection.CollUtil;\nimport com.baomidou.mybatisplus.core.c"
},
{
"path": "yudada-backend/src/main/java/com/yupi/yudada/service/impl/UserServiceImpl.java",
"chars": 8559,
"preview": "package com.yupi.yudada.service.impl;\n\nimport static com.yupi.yudada.constant.UserConstant.USER_LOGIN_STATE;\n\nimport cn."
},
{
"path": "yudada-backend/src/main/java/com/yupi/yudada/utils/NetUtils.java",
"chars": 1602,
"preview": "package com.yupi.yudada.utils;\n\nimport java.net.InetAddress;\nimport javax.servlet.http.HttpServletRequest;\n\n/**\n * 网络工具类"
},
{
"path": "yudada-backend/src/main/java/com/yupi/yudada/utils/SpringContextUtils.java",
"chars": 1440,
"preview": "package com.yupi.yudada.utils;\n\nimport org.jetbrains.annotations.NotNull;\nimport org.springframework.beans.BeansExceptio"
},
{
"path": "yudada-backend/src/main/java/com/yupi/yudada/utils/SqlUtils.java",
"chars": 544,
"preview": "package com.yupi.yudada.utils;\n\nimport org.apache.commons.lang3.StringUtils;\n\n/**\n * SQL 工具\n *\n * @author <a href=\"https"
},
{
"path": "yudada-backend/src/main/resources/META-INF/additional-spring-configuration-metadata.json",
"chars": 868,
"preview": "{\n \"properties\": [\n {\n \"name\": \"cos.client.accessKey\",\n \"type\": \"java.lang.String\",\n \"description\": \""
},
{
"path": "yudada-backend/src/main/resources/application-prod.yml",
"chars": 587,
"preview": "# 线上配置文件\n# @author <a href=\"https://github.com/liyupi\">程序员鱼皮</a>\n# @from <a href=\"https://yupi.icu\">编程导航知识星球</a>\nserver:"
},
{
"path": "yudada-backend/src/main/resources/application-test.yml",
"chars": 440,
"preview": "# 测试配置文件\n# @author <a href=\"https://github.com/liyupi\">程序员鱼皮</a>\n# @from <a href=\"https://yupi.icu\">编程导航知识星球</a>\nserver:"
},
{
"path": "yudada-backend/src/main/resources/application.yml",
"chars": 2450,
"preview": "# 公共配置文件\n# @author <a href=\"https://github.com/liyupi\">程序员鱼皮</a>\n# @from <a href=\"https://yupi.icu\">编程导航知识星球</a>\nspring:"
},
{
"path": "yudada-backend/src/main/resources/banner.txt",
"chars": 65,
"preview": "by 程序员鱼皮:https://github.com/liyupi\n可能是最好的编程学习圈子:https://yupi.icu\n"
},
{
"path": "yudada-backend/src/main/resources/mapper/AppMapper.xml",
"chars": 1675,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper\n PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"\n \"htt"
},
{
"path": "yudada-backend/src/main/resources/mapper/PostFavourMapper.xml",
"chars": 1218,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!-- @author <a href=\"https://github.com/liyupi\">程序员鱼皮</a> -->\n<!-- @from <a href"
},
{
"path": "yudada-backend/src/main/resources/mapper/PostMapper.xml",
"chars": 1492,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!-- @author <a href=\"https://github.com/liyupi\">程序员鱼皮</a> -->\n<!-- @from <a href"
},
{
"path": "yudada-backend/src/main/resources/mapper/PostThumbMapper.xml",
"chars": 930,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!-- @author <a href=\"https://github.com/liyupi\">程序员鱼皮</a> -->\n<!-- @from <a href"
},
{
"path": "yudada-backend/src/main/resources/mapper/QuestionMapper.xml",
"chars": 1013,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper\n PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"\n \"htt"
},
{
"path": "yudada-backend/src/main/resources/mapper/ScoringResultMapper.xml",
"chars": 1420,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper\n PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"\n \"htt"
},
{
"path": "yudada-backend/src/main/resources/mapper/UserAnswerMapper.xml",
"chars": 1680,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper\n PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"\n \"htt"
},
{
"path": "yudada-backend/src/main/resources/mapper/UserMapper.xml",
"chars": 1354,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!-- @author <a href=\"https://github.com/liyupi\">程序员鱼皮</a> -->\n<!-- @from <a href"
},
{
"path": "yudada-backend/src/main/resources/templates/TemplateController.java.ftl",
"chars": 9542,
"preview": "package ${packageName}.controller;\n\nimport com.baomidou.mybatisplus.extension.plugins.pagination.Page;\nimport ${packageN"
},
{
"path": "yudada-backend/src/main/resources/templates/TemplateService.java.ftl",
"chars": 1471,
"preview": "package ${packageName}.service;\n\nimport com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;\nimport com.baomidou"
},
{
"path": "yudada-backend/src/main/resources/templates/TemplateServiceImpl.java.ftl",
"chars": 9610,
"preview": "package ${packageName}.service.impl;\n\nimport cn.hutool.core.collection.CollUtil;\nimport com.baomidou.mybatisplus.core.co"
},
{
"path": "yudada-backend/src/main/resources/templates/model/TemplateAddRequest.java.ftl",
"chars": 556,
"preview": "package ${packageName}.model.dto.${dataKey};\n\nimport lombok.Data;\n\nimport java.io.Serializable;\nimport java.util.List;\n\n"
},
{
"path": "yudada-backend/src/main/resources/templates/model/TemplateEditRequest.java.ftl",
"chars": 605,
"preview": "package ${packageName}.model.dto.${dataKey};\n\nimport lombok.Data;\n\nimport java.io.Serializable;\nimport java.util.List;\n\n"
},
{
"path": "yudada-backend/src/main/resources/templates/model/TemplateQueryRequest.java.ftl",
"chars": 905,
"preview": "package ${packageName}.model.dto.${dataKey};\n\nimport ${packageName}.common.PageRequest;\nimport lombok.Data;\nimport lombo"
},
{
"path": "yudada-backend/src/main/resources/templates/model/TemplateUpdateRequest.java.ftl",
"chars": 607,
"preview": "package ${packageName}.model.dto.${dataKey};\n\nimport lombok.Data;\n\nimport java.io.Serializable;\nimport java.util.List;\n\n"
},
{
"path": "yudada-backend/src/main/resources/templates/model/TemplateVO.java.ftl",
"chars": 1875,
"preview": "package ${packageName}.model.vo;\n\nimport cn.hutool.json.JSONUtil;\nimport ${packageName}.model.entity.${upperDataKey};\nim"
},
{
"path": "yudada-backend/src/test/java/com/yupi/yudada/MainApplicationTests.java",
"chars": 339,
"preview": "package com.yupi.yudada;\n\nimport org.junit.jupiter.api.Test;\nimport org.springframework.boot.test.context.SpringBootTest"
},
{
"path": "yudada-backend/src/test/java/com/yupi/yudada/QuestionControllerTest.java",
"chars": 1179,
"preview": "package com.yupi.yudada;\n\nimport com.yupi.yudada.controller.QuestionController;\nimport com.yupi.yudada.model.dto.questio"
},
{
"path": "yudada-backend/src/test/java/com/yupi/yudada/RxJavaTest.java",
"chars": 792,
"preview": "package com.yupi.yudada;\n\nimport io.reactivex.Flowable;\nimport io.reactivex.schedulers.Schedulers;\nimport org.junit.jupi"
},
{
"path": "yudada-backend/src/test/java/com/yupi/yudada/UserAnswerShardingTest.java",
"chars": 1292,
"preview": "package com.yupi.yudada;\n\nimport cn.hutool.json.JSONUtil;\nimport com.baomidou.mybatisplus.core.toolkit.Wrappers;\nimport "
},
{
"path": "yudada-backend/src/test/java/com/yupi/yudada/ZhiPuAiTest.java",
"chars": 1520,
"preview": "package com.yupi.yudada;\n\nimport com.zhipu.oapi.ClientV4;\nimport com.zhipu.oapi.Constants;\nimport com.zhipu.oapi.service"
},
{
"path": "yudada-backend/src/test/java/com/yupi/yudada/manager/CosManagerTest.java",
"chars": 475,
"preview": "package com.yupi.yudada.manager;\n\nimport javax.annotation.Resource;\nimport org.junit.jupiter.api.Test;\nimport org.spring"
},
{
"path": "yudada-backend/src/test/java/com/yupi/yudada/mapper/PostFavourMapperTest.java",
"chars": 1049,
"preview": "package com.yupi.yudada.mapper;\n\nimport com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;\nimport com.baomidou"
},
{
"path": "yudada-backend/src/test/java/com/yupi/yudada/mapper/PostMapperTest.java",
"chars": 679,
"preview": "package com.yupi.yudada.mapper;\n\nimport com.yupi.yudada.model.entity.Post;\nimport java.util.Date;\nimport java.util.List;"
},
{
"path": "yudada-backend/src/test/java/com/yupi/yudada/service/PostFavourServiceTest.java",
"chars": 1241,
"preview": "package com.yupi.yudada.service;\n\nimport com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;\nimport com.baomido"
},
{
"path": "yudada-backend/src/test/java/com/yupi/yudada/service/PostThumbServiceTest.java",
"chars": 800,
"preview": "package com.yupi.yudada.service;\n\nimport com.yupi.yudada.model.entity.User;\nimport javax.annotation.Resource;\nimport org"
},
{
"path": "yudada-backend/src/test/java/com/yupi/yudada/service/UserServiceTest.java",
"chars": 959,
"preview": "package com.yupi.yudada.service;\n\nimport javax.annotation.Resource;\nimport org.junit.jupiter.api.Assertions;\nimport org."
}
]
// ... and 64 more files (download for full content)
About this extraction
This page contains the full source code of the liyupi/yudada GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 264 files (513.2 KB), approximately 152.0k tokens, and a symbol index with 568 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.