mitao
2024-09-07 2862c3e4da3adbb4bea43151514f0c43b86476d6
APP端冥想首页相关接口
4 文件已复制
6 文件已重命名
70个文件已修改
4个文件已删除
56个文件已添加
4632 ■■■■ 已修改文件
pom.xml 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-api/xinquan-api-system/src/main/java/com/xinquan/system/api/RemoteBannerService.java 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-api/xinquan-api-system/src/main/java/com/xinquan/system/api/RemoteHotWordsService.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-api/xinquan-api-system/src/main/java/com/xinquan/system/api/domain/SysUser.java 22 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-api/xinquan-api-system/src/main/java/com/xinquan/system/api/domain/vo/BannerVO.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-api/xinquan-api-system/src/main/java/com/xinquan/system/api/factory/RemoteBannerFallbackFactory.java 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-api/xinquan-api-system/src/main/java/com/xinquan/system/api/factory/RemoteHotWordsFallbackFactory.java 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-api/xinquan-api-system/src/main/java/com/xinquan/system/api/model/AppCaptchaBody.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-api/xinquan-api-system/src/main/java/com/xinquan/system/api/model/AppLoginUser.java 20 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-api/xinquan-api-system/src/main/java/com/xinquan/system/api/model/AppVerifyCellPhoneBody.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-api/xinquan-api-system/src/main/java/com/xinquan/system/api/model/AppWXLoginBody.java 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-api/xinquan-api-system/src/main/java/com/xinquan/system/api/model/AppleLoginUser.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-api/xinquan-api-system/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-api/xinquan-api-user/src/main/java/com/xinquan/user/api/domain/AppUser.java 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-api/xinquan-api-user/src/main/java/com/xinquan/user/api/domain/dto/AppUserDTO.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-api/xinquan-api-user/src/main/java/com/xinquan/user/api/factory/RemoteAppUserFallbackFactory.java 27 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-api/xinquan-api-user/src/main/java/com/xinquan/user/api/feign/RemoteAppUserService.java 38 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-auth/src/main/java/com/xinquan/auth/controller/TokenController.java 41 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-auth/src/main/java/com/xinquan/auth/form/AppChangePwdBody.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-auth/src/main/java/com/xinquan/auth/form/AppLoginBody.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-auth/src/main/java/com/xinquan/auth/form/AppRegisterBody.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-auth/src/main/java/com/xinquan/auth/service/SysLoginService.java 138 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-common/xinquan-common-core/pom.xml 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-common/xinquan-common-core/src/main/java/com/xinquan/common/core/constant/CacheConstants.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-common/xinquan-common-core/src/main/java/com/xinquan/common/core/domain/R.java 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-common/xinquan-common-core/src/main/java/com/xinquan/common/core/enums/AnswerEnum.java 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-common/xinquan-common-core/src/main/java/com/xinquan/common/core/enums/AnswerTagEnum.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-common/xinquan-common-core/src/main/java/com/xinquan/common/core/enums/ReportStatusEnum.java 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-common/xinquan-common-core/src/main/java/com/xinquan/common/core/utils/Base64DES.java 153 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-common/xinquan-common-core/src/main/java/com/xinquan/common/core/utils/DES.java 158 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-common/xinquan-common-core/src/main/java/com/xinquan/common/core/web/domain/BaseEntity.java 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-common/xinquan-common-security/src/main/java/com/xinquan/common/security/service/TokenService.java 70 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-gateway/src/main/java/com/xinquan/gateway/filter/AuthFilter.java 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-modules/xinquan-course/pom.xml 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-modules/xinquan-course/src/main/java/com/xinquan/course/controller/client/ClientCourseController.java 70 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-modules/xinquan-course/src/main/java/com/xinquan/course/controller/inner/InnerCourseLearningRecordController.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-modules/xinquan-course/src/main/java/com/xinquan/course/domain/CourseLearningRecord.java 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-modules/xinquan-course/src/main/java/com/xinquan/course/domain/vo/ClientCourseCategoryVO.java 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-modules/xinquan-course/src/main/java/com/xinquan/course/domain/vo/ClientCourseVO.java 68 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-modules/xinquan-course/src/main/java/com/xinquan/course/mapper/CourseLearningRecordMapper.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-modules/xinquan-course/src/main/java/com/xinquan/course/service/CourseCategoryService.java 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-modules/xinquan-course/src/main/java/com/xinquan/course/service/CourseLearningRecordService.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-modules/xinquan-course/src/main/java/com/xinquan/course/service/CourseService.java 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-modules/xinquan-course/src/main/java/com/xinquan/course/service/impl/CourseCategoryServiceImpl.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-modules/xinquan-course/src/main/java/com/xinquan/course/service/impl/CourseLearningRecordServiceImpl.java 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-modules/xinquan-course/src/main/java/com/xinquan/course/service/impl/CourseServiceImpl.java 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-modules/xinquan-course/src/main/resources/mapper/course/CourseCategoryMapper.xml 14 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-modules/xinquan-course/src/main/resources/mapper/course/CourseChapterMapper.xml 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-modules/xinquan-course/src/main/resources/mapper/course/CourseLearningRecordMapper.xml 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-modules/xinquan-course/src/main/resources/mapper/course/CourseMapper.xml 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-modules/xinquan-course/src/main/resources/mapper/course/TutorSpecialColumnCourseMapper.xml 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-modules/xinquan-course/src/main/resources/mapper/course/TutorSpecialColumnMapper.xml 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-modules/xinquan-gen/src/main/resources/mapper/generator/MeditationUserFavoriteService.xml 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-modules/xinquan-meditation/pom.xml 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-modules/xinquan-meditation/src/main/java/com/xinquan/meditation/controller/client/ClientHomeController.java 277 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-modules/xinquan-meditation/src/main/java/com/xinquan/meditation/controller/client/ClientMeditationController.java 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-modules/xinquan-meditation/src/main/java/com/xinquan/meditation/controller/client/ClientMeditationUserFavoriteController.java 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-modules/xinquan-meditation/src/main/java/com/xinquan/meditation/controller/client/HomeBackgroundMusicController.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-modules/xinquan-meditation/src/main/java/com/xinquan/meditation/controller/client/HomeBackgroundMusicUserController.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-modules/xinquan-meditation/src/main/java/com/xinquan/meditation/controller/management/MgtClientMeditationUserFavoriteController.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-modules/xinquan-meditation/src/main/java/com/xinquan/meditation/domain/HomeBackgroundMusic.java 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-modules/xinquan-meditation/src/main/java/com/xinquan/meditation/domain/Meditation.java 13 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-modules/xinquan-meditation/src/main/java/com/xinquan/meditation/domain/MeditationCategory.java 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-modules/xinquan-meditation/src/main/java/com/xinquan/meditation/domain/MeditationQuestion.java 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-modules/xinquan-meditation/src/main/java/com/xinquan/meditation/domain/MeditationQuestionLike.java 56 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-modules/xinquan-meditation/src/main/java/com/xinquan/meditation/domain/MeditationUserFavorite.java 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-modules/xinquan-meditation/src/main/java/com/xinquan/meditation/domain/dto/ClientHomeMeditationQueryDTO.java 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-modules/xinquan-meditation/src/main/java/com/xinquan/meditation/domain/dto/ClientMeditationQueryDTO.java 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-modules/xinquan-meditation/src/main/java/com/xinquan/meditation/domain/vo/ClientHomeBackgroundMusicVO.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-modules/xinquan-meditation/src/main/java/com/xinquan/meditation/domain/vo/ClientMeditationAndCateVO.java 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-modules/xinquan-meditation/src/main/java/com/xinquan/meditation/domain/vo/ClientMeditationCategoryVO.java 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-modules/xinquan-meditation/src/main/java/com/xinquan/meditation/domain/vo/ClientMeditationDetailsVO.java 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-modules/xinquan-meditation/src/main/java/com/xinquan/meditation/domain/vo/ClientMeditationEverydayVO.java 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-modules/xinquan-meditation/src/main/java/com/xinquan/meditation/domain/vo/ClientMeditationQuestionVO.java 46 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-modules/xinquan-meditation/src/main/java/com/xinquan/meditation/domain/vo/ClientMeditationVO.java 73 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-modules/xinquan-meditation/src/main/java/com/xinquan/meditation/mapper/MeditationMapper.java 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-modules/xinquan-meditation/src/main/java/com/xinquan/meditation/mapper/MeditationQuestionLikeMapper.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-modules/xinquan-meditation/src/main/java/com/xinquan/meditation/mapper/MeditationUserFavoriteMapper.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-modules/xinquan-meditation/src/main/java/com/xinquan/meditation/mapper/MeditationUserOperationMapper.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-modules/xinquan-meditation/src/main/java/com/xinquan/meditation/service/HomeBackgroundMusicService.java 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-modules/xinquan-meditation/src/main/java/com/xinquan/meditation/service/MeditationCategoryService.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-modules/xinquan-meditation/src/main/java/com/xinquan/meditation/service/MeditationEverydayService.java 9 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-modules/xinquan-meditation/src/main/java/com/xinquan/meditation/service/MeditationQuestionLikeService.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-modules/xinquan-meditation/src/main/java/com/xinquan/meditation/service/MeditationQuestionService.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-modules/xinquan-meditation/src/main/java/com/xinquan/meditation/service/MeditationService.java 76 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-modules/xinquan-meditation/src/main/java/com/xinquan/meditation/service/MeditationUserFavoriteService.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-modules/xinquan-meditation/src/main/java/com/xinquan/meditation/service/MeditationUserOperationService.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-modules/xinquan-meditation/src/main/java/com/xinquan/meditation/service/impl/HomeBackgroundMusicServiceImpl.java 61 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-modules/xinquan-meditation/src/main/java/com/xinquan/meditation/service/impl/MeditationCategoryServiceImpl.java 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-modules/xinquan-meditation/src/main/java/com/xinquan/meditation/service/impl/MeditationEverydayServiceImpl.java 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-modules/xinquan-meditation/src/main/java/com/xinquan/meditation/service/impl/MeditationQuestionLikeServiceImpl.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-modules/xinquan-meditation/src/main/java/com/xinquan/meditation/service/impl/MeditationQuestionServiceImpl.java 65 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-modules/xinquan-meditation/src/main/java/com/xinquan/meditation/service/impl/MeditationServiceImpl.java 333 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-modules/xinquan-meditation/src/main/java/com/xinquan/meditation/service/impl/MeditationUserFavoriteServiceImpl.java 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-modules/xinquan-meditation/src/main/java/com/xinquan/meditation/service/impl/MeditationUserOperationServiceImpl.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-modules/xinquan-meditation/src/main/resources/mapper/meditation/HomeBackgroundMusicMapper.xml 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-modules/xinquan-meditation/src/main/resources/mapper/meditation/MeditationMapper.xml 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-modules/xinquan-meditation/src/main/resources/mapper/meditation/MeditationQuestionLikeMapper.xml 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-modules/xinquan-meditation/src/main/resources/mapper/meditation/MeditationQuestionMapper.xml 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-modules/xinquan-meditation/src/main/resources/mapper/meditation/MeditationUserFavoriteMapper.xml 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-modules/xinquan-order/pom.xml 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-modules/xinquan-order/src/main/java/com/xinquan/order/controller/client/ClientOrderController.java 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-modules/xinquan-order/src/main/java/com/xinquan/order/utils/EncryptUtils.java 61 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-modules/xinquan-order/src/main/java/com/xinquan/order/utils/JuHeFuUtil.java 265 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-modules/xinquan-order/src/main/resources/mapper/order/OrderMapper.xml 38 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-modules/xinquan-order/src/main/resources/mapper/order/OrderPaymentRecordMapper.xml 14 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-modules/xinquan-system/pom.xml 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-modules/xinquan-system/src/main/java/com/xinquan/system/controller/SysRoleController.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-modules/xinquan-system/src/main/java/com/xinquan/system/controller/SysUserController.java 11 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-modules/xinquan-system/src/main/java/com/xinquan/system/controller/client/PublicController.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-modules/xinquan-system/src/main/java/com/xinquan/system/controller/inner/InnerBannerController.java 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-modules/xinquan-system/src/main/java/com/xinquan/system/controller/inner/InnerHotWordsController.java 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-modules/xinquan-system/src/main/java/com/xinquan/system/domain/HotWords.java 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-modules/xinquan-system/src/main/java/com/xinquan/system/domain/SysMenus.java 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-modules/xinquan-system/src/main/java/com/xinquan/system/mapper/HotWordsMapper.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-modules/xinquan-system/src/main/java/com/xinquan/system/service/BannerService.java 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-modules/xinquan-system/src/main/java/com/xinquan/system/service/HotWordsService.java 9 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-modules/xinquan-system/src/main/java/com/xinquan/system/service/impl/BannerServiceImpl.java 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-modules/xinquan-system/src/main/java/com/xinquan/system/service/impl/HotWordsServiceImpl.java 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-modules/xinquan-system/src/main/resources/mapper/system/SysUserMapper.xml 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-modules/xinquan-user/pom.xml 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-modules/xinquan-user/src/main/java/com/xinquan/user/controller/client/ClientAppUserController.java 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-modules/xinquan-user/src/main/java/com/xinquan/user/controller/client/ClientAppUserViewingHistoryController.java 50 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-modules/xinquan-user/src/main/java/com/xinquan/user/controller/inner/InnerAppUserController.java 41 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-modules/xinquan-user/src/main/java/com/xinquan/user/controller/management/MgtAppUserViewingHistoryController.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-modules/xinquan-user/src/main/java/com/xinquan/user/domain/AppUserQuestion.java 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-modules/xinquan-user/src/main/java/com/xinquan/user/domain/AppUserViewingHistory.java 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-modules/xinquan-user/src/main/java/com/xinquan/user/domain/Tag.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-modules/xinquan-user/src/main/java/com/xinquan/user/domain/dto/UserAnswerDTO.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-modules/xinquan-user/src/main/java/com/xinquan/user/domain/dto/UserAnswerOneDTO.java 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-modules/xinquan-user/src/main/java/com/xinquan/user/domain/vo/AppUserVO.java 109 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-modules/xinquan-user/src/main/java/com/xinquan/user/domain/vo/TagVO.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-modules/xinquan-user/src/main/java/com/xinquan/user/mapper/AppUserViewingHistoryMapper.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-modules/xinquan-user/src/main/java/com/xinquan/user/service/AppUserService.java 47 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-modules/xinquan-user/src/main/java/com/xinquan/user/service/AppUserViewingHistoryService.java 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-modules/xinquan-user/src/main/java/com/xinquan/user/service/impl/AppUserServiceImpl.java 246 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-modules/xinquan-user/src/main/java/com/xinquan/user/service/impl/AppUserViewingHistoryServiceImpl.java 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-modules/xinquan-user/src/main/resources/mapper/user/AppUserMapper.xml 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-modules/xinquan-user/src/main/resources/mapper/user/AppUserViewingHistoryMapper.xml 11 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
xinquan-modules/xinquan-user/src/main/resources/mapper/user/TagMapper.xml 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pom.xml
@@ -37,6 +37,7 @@
        <transmittable-thread-local.version>2.14.2</transmittable-thread-local.version>
        <knife4j.version>3.0.3</knife4j.version>
        <hutool.version>5.7.17</hutool.version>
        <easyexcel.version>4.0.2</easyexcel.version>
    </properties>
    <!-- 依赖声明 -->
@@ -222,12 +223,20 @@
                <artifactId>knife4j-micro-spring-boot-starter</artifactId>
                <version>${knife4j.version}</version>
            </dependency>
            <!--hutool工具包-->
            <dependency>
                <groupId>cn.hutool</groupId>
                <artifactId>hutool-all</artifactId>
                <version>${hutool.version}</version>
            </dependency>
            <!-- https://mvnrepository.com/artifact/com.alibaba/easyexcel -->
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>easyexcel</artifactId>
                <version>${easyexcel.version}</version>
            </dependency>
        </dependencies>
    </dependencyManagement>
xinquan-api/xinquan-api-system/src/main/java/com/xinquan/system/api/RemoteBannerService.java
New file
@@ -0,0 +1,30 @@
package com.xinquan.system.api;
import com.xinquan.common.core.constant.SecurityConstants;
import com.xinquan.common.core.constant.ServiceNameConstants;
import com.xinquan.common.core.domain.R;
import com.xinquan.system.api.domain.vo.BannerVO;
import com.xinquan.system.api.factory.RemoteBannerFallbackFactory;
import java.util.List;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestHeader;
/**
 * 用户服务
 *
 * @author ruoyi
 */
@FeignClient(contextId = "remoteBannerService", value = ServiceNameConstants.SYSTEM_SERVICE, fallbackFactory = RemoteBannerFallbackFactory.class)
public interface RemoteBannerService {
    /**
     * 查询轮播图列表
     *
     * @param source 请求来源
     * @return 轮播图列表
     */
    @GetMapping("/inner/system/banner/getBannerList")
    R<List<BannerVO>> getBannerList(
            @RequestHeader(SecurityConstants.FROM_SOURCE) String source);
}
xinquan-api/xinquan-api-system/src/main/java/com/xinquan/system/api/RemoteHotWordsService.java
New file
@@ -0,0 +1,29 @@
package com.xinquan.system.api;
import com.xinquan.common.core.constant.SecurityConstants;
import com.xinquan.common.core.constant.ServiceNameConstants;
import com.xinquan.common.core.domain.R;
import com.xinquan.system.api.factory.RemoteHotWordsFallbackFactory;
import java.util.List;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestHeader;
/**
 * 用户服务
 *
 * @author ruoyi
 */
@FeignClient(contextId = "remoteHotWordsService", value = ServiceNameConstants.SYSTEM_SERVICE, fallbackFactory = RemoteHotWordsFallbackFactory.class)
public interface RemoteHotWordsService {
    /**
     * 查询热词列表
     *
     * @param source
     * @return
     */
    @GetMapping("/inner/system/hot-words/getHotWordList")
    R<List<String>> getHotWordList(
            @RequestHeader(SecurityConstants.FROM_SOURCE) String source);
}
xinquan-api/xinquan-api-system/src/main/java/com/xinquan/system/api/domain/SysUser.java
@@ -1,7 +1,6 @@
package com.xinquan.system.api.domain;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.xinquan.common.core.annotation.Excel;
import com.xinquan.common.core.annotation.Excel.ColumnType;
import com.xinquan.common.core.annotation.Excel.Type;
@@ -9,7 +8,7 @@
import com.xinquan.common.core.web.domain.BaseEntity;
import com.xinquan.common.core.xss.Xss;
import io.swagger.annotations.ApiModelProperty;
import java.util.Date;
import java.time.LocalDateTime;
import java.util.List;
import javax.validation.constraints.Email;
import javax.validation.constraints.NotBlank;
@@ -29,7 +28,7 @@
    /** 用户ID */
    @Excel(name = "用户序号", cellType = ColumnType.NUMERIC, prompt = "用户编号")
    @TableId("user_id")
    @TableField("user_id")
    private Long userId;
    /** 部门ID */
@@ -85,7 +84,7 @@
    @Excel(name = "最后登录时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss", type = Type.EXPORT)
    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @TableField("login_date")
    private Date loginDate;
    private LocalDateTime loginDate;
    /** 部门对象 */
    @Excels({
@@ -113,6 +112,17 @@
    @ApiModelProperty(value = "备注")
    private String remark;
    @ApiModelProperty(value = "用户类型 00:管理后台用户 01:APP用户")
    private String userType;
    public String getUserType() {
        return userType;
    }
    public void setUserType(String userType) {
        this.userType = userType;
    }
    @Override
    public String getRemark() {
@@ -271,12 +281,12 @@
        this.loginIp = loginIp;
    }
    public Date getLoginDate()
    public LocalDateTime getLoginDate()
    {
        return loginDate;
    }
    public void setLoginDate(Date loginDate)
    public void setLoginDate(LocalDateTime loginDate)
    {
        this.loginDate = loginDate;
    }
xinquan-api/xinquan-api-system/src/main/java/com/xinquan/system/api/domain/vo/BannerVO.java
New file
@@ -0,0 +1,26 @@
package com.xinquan.system.api.domain.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import java.io.Serializable;
import lombok.Data;
/**
 * @author mitao
 * @date 2024/9/7
 */
@Data
@ApiModel(value = "轮播图视图对象")
public class BannerVO implements Serializable {
    private static final long serialVersionUID = -7433787602665988956L;
    @ApiModelProperty(value = "轮播图id")
    private Long id;
    @ApiModelProperty(value = "轮播图")
    private String imageUrl;
    @ApiModelProperty(value = "跳转课程id")
    private Integer courseId;
}
xinquan-api/xinquan-api-system/src/main/java/com/xinquan/system/api/factory/RemoteBannerFallbackFactory.java
New file
@@ -0,0 +1,31 @@
package com.xinquan.system.api.factory;
import com.xinquan.common.core.domain.R;
import com.xinquan.system.api.RemoteBannerService;
import com.xinquan.system.api.domain.vo.BannerVO;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.openfeign.FallbackFactory;
import org.springframework.stereotype.Component;
/**
 * 用户服务降级处理
 *
 * @author ruoyi
 */
@Component
public class RemoteBannerFallbackFactory implements FallbackFactory<RemoteBannerService> {
    private static final Logger log = LoggerFactory.getLogger(RemoteBannerFallbackFactory.class);
    @Override
    public RemoteBannerService create(Throwable cause) {
        return new RemoteBannerService() {
            @Override
            public R<List<BannerVO>> getBannerList(String source) {
                return R.fail("获取轮播图失败:" + cause.getMessage());
            }
        };
    }
}
xinquan-api/xinquan-api-system/src/main/java/com/xinquan/system/api/factory/RemoteHotWordsFallbackFactory.java
New file
@@ -0,0 +1,30 @@
package com.xinquan.system.api.factory;
import com.xinquan.common.core.domain.R;
import com.xinquan.system.api.RemoteHotWordsService;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.openfeign.FallbackFactory;
import org.springframework.stereotype.Component;
/**
 * 用户服务降级处理
 *
 * @author ruoyi
 */
@Component
public class RemoteHotWordsFallbackFactory implements FallbackFactory<RemoteHotWordsService> {
    private static final Logger log = LoggerFactory.getLogger(RemoteHotWordsFallbackFactory.class);
    @Override
    public RemoteHotWordsService create(Throwable cause) {
        return new RemoteHotWordsService() {
            @Override
            public R<List<String>> getHotWordList(String source) {
                return R.fail("获取热词列表失败");
            }
        };
    }
}
xinquan-api/xinquan-api-system/src/main/java/com/xinquan/system/api/model/AppCaptchaBody.java
@@ -16,11 +16,11 @@
    private static final long serialVersionUID = 6079425494597881288L;
    @ApiModelProperty(value = "手机号")
    @ApiModelProperty(value = "手机号", required = true)
    @NotBlank(message = "手机号不能为空")
    private String cellPhone;
    @ApiModelProperty("验证码")
    @ApiModelProperty(value = "验证码", required = true)
    @NotBlank(message = "验证码不能为空")
    private String captcha;
}
xinquan-api/xinquan-api-system/src/main/java/com/xinquan/system/api/model/AppLoginUser.java
File was renamed from xinquan-api/xinquan-api-system/src/main/java/com/xinquan/system/api/model/WXLoginUser.java
@@ -12,16 +12,16 @@
 * @author ruoyi
 */
@Data
@ApiModel(value = "微信登录用户信息")
public class WXLoginUser implements Serializable {
@ApiModel(value = "APP登录用户信息")
public class AppLoginUser implements Serializable {
    private static final long serialVersionUID = 1L;
    /**
     * 用户唯一标识
     */
    @ApiModelProperty(value = "APP用户id")
    private Long token;
    @ApiModelProperty(value = "用户唯一标识")
    private String token;
    /**
     * 用户id
@@ -53,16 +53,24 @@
    @ApiModelProperty(value = "登录IP地址")
    private String ipaddr;
    @ApiModelProperty(value = "绑定手机号状态 1=是 2=否")
    @ApiModelProperty(value = "是否需要验证手机号 1=是 2=否")
    private Integer bindStatus = 2;
    @ApiModelProperty(value = "用户手机")
    private String cellPhone;
    @ApiModelProperty(value = "APP用户id")
    private Long appUserId;
    @ApiModelProperty(value = "token")
    private String accessToken;
    @ApiModelProperty(value = "token过期时间")
    private Long expireIn;
    /**
     * 系统用户信息
     */
    @ApiModelProperty(value = "系统用户信息")
    @ApiModelProperty(value = "系统用户信息", hidden = true)
    private SysUser sysUser;
}
xinquan-api/xinquan-api-system/src/main/java/com/xinquan/system/api/model/AppVerifyCellPhoneBody.java
@@ -18,11 +18,11 @@
    private static final long serialVersionUID = -6119279151800382508L;
    @NotBlank(message = "微信openid或苹果appleId不能为空")
    @ApiModelProperty(value = "微信openid或苹果appleId")
    @ApiModelProperty(value = "微信openid或苹果appleId", required = true)
    private String wxOrAppleId;
    @NotNull(message = "登录类型不能为空")
    @ApiModelProperty(value = "登录类型 1=微信 2=苹果")
    @ApiModelProperty(value = "登录类型 1=微信 2=苹果", required = true)
    private Integer loginType;
}
xinquan-api/xinquan-api-system/src/main/java/com/xinquan/system/api/model/AppWXLoginBody.java
@@ -21,28 +21,28 @@
    /**
     * 微信openId
     */
    @ApiModelProperty(value = "微信openId")
    @ApiModelProperty(value = "微信openId", required = true)
    @NotBlank(message = "微信openId不能为空")
    private String wxOpenId;
    /**
     * 用户昵称
     */
    @ApiModelProperty(value = "用户昵称")
    @ApiModelProperty(value = "用户昵称", required = true)
    @NotBlank(message = "用户昵称不能为空")
    private String nickname;
    /**
     * 用户头像
     */
    @ApiModelProperty(value = "用户头像")
    @ApiModelProperty(value = "用户头像", required = true)
    @NotBlank(message = "用户头像不能为空")
    private String headImgUrl;
    /**
     * 用户头像
     */
    @ApiModelProperty(value = "用户性别")
    @ApiModelProperty(value = "用户性别", required = true)
    @NotNull(message = "用户性别不能为空")
    private Integer sex;
xinquan-api/xinquan-api-system/src/main/java/com/xinquan/system/api/model/AppleLoginUser.java
@@ -12,7 +12,7 @@
@Data
@EqualsAndHashCode(callSuper = true)
@ApiModel(value = "苹果登录用户信息")
public class AppleLoginUser extends WXLoginUser {
public class AppleLoginUser extends AppLoginUser {
    private static final long serialVersionUID = 6891070491318038281L;
}
xinquan-api/xinquan-api-system/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
@@ -2,3 +2,5 @@
com.xinquan.system.api.factory.RemoteLogFallbackFactory
com.xinquan.system.api.factory.RemoteFileFallbackFactory
com.xinquan.system.api.factory.SysUserFallbackFactory
com.xinquan.system.api.factory.RemoteHotWordsFallbackFactory
com.xinquan.system.api.factory.RemoteBannerFallbackFactory
xinquan-api/xinquan-api-user/src/main/java/com/xinquan/user/api/domain/AppUser.java
@@ -144,5 +144,7 @@
    @TableField("level_setting_id")
    private Integer levelSettingId;
    @ApiModelProperty(value = "标签id,多个id使用英文逗号拼接")
    @TableField("tag_id")
    private String tagId;
}
xinquan-api/xinquan-api-user/src/main/java/com/xinquan/user/api/domain/dto/AppUserDTO.java
@@ -4,8 +4,10 @@
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
 * @author mitao
@@ -13,6 +15,8 @@
 */
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class AppUserDTO implements Serializable {
    private static final long serialVersionUID = -3850851853092651572L;
@@ -103,4 +107,7 @@
    @ApiModelProperty(value = "用户等级id")
    private Integer levelSettingId;
    @ApiModelProperty(value = "首页背景音乐id")
    private Long homeBackgroundMusicId;
}
xinquan-api/xinquan-api-user/src/main/java/com/xinquan/user/api/factory/RemoteAppUserFallbackFactory.java
@@ -2,14 +2,14 @@
import com.xinquan.common.core.domain.R;
import com.xinquan.system.api.model.AppCaptchaBody;
import com.xinquan.system.api.model.AppLoginUser;
import com.xinquan.system.api.model.AppVerifyCellPhoneBody;
import com.xinquan.system.api.model.AppWXLoginBody;
import com.xinquan.system.api.model.AppleLoginUser;
import com.xinquan.system.api.model.LoginUser;
import com.xinquan.system.api.model.WXLoginUser;
import com.xinquan.user.api.domain.AppUser;
import com.xinquan.user.api.domain.dto.AppUserDTO;
import com.xinquan.user.api.feign.RemoteAppUserService;
import java.util.Collection;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.openfeign.FallbackFactory;
@@ -31,29 +31,40 @@
            }
            @Override
            public R<Boolean> registerAppUser(AppUserDTO appUserDTO, String source) {
            public R<AppUser> registerAppUser(AppUserDTO appUserDTO, String source) {
                return R.fail("APP用户注册失败。");
            }
            @Override
            public R<WXLoginUser> wxLogin(AppWXLoginBody body, String inner) {
            public R<AppLoginUser> wxLogin(AppWXLoginBody body, String inner) {
                return R.fail("APP用户微信登录失败。");
            }
            @Override
            public R<AppleLoginUser> appleLogin(String appleId, String source) {
            public R<AppLoginUser> appleLogin(String appleId, String source) {
                return R.fail("APP用户苹果登录失败。");
            }
            @Override
            public R<LoginUser> appCaptchaLogin(AppCaptchaBody body, String source) {
            public R<AppLoginUser> appCaptchaLogin(AppCaptchaBody body, String source) {
                return R.fail("APP用户验证码登录失败。");
            }
            @Override
            public R<WXLoginUser> verifyCellPhone(AppVerifyCellPhoneBody body, String source) {
            public R<AppLoginUser> verifyCellPhone(AppVerifyCellPhoneBody body,
                    String source) {
                return R.fail("APP用户微信苹果登录验证手机号失败。");
            }
            @Override
            public R<?> updateAppUser(AppUserDTO dto, String source) {
                return R.fail("修改APP用户信息失败。");
            }
            @Override
            public R<List<AppUser>> getAppUserList(Collection<Long> appUserIdSet, String source) {
                return R.fail("获取APP用户列表失败。");
            }
        };
    }
}
xinquan-api/xinquan-api-user/src/main/java/com/xinquan/user/api/feign/RemoteAppUserService.java
@@ -4,14 +4,14 @@
import com.xinquan.common.core.constant.ServiceNameConstants;
import com.xinquan.common.core.domain.R;
import com.xinquan.system.api.model.AppCaptchaBody;
import com.xinquan.system.api.model.AppLoginUser;
import com.xinquan.system.api.model.AppVerifyCellPhoneBody;
import com.xinquan.system.api.model.AppWXLoginBody;
import com.xinquan.system.api.model.AppleLoginUser;
import com.xinquan.system.api.model.LoginUser;
import com.xinquan.system.api.model.WXLoginUser;
import com.xinquan.user.api.domain.AppUser;
import com.xinquan.user.api.domain.dto.AppUserDTO;
import com.xinquan.user.api.factory.RemoteAppUserFallbackFactory;
import java.util.Collection;
import java.util.List;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
@@ -44,7 +44,7 @@
     * @return
     */
    @PostMapping("/inner/app-user/registerAppUser")
    R<Boolean> registerAppUser(@RequestBody AppUserDTO appUserDTO,
    R<AppUser> registerAppUser(@RequestBody AppUserDTO appUserDTO,
            @RequestHeader(SecurityConstants.FROM_SOURCE) String source);
    /**
@@ -55,7 +55,7 @@
     * @return
     */
    @PostMapping("/inner/app-user/wxLogin")
    R<WXLoginUser> wxLogin(@RequestBody AppWXLoginBody body,
    R<AppLoginUser> wxLogin(@RequestBody AppWXLoginBody body,
            @RequestHeader(SecurityConstants.FROM_SOURCE) String source);
    /**
@@ -66,7 +66,7 @@
     * @return
     */
    @PostMapping("/inner/app-user/appleLogin")
    R<AppleLoginUser> appleLogin(@RequestParam("appleId") String appleId,
    R<AppLoginUser> appleLogin(@RequestParam("appleId") String appleId,
            @RequestHeader(SecurityConstants.FROM_SOURCE) String source);
    /**
@@ -77,7 +77,7 @@
     * @return
     */
    @PostMapping("/inner/app-user/captchaLogin")
    R<LoginUser> appCaptchaLogin(@RequestBody AppCaptchaBody body,
    R<AppLoginUser> appCaptchaLogin(@RequestBody AppCaptchaBody body,
            @RequestHeader(SecurityConstants.FROM_SOURCE) String source);
    /**
@@ -88,6 +88,28 @@
     * @return
     */
    @PostMapping("/inner/app-user/verifyCellPhone")
    R<WXLoginUser> verifyCellPhone(@RequestBody AppVerifyCellPhoneBody body,
    R<AppLoginUser> verifyCellPhone(@RequestBody AppVerifyCellPhoneBody body,
            @RequestHeader(SecurityConstants.FROM_SOURCE) String source);
    /**
     * 更新用户信息
     *
     * @param dto
     * @param source
     * @return
     */
    @PostMapping("/inner/app-user/updateAppUser")
    R<?> updateAppUser(@RequestBody AppUserDTO dto,
            @RequestHeader(SecurityConstants.FROM_SOURCE) String source);
    /**
     * 获取用户列表
     *
     * @param appUserIdSet 用户id列表
     * @param source       请求来源
     * @return
     */
    @PostMapping("/inner/app-user/getAppUserList")
    R<List<AppUser>> getAppUserList(@RequestBody Collection<Long> appUserIdSet,
            @RequestHeader(SecurityConstants.FROM_SOURCE) String source);
}
xinquan-auth/src/main/java/com/xinquan/auth/controller/TokenController.java
@@ -17,15 +17,14 @@
import com.xinquan.system.api.domain.SysUser;
import com.xinquan.system.api.feignClient.SysUserClient;
import com.xinquan.system.api.model.AppCaptchaBody;
import com.xinquan.system.api.model.AppLoginUser;
import com.xinquan.system.api.model.AppVerifyCellPhoneBody;
import com.xinquan.system.api.model.AppWXLoginBody;
import com.xinquan.system.api.model.AppleLoginUser;
import com.xinquan.system.api.model.LoginUser;
import com.xinquan.system.api.model.WXLoginUser;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import java.util.Date;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
@@ -80,11 +79,11 @@
     */
    @ApiOperation(value = "注册账户",tags = {"APP端"})
    @PostMapping("/app/register")
    public R<?> appRegister(@Validated @RequestBody AppRegisterBody appRegisterBody)
    public R<AppLoginUser> appRegister(@Validated @RequestBody AppRegisterBody appRegisterBody)
    {
        // 用户注册
        sysLoginService.appRegister(appRegisterBody);
        return R.ok();
        AppLoginUser appLoginUser = sysLoginService.appRegister(appRegisterBody);
        return R.ok(tokenService.createToken4AppLoginUser(appLoginUser));
    }
    /**
@@ -95,9 +94,9 @@
     */
    @ApiOperation(value = "账号密码登录", tags = {"APP端"})
    @PostMapping("/app/login")
    public R<?> appLogin(@Validated @RequestBody AppLoginBody body) {
        LoginUser userInfo = sysLoginService.appLogin(body);
        return R.ok(tokenService.createToken(userInfo));
    public R<AppLoginUser> appLogin(@Validated @RequestBody AppLoginBody body) {
        AppLoginUser appLoginUser = sysLoginService.appLogin(body);
        return R.ok(tokenService.createToken4AppLoginUser(appLoginUser));
    }
    /**
@@ -108,9 +107,9 @@
     */
    @ApiOperation(value = "验证码登录", tags = {"APP端"})
    @PostMapping("/app/captchaLogin")
    public R<?> appCaptchaLogin(@Validated @RequestBody AppCaptchaBody body) {
        LoginUser userInfo = sysLoginService.appCaptchaLogin(body);
        return R.ok(tokenService.createToken(userInfo));
    public R<AppLoginUser> appCaptchaLogin(@Validated @RequestBody AppCaptchaBody body) {
        AppLoginUser appLoginUser = sysLoginService.appCaptchaLogin(body);
        return R.ok(tokenService.createToken4AppLoginUser(appLoginUser));
    }
    /**
@@ -146,9 +145,9 @@
     */
    @ApiOperation(value = "微信登录", tags = {"APP端"})
    @PostMapping("/app/wxLogin")
    public R<?> thirdLogin(@Validated @RequestBody AppWXLoginBody body) {
        WXLoginUser wxLoginUser = sysLoginService.wxLogin(body);
        return R.ok(tokenService.createToken4WXLoginUser(wxLoginUser));
    public R<AppLoginUser> thirdLogin(@Validated @RequestBody AppWXLoginBody body) {
        AppLoginUser appLoginUser = sysLoginService.wxLogin(body);
        return R.ok(tokenService.createToken4AppLoginUser(appLoginUser));
    }
    /**
@@ -161,9 +160,10 @@
    @PostMapping("/app/appleLogin")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "appleId", value = "苹果ID", required = true, dataType = "String", paramType = "query")})
    public R<?> appleLogin(@RequestParam(value = "appleId", required = true) String appleId) {
        AppleLoginUser appleLoginUser = sysLoginService.appleLogin(appleId);
        return R.ok(tokenService.createToken4WXLoginUser(appleLoginUser));
    public R<AppLoginUser> appleLogin(
            @RequestParam(value = "appleId", required = true) String appleId) {
        AppLoginUser appLoginUser = sysLoginService.appleLogin(appleId);
        return R.ok(tokenService.createToken4AppLoginUser(appLoginUser));
    }
    /**
@@ -175,7 +175,8 @@
    @ApiOperation(value = "微信苹果登录-验证手机号", tags = {"APP端"})
    @PostMapping("/app/verifyCellPhone")
    public R<?> verifyCellPhone(@Validated @RequestBody AppVerifyCellPhoneBody body) {
        return R.ok(tokenService.createToken4WXLoginUser(sysLoginService.verifyCellPhone(body)));
        return R.ok(tokenService.createToken4AppLoginUser(
                sysLoginService.verifyCellPhone(body)));
    }
    /**
@@ -202,7 +203,7 @@
        // 修改用户最后登录时间
        SysUser sysUser = new SysUser();
        sysUser.setUserId(userInfo.getSysUser().getUserId());
        sysUser.setLoginDate(new Date());
        sysUser.setLoginDate(LocalDateTime.now());
        System.out.println("修改用户登录时间"+sysUser);
        userClient.updateSysUser(sysUser);
        // 获取登录token
xinquan-auth/src/main/java/com/xinquan/auth/form/AppChangePwdBody.java
@@ -22,7 +22,7 @@
    /**
     * 用户密码
     */
    @ApiModelProperty(value = "密码修改秘钥")
    @ApiModelProperty(value = "密码修改秘钥", required = true)
    @NotBlank(message = "密码修改秘钥不能为空")
    private String secret;
xinquan-auth/src/main/java/com/xinquan/auth/form/AppLoginBody.java
@@ -20,14 +20,14 @@
    /**
     * 手机号
     */
    @ApiModelProperty(value = "手机号")
    @ApiModelProperty(value = "手机号", required = true)
    @NotBlank(message = "手机号不能为空")
    private String cellPhone;
    /**
     * 用户密码
     */
    @ApiModelProperty(value = "用户密码")
    @ApiModelProperty(value = "用户密码", required = true)
    @NotBlank(message = "密码不能为空")
    private String password;
xinquan-auth/src/main/java/com/xinquan/auth/form/AppRegisterBody.java
@@ -21,7 +21,7 @@
    private static final long serialVersionUID = 438348119520198682L;
    @ApiModelProperty(value = "用户密码")
    @ApiModelProperty(value = "用户密码", required = true)
    @NotBlank(message = "用户密码不能为空")
    private String password;
xinquan-auth/src/main/java/com/xinquan/auth/service/SysLoginService.java
@@ -6,7 +6,6 @@
import com.xinquan.auth.form.AppLoginBody;
import com.xinquan.auth.form.AppRegisterBody;
import com.xinquan.auth.form.VerifyResultVO;
import com.xinquan.auth.util.HuaWeiSMSUtil;
import com.xinquan.common.core.constant.CacheConstants;
import com.xinquan.common.core.constant.Constants;
import com.xinquan.common.core.constant.SecurityConstants;
@@ -24,11 +23,10 @@
import com.xinquan.system.api.domain.SysUser;
import com.xinquan.system.api.feignClient.SysUserClient;
import com.xinquan.system.api.model.AppCaptchaBody;
import com.xinquan.system.api.model.AppLoginUser;
import com.xinquan.system.api.model.AppVerifyCellPhoneBody;
import com.xinquan.system.api.model.AppWXLoginBody;
import com.xinquan.system.api.model.AppleLoginUser;
import com.xinquan.system.api.model.LoginUser;
import com.xinquan.system.api.model.WXLoginUser;
import com.xinquan.user.api.domain.AppUser;
import com.xinquan.user.api.domain.dto.AppUserDTO;
import com.xinquan.user.api.feign.RemoteAppUserService;
@@ -36,11 +34,10 @@
import java.time.LocalDateTime;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
/**
 * 登录校验方法
@@ -173,7 +170,8 @@
    /**
     * 注册
     */
    public void appRegister(AppRegisterBody appRegisterBody)
    @Transactional(rollbackFor = Exception.class)
    public AppLoginUser appRegister(AppRegisterBody appRegisterBody)
    {
        String cellPhone = appRegisterBody.getCellPhone();
        String password = appRegisterBody.getPassword();
@@ -191,16 +189,11 @@
        {
            throw new ServiceException("该手机号已注册");
        }
        if (password.length() < UserConstants.PASSWORD_MIN_LENGTH
                || password.length() > UserConstants.PASSWORD_MAX_LENGTH)
        {
            throw new ServiceException("密码长度必须在5到20个字符之间");
        }
        String nickname = "用户" + IdUtil.fastSimpleUUID().substring(0, 6);
        // 注册用户信息
        SysUser sysUser = new SysUser();
        sysUser.setUserName(cellPhone);
        sysUser.setUserType("01");
        sysUser.setNickName(nickname);
        sysUser.setPassword(SecurityUtils.encryptPassword(password));
        R<SysUser> registerResult = remoteUserService.registerAppUserInfo(sysUser, SecurityConstants.INNER);
@@ -209,6 +202,7 @@
        {
            throw new ServiceException(registerResult.getMsg());
        }
        sysUser = registerResult.getData();
        recordLogService.recordLogininfor(cellPhone, Constants.REGISTER, "注册成功");
        //TODO 默认头像待完善
        AppUserDTO appUserDTO = AppUserDTO.builder().userId(sysUser.getUserId()).cellPhone(cellPhone)
@@ -216,12 +210,21 @@
                        nickname).userStatus(1).sanskritFlag(2).balance(BigDecimal.ZERO)
                .income(BigDecimal.ZERO).totalEnergyValue(0).registerTime(
                        LocalDateTime.now()).levelSettingId(1).build();
        Boolean result = remoteAppUserService.registerAppUser(appUserDTO, SecurityConstants.INNER)
                .getData();
        if (!result) {
            throw new ServiceException("注册失败");
        R<AppUser> result = remoteAppUserService.registerAppUser(appUserDTO,
                SecurityConstants.INNER);
        if (R.FAIL == result.getCode()) {
            throw new ServiceException(result.getMsg());
        }
        // 构建AppLoginUser
        SysUser sysUserInfo = registerResult.getData();
        AppUser appUserInfo = result.getData();
        AppLoginUser appLoginUser = new AppLoginUser();
        appLoginUser.setUserid(sysUserInfo.getUserId());
        appLoginUser.setUsername(appUserInfo.getNickname());
        appLoginUser.setCellPhone(cellPhone);
        appLoginUser.setAppUserId(appUserInfo.getId());
        appLoginUser.setSysUser(sysUser);
        return appLoginUser;
    }
    /**
@@ -231,10 +234,13 @@
     * @return true/false
     */
    private boolean verifyCaptcha(String cellPhone, String captcha, String keyPrefix) {
        if (StringUtils.isNotBlank(cellPhone) && StringUtils.isNotBlank(captcha)) {
            String key = keyPrefix + cellPhone;
            String code = redisService.getCacheObject(key);
            if (StringUtils.isNotBlank(code) && code.equals(captcha)) {
            // 万能验证码
            if (captcha.equals("123456") || (StringUtils.isNotBlank(code) && code.equals(
                    captcha))) {
                redisService.deleteObject(key);
                return true;
            }
@@ -270,23 +276,19 @@
     */
    public void sendCaptchaCode(String cellPhone, Integer type) {
        String key = getCaptchaCodeByTypePrefix(type);
        Boolean result = redisService.hasKey(key + cellPhone);
        if (result) {
            throw new ServiceException("请勿重复发送验证码");
        }
        String code = RandomUtil.randomNumbers(6);
        try {
            // 将验证码放入redis
            redisService.setCacheObject(key + cellPhone, code, 5L,
                    TimeUnit.MINUTES);
            log.info("发送验证码成功,手机号:{} 验证码:{}", cellPhone, code);
            // TODO 修改sender参数及templateId
            HuaWeiSMSUtil.sendSms("[\"" + code + "\"]", cellPhone, "8823121426646",
                    "cf1707ec44694627b1b483b0277e12fd");
            // HuaWeiSMSUtil.sendSms("[\"" + code + "\"]", cellPhone, "8823121426646",
            //         "cf1707ec44694627b1b483b0277e12fd");
        } catch (Exception e) {
            log.error("发送短信失败", e);
            throw new ServiceException("验证码发送失败");
        }
        //将验证码放入redis
        redisService.setCacheObject(key + cellPhone, code, 5L,
                TimeUnit.MINUTES);
    }
    /**
@@ -295,7 +297,7 @@
     * @param body
     * @return
     */
    public LoginUser appLogin(AppLoginBody body) {
    public AppLoginUser appLogin(AppLoginBody body) {
        // IP黑名单校验
        String blackStr = Convert.toStr(
@@ -334,11 +336,14 @@
        }
        // 校验账号密码
        passwordService.validate(sysUser, body.getPassword());
        LoginUser userInfo = new LoginUser();
        userInfo.setSysUser(sysUser);
        AppLoginUser appLoginUser = new AppLoginUser();
        appLoginUser.setAppUserId(appUser.getUserId());
        appLoginUser.setCellPhone(appUser.getCellPhone());
        appLoginUser.setUsername(sysUser.getNickName());
        appLoginUser.setSysUser(sysUser);
        recordLogService.recordLogininfor(sysUser.getUserName(), Constants.LOGIN_SUCCESS,
                "登录成功");
        return userInfo;
        return appLoginUser;
    }
    /**
@@ -347,14 +352,34 @@
     * @param body
     * @return
     */
    public WXLoginUser wxLogin(AppWXLoginBody body) {
    public AppLoginUser wxLogin(AppWXLoginBody body) {
        // 通过wxOpenId查询APP用户信息
        R<WXLoginUser> userResult = remoteAppUserService.wxLogin(body, SecurityConstants.INNER);
        R<AppLoginUser> userResult = remoteAppUserService.wxLogin(body,
                SecurityConstants.INNER);
        AppLoginUser data = userResult.getData();
        SysUser sysUser = data.getSysUser();
        if (R.FAIL == userResult.getCode()) {
            recordLogService.recordLogininfor(body.getNickname(), Constants.LOGIN_FAIL,
                    userResult.getMsg());
            throw new ServiceException(userResult.getMsg());
        }
        if (StringUtils.isNull(sysUser)) {
            recordLogService.recordLogininfor(data.getCellPhone(), Constants.LOGIN_FAIL,
                    "登录用户不存在");
            throw new ServiceException("登录用户:" + data.getCellPhone() + " 不存在");
        }
        // IP黑名单校验
        String blackStr = Convert.toStr(
                redisService.getCacheObject(CacheConstants.SYS_LOGIN_BLACKIPLIST));
        if (IpUtils.isMatchedIp(blackStr, IpUtils.getIpAddr())) {
            recordLogService.recordLogininfor(sysUser.getUserName(), Constants.LOGIN_FAIL,
                    "很遗憾,访问IP已被列入系统黑名单");
            throw new ServiceException("很遗憾,访问IP已被列入系统黑名单");
        }
        return userResult.getData();
        recordLogService.recordLogininfor(sysUser.getUserName(), Constants.LOGIN_SUCCESS,
                "登录成功");
        return data;
    }
    /**
@@ -363,13 +388,30 @@
     * @param appleId 苹果id
     * @return
     */
    public AppleLoginUser appleLogin(String appleId) {
        R<AppleLoginUser> userResult = remoteAppUserService.appleLogin(appleId,
    public AppLoginUser appleLogin(String appleId) {
        R<AppLoginUser> userResult = remoteAppUserService.appleLogin(appleId,
                SecurityConstants.INNER);
        if (R.FAIL == userResult.getCode()) {
            throw new ServiceException(userResult.getMsg());
        }
        return userResult.getData();
        AppLoginUser data = userResult.getData();
        SysUser sysUser = data.getSysUser();
        if (StringUtils.isNull(sysUser)) {
            recordLogService.recordLogininfor(data.getCellPhone(), Constants.LOGIN_FAIL,
                    "登录用户不存在");
            throw new ServiceException("登录用户:" + data.getCellPhone() + " 不存在");
        }
        // IP黑名单校验
        String blackStr = Convert.toStr(
                redisService.getCacheObject(CacheConstants.SYS_LOGIN_BLACKIPLIST));
        if (IpUtils.isMatchedIp(blackStr, IpUtils.getIpAddr())) {
            recordLogService.recordLogininfor(sysUser.getUserName(), Constants.LOGIN_FAIL,
                    "很遗憾,访问IP已被列入系统黑名单");
            throw new ServiceException("很遗憾,访问IP已被列入系统黑名单");
        }
        recordLogService.recordLogininfor(sysUser.getUserName(), Constants.LOGIN_SUCCESS,
                "登录成功");
        return data;
    }
    /**
@@ -404,13 +446,6 @@
        if (!body.getSecret().equals(baseSecret.toString())) {
            throw new ServiceException("密码修改秘钥不正确");
        }
        // 校验密码,密码至少8个字符,不能全是字母或者数字
        String regex = "^(?=.*[A-Za-z])(?=.*\\d)[A-Za-z\\d]{8,}$\n";
        Pattern pattern = Pattern.compile(regex);
        Matcher matcher = pattern.matcher(body.getPassword());
        if (!matcher.matches()) {
            throw new ServiceException("密码至少8个字符,不能全是字母或者数字");
        }
        // 根据手机号查询用户
        AppUser appUser = remoteAppUserService.getUserByCondition(
                AppUserDTO.builder().cellPhone(body.getCellPhone()).build(),
@@ -435,13 +470,13 @@
     * @param body
     * @return
     */
    public LoginUser appCaptchaLogin(AppCaptchaBody body) {
    public AppLoginUser appCaptchaLogin(AppCaptchaBody body) {
        // 校验验证码
        if (!verifyCaptcha(body.getCellPhone(), body.getCaptcha(),
                CacheConstants.APP_LOGIN_CAPTCHA_CODE_PREFIX)) {
            throw new ServiceException("验证码不正确");
        }
        R<LoginUser> loginUserR = remoteAppUserService.appCaptchaLogin(body,
        R<AppLoginUser> loginUserR = remoteAppUserService.appCaptchaLogin(body,
                SecurityConstants.INNER);
        if (R.FAIL == loginUserR.getCode()) {
            throw new ServiceException(loginUserR.getMsg());
@@ -449,19 +484,20 @@
        return loginUserR.getData();
    }
    public WXLoginUser verifyCellPhone(AppVerifyCellPhoneBody body) {
        String captcha = redisService.getCacheObject(
    public AppLoginUser verifyCellPhone(AppVerifyCellPhoneBody body) {
        Boolean res = redisService.hasKey(
                CacheConstants.APP_VERIFY_CAPTCHA_CODE_PREFIX + body.getCellPhone());
        if (Objects.isNull(captcha)) {
        if (!res) {
            throw new ServiceException("验证码已失效,请重新获取");
        }
        // 校验验证码
        if (!verifyCaptcha(body.getCellPhone(), body.getCaptcha(),
                CacheConstants.APP_LOGIN_CAPTCHA_CODE_PREFIX)) {
                CacheConstants.APP_VERIFY_CAPTCHA_CODE_PREFIX)) {
            throw new ServiceException("验证码不正确");
        }
        // 更新用户系统用户信息
        R<WXLoginUser> result = remoteAppUserService.verifyCellPhone(body, SecurityConstants.INNER);
        R<AppLoginUser> result = remoteAppUserService.verifyCellPhone(body,
                SecurityConstants.INNER);
        if (R.FAIL == result.getCode()) {
            throw new ServiceException(result.getMsg());
        }
xinquan-common/xinquan-common-core/pom.xml
@@ -140,6 +140,11 @@
            <version>2.8.0</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/com.alibaba/easyexcel -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>easyexcel</artifactId>
        </dependency>
    </dependencies>
xinquan-common/xinquan-common-core/src/main/java/com/xinquan/common/core/constant/CacheConstants.java
@@ -31,6 +31,10 @@
     * 权限缓存前缀
     */
    public final static String LOGIN_TOKEN_KEY = "login_tokens:";
    /**
     * APP用户登录token缓存前缀
     */
    public final static String APP_USER_LOGIN_TOKEN_KEY = "app_login_tokens:";
    /**
     * 验证码 redis key
xinquan-common/xinquan-common-core/src/main/java/com/xinquan/common/core/domain/R.java
@@ -1,7 +1,8 @@
package com.xinquan.common.core.domain;
import java.io.Serializable;
import com.xinquan.common.core.constant.Constants;
import io.swagger.annotations.ApiModelProperty;
import java.io.Serializable;
/**
 * 响应信息主体
@@ -18,10 +19,13 @@
    /** 失败 */
    public static final int FAIL = Constants.FAIL;
    @ApiModelProperty(value = "业务状态码,200-成功,其它-失败")
    private int code;
    @ApiModelProperty(value = "响应消息", example = "操作成功")
    private String msg;
    @ApiModelProperty(value = "响应数据")
    private T data;
    public static <T> R<T> ok()
xinquan-common/xinquan-common-core/src/main/java/com/xinquan/common/core/enums/AnswerEnum.java
New file
@@ -0,0 +1,24 @@
package com.xinquan.common.core.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
@Getter
@AllArgsConstructor
public enum AnswerEnum {
    YES("1", "是"),
    NO("2", "否"),
    SOMETIMES("3", "偶尔有");
    private final String code;
    private final String desc;
    public static AnswerEnum getEnumByCode(String code) {
        for (AnswerEnum e : AnswerEnum.values()) {
            if (e.code.equals(code)) {
                return e;
            }
        }
        return null;
    }
}
xinquan-common/xinquan-common-core/src/main/java/com/xinquan/common/core/enums/AnswerTagEnum.java
New file
@@ -0,0 +1,29 @@
package com.xinquan.common.core.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
@Getter
@AllArgsConstructor
public enum AnswerTagEnum {
    QUESTION_ONE(1, 1L, "您是否内心纠结敏感多疑?"),
    QUESTION_TWO(2, 2L, "您是否辗转反侧彻夜难眠?"),
    QUESTION_THREE(3, 3L, "您是否情绪失控暴躁易怒?"),
    QUESTION_FOUR(4, 4L, "您是否沉迷情感倍受伤害?"),
    QUESTION_FIVE(5, 5L, "您是否人生迷茫踌躇彷徨?");
    private final Integer code;
    private final Long tagId;
    private final String desc;
    public static AnswerTagEnum getEnumByCode(Integer code) {
        for (AnswerTagEnum e : AnswerTagEnum.values()) {
            if (e.getCode().equals(code)) {
                return e;
            }
        }
        return null;
    }
}
xinquan-common/xinquan-common-core/src/main/java/com/xinquan/common/core/enums/ReportStatusEnum.java
New file
@@ -0,0 +1,23 @@
package com.xinquan.common.core.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
@Getter
@AllArgsConstructor
public enum ReportStatusEnum {
    NOT_REPORTED(1, "未被举报"),
    REPORTED(2, "已被举报");
    private final Integer code;
    private final String desc;
    public static ReportStatusEnum getEnumByCode(Integer code) {
        for (ReportStatusEnum e : ReportStatusEnum.values()) {
            if (e.code.equals(code)) {
                return e;
            }
        }
        return null;
    }
}
xinquan-common/xinquan-common-core/src/main/java/com/xinquan/common/core/utils/Base64DES.java
New file
@@ -0,0 +1,153 @@
package com.xinquan.common.core.utils;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
public class Base64DES {
    private static final char[] legalChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".toCharArray();
    public static String encode(byte[] data) {
        int start = 0;
        int len = data.length;
        StringBuffer buf = new StringBuffer(data.length * 3 / 2);
        int end = len - 3;
        int i = start;
        int n = 0;
        while (i <= end) {
            int d = ((((int) data[i]) & 0x0ff) << 16)
                    | ((((int) data[i + 1]) & 0x0ff) << 8)
                    | (((int) data[i + 2]) & 0x0ff);
            buf.append(legalChars[(d >> 18) & 63]);
            buf.append(legalChars[(d >> 12) & 63]);
            buf.append(legalChars[(d >> 6) & 63]);
            buf.append(legalChars[d & 63]);
            i += 3;
            if (n++ >= 14) {
                n = 0;
                buf.append(" ");
            }
        }
        if (i == start + len - 2) {
            int d = ((((int) data[i]) & 0x0ff) << 16)
                    | ((((int) data[i + 1]) & 255) << 8);
            buf.append(legalChars[(d >> 18) & 63]);
            buf.append(legalChars[(d >> 12) & 63]);
            buf.append(legalChars[(d >> 6) & 63]);
            buf.append("=");
        } else if (i == start + len - 1) {
            int d = (((int) data[i]) & 0x0ff) << 16;
            buf.append(legalChars[(d >> 18) & 63]);
            buf.append(legalChars[(d >> 12) & 63]);
            buf.append("==");
        }
        return buf.toString();
    }
    public static byte[] decode(String s) {
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        try {
            decode(s, bos);
        } catch (IOException e) {
            throw new RuntimeException();
        }
        byte[] decodedBytes = bos.toByteArray();
        try {
            bos.close();
            // bos = null;
        } catch (IOException ex) {
            System.err.println("Error while decoding BASE64: " + ex.toString());
        }
        return decodedBytes;
    }
    private static void decode(String s, OutputStream os) throws IOException {
        int i = 0;
        int len = s.length();
        while (true) {
            while (i < len && s.charAt(i) <= ' ') {
                i++;
            }
            if (i == len) {
                break;
            }
            int tri = (decode(s.charAt(i)) << 18)
                    + (decode(s.charAt(i + 1)) << 12)
                    + (decode(s.charAt(i + 2)) << 6)
                    + (decode(s.charAt(i + 3)));
            os.write((tri >> 16) & 255);
            if (s.charAt(i + 2) == '=') {
                break;
            }
            os.write((tri >> 8) & 255);
            if (s.charAt(i + 3) == '=') {
                break;
            }
            os.write(tri & 255);
            i += 4;
        }
    }
    private static int decode(char c) {
        if (c >= 'A' && c <= 'Z') {
            return ((int) c) - 65;
        } else if (c >= 'a' && c <= 'z') {
            return ((int) c) - 97 + 26;
        } else if (c >= '0' && c <= '9') {
            return ((int) c) - 48 + 26 + 26;
        } else {
            switch (c) {
                case '+':
                    return 62;
                case '/':
                    return 63;
                case '=':
                    return 0;
                default:
                    throw new RuntimeException("unexpected code: " + c);
            }
        }
    }
}
xinquan-common/xinquan-common-core/src/main/java/com/xinquan/common/core/utils/DES.java
New file
@@ -0,0 +1,158 @@
package com.xinquan.common.core.utils;
import java.net.URLDecoder;
import java.net.URLEncoder;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
public class DES {
    /*
     * 不要写成:Cipher cipher = Cipher.getInstance("DES");
     * 或:Cipher cipher = Cipher.getInstance("DES/CBC/NoPadding");
     * 原因是Cipher cipher = Cipher.getInstance("DES");
     * 与Cipher cipher = Cipher.getInstance("DES/ECB/PKCS5Padding");
     * 等同,填充方式错误,加密的时候会得到16长度的字节数组。
     * 应该写成
     * Cipher cipher = Cipher.getInstance("DES/ECB/NoPadding");
     */
    private static final String KEY = "hfh7L4nUzKcnK6JtJ5z27ygad5p9EanZn5wCT6Zw9KvctK5hs7Z8FsjDEpShmC5W";
    private static byte[] iv = {25, 24, 40, 41, 60, 68, 70, 97};
    /**
     * 指定密钥加密
     *
     * @param encryptString 原文
     * @param encryptKey    密钥
     * @return 密文
     * @throws Exception
     */
    public static String encryptDES(String encryptString, String encryptKey)
            throws Exception {
        IvParameterSpec zeroIv = new IvParameterSpec(iv);
        SecretKeySpec key = new SecretKeySpec(encryptKey.getBytes(), "DES");
        Cipher cipher = Cipher.getInstance("DES/CBC/PKCS5Padding");
        cipher.init(Cipher.ENCRYPT_MODE, key, zeroIv);
        byte[] encryptedData = cipher.doFinal(encryptString.getBytes());
        return Base64DES.encode(encryptedData);
    }
    /**
     * 指定密钥进行解密
     *
     * @param decryptString 密文
     * @param decryptKey    密钥
     * @return 原文
     * @throws Exception
     */
    public static String decryptDES(String decryptString, String decryptKey)
            throws Exception {
        byte[] byteMi = Base64DES.decode(decryptString);
        IvParameterSpec zeroIv = new IvParameterSpec(iv);
        SecretKeySpec key = new SecretKeySpec(decryptKey.getBytes(), "DES");
        Cipher cipher = Cipher.getInstance("DES/CBC/PKCS5Padding");
        cipher.init(Cipher.DECRYPT_MODE, key, zeroIv);
        byte decryptedData[] = cipher.doFinal(byteMi);
        return new String(decryptedData);
    }
    /**
     * 默认密钥加密
     *
     * @param encryptString 原文
     * @return 密文
     * @throws Exception
     */
    public static String encryptDES(String encryptString)
            throws Exception {
        IvParameterSpec zeroIv = new IvParameterSpec(iv);
        SecretKeySpec key = new SecretKeySpec(KEY.getBytes(), "DES");
        Cipher cipher = Cipher.getInstance("DES/CBC/PKCS5Padding");
        cipher.init(Cipher.ENCRYPT_MODE, key, zeroIv);
        byte[] encryptedData = cipher.doFinal(encryptString.getBytes());
        return Base64DES.encode(encryptedData);
    }
    /**
     * 默认密钥解密
     *
     * @param decryptString 密文
     * @return 解密得到的原文
     * @throws Exception
     */
    public static String decryptDES(String decryptString)
            throws Exception {
        byte[] byteMi = Base64DES.decode(decryptString);
        IvParameterSpec zeroIv = new IvParameterSpec(iv);
        SecretKeySpec key = new SecretKeySpec(KEY.getBytes(), "DES");
        Cipher cipher = Cipher.getInstance("DES/CBC/PKCS5Padding");
        cipher.init(Cipher.DECRYPT_MODE, key, zeroIv);
        byte decryptedData[] = cipher.doFinal(byteMi);
        return new String(decryptedData, "UTF-8");
    }
    public static String parseByte2HexStr(byte buf[]) {
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < buf.length; i++) {
            String hex = Integer.toHexString(buf[i] & 0xFF);
            if (hex.length() == 1) {
                hex = '0' + hex;
            }
            sb.append(hex.toUpperCase());
        }
        return sb.toString();
    }
    public static void main(String[] args) {
        String plaintext = "server=/app/public/getAllRegionList";
        try {
            System.out.println("明文:" + plaintext);
            System.out.println("密钥:" + KEY);
            System.out.println("DES密文:" + DES.encryptDES(plaintext));
            System.out.println("DES解密:" + DES.decryptDES(DES.encryptDES(plaintext)));
            System.out.println(
                    "URLEncoder加密:" + URLEncoder.encode(DES.encryptDES(plaintext), "UTF-8"));
            System.out.println("URLEncoder解密:" + URLDecoder.decode(
                    URLEncoder.encode(DES.encryptDES(plaintext), "UTF-8"), "UTF-8"));
            System.out.println("长度:"
                    + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".length());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
xinquan-common/xinquan-common-core/src/main/java/com/xinquan/common/core/web/domain/BaseEntity.java
@@ -1,10 +1,5 @@
package com.xinquan.common.core.web.domain;
import java.io.Serializable;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableLogic;
@@ -13,6 +8,10 @@
import com.fasterxml.jackson.annotation.JsonInclude;
import com.xinquan.common.core.annotation.Excel;
import io.swagger.annotations.ApiModelProperty;
import java.io.Serializable;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
/**
 * Entity基类
@@ -36,7 +35,7 @@
    /** 创建时间 */
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @TableField("create_time")
    private Date createTime;
    private LocalDateTime createTime;
    /** 更新者 */
    @ApiModelProperty(value = "记录修改人,前端忽略")
@@ -47,7 +46,7 @@
    /** 更新时间 */
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @TableField("update_time")
    private Date updateTime;
    private LocalDateTime updateTime;
    /** 是否删除 1未删除 0已删除 */
    @TableLogic
@@ -91,16 +90,6 @@
        this.createBy = createBy;
    }
    public Date getCreateTime()
    {
        return createTime;
    }
    public void setCreateTime(Date createTime)
    {
        this.createTime = createTime;
    }
    public String getUpdateBy()
    {
        return updateBy;
@@ -111,13 +100,19 @@
        this.updateBy = updateBy;
    }
    public Date getUpdateTime()
    {
    public LocalDateTime getCreateTime() {
        return createTime;
    }
    public void setCreateTime(LocalDateTime createTime) {
        this.createTime = createTime;
    }
    public LocalDateTime getUpdateTime() {
        return updateTime;
    }
    public void setUpdateTime(Date updateTime)
    {
    public void setUpdateTime(LocalDateTime updateTime) {
        this.updateTime = updateTime;
    }
xinquan-common/xinquan-common-security/src/main/java/com/xinquan/common/security/service/TokenService.java
@@ -9,8 +9,8 @@
import com.xinquan.common.core.utils.uuid.IdUtils;
import com.xinquan.common.redis.service.RedisService;
import com.xinquan.common.security.utils.SecurityUtils;
import com.xinquan.system.api.model.AppLoginUser;
import com.xinquan.system.api.model.LoginUser;
import com.xinquan.system.api.model.WXLoginUser;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
@@ -36,6 +36,7 @@
    private final static long expireTime = CacheConstants.EXPIRATION;
    private final static String ACCESS_TOKEN = CacheConstants.LOGIN_TOKEN_KEY;
    private final static String APP_USER_ACCESS_TOKEN = CacheConstants.APP_USER_LOGIN_TOKEN_KEY;
    private final static Long MILLIS_MINUTE_TEN = CacheConstants.REFRESH_TIME * MILLIS_MINUTE;
@@ -163,40 +164,55 @@
        redisService.setCacheObject(userKey, loginUser, expireTime, TimeUnit.MINUTES);
    }
    private void refreshToken4AppLoginUser(AppLoginUser appLoginUser) {
        appLoginUser.setLoginTime(System.currentTimeMillis());
        appLoginUser.setExpireTime(appLoginUser.getLoginTime() + expireTime * MILLIS_MINUTE);
        // 根据uuid将loginUser缓存
        String userKey = getTokenKeyByAppLoginUser(appLoginUser.getToken());
        redisService.setCacheObject(userKey, appLoginUser, expireTime, TimeUnit.MINUTES);
    }
    private String getTokenKeyByAppLoginUser(String token) {
        return APP_USER_ACCESS_TOKEN + token;
    }
    private String getTokenKey(String token)
    {
        return ACCESS_TOKEN + token;
    }
    public Map<String, Object> createToken4WXLoginUser(WXLoginUser wxLoginUser) {
        LoginUser loginUser = new LoginUser();
    /**
     * app用户创建token
     *
     * @param appLoginUser
     * @return
     */
    public AppLoginUser createToken4AppLoginUser(AppLoginUser appLoginUser) {
        // Jwt存储信息
        Map<String, Object> claimsMap = new HashMap<String, Object>();
        // 接口返回信息
        Map<String, Object> rspMap = new HashMap<String, Object>();
        // 用户为空只返回openid
        rspMap.put("cellPhone", "");
        rspMap.put("access_token", "");
        if (wxLoginUser.getSysUser() != null) {
            loginUser.setSysUser(wxLoginUser.getSysUser());
            String token = IdUtils.fastUUID();
            Long userId = loginUser.getSysUser().getUserId();
            String userName = loginUser.getSysUser().getUserName();
            loginUser.setToken(token);
            loginUser.setUserid(userId);
            loginUser.setIpaddr(IpUtils.getIpAddr());
            refreshToken(loginUser);
            claimsMap.put(SecurityConstants.USER_KEY, token);
            claimsMap.put(SecurityConstants.DETAILS_USER_ID, userId);
            claimsMap.put(SecurityConstants.DETAILS_USERNAME, userName);
            if (StringUtils.isNotBlank(wxLoginUser.getCellPhone())) {
                rspMap.put("cellPhone", wxLoginUser.getCellPhone());
            } else {
                rspMap.put("cellPhone", "");
            }
            rspMap.put("access_token", JwtUtils.createToken(claimsMap));
            rspMap.put("expires_in", expireTime);
        }
        return rspMap;
        String token = IdUtils.fastUUID();
        Long userId = appLoginUser.getAppUserId();
        String userName = appLoginUser.getSysUser().getNickName();
        appLoginUser.setToken(token);
        appLoginUser.setUserid(userId);
        appLoginUser.setIpaddr(IpUtils.getIpAddr());
        LoginUser loginUser = new LoginUser();
        loginUser.setSysUser(appLoginUser.getSysUser());
        loginUser.setToken(token);
        loginUser.setUserid(userId);
        loginUser.setUsername(userName);
        loginUser.setIpaddr(IpUtils.getIpAddr());
        refreshToken(loginUser);
        // refreshToken4AppLoginUser(appLoginUser);
        claimsMap.put(SecurityConstants.USER_KEY, token);
        claimsMap.put(SecurityConstants.DETAILS_USER_ID, userId);
        claimsMap.put(SecurityConstants.DETAILS_USERNAME, userName);
        appLoginUser.setAccessToken(JwtUtils.createToken(claimsMap));
        appLoginUser.setExpireIn(expireTime);
        return appLoginUser;
    }
}
xinquan-gateway/src/main/java/com/xinquan/gateway/filter/AuthFilter.java
@@ -1,14 +1,5 @@
package com.xinquan.gateway.filter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import com.xinquan.common.core.constant.CacheConstants;
import com.xinquan.common.core.constant.HttpStatus;
import com.xinquan.common.core.constant.SecurityConstants;
@@ -19,6 +10,15 @@
import com.xinquan.common.redis.service.RedisService;
import com.xinquan.gateway.config.properties.IgnoreWhiteProperties;
import io.jsonwebtoken.Claims;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
/**
@@ -83,6 +83,10 @@
        return chain.filter(exchange.mutate().request(mutate.build()).build());
    }
    private String getAppUserTokenKey(String userkey) {
        return CacheConstants.APP_USER_LOGIN_TOKEN_KEY + userkey;
    }
    private void addHeader(ServerHttpRequest.Builder mutate, String name, Object value)
    {
        if (value == null)
xinquan-modules/xinquan-course/pom.xml
@@ -121,19 +121,6 @@
      <version>1.2.47</version>
    </dependency>
    <dependency>
      <groupId>cn.afterturn</groupId>
      <artifactId>easypoi-spring-boot-starter</artifactId>
      <version>4.0.0</version>
      <exclusions>
        <exclusion>
          <artifactId>guava</artifactId>
          <groupId>com.google.guava</groupId>
        </exclusion>
      </exclusions>
    </dependency>
  </dependencies>
  <build>
xinquan-modules/xinquan-course/src/main/java/com/xinquan/course/controller/client/ClientCourseController.java
@@ -1,7 +1,25 @@
package com.xinquan.course.controller.client;
import com.xinquan.common.core.constant.SecurityConstants;
import com.xinquan.common.core.domain.R;
import com.xinquan.common.core.utils.page.PageDTO;
import com.xinquan.course.domain.vo.ClientCourseCategoryVO;
import com.xinquan.course.domain.vo.ClientCourseVO;
import com.xinquan.course.service.CourseCategoryService;
import com.xinquan.course.service.CourseService;
import com.xinquan.system.api.RemoteBannerService;
import com.xinquan.system.api.domain.vo.BannerVO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
/**
@@ -12,9 +30,61 @@
 * @author mitao
 * @since 2024-08-21
 */
@Api(tags = {"用户端-课程相关接口"})
@RestController
@RequiredArgsConstructor
@RequestMapping("/client/course/course")
public class ClientCourseController {
    private final CourseCategoryService courseCategoryService;
    private final CourseService courseService;
    private final RemoteBannerService remoteBannerService;
    /**
     * 获取轮播图列表
     *
     * @return 轮播图列表
     */
    @GetMapping("/getBannerList")
    @ApiOperation(value = "获取轮播图列表")
    public R<List<BannerVO>> getBannerList() {
        return remoteBannerService.getBannerList(SecurityConstants.INNER);
    }
    /**
     * 获取课程分类列表
     *
     * @return 课程分类列表
     */
    @GetMapping("/getCourseCategoryList")
    @ApiOperation(value = "获取课程分类列表")
    public R<List<ClientCourseCategoryVO>> getCourseCategoryList() {
        return R.ok(courseCategoryService.getCourseCategoryList());
    }
    /**
     * 获取课程列表-分页
     *
     * @param courseTitle 课程标题
     * @param cateId      分类id
     * @param pageCurr    分页参数,当前页码
     * @param pageSize    分页参数,每页数量
     * @return 课程分页列表
     */
    @PostMapping("/getCoursePageList")
    @ApiOperation(value = "获取课程列表-分页")
    @ApiImplicitParams({
            @ApiImplicitParam(value = "课程标题", name = "courseTitle", required = false, dataType = "String"),
            @ApiImplicitParam(value = "课程分类id", name = "cateId", required = false, dataType = "Long"),
            @ApiImplicitParam(value = "分页参数,当前页码", name = "pageCurr", required = true, dataType = "Integer"),
            @ApiImplicitParam(value = "分页参数,每页数量", name = "pageSize", required = true, dataType = "Integer")
    })
    public R<PageDTO<ClientCourseVO>> getCourseList(
            @RequestParam(defaultValue = "", value = "courseTitle", required = false) String courseTitle,
            @RequestParam(required = false) Long cateId,
            @RequestParam(value = "pageCurr", defaultValue = "1") Integer pageCurr,
            @RequestParam(value = "pageSize", defaultValue = "10") Integer pageSize) {
        return R.ok(courseService.getCoursePageList(courseTitle, cateId, pageCurr, pageSize));
    }
}
xinquan-modules/xinquan-course/src/main/java/com/xinquan/course/controller/inner/InnerCourseLearningRecordController.java
New file
@@ -0,0 +1,20 @@
package com.xinquan.course.controller.inner;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
 * <p>
 * 用户学习记录 前端控制器
 * </p>
 *
 * @author mitao
 * @since 2024-09-06
 */
@RestController
@RequestMapping("/inner/course/course-learning-record")
public class InnerCourseLearningRecordController {
}
xinquan-modules/xinquan-course/src/main/java/com/xinquan/course/domain/CourseLearningRecord.java
New file
@@ -0,0 +1,40 @@
package com.xinquan.course.domain;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.xinquan.common.core.web.domain.BaseModel;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
 * <p>
 * 用户学习记录
 * </p>
 *
 * @author mitao
 * @since 2024-09-06
 */
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("t_course_learning_record")
@ApiModel(value = "CourseLearningRecord对象", description = "用户学习记录")
public class CourseLearningRecord extends BaseModel {
    private static final long serialVersionUID = 1L;
    @ApiModelProperty(value = "用户树苗id")
    @TableId("id")
    private Long id;
    @ApiModelProperty(value = "用户id")
    @TableField("app_user_id")
    private Long appUserId;
    @ApiModelProperty(value = "课程id ")
    @TableField("course_id")
    private Long courseId;
}
xinquan-modules/xinquan-course/src/main/java/com/xinquan/course/domain/vo/ClientCourseCategoryVO.java
New file
@@ -0,0 +1,27 @@
package com.xinquan.course.domain.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import java.io.Serializable;
import lombok.Data;
/**
 * @author mitao
 * @date 2024/9/7
 */
@Data
@ApiModel(value = "课程分类视图对象")
public class ClientCourseCategoryVO implements Serializable {
    private static final long serialVersionUID = 6293214581320328382L;
    @ApiModelProperty(value = "id")
    private Long id;
    @ApiModelProperty(value = "分类名称")
    private String name;
    @ApiModelProperty(value = "分类图片")
    private String imageUrl;
}
xinquan-modules/xinquan-course/src/main/java/com/xinquan/course/domain/vo/ClientCourseVO.java
New file
@@ -0,0 +1,68 @@
package com.xinquan.course.domain.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
 * @author mitao
 * @date 2024/9/7
 */
@Data
@ApiModel(value = "课程视图对象")
public class ClientCourseVO {
    @ApiModelProperty(value = "课程id")
    private Long id;
    @ApiModelProperty(value = "课程类型 1=线上课程 2=线下课程")
    private Integer courseType;
    @ApiModelProperty(value = "课程分类id")
    private Long cateId;
    @ApiModelProperty(value = "课程标题")
    private String courseTitle;
    @ApiModelProperty(value = "课程描述")
    private String description;
    @ApiModelProperty(value = "上架状态 1=上架 2=下架")
    private Integer listingStatus;
    @ApiModelProperty(value = "推荐课程 1=推荐 2=不推荐")
    private Integer recommend;
    @ApiModelProperty(value = "价格设定 1=免费 2=会员免费 3=单独收费")
    private Integer chargeType;
    @ApiModelProperty(value = "排序权重")
    private Integer sortNum;
    @ApiModelProperty(value = "导师")
    private String tutor;
    @ApiModelProperty(value = "列表封面图")
    private String coverUrl;
    @ApiModelProperty(value = "详情页封面图|详情页视频")
    private String detailUrl;
    @ApiModelProperty(value = "课程简介")
    private String briefIntroduction;
    @ApiModelProperty(value = "微信二维码")
    private String wxQrCode;
    @ApiModelProperty(value = "课程地址")
    private String address;
    @ApiModelProperty(value = "详细地址")
    private String addressDetail;
    @ApiModelProperty(value = "课程地址经度")
    private Double longitude;
    @ApiModelProperty(value = "课程地址纬度")
    private Double latitude;
}
xinquan-modules/xinquan-course/src/main/java/com/xinquan/course/mapper/CourseLearningRecordMapper.java
New file
@@ -0,0 +1,16 @@
package com.xinquan.course.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.xinquan.course.domain.CourseLearningRecord;
/**
 * <p>
 * 用户学习记录 Mapper 接口
 * </p>
 *
 * @author mitao
 * @since 2024-09-06
 */
public interface CourseLearningRecordMapper extends BaseMapper<CourseLearningRecord> {
}
xinquan-modules/xinquan-course/src/main/java/com/xinquan/course/service/CourseCategoryService.java
@@ -1,7 +1,9 @@
package com.xinquan.course.service;
import com.xinquan.course.domain.CourseCategory;
import com.baomidou.mybatisplus.extension.service.IService;
import com.xinquan.course.domain.CourseCategory;
import com.xinquan.course.domain.vo.ClientCourseCategoryVO;
import java.util.List;
/**
 * <p>
@@ -13,4 +15,10 @@
 */
public interface CourseCategoryService extends IService<CourseCategory> {
    /**
     * 获取课程分类列表
     *
     * @return 课程分类列表
     */
    List<ClientCourseCategoryVO> getCourseCategoryList();
}
xinquan-modules/xinquan-course/src/main/java/com/xinquan/course/service/CourseLearningRecordService.java
New file
@@ -0,0 +1,16 @@
package com.xinquan.course.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.xinquan.course.domain.CourseLearningRecord;
/**
 * <p>
 * 用户学习记录 服务类
 * </p>
 *
 * @author mitao
 * @since 2024-09-06
 */
public interface CourseLearningRecordService extends IService<CourseLearningRecord> {
}
xinquan-modules/xinquan-course/src/main/java/com/xinquan/course/service/CourseService.java
@@ -1,7 +1,9 @@
package com.xinquan.course.service;
import com.xinquan.course.domain.Course;
import com.baomidou.mybatisplus.extension.service.IService;
import com.xinquan.common.core.utils.page.PageDTO;
import com.xinquan.course.domain.Course;
import com.xinquan.course.domain.vo.ClientCourseVO;
/**
 * <p>
@@ -13,4 +15,15 @@
 */
public interface CourseService extends IService<Course> {
    /**
     * 获取课程列表-分页
     *
     * @param courseTitle 课程标题
     * @param cateId      分类id
     * @param pageCurr    分页参数,当前页码
     * @param pageSize    分页参数,每页数量
     * @return 课程分页列表
     */
    PageDTO<ClientCourseVO> getCoursePageList(String courseTitle, Long cateId, Integer pageCurr,
            Integer pageSize);
}
xinquan-modules/xinquan-course/src/main/java/com/xinquan/course/service/impl/CourseCategoryServiceImpl.java
@@ -1,9 +1,13 @@
package com.xinquan.course.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xinquan.common.core.enums.DisabledEnum;
import com.xinquan.common.core.utils.page.BeanUtils;
import com.xinquan.course.domain.CourseCategory;
import com.xinquan.course.domain.vo.ClientCourseCategoryVO;
import com.xinquan.course.mapper.CourseCategoryMapper;
import com.xinquan.course.service.CourseCategoryService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import java.util.List;
import org.springframework.stereotype.Service;
/**
@@ -17,4 +21,16 @@
@Service
public class CourseCategoryServiceImpl extends ServiceImpl<CourseCategoryMapper, CourseCategory> implements CourseCategoryService {
    /**
     * 获取课程分类列表
     *
     * @return 课程分类列表
     */
    @Override
    public List<ClientCourseCategoryVO> getCourseCategoryList() {
        List<CourseCategory> list = this.lambdaQuery()
                .eq(CourseCategory::getShowOnCourseHomepage, DisabledEnum.YES.getCode())
                .list();
        return BeanUtils.copyList(list, ClientCourseCategoryVO.class);
    }
}
xinquan-modules/xinquan-course/src/main/java/com/xinquan/course/service/impl/CourseLearningRecordServiceImpl.java
New file
@@ -0,0 +1,22 @@
package com.xinquan.course.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xinquan.course.domain.CourseLearningRecord;
import com.xinquan.course.mapper.CourseLearningRecordMapper;
import com.xinquan.course.service.CourseLearningRecordService;
import org.springframework.stereotype.Service;
/**
 * <p>
 * 用户学习记录 服务实现类
 * </p>
 *
 * @author mitao
 * @since 2024-09-06
 */
@Service
public class CourseLearningRecordServiceImpl extends
        ServiceImpl<CourseLearningRecordMapper, CourseLearningRecord> implements
        CourseLearningRecordService {
}
xinquan-modules/xinquan-course/src/main/java/com/xinquan/course/service/impl/CourseServiceImpl.java
@@ -1,9 +1,15 @@
package com.xinquan.course.service.impl;
import com.alibaba.nacos.common.utils.StringUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xinquan.common.core.utils.page.CollUtils;
import com.xinquan.common.core.utils.page.PageDTO;
import com.xinquan.course.domain.Course;
import com.xinquan.course.domain.vo.ClientCourseVO;
import com.xinquan.course.mapper.CourseMapper;
import com.xinquan.course.service.CourseService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import java.util.Objects;
import org.springframework.stereotype.Service;
/**
@@ -17,4 +23,26 @@
@Service
public class CourseServiceImpl extends ServiceImpl<CourseMapper, Course> implements CourseService {
    /**
     * 获取课程列表-分页
     *
     * @param courseTitle 课程标题
     * @param cateId      分类id
     * @param pageCurr    分页参数,当前页码
     * @param pageSize    分页参数,每页数量
     * @return 课程分页列表
     */
    @Override
    public PageDTO<ClientCourseVO> getCoursePageList(String courseTitle, Long cateId,
            Integer pageCurr, Integer pageSize) {
        Page<Course> page = this.lambdaQuery()
                .like(StringUtils.isNotBlank(courseTitle), Course::getCourseTitle, courseTitle)
                .eq(Objects.nonNull(cateId), Course::getCateId, cateId)
                .orderByDesc(Course::getSortNum)
                .page(new Page<>(pageCurr, pageSize));
        if (CollUtils.isEmpty(page.getRecords())) {
            return PageDTO.empty(page);
        }
        return PageDTO.of(page, ClientCourseVO.class);
    }
}
xinquan-modules/xinquan-course/src/main/resources/mapper/course/CourseCategoryMapper.xml
copy from xinquan-modules/xinquan-meditation/src/main/resources/mapper/meditation/MeditationUserOperationMapper.xml copy to xinquan-modules/xinquan-course/src/main/resources/mapper/course/CourseCategoryMapper.xml
File was copied from xinquan-modules/xinquan-meditation/src/main/resources/mapper/meditation/MeditationUserOperationMapper.xml
@@ -1,19 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.xinquan.meditation.mapper.MeditationUserOperationMapper">
<mapper namespace="com.xinquan.course.mapper.CourseCategoryMapper">
    <!-- 通用查询映射结果 -->
    <resultMap id="BaseResultMap" type="com.xinquan.meditation.domain.MeditationUserOperation">
    <resultMap id="BaseResultMap" type="com.xinquan.course.domain.CourseCategory">
        <id column="id" property="id" />
        <result column="del_flag" property="delFlag" />
        <result column="create_by" property="createBy" />
        <result column="create_time" property="createTime" />
        <result column="update_by" property="updateBy" />
        <result column="update_time" property="updateTime" />
        <result column="like" property="like" />
        <result column="favorite" property="favorite" />
        <result column="meditation_id" property="meditationId" />
        <result column="app_user_id" property="appUserId" />
        <result column="name" property="name" />
        <result column="image_url" property="imageUrl" />
        <result column="sort_num" property="sortNum" />
        <result column="show_on_course_homepage" property="showOnCourseHomepage" />
    </resultMap>
    <!-- 通用查询结果列 -->
@@ -23,7 +23,7 @@
        create_time,
        update_by,
        update_time,
        id, like, favorite, meditation_id, app_user_id
        id, name, image_url, sort_num, show_on_course_homepage
    </sql>
</mapper>
xinquan-modules/xinquan-course/src/main/resources/mapper/course/CourseChapterMapper.xml
New file
@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.xinquan.course.mapper.CourseChapterMapper">
    <!-- 通用查询映射结果 -->
    <resultMap id="BaseResultMap" type="com.xinquan.course.domain.CourseChapter">
        <id column="id" property="id" />
        <result column="del_flag" property="delFlag" />
        <result column="create_by" property="createBy" />
        <result column="create_time" property="createTime" />
        <result column="update_by" property="updateBy" />
        <result column="update_time" property="updateTime" />
        <result column="course_id" property="courseId" />
        <result column="chapter_title" property="chapterTitle" />
        <result column="sort_num" property="sortNum" />
        <result column="video_url" property="videoUrl" />
        <result column="real_learned_num" property="realLearnedNum" />
        <result column="virtual_learned_num" property="virtualLearnedNum" />
        <result column="duration" property="duration" />
    </resultMap>
    <!-- 通用查询结果列 -->
    <sql id="Base_Column_List">
        del_flag,
        create_by,
        create_time,
        update_by,
        update_time,
        id, course_id, chapter_title, sort_num, video_url, real_learned_num, virtual_learned_num, duration
    </sql>
</mapper>
xinquan-modules/xinquan-course/src/main/resources/mapper/course/CourseLearningRecordMapper.xml
copy from xinquan-modules/xinquan-meditation/src/main/resources/mapper/meditation/MeditationUserOperationMapper.xml copy to xinquan-modules/xinquan-course/src/main/resources/mapper/course/CourseLearningRecordMapper.xml
File was copied from xinquan-modules/xinquan-meditation/src/main/resources/mapper/meditation/MeditationUserOperationMapper.xml
@@ -1,19 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.xinquan.meditation.mapper.MeditationUserOperationMapper">
<mapper namespace="com.xinquan.course.mapper.CourseLearningRecordMapper">
    <!-- 通用查询映射结果 -->
    <resultMap id="BaseResultMap" type="com.xinquan.meditation.domain.MeditationUserOperation">
    <resultMap id="BaseResultMap" type="com.xinquan.course.domain.CourseLearningRecord">
        <id column="id" property="id" />
        <result column="del_flag" property="delFlag" />
        <result column="create_by" property="createBy" />
        <result column="create_time" property="createTime" />
        <result column="update_by" property="updateBy" />
        <result column="update_time" property="updateTime" />
        <result column="like" property="like" />
        <result column="favorite" property="favorite" />
        <result column="meditation_id" property="meditationId" />
        <result column="app_user_id" property="appUserId" />
        <result column="course_id" property="courseId" />
    </resultMap>
    <!-- 通用查询结果列 -->
@@ -23,7 +21,7 @@
        create_time,
        update_by,
        update_time,
        id, like, favorite, meditation_id, app_user_id
        id, app_user_id, course_id
    </sql>
</mapper>
xinquan-modules/xinquan-course/src/main/resources/mapper/course/CourseMapper.xml
New file
@@ -0,0 +1,42 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.xinquan.course.mapper.CourseMapper">
    <!-- 通用查询映射结果 -->
    <resultMap id="BaseResultMap" type="com.xinquan.course.domain.Course">
        <id column="id" property="id" />
        <result column="del_flag" property="delFlag" />
        <result column="create_by" property="createBy" />
        <result column="create_time" property="createTime" />
        <result column="update_by" property="updateBy" />
        <result column="update_time" property="updateTime" />
        <result column="course_type" property="courseType" />
        <result column="cate_id" property="cateId" />
        <result column="course_title" property="courseTitle" />
        <result column="description" property="description" />
        <result column="listing_status" property="listingStatus" />
        <result column="recommend" property="recommend" />
        <result column="charge_type" property="chargeType" />
        <result column="sort_num" property="sortNum" />
        <result column="tutor" property="tutor" />
        <result column="cover_url" property="coverUrl" />
        <result column="detail_url" property="detailUrl" />
        <result column="brief_introduction" property="briefIntroduction" />
        <result column="wx_qr_code" property="wxQrCode" />
        <result column="address" property="address" />
        <result column="address_detail" property="addressDetail" />
        <result column="longitude" property="longitude" />
        <result column="latitude" property="latitude" />
    </resultMap>
    <!-- 通用查询结果列 -->
    <sql id="Base_Column_List">
        del_flag,
        create_by,
        create_time,
        update_by,
        update_time,
        id, course_type, cate_id, course_title, description, listing_status, recommend, charge_type, sort_num, tutor, cover_url, detail_url, brief_introduction, wx_qr_code, address, address_detail, longitude, latitude
    </sql>
</mapper>
xinquan-modules/xinquan-course/src/main/resources/mapper/course/TutorSpecialColumnCourseMapper.xml
New file
@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.xinquan.course.mapper.TutorSpecialColumnCourseMapper">
    <!-- 通用查询映射结果 -->
    <resultMap id="BaseResultMap" type="com.xinquan.course.domain.TutorSpecialColumnCourse">
        <id column="id" property="id" />
        <result column="course_id" property="courseId" />
        <result column="tutor_special_column_id" property="tutorSpecialColumnId" />
    </resultMap>
    <!-- 通用查询结果列 -->
    <sql id="Base_Column_List">
        id, course_id, tutor_special_column_id
    </sql>
</mapper>
xinquan-modules/xinquan-course/src/main/resources/mapper/course/TutorSpecialColumnMapper.xml
copy from xinquan-modules/xinquan-meditation/src/main/resources/mapper/meditation/MeditationUserOperationMapper.xml copy to xinquan-modules/xinquan-course/src/main/resources/mapper/course/TutorSpecialColumnMapper.xml
File was copied from xinquan-modules/xinquan-meditation/src/main/resources/mapper/meditation/MeditationUserOperationMapper.xml
@@ -1,19 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.xinquan.meditation.mapper.MeditationUserOperationMapper">
<mapper namespace="com.xinquan.course.mapper.TutorSpecialColumnMapper">
    <!-- 通用查询映射结果 -->
    <resultMap id="BaseResultMap" type="com.xinquan.meditation.domain.MeditationUserOperation">
    <resultMap id="BaseResultMap" type="com.xinquan.course.domain.TutorSpecialColumn">
        <id column="id" property="id" />
        <result column="del_flag" property="delFlag" />
        <result column="create_by" property="createBy" />
        <result column="create_time" property="createTime" />
        <result column="update_by" property="updateBy" />
        <result column="update_time" property="updateTime" />
        <result column="like" property="like" />
        <result column="favorite" property="favorite" />
        <result column="meditation_id" property="meditationId" />
        <result column="app_user_id" property="appUserId" />
        <result column="video_url" property="videoUrl" />
        <result column="tutor_introduction" property="tutorIntroduction" />
    </resultMap>
    <!-- 通用查询结果列 -->
@@ -23,7 +21,7 @@
        create_time,
        update_by,
        update_time,
        id, like, favorite, meditation_id, app_user_id
        id, video_url, tutor_introduction
    </sql>
</mapper>
xinquan-modules/xinquan-gen/src/main/resources/mapper/generator/MeditationUserFavoriteService.xml
xinquan-modules/xinquan-meditation/pom.xml
@@ -120,19 +120,6 @@
      <version>1.2.47</version>
    </dependency>
    <dependency>
      <groupId>cn.afterturn</groupId>
      <artifactId>easypoi-spring-boot-starter</artifactId>
      <version>4.0.0</version>
      <exclusions>
        <exclusion>
          <artifactId>guava</artifactId>
          <groupId>com.google.guava</groupId>
        </exclusion>
      </exclusions>
    </dependency>
  </dependencies>
  <build>
xinquan-modules/xinquan-meditation/src/main/java/com/xinquan/meditation/controller/client/ClientHomeController.java
New file
@@ -0,0 +1,277 @@
package com.xinquan.meditation.controller.client;
import com.xinquan.common.core.constant.SecurityConstants;
import com.xinquan.common.core.domain.R;
import com.xinquan.common.core.utils.page.PageDTO;
import com.xinquan.meditation.domain.vo.ClientHomeBackgroundMusicVO;
import com.xinquan.meditation.domain.vo.ClientMeditationAndCateVO;
import com.xinquan.meditation.domain.vo.ClientMeditationCategoryVO;
import com.xinquan.meditation.domain.vo.ClientMeditationDetailsVO;
import com.xinquan.meditation.domain.vo.ClientMeditationEverydayVO;
import com.xinquan.meditation.domain.vo.ClientMeditationQuestionVO;
import com.xinquan.meditation.domain.vo.ClientMeditationVO;
import com.xinquan.meditation.service.HomeBackgroundMusicService;
import com.xinquan.meditation.service.MeditationCategoryService;
import com.xinquan.meditation.service.MeditationEverydayService;
import com.xinquan.meditation.service.MeditationQuestionService;
import com.xinquan.meditation.service.MeditationService;
import com.xinquan.system.api.RemoteHotWordsService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
/**
 * <p>
 * 首页相关接口
 * </p>
 *
 * @author mitao
 * @since 2024-08-21
 */
@Api(tags = {"用户端-首页相关接口"})
@RestController
@RequiredArgsConstructor
@RequestMapping("/client/meditation/home")
public class ClientHomeController {
    private final HomeBackgroundMusicService homeBackgroundMusicService;
    private final MeditationService meditationService;
    private final MeditationCategoryService meditationCategoryService;
    private final MeditationEverydayService meditationEverydayService;
    private final RemoteHotWordsService remoteHotWordsService;
    private final MeditationQuestionService meditationQuestionService;
    /**
     * 获取首页背景音乐列表
     *
     * @return 背景音乐列表
     */
    @GetMapping("/getHomeBackgroundMusicList")
    @ApiOperation(value = "获取首页背景音乐列表", notes = "首页顶部默认展示该接口返回的列表中第一条数据的背景图片")
    public R<List<ClientHomeBackgroundMusicVO>> getHomeBackgroundMusicList() {
        return R.ok(homeBackgroundMusicService.getHomeBackgroundMusicList());
    }
    /**
     * 保存用户首页背景音乐设置
     *
     * @param id 首页背景音乐id
     */
    @PostMapping("/savePersonalitySetting")
    @ApiOperation(value = "保存用户首页背景音乐设置")
    @ApiImplicitParam(name = "id", value = "首页背景音乐id", required = true)
    public R<?> savePersonalitySetting(@RequestParam("id") Long id) {
        homeBackgroundMusicService.savePersonalitySetting(id);
        return R.ok();
    }
    /**
     * 根据类型获取冥想分类列表
     *
     * @param type 分类类型
     * @return 分类列表
     */
    @PostMapping("/getCategoryListByType")
    @ApiOperation("根据类型获取冥想音频分类列表")
    @ApiImplicitParam(name = "type", value = "类型 1=顶部4个 2=下面3个", required = true)
    public R<List<ClientMeditationCategoryVO>> getCategoryListByType(
            @RequestParam(value = "type") Integer type) {
        return R.ok(meditationCategoryService.getCategoryListByType(type));
    }
    /**
     * 获取冥想分类列表
     *
     * @return 冥想分类列表
     */
    @GetMapping("/getCategoryList")
    @ApiOperation("获取冥想音频分类列表")
    public R<List<ClientMeditationCategoryVO>> getCategoryList() {
        return R.ok(meditationCategoryService.getCategoryList());
    }
    /**
     * 查询每日冥想
     *
     * @return 每日冥想
     */
    @GetMapping("/getTodayMeditation")
    @ApiOperation("获取今日冥想")
    public R<ClientMeditationEverydayVO> getTodayMeditation() {
        return R.ok(meditationEverydayService.getTodayMeditation());
    }
    /**
     * 获取私人订制
     *
     * @return 冥想列表
     */
    @GetMapping("/getPersonalityPlan")
    @ApiOperation("获取私人订制")
    public R<List<ClientMeditationVO>> getPersonalityPlan() {
        return R.ok(meditationService.getPersonalityPlan());
    }
    /**
     * 根据分类id获取冥想列表
     *
     * @param cateId 分类id
     * @return 冥想列表
     */
    @PostMapping("/getMeditationListByCateId")
    @ApiOperation("根据分类id获取冥想音频列表")
    @ApiImplicitParam(name = "cateId", value = "分类id", dataType = "Long", required = true)
    public R<List<ClientMeditationVO>> getMeditationListByCateId(
            @RequestParam("cateId") Long cateId) {
        return R.ok(meditationService.getMeditationListByCateId(cateId));
    }
    /**
     * 获取全部的冥想及分类列表
     *
     * @return 冥想及分类列表
     */
    @GetMapping("/getMeditationAndCateList")
    @ApiOperation(value = "获取全部的冥想音频及分类列表", notes = "用于首页列表展示")
    public R<List<ClientMeditationAndCateVO>> getMeditationAndCateList() {
        return R.ok(meditationService.getMeditationAndCateList());
    }
    /**
     * 根据分类id获取冥想列表-分页
     *
     * @param cateId   分类id
     * @param pageCurr 当前页码
     * @param pageSize 每页数量
     * @return 冥想列表
     */
    @PostMapping("/getMeditationPageByCateId")
    @ApiOperation(value = "根据分类id获取冥想音频列表-分页", notes = "查看更多")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "cateId", value = "分类id", dataType = "Long", required = true),
            @ApiImplicitParam(name = "pageCurr", value = "分页参数,当前页码", dataType = "Integer", required = true),
            @ApiImplicitParam(name = "pageSize", value = "分页参数,每页数量", dataType = "Integer", required = true)})
    public R<PageDTO<ClientMeditationVO>> getMeditationPageByCateId(@RequestParam Long cateId,
            @RequestParam Integer pageCurr, @RequestParam Integer pageSize) {
        return R.ok(meditationService.getMeditationPageByCateId(cateId, pageCurr, pageSize));
    }
    /**
     * 首页搜索首页
     *
     * @param condition 搜索条件
     * @param pageCurr  当前页码
     * @param pageSize  每页数量
     * @return 冥想列表
     */
    @PostMapping("/search")
    @ApiOperation("首页搜索冥想音频")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "condition", value = "查询条件", dataType = "String", required = true),
            @ApiImplicitParam(name = "pageCurr", value = "分页参数,当前页码", dataType = "Integer", required = true),
            @ApiImplicitParam(name = "pageSize", value = "分页参数,每页数量", dataType = "Integer", required = true)
    })
    public R<PageDTO<ClientMeditationVO>> search(@RequestParam String condition,
            @RequestParam Integer
                    pageCurr, @RequestParam Integer pageSize) {
        return R.ok(meditationService.search(condition, pageCurr, pageSize));
    }
    /**
     * 获取热词列表
     *
     * @return 热词列表
     */
    @GetMapping("/getHotWordList")
    @ApiOperation("获取热词列表")
    public R<List<String>> getHotWordList() {
        return R.ok(remoteHotWordsService.getHotWordList(SecurityConstants.INNER).getData());
    }
    /**
     * 获取冥想音频详情
     *
     * @param id 冥想音频id
     * @return 客户端冥想详情视图对象
     */
    @GetMapping("/getMeditationDetails")
    @ApiOperation("获取冥想音频详情")
    @ApiImplicitParam(name = "id", value = "冥想音频id", dataType = "Long", required = true)
    public R<ClientMeditationDetailsVO> getMeditationDetails(@RequestParam("id") Long id) {
        return R.ok(meditationService.getMeditationDetails(id));
    }
    /**
     * 收藏/取消收藏
     *
     * @param id 冥想音频id
     */
    @PostMapping("/favorite")
    @ApiOperation(value = "收藏/取消收藏")
    @ApiImplicitParam(name = "id", value = "冥想音频id", dataType = "Long", required = true)
    public R<?> favorite(@RequestParam("id") Long id) {
        meditationService.favorite(id);
        return R.ok();
    }
    /**
     * 获取冥想音频提问列表-分页
     *
     * @param id       冥想音频id
     * @param pageCurr 当前页码
     * @param pageSize 每页数量
     * @return 冥想音频提问分页列表
     */
    @GetMapping("/getMeditationQuestionPage")
    @ApiOperation(value = "获取冥想音频提问列表-分页")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "id", value = "冥想音频id", dataType = "Long", required = true),
            @ApiImplicitParam(name = "pageCurr", value = "分页参数,当前页码", dataType = "Integer", required = true),
            @ApiImplicitParam(name = "pageSize", value = "分页参数,每页数量", dataType = "Integer", required = true)
    })
    public R<PageDTO<ClientMeditationQuestionVO>> getMeditationQuestionPage(
            @RequestParam("id") Long id, @RequestParam("pageCurr") Integer pageCurr,
            @RequestParam("pageSize") Integer pageSize) {
        return R.ok(meditationService.getMeditationQuestionPage(id, pageCurr, pageSize));
    }
    /**
     * 举报提问
     *
     * @param id      提问id
     * @param content 举报内容
     */
    @PostMapping("/report")
    @ApiOperation("举报提问")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "id", value = "提问id", dataType = "Long", required = true),
            @ApiImplicitParam(name = "reason", value = "举报原因", dataType = "String", required = true)
    })
    public R<?> report(@RequestParam("id") Long id, @RequestParam("reason") String content) {
        meditationQuestionService.report(id, content);
        return R.ok();
    }
    /**
     * 点赞/取消点赞提问
     *
     * @param id 提问id
     */
    @PostMapping("/likeQuestion")
    @ApiOperation("点赞/取消点赞提问")
    @ApiImplicitParam(name = "id", value = "提问id", dataType = "Long", required = true)
    public R<?> likeQuestion(@RequestParam("id") Long id) {
        meditationQuestionService.likeQuestion(id);
        return R.ok();
    }
}
xinquan-modules/xinquan-meditation/src/main/java/com/xinquan/meditation/controller/client/ClientMeditationController.java
@@ -1,8 +1,9 @@
package com.xinquan.meditation.controller.client;
import com.xinquan.meditation.service.MeditationService;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
@@ -14,8 +15,11 @@
 * @since 2024-08-21
 */
@RestController
@RequiredArgsConstructor
@RequestMapping("/client/meditation/meditation")
public class ClientMeditationController {
    private final MeditationService meditationService;
}
xinquan-modules/xinquan-meditation/src/main/java/com/xinquan/meditation/controller/client/ClientMeditationUserFavoriteController.java
File was renamed from xinquan-modules/xinquan-meditation/src/main/java/com/xinquan/meditation/controller/client/ClientMeditationUserOperationController.java
@@ -2,7 +2,6 @@
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
@@ -15,7 +14,7 @@
 */
@RestController
@RequestMapping("/client/meditation/meditation-user-operation")
public class ClientMeditationUserOperationController {
public class ClientMeditationUserFavoriteController {
}
xinquan-modules/xinquan-meditation/src/main/java/com/xinquan/meditation/controller/client/HomeBackgroundMusicController.java
File was deleted
xinquan-modules/xinquan-meditation/src/main/java/com/xinquan/meditation/controller/client/HomeBackgroundMusicUserController.java
@@ -13,7 +13,7 @@
 * @since 2024-08-23
 */
@RestController
@RequestMapping("/meditation/home-background-music-user")
@RequestMapping("/client/meditation/home-background-music-user")
public class HomeBackgroundMusicUserController {
}
xinquan-modules/xinquan-meditation/src/main/java/com/xinquan/meditation/controller/management/MgtClientMeditationUserFavoriteController.java
File was renamed from xinquan-modules/xinquan-meditation/src/main/java/com/xinquan/meditation/controller/management/MgtClientMeditationUserOperationController.java
@@ -14,7 +14,7 @@
 */
@RestController
@RequestMapping("/mgt/meditation/meditation-user-operation")
public class MgtClientMeditationUserOperationController {
public class MgtClientMeditationUserFavoriteController {
}
xinquan-modules/xinquan-meditation/src/main/java/com/xinquan/meditation/domain/HomeBackgroundMusic.java
@@ -21,7 +21,7 @@
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("t_home_background_music")
@ApiModel(value="HomeBackgroundMusic对象", description="首页背景音乐表        ")
@ApiModel(value = "HomeBackgroundMusic对象", description = "首页背景音乐表")
public class HomeBackgroundMusic extends BaseModel {
    private static final long serialVersionUID = 1L;
@@ -38,6 +38,10 @@
    @TableField("audio_file")
    private String audioFile;
    @ApiModelProperty(value = "音频文件名称")
    @TableField("audio_file_name")
    private String audioFileName;
    @ApiModelProperty(value = "排序权重")
    @TableField("sort_num")
    private Integer sortNum;
xinquan-modules/xinquan-meditation/src/main/java/com/xinquan/meditation/domain/Meditation.java
@@ -1,12 +1,13 @@
package com.xinquan.meditation.domain;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.xinquan.common.core.web.domain.BaseModel;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableField;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import java.math.BigDecimal;
import lombok.Data;
import lombok.EqualsAndHashCode;
@@ -50,6 +51,14 @@
    @TableField("charge_type")
    private Integer chargeType;
    @ApiModelProperty(value = "通用价格")
    @TableField("general_price")
    private BigDecimal generalPrice;
    @ApiModelProperty(value = "IOS内购价格")
    @TableField("ios_price")
    private BigDecimal iosPrice;
    @ApiModelProperty(value = "排序权重")
    @TableField("sort_num")
    private Integer sortNum;
xinquan-modules/xinquan-meditation/src/main/java/com/xinquan/meditation/domain/MeditationCategory.java
@@ -1,10 +1,10 @@
package com.xinquan.meditation.domain;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.xinquan.common.core.web.domain.BaseModel;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableField;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@@ -27,7 +27,7 @@
    private static final long serialVersionUID = 1L;
    @ApiModelProperty(value = "冥想分类id")
    @TableId(value = "id",type = IdType.ASSIGN_ID)
    @TableId(value = "id", type = IdType.AUTO)
    private Long id;
    @ApiModelProperty(value = "冥想分类课程")
xinquan-modules/xinquan-meditation/src/main/java/com/xinquan/meditation/domain/MeditationQuestion.java
@@ -1,13 +1,13 @@
package com.xinquan.meditation.domain;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.xinquan.common.core.web.domain.BaseModel;
import com.baomidou.mybatisplus.annotation.TableId;
import java.time.LocalDateTime;
import com.baomidou.mybatisplus.annotation.TableField;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import java.time.LocalDateTime;
import lombok.Data;
import lombok.EqualsAndHashCode;
@@ -31,6 +31,10 @@
    @TableId(value = "id",type = IdType.ASSIGN_ID)
    private Long id;
    @ApiModelProperty(value = "冥想id")
    @TableField("meditation_id")
    private Long meditationId;
    @ApiModelProperty(value = "点赞数")
    @TableField("like_count")
    private Integer likeCount;
xinquan-modules/xinquan-meditation/src/main/java/com/xinquan/meditation/domain/MeditationQuestionLike.java
New file
@@ -0,0 +1,56 @@
package com.xinquan.meditation.domain;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import java.io.Serializable;
import java.time.LocalDateTime;
import lombok.Data;
/**
 * <p>
 * 冥想提问点赞表
 * </p>
 *
 * @author mitao
 * @since 2024-09-06
 */
@Data
@TableName("t_meditation_question_like")
@ApiModel(value = "MeditationQuestionLike对象", description = "冥想提问点赞表")
public class MeditationQuestionLike implements Serializable {
    private static final long serialVersionUID = 1L;
    @ApiModelProperty(value = "id")
    @TableId("id")
    private Long id;
    @ApiModelProperty(value = "冥想id")
    @TableField("meditation_question_id")
    private Long meditationQuestionId;
    @ApiModelProperty(value = "用户id")
    @TableField("app_user_id")
    private Long appUserId;
    /**
     * 创建者
     */
    @ApiModelProperty(value = "记录创建人,前端忽略")
    @TableField(value = "create_by", fill = FieldFill.INSERT)
    private String createBy;
    /**
     * 创建时间
     */
    @ApiModelProperty(value = "记录创建时间,前端忽略")
    // @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @TableField(value = "create_time", fill = FieldFill.INSERT)
    private LocalDateTime createTime;
}
xinquan-modules/xinquan-meditation/src/main/java/com/xinquan/meditation/domain/MeditationUserFavorite.java
File was renamed from xinquan-modules/xinquan-meditation/src/main/java/com/xinquan/meditation/domain/MeditationUserOperation.java
@@ -1,14 +1,15 @@
package com.xinquan.meditation.domain;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableName;
import com.xinquan.common.core.web.domain.BaseModel;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import java.io.Serializable;
import java.time.LocalDateTime;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
 * <p>
@@ -19,24 +20,15 @@
 * @since 2024-08-21
 */
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("t_meditation_user_operation")
@ApiModel(value="MeditationUserOperation对象", description="冥想用户操作表")
public class MeditationUserOperation extends BaseModel {
public class MeditationUserFavorite implements Serializable {
    private static final long serialVersionUID = 1L;
    @ApiModelProperty(value = "id")
    @TableId(value = "id",type = IdType.ASSIGN_ID)
    private Long id;
    @ApiModelProperty(value = "是否点赞 1=是 2=否")
    @TableField("like")
    private Integer like;
    @ApiModelProperty(value = "是否收藏 1=是 2=否")
    @TableField("favorite")
    private Integer favorite;
    @ApiModelProperty(value = "冥想id")
    @TableField("meditation_id")
@@ -46,5 +38,18 @@
    @TableField("app_user_id")
    private Long appUserId;
    /**
     * 创建者
     */
    @ApiModelProperty(value = "记录创建人,前端忽略")
    @TableField(value = "create_by", fill = FieldFill.INSERT)
    private String createBy;
    /**
     * 创建时间
     */
    @ApiModelProperty(value = "记录创建时间,前端忽略")
    // @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @TableField(value = "create_time", fill = FieldFill.INSERT)
    private LocalDateTime createTime;
}
xinquan-modules/xinquan-meditation/src/main/java/com/xinquan/meditation/domain/dto/ClientHomeMeditationQueryDTO.java
New file
@@ -0,0 +1,25 @@
package com.xinquan.meditation.domain.dto;
import com.xinquan.common.core.web.page.BasePage;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import java.io.Serializable;
import javax.validation.constraints.NotBlank;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
 * @author mitao
 * @date 2024/9/4
 */
@Data
@EqualsAndHashCode(callSuper = true)
@ApiModel("客户端-首页查询参数")
public class ClientHomeMeditationQueryDTO extends BasePage implements Serializable {
    private static final long serialVersionUID = -6691807074482685234L;
    @ApiModelProperty(value = "查询条件")
    @NotBlank(message = "查询条件不能为空")
    private String condition;
}
xinquan-modules/xinquan-meditation/src/main/java/com/xinquan/meditation/domain/dto/ClientMeditationQueryDTO.java
New file
@@ -0,0 +1,25 @@
package com.xinquan.meditation.domain.dto;
import com.xinquan.common.core.web.page.BasePage;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import java.io.Serializable;
import javax.validation.constraints.NotNull;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
 * @author mitao
 * @date 2024/9/4
 */
@Data
@EqualsAndHashCode(callSuper = true)
@ApiModel("客户端-冥想查询参数")
public class ClientMeditationQueryDTO extends BasePage implements Serializable {
    private static final long serialVersionUID = -6691807074482685234L;
    @ApiModelProperty(value = "分类id")
    @NotNull(message = "分类id不能为空")
    private Long cateId;
}
xinquan-modules/xinquan-meditation/src/main/java/com/xinquan/meditation/domain/vo/ClientHomeBackgroundMusicVO.java
New file
@@ -0,0 +1,29 @@
package com.xinquan.meditation.domain.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import java.io.Serializable;
import lombok.Data;
/**
 * @author mitao
 * @date 2024/9/4
 */
@Data
@ApiModel(value = "首页背景音乐视图对象")
public class ClientHomeBackgroundMusicVO implements Serializable {
    private static final long serialVersionUID = 8868579953762043190L;
    @ApiModelProperty(value = "首页背景音乐id")
    private Long id;
    @ApiModelProperty(value = "背景图片")
    private String imageUrl;
    @ApiModelProperty(value = "音频文件")
    private String audioFile;
    @ApiModelProperty(value = "文件名称")
    private String audioFileName;
}
xinquan-modules/xinquan-meditation/src/main/java/com/xinquan/meditation/domain/vo/ClientMeditationAndCateVO.java
New file
@@ -0,0 +1,25 @@
package com.xinquan.meditation.domain.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import java.io.Serializable;
import java.util.List;
import lombok.Data;
/**
 * @author mitao
 * @date 2024/9/5
 */
@Data
@ApiModel(value = "冥想及分类视图对象")
public class ClientMeditationAndCateVO implements Serializable {
    private static final long serialVersionUID = -2154691485115564661L;
    @ApiModelProperty("冥想分类")
    private ClientMeditationCategoryVO clientMeditationCategoryVO;
    @ApiModelProperty(value = "冥想列表")
    private List<ClientMeditationVO> clientMeditationVOList;
}
xinquan-modules/xinquan-meditation/src/main/java/com/xinquan/meditation/domain/vo/ClientMeditationCategoryVO.java
New file
@@ -0,0 +1,32 @@
package com.xinquan.meditation.domain.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
 * @author mitao
 * @date 2024/9/4
 */
@Data
@ApiModel(value = "冥想分类视图对象")
public class ClientMeditationCategoryVO {
    @ApiModelProperty(value = "冥想分类id")
    private Long id;
    @ApiModelProperty(value = "冥想分类课程")
    private String categoryName;
    @ApiModelProperty(value = "分类描述")
    private String description;
    @ApiModelProperty(value = "icon图片 用于顶部四个分类展示")
    private String firstIconUrl;
    @ApiModelProperty(value = "icon图片 用于下面的三个分类展示    ")
    private String secondIconUrl;
    @ApiModelProperty(value = "展示方式 1=横版 2=竖版")
    private Integer showType;
}
xinquan-modules/xinquan-meditation/src/main/java/com/xinquan/meditation/domain/vo/ClientMeditationDetailsVO.java
New file
@@ -0,0 +1,25 @@
package com.xinquan.meditation.domain.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import java.util.List;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
 * @author mitao
 * @date 2024/9/5
 */
@Data
@EqualsAndHashCode(callSuper = true)
@ApiModel(value = "客户端冥想详情视图对象")
public class ClientMeditationDetailsVO extends ClientMeditationVO {
    private static final long serialVersionUID = -2959289858083878221L;
    @ApiModelProperty(value = "冥想背景音频列表")
    private List<String> meditationMusicList;
    @ApiModelProperty(value = "是否收藏 1:是 2:否")
    private Integer favorite;
}
xinquan-modules/xinquan-meditation/src/main/java/com/xinquan/meditation/domain/vo/ClientMeditationEverydayVO.java
New file
@@ -0,0 +1,36 @@
package com.xinquan.meditation.domain.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import java.io.Serializable;
import java.time.LocalDateTime;
import lombok.Data;
/**
 * @author mitao
 * @date 2024/9/4
 */
@Data
@ApiModel(value = "每日冥想视图对象")
public class ClientMeditationEverydayVO implements Serializable {
    private static final long serialVersionUID = -1776068531441762518L;
    @ApiModelProperty(value = "每日冥想id")
    private Long id;
    @ApiModelProperty(value = "冥想id")
    private Long meditationId;
    @ApiModelProperty(value = "播放时间段开始")
    private LocalDateTime startPlayTime;
    @ApiModelProperty(value = "播放时间段结束")
    private LocalDateTime endPlayTime;
    @ApiModelProperty(value = "每日冥想背景图片")
    private String imageUrl;
    @ApiModelProperty(value = "每日冥想")
    private ClientMeditationVO clientMeditationVO;
}
xinquan-modules/xinquan-meditation/src/main/java/com/xinquan/meditation/domain/vo/ClientMeditationQuestionVO.java
New file
@@ -0,0 +1,46 @@
package com.xinquan.meditation.domain.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import java.time.LocalDateTime;
import lombok.Data;
/**
 * @author mitao
 * @date 2024/9/5
 */
@Data
@ApiModel(value = "客户端冥想评论视图对象")
public class ClientMeditationQuestionVO {
    @ApiModelProperty(value = "id")
    private Long id;
    @ApiModelProperty(value = "点赞数")
    private Integer likeCount;
    @ApiModelProperty(value = "是否点赞 1=是 2=否")
    private Integer like = 2;
    @ApiModelProperty(value = "提问内容")
    private String content;
    @ApiModelProperty(value = "提问时间")
    private LocalDateTime publishTime;
    @ApiModelProperty(value = "用户id")
    private Long appUserId;
    @ApiModelProperty(value = "头像")
    private String avatar;
    @ApiModelProperty(value = "昵称")
    private String nickname;
    @ApiModelProperty(value = "回复内容")
    private String replyContent;
    @ApiModelProperty(value = "回复时间")
    private LocalDateTime replyTime;
}
xinquan-modules/xinquan-meditation/src/main/java/com/xinquan/meditation/domain/vo/ClientMeditationVO.java
New file
@@ -0,0 +1,73 @@
package com.xinquan.meditation.domain.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import java.io.Serializable;
import java.math.BigDecimal;
import lombok.Data;
/**
 * @author mitao
 * @date 2024/9/4
 */
@Data
@ApiModel(value = "冥想视图对象")
public class ClientMeditationVO implements Serializable {
    private static final long serialVersionUID = -1776068531441762518L;
    @ApiModelProperty(value = "冥想id")
    private Long id;
    @ApiModelProperty(value = "冥想音频分类id")
    private Long cateId;
    @ApiModelProperty(value = "冥想标题")
    private String meditationTitle;
    @ApiModelProperty(value = "上架状态 1=上架 2=下架")
    private Integer listingStatus;
    @ApiModelProperty(value = "梵文 1=是 2否")
    private Integer sanskrit;
    @ApiModelProperty(value = "价格设定 1=免费 2=会员免费 3=单独收费")
    private Integer chargeType;
    @ApiModelProperty(value = "通用价格", notes = "价格设定为单独收费")
    private BigDecimal generalPrice;
    @ApiModelProperty(value = "IOS内购价格", notes = "价格设定为单独收费")
    private BigDecimal iosPrice;
    @ApiModelProperty(value = "排序权重")
    private Integer sortNum;
    @ApiModelProperty(value = "实际已学习数")
    private Integer realLearnedNum;
    @ApiModelProperty(value = "虚拟已学习数")
    private Integer virtualLearnedNum;
    @ApiModelProperty(value = "icon封面图")
    private String iconUrl;
    @ApiModelProperty(value = "封面图")
    private String coverUrl;
    @ApiModelProperty(value = "背景图")
    private String backgroundUrl;
    @ApiModelProperty(value = "详情描述")
    private String detailDescription;
    @ApiModelProperty(value = "封面描述")
    private String coverDescription;
    @ApiModelProperty(value = "导师音频")
    private String tutorAudioUrl;
    @ApiModelProperty(value = "被收藏数")
    private Integer favoriteCount;
}
xinquan-modules/xinquan-meditation/src/main/java/com/xinquan/meditation/mapper/MeditationMapper.java
@@ -1,7 +1,11 @@
package com.xinquan.meditation.mapper;
import com.xinquan.meditation.domain.Meditation;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.xinquan.meditation.domain.Meditation;
import com.xinquan.meditation.domain.vo.ClientMeditationVO;
import java.util.List;
import java.util.Set;
import org.apache.ibatis.annotations.Param;
/**
 * <p>
@@ -13,4 +17,13 @@
 */
public interface MeditationMapper extends BaseMapper<Meditation> {
    /**
     * 根据用户梵文权限和标签查询冥想列表
     *
     * @param tagIdSet     标签id集合
     * @param sanskritFlag 梵文权限
     * @return 冥想列表
     */
    List<ClientMeditationVO> getMeditationListByTagId(@Param("tagIdSet") Set<Long> tagIdSet,
            @Param("sanskritFlag") Integer sanskritFlag);
}
xinquan-modules/xinquan-meditation/src/main/java/com/xinquan/meditation/mapper/MeditationQuestionLikeMapper.java
New file
@@ -0,0 +1,16 @@
package com.xinquan.meditation.mapper;
import com.xinquan.meditation.domain.MeditationQuestionLike;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
/**
 * <p>
 * 冥想提问点赞表 Mapper 接口
 * </p>
 *
 * @author mitao
 * @since 2024-09-06
 */
public interface MeditationQuestionLikeMapper extends BaseMapper<MeditationQuestionLike> {
}
xinquan-modules/xinquan-meditation/src/main/java/com/xinquan/meditation/mapper/MeditationUserFavoriteMapper.java
New file
@@ -0,0 +1,16 @@
package com.xinquan.meditation.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.xinquan.meditation.domain.MeditationUserFavorite;
/**
 * <p>
 * 冥想用户操作表 Mapper 接口
 * </p>
 *
 * @author mitao
 * @since 2024-08-21
 */
public interface MeditationUserFavoriteMapper extends BaseMapper<MeditationUserFavorite> {
}
xinquan-modules/xinquan-meditation/src/main/java/com/xinquan/meditation/mapper/MeditationUserOperationMapper.java
File was deleted
xinquan-modules/xinquan-meditation/src/main/java/com/xinquan/meditation/service/HomeBackgroundMusicService.java
New file
@@ -0,0 +1,31 @@
package com.xinquan.meditation.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.xinquan.meditation.domain.HomeBackgroundMusic;
import com.xinquan.meditation.domain.vo.ClientHomeBackgroundMusicVO;
import java.util.List;
/**
 * <p>
 * 首页背景音乐用户关系表         服务类
 * </p>
 *
 * @author mitao
 * @since 2024-08-23
 */
public interface HomeBackgroundMusicService extends IService<HomeBackgroundMusic> {
    /**
     * 获取首页背景音乐列表
     *
     * @return 背景音乐列表
     */
    List<ClientHomeBackgroundMusicVO> getHomeBackgroundMusicList();
    /**
     * 保存用户首页背景音乐设置
     *
     * @param id 首页背景音乐id
     */
    void savePersonalitySetting(Long id);
}
xinquan-modules/xinquan-meditation/src/main/java/com/xinquan/meditation/service/MeditationCategoryService.java
@@ -1,7 +1,9 @@
package com.xinquan.meditation.service;
import com.xinquan.meditation.domain.MeditationCategory;
import com.baomidou.mybatisplus.extension.service.IService;
import com.xinquan.meditation.domain.MeditationCategory;
import com.xinquan.meditation.domain.vo.ClientMeditationCategoryVO;
import java.util.List;
/**
 * <p>
@@ -13,4 +15,18 @@
 */
public interface MeditationCategoryService extends IService<MeditationCategory> {
    /**
     * 获取冥想分类列表
     *
     * @param type 分类类型
     * @return 分类列表
     */
    List<ClientMeditationCategoryVO> getCategoryListByType(Integer type);
    /**
     * 获取冥想分类列表
     *
     * @return 冥想分类列表
     */
    List<ClientMeditationCategoryVO> getCategoryList();
}
xinquan-modules/xinquan-meditation/src/main/java/com/xinquan/meditation/service/MeditationEverydayService.java
@@ -1,7 +1,8 @@
package com.xinquan.meditation.service;
import com.xinquan.meditation.domain.MeditationEveryday;
import com.baomidou.mybatisplus.extension.service.IService;
import com.xinquan.meditation.domain.MeditationEveryday;
import com.xinquan.meditation.domain.vo.ClientMeditationEverydayVO;
/**
 * <p>
@@ -13,4 +14,10 @@
 */
public interface MeditationEverydayService extends IService<MeditationEveryday> {
    /**
     * 查询每日冥想
     *
     * @return 每日冥想
     */
    ClientMeditationEverydayVO getTodayMeditation();
}
xinquan-modules/xinquan-meditation/src/main/java/com/xinquan/meditation/service/MeditationQuestionLikeService.java
New file
@@ -0,0 +1,16 @@
package com.xinquan.meditation.service;
import com.xinquan.meditation.domain.MeditationQuestionLike;
import com.baomidou.mybatisplus.extension.service.IService;
/**
 * <p>
 * 冥想提问点赞表 服务类
 * </p>
 *
 * @author mitao
 * @since 2024-09-06
 */
public interface MeditationQuestionLikeService extends IService<MeditationQuestionLike> {
}
xinquan-modules/xinquan-meditation/src/main/java/com/xinquan/meditation/service/MeditationQuestionService.java
@@ -1,7 +1,7 @@
package com.xinquan.meditation.service;
import com.xinquan.meditation.domain.MeditationQuestion;
import com.baomidou.mybatisplus.extension.service.IService;
import com.xinquan.meditation.domain.MeditationQuestion;
/**
 * <p>
@@ -13,4 +13,18 @@
 */
public interface MeditationQuestionService extends IService<MeditationQuestion> {
    /**
     * 举报提问
     *
     * @param id      提问id
     * @param content 举报内容
     */
    void report(Long id, String content);
    /**
     * 点赞/取消点赞提问
     *
     * @param id 提问id
     */
    void likeQuestion(Long id);
}
xinquan-modules/xinquan-meditation/src/main/java/com/xinquan/meditation/service/MeditationService.java
@@ -1,7 +1,13 @@
package com.xinquan.meditation.service;
import com.xinquan.meditation.domain.Meditation;
import com.baomidou.mybatisplus.extension.service.IService;
import com.xinquan.common.core.utils.page.PageDTO;
import com.xinquan.meditation.domain.Meditation;
import com.xinquan.meditation.domain.vo.ClientMeditationAndCateVO;
import com.xinquan.meditation.domain.vo.ClientMeditationDetailsVO;
import com.xinquan.meditation.domain.vo.ClientMeditationQuestionVO;
import com.xinquan.meditation.domain.vo.ClientMeditationVO;
import java.util.List;
/**
 * <p>
@@ -13,4 +19,72 @@
 */
public interface MeditationService extends IService<Meditation> {
    /**
     * 获取私人订制
     *
     * @return 冥想列表
     */
    List<ClientMeditationVO> getPersonalityPlan();
    /**
     * 根据分类id获取冥想列表
     *
     * @param cateId 分类id
     * @return 冥想列表
     */
    List<ClientMeditationVO> getMeditationListByCateId(Long cateId);
    /**
     * 根据分类id获取冥想列表-分页
     *
     * @param cateId   分类id
     * @param pageCurr 当前页码
     * @param pageSize 每页数量
     * @return 冥想列表
     */
    PageDTO<ClientMeditationVO> getMeditationPageByCateId(Long cateId, Integer pageCurr,
            Integer pageSize);
    /**
     * 首页搜索首页
     *
     * @param condition 搜索条件
     * @param pageCurr  当前页码
     * @param pageSize  每页数量
     * @return 冥想列表
     */
    PageDTO<ClientMeditationVO> search(String condition, Integer pageCurr, Integer pageSize);
    /**
     * 获取全部的冥想及分类列表
     *
     * @return 冥想和分类列表
     */
    List<ClientMeditationAndCateVO> getMeditationAndCateList();
    /**
     * 获取冥想音频详情
     *
     * @param id 冥想音频id
     * @return 客户端冥想详情视图对象
     */
    ClientMeditationDetailsVO getMeditationDetails(Long id);
    /**
     * 获取冥想音频提问列表-分页
     *
     * @param id       冥想音频id
     * @param pageCurr 当前页码
     * @param pageSize 每页数量
     * @return 冥想音频提问分页列表
     */
    PageDTO<ClientMeditationQuestionVO> getMeditationQuestionPage(Long id,
            Integer pageCurr, Integer pageSize);
    /**
     * 收藏/取消收藏
     *
     * @param id 冥想音频id
     */
    void favorite(Long id);
}
xinquan-modules/xinquan-meditation/src/main/java/com/xinquan/meditation/service/MeditationUserFavoriteService.java
New file
@@ -0,0 +1,16 @@
package com.xinquan.meditation.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.xinquan.meditation.domain.MeditationUserFavorite;
/**
 * <p>
 * 冥想用户操作表 服务类
 * </p>
 *
 * @author mitao
 * @since 2024-08-21
 */
public interface MeditationUserFavoriteService extends IService<MeditationUserFavorite> {
}
xinquan-modules/xinquan-meditation/src/main/java/com/xinquan/meditation/service/MeditationUserOperationService.java
File was deleted
xinquan-modules/xinquan-meditation/src/main/java/com/xinquan/meditation/service/impl/HomeBackgroundMusicServiceImpl.java
New file
@@ -0,0 +1,61 @@
package com.xinquan.meditation.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xinquan.common.core.constant.SecurityConstants;
import com.xinquan.common.core.utils.page.BeanUtils;
import com.xinquan.common.security.utils.SecurityUtils;
import com.xinquan.meditation.domain.HomeBackgroundMusic;
import com.xinquan.meditation.domain.vo.ClientHomeBackgroundMusicVO;
import com.xinquan.meditation.mapper.HomeBackgroundMusicMapper;
import com.xinquan.meditation.service.HomeBackgroundMusicService;
import com.xinquan.user.api.domain.dto.AppUserDTO;
import com.xinquan.user.api.feign.RemoteAppUserService;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
/**
 * <p>
 * 首页背景音乐用户关系表         服务实现类
 * </p>
 *
 * @author mitao
 * @since 2024-08-23
 */
@Service
@RequiredArgsConstructor
public class HomeBackgroundMusicServiceImpl extends
        ServiceImpl<HomeBackgroundMusicMapper, HomeBackgroundMusic> implements
        HomeBackgroundMusicService {
    private final RemoteAppUserService remoteAppUserService;
    /**
     * 获取首页背景音乐列表
     *
     * @return 背景音乐列表
     */
    @Override
    public List<ClientHomeBackgroundMusicVO> getHomeBackgroundMusicList() {
        List<HomeBackgroundMusic> list = this.lambdaQuery()
                .orderByDesc(HomeBackgroundMusic::getSortNum).list();
        return BeanUtils.copyList(list, ClientHomeBackgroundMusicVO.class);
    }
    /**
     * 保存用户首页背景音乐设置
     *
     * @param id 首页背景音乐id
     */
    @Override
    public void savePersonalitySetting(Long id) {
        // 获取当前登录用户id
        Long userId = SecurityUtils.getUserId();
        // 远程调用修改用户信息
        remoteAppUserService.updateAppUser(
                AppUserDTO.builder()
                        .id(userId)
                        .homeBackgroundMusicId(id).build(),
                SecurityConstants.INNER);
    }
}
xinquan-modules/xinquan-meditation/src/main/java/com/xinquan/meditation/service/impl/MeditationCategoryServiceImpl.java
@@ -1,9 +1,12 @@
package com.xinquan.meditation.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xinquan.common.core.utils.page.BeanUtils;
import com.xinquan.meditation.domain.MeditationCategory;
import com.xinquan.meditation.domain.vo.ClientMeditationCategoryVO;
import com.xinquan.meditation.mapper.MeditationCategoryMapper;
import com.xinquan.meditation.service.MeditationCategoryService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import java.util.List;
import org.springframework.stereotype.Service;
/**
@@ -17,4 +20,29 @@
@Service
public class MeditationCategoryServiceImpl extends ServiceImpl<MeditationCategoryMapper, MeditationCategory> implements MeditationCategoryService {
    /**
     * 获取冥想分类列表
     *
     * @param type 分类类型
     * @return 分类列表
     */
    @Override
    public List<ClientMeditationCategoryVO> getCategoryListByType(Integer type) {
        List<MeditationCategory> list = this.lambdaQuery()
                .orderByDesc(MeditationCategory::getSortNum).last(type == 1, "limit 4")
                .last(type == 2, "limit 4,3").list();
        return BeanUtils.copyList(list, ClientMeditationCategoryVO.class);
    }
    /**
     * 获取冥想分类列表
     *
     * @return 冥想分类列表
     */
    @Override
    public List<ClientMeditationCategoryVO> getCategoryList() {
        List<MeditationCategory> list = this.lambdaQuery()
                .orderByDesc(MeditationCategory::getSortNum).list();
        return BeanUtils.copyList(list, ClientMeditationCategoryVO.class);
    }
}
xinquan-modules/xinquan-meditation/src/main/java/com/xinquan/meditation/service/impl/MeditationEverydayServiceImpl.java
@@ -1,9 +1,17 @@
package com.xinquan.meditation.service.impl;
import com.xinquan.meditation.domain.MeditationEveryday;
import com.xinquan.meditation.mapper.MeditationEverydayMapper;
import com.xinquan.meditation.service.MeditationEverydayService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xinquan.common.core.utils.page.BeanUtils;
import com.xinquan.meditation.domain.Meditation;
import com.xinquan.meditation.domain.MeditationEveryday;
import com.xinquan.meditation.domain.vo.ClientMeditationEverydayVO;
import com.xinquan.meditation.domain.vo.ClientMeditationVO;
import com.xinquan.meditation.mapper.MeditationEverydayMapper;
import com.xinquan.meditation.mapper.MeditationMapper;
import com.xinquan.meditation.service.MeditationEverydayService;
import java.time.LocalDateTime;
import java.util.Optional;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
/**
@@ -15,6 +23,50 @@
 * @since 2024-08-21
 */
@Service
@RequiredArgsConstructor
public class MeditationEverydayServiceImpl extends ServiceImpl<MeditationEverydayMapper, MeditationEveryday> implements MeditationEverydayService {
    private final MeditationMapper meditationMapper;
    /**
     * 查询每日冥想
     *
     * @return 每日冥想
     */
    @Override
    public ClientMeditationEverydayVO getTodayMeditation() {
        ClientMeditationEverydayVO vo = null;
        // 当前时间
        LocalDateTime now = LocalDateTime.now();
        // 查询播放时间段在当前时间的每日冥想
        Optional<MeditationEveryday> meditationEverydayOpt = this.lambdaQuery()
                .le(MeditationEveryday::getStartPlayTime, now)
                .ge(MeditationEveryday::getEndPlayTime, now)
                .orderByDesc(MeditationEveryday::getUpdateTime)
                .last("limit 1").oneOpt();
        if (!meditationEverydayOpt.isPresent()) {
            // 查询结束时间离当前时间最近的一个每日冥想
            meditationEverydayOpt = this.lambdaQuery()
                    .le(MeditationEveryday::getStartPlayTime, now)
                    .orderByDesc(MeditationEveryday::getEndPlayTime)
                    .last("limit 1").oneOpt();
        }
        // 判断是否存在
        if (meditationEverydayOpt.isPresent()) {
            // 查询冥想详情
            MeditationEveryday meditationEveryday = meditationEverydayOpt.get();
            Optional<Meditation> meditationOpt = Optional.ofNullable(
                    meditationMapper.selectById(
                            meditationEveryday.getMeditationId()));
            if (meditationOpt.isPresent()) {
                // 封装返回值
                ClientMeditationVO clientMeditationVO = BeanUtils.copyBean(meditationOpt.get(),
                        ClientMeditationVO.class);
                vo = BeanUtils.copyBean(meditationEveryday,
                        ClientMeditationEverydayVO.class);
                vo.setClientMeditationVO(clientMeditationVO);
            }
        }
        return vo;
    }
}
xinquan-modules/xinquan-meditation/src/main/java/com/xinquan/meditation/service/impl/MeditationQuestionLikeServiceImpl.java
New file
@@ -0,0 +1,20 @@
package com.xinquan.meditation.service.impl;
import com.xinquan.meditation.domain.MeditationQuestionLike;
import com.xinquan.meditation.mapper.MeditationQuestionLikeMapper;
import com.xinquan.meditation.service.MeditationQuestionLikeService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
/**
 * <p>
 * 冥想提问点赞表 服务实现类
 * </p>
 *
 * @author mitao
 * @since 2024-09-06
 */
@Service
public class MeditationQuestionLikeServiceImpl extends ServiceImpl<MeditationQuestionLikeMapper, MeditationQuestionLike> implements MeditationQuestionLikeService {
}
xinquan-modules/xinquan-meditation/src/main/java/com/xinquan/meditation/service/impl/MeditationQuestionServiceImpl.java
@@ -1,9 +1,17 @@
package com.xinquan.meditation.service.impl;
import com.xinquan.meditation.domain.MeditationQuestion;
import com.xinquan.meditation.mapper.MeditationQuestionMapper;
import com.xinquan.meditation.service.MeditationQuestionService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xinquan.common.core.enums.ReportStatusEnum;
import com.xinquan.common.core.exception.ServiceException;
import com.xinquan.common.security.utils.SecurityUtils;
import com.xinquan.meditation.domain.MeditationQuestion;
import com.xinquan.meditation.domain.MeditationQuestionLike;
import com.xinquan.meditation.mapper.MeditationQuestionMapper;
import com.xinquan.meditation.service.MeditationQuestionLikeService;
import com.xinquan.meditation.service.MeditationQuestionService;
import java.util.Objects;
import java.util.Optional;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
/**
@@ -15,6 +23,57 @@
 * @since 2024-08-21
 */
@Service
@RequiredArgsConstructor
public class MeditationQuestionServiceImpl extends ServiceImpl<MeditationQuestionMapper, MeditationQuestion> implements MeditationQuestionService {
    private final MeditationQuestionLikeService meditationQuestionLikeService;
    /**
     * 举报提问
     *
     * @param id      提问id
     * @param content 举报内容
     */
    @Override
    public void report(Long id, String content) {
        MeditationQuestion meditationQuestion = this.getById(id);
        if (Optional.ofNullable(meditationQuestion).isPresent()) {
            meditationQuestion.setReportStatus(ReportStatusEnum.REPORTED.getCode());
            meditationQuestion.setReportUserId(SecurityUtils.getUserId());
            meditationQuestion.setReportContent(content);
            this.updateById(meditationQuestion);
        }
    }
    /**
     * 点赞/取消点赞提问
     *
     * @param id 提问id
     */
    @Override
    public void likeQuestion(Long id) {
        MeditationQuestion meditationQuestion = this.getById(id);
        if (Objects.isNull(meditationQuestion)) {
            throw new ServiceException("提问不存在");
        }
        // 判断是否已点赞
        Optional<MeditationQuestionLike> likeOpt = meditationQuestionLikeService.lambdaQuery()
                .eq(MeditationQuestionLike::getMeditationQuestionId, id)
                .eq(MeditationQuestionLike::getAppUserId, SecurityUtils.getUserId()).last("limit 1")
                .oneOpt();
        if (likeOpt.isPresent()) {
            // 已点赞则取消点赞
            meditationQuestionLikeService.removeById(likeOpt.get());
            meditationQuestion.setLikeCount(meditationQuestion.getLikeCount() - 1);
        } else {
            // 未点赞则点赞
            MeditationQuestionLike meditationQuestionLike = new MeditationQuestionLike();
            meditationQuestionLike.setAppUserId(SecurityUtils.getUserId());
            meditationQuestionLike.setMeditationQuestionId(id);
            if (meditationQuestionLikeService.save(meditationQuestionLike)) {
                meditationQuestion.setLikeCount(meditationQuestion.getLikeCount() + 1);
            }
        }
        this.updateById(meditationQuestion);
    }
}
xinquan-modules/xinquan-meditation/src/main/java/com/xinquan/meditation/service/impl/MeditationServiceImpl.java
@@ -1,9 +1,46 @@
package com.xinquan.meditation.service.impl;
import com.xinquan.meditation.domain.Meditation;
import com.xinquan.meditation.mapper.MeditationMapper;
import com.xinquan.meditation.service.MeditationService;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xinquan.common.core.constant.SecurityConstants;
import com.xinquan.common.core.enums.DisabledEnum;
import com.xinquan.common.core.exception.ServiceException;
import com.xinquan.common.core.utils.page.BeanUtils;
import com.xinquan.common.core.utils.page.CollUtils;
import com.xinquan.common.core.utils.page.PageDTO;
import com.xinquan.common.security.utils.SecurityUtils;
import com.xinquan.meditation.domain.Meditation;
import com.xinquan.meditation.domain.MeditationCategory;
import com.xinquan.meditation.domain.MeditationMusic;
import com.xinquan.meditation.domain.MeditationQuestion;
import com.xinquan.meditation.domain.MeditationQuestionLike;
import com.xinquan.meditation.domain.MeditationUserFavorite;
import com.xinquan.meditation.domain.vo.ClientMeditationAndCateVO;
import com.xinquan.meditation.domain.vo.ClientMeditationCategoryVO;
import com.xinquan.meditation.domain.vo.ClientMeditationDetailsVO;
import com.xinquan.meditation.domain.vo.ClientMeditationQuestionVO;
import com.xinquan.meditation.domain.vo.ClientMeditationVO;
import com.xinquan.meditation.mapper.MeditationCategoryMapper;
import com.xinquan.meditation.mapper.MeditationMapper;
import com.xinquan.meditation.mapper.MeditationMusicMapper;
import com.xinquan.meditation.mapper.MeditationQuestionMapper;
import com.xinquan.meditation.service.MeditationQuestionLikeService;
import com.xinquan.meditation.service.MeditationService;
import com.xinquan.meditation.service.MeditationUserFavoriteService;
import com.xinquan.user.api.domain.AppUser;
import com.xinquan.user.api.domain.dto.AppUserDTO;
import com.xinquan.user.api.feign.RemoteAppUserService;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
/**
@@ -15,6 +52,296 @@
 * @since 2024-08-21
 */
@Service
@RequiredArgsConstructor
public class MeditationServiceImpl extends ServiceImpl<MeditationMapper, Meditation> implements MeditationService {
    private final RemoteAppUserService remoteAppUserService;
    private final MeditationCategoryMapper meditationCategoryMapper;
    private final MeditationMusicMapper meditationMusicMapper;
    private final MeditationQuestionMapper meditationQuestionMapper;
    private final MeditationQuestionLikeService meditationQuestionLikeService;
    private final MeditationUserFavoriteService meditationUserFavoriteService;
    /**
     * 获取私人订制
     *
     * @return 冥想列表
     */
    @Override
    public List<ClientMeditationVO> getPersonalityPlan() {
        List<ClientMeditationVO> voList = null;
        // 获取当前登录用户id
        AppUser appUser = getCurrentUser();
        if (Optional.ofNullable(appUser.getTagId()).isPresent()) {
            String tagId = appUser.getTagId();
            Integer sanskritFlag = appUser.getSanskritFlag();
            Set<Long> tagIdSet = Arrays.stream(tagId.split(",")).map(Long::parseLong)
                    .collect(Collectors.toSet());
            // 查询冥想列表
            voList = baseMapper.getMeditationListByTagId(tagIdSet, sanskritFlag);
        }
        return voList;
    }
    /**
     * 根据分类id获取冥想列表
     *
     * @param cateId 分类id
     * @return 冥想列表
     */
    @Override
    public List<ClientMeditationVO> getMeditationListByCateId(Long cateId) {
        List<ClientMeditationVO> voList = null;
        // 获取当前登录用户id
        AppUser appUser = getCurrentUser();
        // 查询
        List<Meditation> list = this.lambdaQuery().eq(Meditation::getCateId, cateId)
                .eq(Meditation::getSanskrit, appUser.getSanskritFlag())
                .orderByDesc(Meditation::getSortNum).last("limit 8").list();
        if (CollUtils.isNotEmpty(list)) {
            // 对象拷贝
            voList = BeanUtils.copyList(list, ClientMeditationVO.class);
        }
        return voList;
    }
    /**
     * 获取当前登录用户
     *
     * @return AppUser
     */
    private AppUser getCurrentUser() {
        Long userId = SecurityUtils.getUserId();
        return remoteAppUserService.getUserByCondition(
                AppUserDTO.builder().id(userId).build(),
                SecurityConstants.INNER).getData();
    }
    /**
     * 获取全部的冥想及分类列表
     *
     * @return 冥想和分类列表
     */
    @Override
    public List<ClientMeditationAndCateVO> getMeditationAndCateList() {
        // 获取当前登录用户
        AppUser appUser = getCurrentUser();
        List<ClientMeditationAndCateVO> voList = CollUtils.newArrayList();
        // 查询分类列表
        List<MeditationCategory> cateList = meditationCategoryMapper.selectList(
                Wrappers.lambdaQuery(MeditationCategory.class)
                        .orderByDesc(MeditationCategory::getSortNum));
        if (CollUtils.isNotEmpty(cateList)) {
            for (MeditationCategory cate : cateList) {
                ClientMeditationAndCateVO vo = new ClientMeditationAndCateVO();
                vo.setClientMeditationCategoryVO(
                        BeanUtils.copyBean(cateList, ClientMeditationCategoryVO.class));
                vo.setClientMeditationVOList(getMeditationListByCateId(cate.getId()));
                voList.add(vo);
            }
        }
        return voList;
    }
    /**
     * 根据分类id获取冥想列表-分页
     *
     * @param cateId   分类id
     * @param pageCurr 当前页码
     * @param pageSize 每页数量
     * @return 冥想列表
     */
    @Override
    public PageDTO<ClientMeditationVO> getMeditationPageByCateId(Long cateId, Integer pageCurr,
            Integer pageSize) {
        PageDTO<ClientMeditationVO> pageDTO = PageDTO.empty(0L, 0L);
        // 获取当前登录用户id
        AppUser appUser = getCurrentUser();
        // 查询
        Page<Meditation> page = this.lambdaQuery().eq(Meditation::getCateId, cateId)
                .eq(Meditation::getSanskrit, appUser.getSanskritFlag())
                .orderByDesc(Meditation::getSortNum)
                .page(new Page<>(pageCurr, pageSize));
        // 如果没有查到数据直接返回
        if (Optional.ofNullable(page.getRecords()).isPresent()) {
            return pageDTO;
        }
        // 将实体类转换为VO
        pageDTO = PageDTO.of(page, ClientMeditationVO.class);
        return pageDTO;
    }
    /**
     * 首页搜索首页
     *
     * @param condition 搜索条件
     * @param pageCurr  当前页码
     * @param pageSize  每页数量
     * @return 冥想列表
     */
    @Override
    public PageDTO<ClientMeditationVO> search(String condition, Integer pageCurr,
            Integer pageSize) {
        PageDTO<ClientMeditationVO> pageDTO = PageDTO.empty(0L, 0L);
        // 获取当前登录用户id
        AppUser appUser = getCurrentUser();
        // 查询
        Page<Meditation> page = this.lambdaQuery()
                .like(Meditation::getMeditationTitle, condition).or()
                .eq(Meditation::getSanskrit, appUser.getSanskritFlag())
                .orderByDesc(Meditation::getSortNum)
                .page(new Page<>(pageCurr, pageSize));
        // 如果没有查到数据直接返回
        if (Optional.ofNullable(page.getRecords()).isPresent()) {
            return pageDTO;
        }
        // 音频链接加密
    /*    page.getRecords().forEach(meditation -> {
            try {
                meditation.setTutorAudioUrl(DES.encryptDES(meditation.getTutorAudioUrl()));
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        });*/
        // 将实体类转换为VO
        pageDTO = PageDTO.of(page, ClientMeditationVO.class);
        return pageDTO;
    }
    /**
     * 获取冥想音频详情
     *
     * @param id 冥想音频id
     * @return 客户端冥想详情视图对象
     */
    @Override
    public ClientMeditationDetailsVO getMeditationDetails(Long id) {
        // 查询冥想音频
        Meditation meditation = this.getById(id);
        ClientMeditationDetailsVO vo = BeanUtils.copyBean(meditation,
                ClientMeditationDetailsVO.class);
        // 查询冥想音频
        List<MeditationMusic> meditationMusics = meditationMusicMapper.selectList(
                Wrappers.lambdaQuery(MeditationMusic.class)
                        .eq(MeditationMusic::getMeditationId, id));
        if (CollUtils.isNotEmpty(meditationMusics)) {
            // 获取音频链接
            List<String> musicUrlList = meditationMusics.stream()
                    .map(MeditationMusic::getMusicUrl).collect(Collectors.toList());
            vo.setMeditationMusicList(musicUrlList);
        }
        // 查询收藏记录
        vo.setFavorite(meditationUserFavoriteService.lambdaQuery()
                .eq(MeditationUserFavorite::getMeditationId, id)
                .eq(MeditationUserFavorite::getAppUserId, SecurityUtils.getUserId())
                .count() > 0 ? 1 : 2);
        return vo;
    }
    /**
     * 收藏/取消收藏
     *
     * @param id 冥想音频id
     */
    @Override
    public void favorite(Long id) {
        Meditation meditation = this.getById(id);
        if (Objects.isNull(meditation)) {
            throw new ServiceException("该冥想音频不存在");
        }
        // 查询收藏记录
        Optional<MeditationUserFavorite> userOperationOpt = meditationUserFavoriteService.lambdaQuery()
                .eq(MeditationUserFavorite::getMeditationId, id)
                .eq(MeditationUserFavorite::getAppUserId, SecurityUtils.getUserId())
                .last("limit 1").oneOpt();
        if (userOperationOpt.isPresent()) {
            meditationUserFavoriteService.removeById(userOperationOpt.get());
            // 增加收藏数
            meditation.setFavoriteCount(meditation.getFavoriteCount() - 1);
        } else {
            // 增加收藏记录
            MeditationUserFavorite meditationUserFavorite = new MeditationUserFavorite();
            meditationUserFavorite.setMeditationId(id);
            meditationUserFavorite.setAppUserId(SecurityUtils.getUserId());
            meditationUserFavoriteService.save(meditationUserFavorite);
            // 减少收藏数
            meditation.setFavoriteCount(meditation.getFavoriteCount() + 1);
        }
        this.updateById(meditation);
    }
    /**
     * 获取冥想音频提问列表-分页
     *
     * @param id       冥想音频id
     * @param pageCurr 当前页码
     * @param pageSize 每页数量
     * @return 冥想音频提问分页列表
     */
    @Override
    public PageDTO<ClientMeditationQuestionVO> getMeditationQuestionPage(Long id,
            Integer pageCurr, Integer pageSize) {
        // 查询冥想提问列表
        Page<MeditationQuestion> questionPage = meditationQuestionMapper.selectPage(
                new Page<>(pageCurr, pageSize),
                Wrappers.lambdaQuery(MeditationQuestion.class)
                        .eq(MeditationQuestion::getMeditationId, id)
                        .eq(MeditationQuestion::getShowFlag, DisabledEnum.YES)
                        .orderByDesc(MeditationQuestion::getPublishTime)
        );
        if (CollUtils.isEmpty(questionPage.getRecords())) {
            return PageDTO.empty(questionPage);
        }
        // 将 questionPage 转换为 PageDTO
        PageDTO<ClientMeditationQuestionVO> pageDTO = PageDTO.of(
                questionPage, ClientMeditationQuestionVO.class);
        List<ClientMeditationQuestionVO> questionVOList = pageDTO.getList();
        // 提取用户ID集合
        Set<Long> appUserIdSet = questionVOList.stream()
                .map(ClientMeditationQuestionVO::getAppUserId)
                .collect(Collectors.toSet());
        // 远程调用,获取APP用户信息
        Map<Long, AppUser> appUserMap = Collections.emptyMap();
        if (!appUserIdSet.isEmpty()) {
            List<AppUser> appUserList = remoteAppUserService.getAppUserList(appUserIdSet,
                            SecurityConstants.INNER)
                    .getData();
            appUserMap = appUserList.stream()
                    .collect(Collectors.toMap(AppUser::getId, Function.identity()));
        }
        // 封装用户信息
        for (ClientMeditationQuestionVO questionVO : questionVOList) {
            AppUser appUser = appUserMap.get(questionVO.getAppUserId());
            if (appUser != null) {
                questionVO.setNickname(appUser.getNickname());
                questionVO.setAvatar(appUser.getAvatar());
            }
        }
        // 获取当前登录用户ID
        Long userId = SecurityUtils.getUserId();
        // 查询用户已点赞提问列表
        Set<Long> likedQuestionIds = meditationQuestionLikeService.lambdaQuery()
                .eq(MeditationQuestionLike::getAppUserId, userId)
                .list()
                .stream()
                .map(MeditationQuestionLike::getMeditationQuestionId)
                .collect(Collectors.toSet());
        // 设置问题的点赞状态
        questionVOList.forEach(question -> {
            if (likedQuestionIds.contains(question.getId())) {
                question.setLike(DisabledEnum.YES.getCode());
            }
        });
        return pageDTO;
    }
}
xinquan-modules/xinquan-meditation/src/main/java/com/xinquan/meditation/service/impl/MeditationUserFavoriteServiceImpl.java
New file
@@ -0,0 +1,22 @@
package com.xinquan.meditation.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xinquan.meditation.domain.MeditationUserFavorite;
import com.xinquan.meditation.mapper.MeditationUserFavoriteMapper;
import com.xinquan.meditation.service.MeditationUserFavoriteService;
import org.springframework.stereotype.Service;
/**
 * <p>
 * 冥想用户操作表 服务实现类
 * </p>
 *
 * @author mitao
 * @since 2024-08-21
 */
@Service
public class MeditationUserFavoriteServiceImpl extends
        ServiceImpl<MeditationUserFavoriteMapper, MeditationUserFavorite> implements
        MeditationUserFavoriteService {
}
xinquan-modules/xinquan-meditation/src/main/java/com/xinquan/meditation/service/impl/MeditationUserOperationServiceImpl.java
File was deleted
xinquan-modules/xinquan-meditation/src/main/resources/mapper/meditation/HomeBackgroundMusicMapper.xml
@@ -12,6 +12,7 @@
        <result column="update_time" property="updateTime" />
        <result column="image_url" property="imageUrl" />
        <result column="audio_file" property="audioFile" />
        <result column="audio_file_name" property="audioFileName"/>
        <result column="sort_num" property="sortNum" />
    </resultMap>
xinquan-modules/xinquan-meditation/src/main/resources/mapper/meditation/MeditationMapper.xml
@@ -24,7 +24,6 @@
        <result column="detail_description" property="detailDescription" />
        <result column="cover_description" property="coverDescription" />
        <result column="tutor_audio_url" property="tutorAudioUrl" />
        <result column="like_count" property="likeCount" />
        <result column="favorite_count" property="favoriteCount" />
    </resultMap>
@@ -37,5 +36,23 @@
        update_time,
        id, cate_id, meditation_title, listing_status, sanskrit, charge_type, sort_num, real_learned_num, virtual_learned_num, icon_url, cover_url, background_url, detail_description, cover_description, tutor_audio_url, like_count, favorite_count
    </sql>
    <select id="getMeditationListByTagId"
      resultType="com.xinquan.meditation.domain.vo.ClientMeditationVO"
      parameterType="java.util.Set">
        SELECT
        <include refid="Base_Column_List"/>
        FROM
        t_meditation m
        RIGHT JOIN t_meditation_tag mt ON m.id = mt.meditation_id
        <where>
            <if test="sanskritFlag != null and sanskritFlag == 2">
                AND m.sanskrit = #{sanskritFlag}
            </if>
            <if test="tagIdSet !=null and tagIdSet.size()>0">
                AND mt.tag_id IN (#{tagIdSet})
            </if>
        </where>
        ORDER BY sort_num DESC
    </select>
</mapper>
xinquan-modules/xinquan-meditation/src/main/resources/mapper/meditation/MeditationQuestionLikeMapper.xml
New file
@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.xinquan.meditation.mapper.MeditationQuestionLikeMapper">
  <!-- 通用查询映射结果 -->
  <resultMap id="BaseResultMap" type="com.xinquan.meditation.domain.MeditationQuestionLike">
    <id column="id" property="id"/>
    <result column="create_by" property="createBy"/>
    <result column="create_time" property="createTime"/>
    <result column="meditation_question_id" property="meditationQuestionId"/>
    <result column="app_user_id" property="appUserId"/>
  </resultMap>
  <!-- 通用查询结果列 -->
  <sql id="Base_Column_List">
    create_by,
        create_time,
        id, meditation_question_id, app_user_id
  </sql>
</mapper>
xinquan-modules/xinquan-meditation/src/main/resources/mapper/meditation/MeditationQuestionMapper.xml
@@ -5,6 +5,7 @@
    <!-- 通用查询映射结果 -->
    <resultMap id="BaseResultMap" type="com.xinquan.meditation.domain.MeditationQuestion">
        <id column="id" property="id" />
        <result column="meditation_id" property="meditationId"/>
        <result column="del_flag" property="delFlag" />
        <result column="create_by" property="createBy" />
        <result column="create_time" property="createTime" />
xinquan-modules/xinquan-meditation/src/main/resources/mapper/meditation/MeditationUserFavoriteMapper.xml
New file
@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.xinquan.meditation.mapper.MeditationUserFavoriteMapper">
    <!-- 通用查询映射结果 -->
    <resultMap id="BaseResultMap" type="com.xinquan.meditation.domain.MeditationUserFavorite">
        <id column="id" property="id" />
        <result column="create_by" property="createBy" />
        <result column="create_time" property="createTime" />
        <result column="meditation_id" property="meditationId" />
        <result column="app_user_id" property="appUserId" />
    </resultMap>
    <!-- 通用查询结果列 -->
    <sql id="Base_Column_List">
        create_by,
        create_time,
        id, favorite, meditation_id, app_user_id
    </sql>
</mapper>
xinquan-modules/xinquan-order/pom.xml
@@ -115,23 +115,17 @@
      <artifactId>druid-spring-boot-starter</artifactId>
      <version>${druid.version}</version>
    </dependency>
    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>fastjson</artifactId>
      <version>1.2.47</version>
    </dependency>
    <!--bouncycastle,AES加密依赖-->
    <dependency>
      <groupId>cn.afterturn</groupId>
      <artifactId>easypoi-spring-boot-starter</artifactId>
      <version>4.0.0</version>
      <exclusions>
        <exclusion>
          <artifactId>guava</artifactId>
          <groupId>com.google.guava</groupId>
        </exclusion>
      </exclusions>
      <groupId>org.bouncycastle</groupId>
      <artifactId>bcprov-jdk15on</artifactId>
      <version>1.64</version>
    </dependency>
  </dependencies>
xinquan-modules/xinquan-order/src/main/java/com/xinquan/order/controller/client/ClientOrderController.java
@@ -1,6 +1,12 @@
package com.xinquan.order.controller.client;
import com.xinquan.common.core.domain.R;
import com.xinquan.order.service.OrderService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@@ -13,8 +19,17 @@
 * @since 2024-08-21
 */
@RestController
@RequiredArgsConstructor
@Api(tags = {"用户端-订单相关接口"})
@RequestMapping("/client/order/order")
public class ClientOrderController {
    private OrderService orderService;
    @PostMapping("/createOrder")
    @ApiOperation(value = "创建订单")
    public R<?> createOrder() {
        return R.ok();
    }
}
xinquan-modules/xinquan-order/src/main/java/com/xinquan/order/utils/EncryptUtils.java
New file
@@ -0,0 +1,61 @@
package com.xinquan.order.utils;
import java.nio.charset.StandardCharsets;
import java.security.Security;
import java.util.Base64;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
public class EncryptUtils {
    private static final String SECRET = "AES";
    private static final String CIPHER_ALGORITHM = "AES/ECB/PKCS7Padding";
    static {
        Security.addProvider(new BouncyCastleProvider());
    }
    /**
     * AES加密ECB模式PKCS7Padding填充方式
     *
     * @param str 字符串
     * @param key 密钥
     * @return 加密字符串
     * @throws Exception 异常信息
     */
    public static String aes256ECBPkcs7PaddingEncrypt(String str, String key) throws Exception {
        Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
        byte[] keyBytes = key.getBytes(StandardCharsets.UTF_8);
        cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(keyBytes, SECRET));
        byte[] doFinal = cipher.doFinal(str.getBytes(StandardCharsets.UTF_8));
        return new String(Base64.getEncoder().encode(doFinal));
    }
    /**
     * AES解密ECB模式PKCS7Padding填充方式
     *
     * @param str 字符串
     * @param key 密钥
     * @return 解密字符串
     * @throws Exception 异常信息
     */
    public static String aes256ECBPkcs7PaddingDecrypt(String str, String key) throws Exception {
        Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
        byte[] keyBytes = key.getBytes(StandardCharsets.UTF_8);
        cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(keyBytes, SECRET));
        byte[] doFinal = cipher.doFinal(Base64.getDecoder().decode(str));
        return new String(doFinal);
    }
    public static void main(String[] args) throws Exception {
        String str = "a1234567";
        System.out.println("字符串:" + str);
        String encryptStr = EncryptUtils.aes256ECBPkcs7PaddingEncrypt(str, "AES454-HTJSQ9-IT");
        System.out.println("加密后字符串:" + encryptStr);
        String decryptStr = EncryptUtils.aes256ECBPkcs7PaddingDecrypt(encryptStr,
                "AES454-HTJSQ9-IT");
        System.out.println("解密后字符串:" + decryptStr);
    }
}
xinquan-modules/xinquan-order/src/main/java/com/xinquan/order/utils/JuHeFuUtil.java
New file
@@ -0,0 +1,265 @@
package com.xinquan.order.utils;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.xinquan.common.core.exception.ServiceException;
import com.xinquan.common.core.utils.DateUtils;
import com.xinquan.common.core.utils.StringUtils;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.Signature;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
 * 桔禾付支付工具类
 *
 * @author mitao
 * @date 2024/9/7
 */
public class JuHeFuUtil {
    private static Logger logger = LoggerFactory.getLogger(JuHeFuUtil.class);
    // 商户RSA私钥 TODO
    private static final String MERCHANT_PRIVATE_KEY = "";
    // 平台RSA公钥 TODO
    private static final String PLAT_PUBLIC_KEY = "";
    // 应用的app_id TODO
    private static final String APP_ID = "";
    // 商户id TODO
    private static final String MER_ID = "";
    // AES密钥 TODO
    private static final String AES_KEY = "";
    // 创建用户接口
    private static final String CREATE_USER_INTERFACE = "/api/member/create_user_a";
    // 创建支付订单
    private static final String CREATE_PAYMENT_INTERFACE = "/api/payment/create_payment";
    // 退款接口
    private static final String PAYMENT_REFUND_INTERFACE = "/api/payment/payment_refund";
    private static final String MERCHANT_PAY_INTERFACE = "/api/account/merchant_pay";
    // 接口地址 TODO
    private static final String BASE_URL = "";
    // 支付宝支付渠道
    private static final String PAY_CHANNEL_ALIPAY = "alipay_qr";
    // 微信支付渠道
    private static final String PAY_CHANNEL_WECHAT = "wx_lite";
    // 微信小程序id TODO
    private static final String WX_APP_ID = "";
    /**
     * 创建用户
     *
     * @param memberId 用户id
     * @param memberP  用户信息
     * @return boolean
     * @throws Exception
     */
    public static boolean createUser(String memberId, Map<String, Object> memberP)
            throws Exception {
        JSONObject data = new JSONObject();
        data.put("app_id", APP_ID);
        data.put("member_id", memberId);
        data.put("member_type", "01");
        data.put("member_p", memberP);
        JSONObject body = new JSONObject();
        body.put("merId", MER_ID);
        body.put("sign", sign(JSONObject.toJSONString(data)));
        body.put("reqCipher",
                EncryptUtils.aes256ECBPkcs7PaddingEncrypt(JSONObject.toJSONString(data), AES_KEY));
        body.put("reqTime", DateUtils.dateTimeNow("yyyyMMddHHmmssfff"));
        // 发送Post请求
        logger.info("桔禾付创建用户请求体====================>{}", body.toJSONString());
        String result = sendPost(BASE_URL + CREATE_USER_INTERFACE, body);
        logger.info("桔禾付创建用户请求结果====================>{}", result);
        if (StringUtils.isBlank(result)) {
            throw new ServiceException("请求第三方支付平台异常");
        }
        JSONObject resJsonObject = JSONObject.parseObject(result);
        if (resJsonObject.getString("status").equals("succeeded")) {
            return true;
        } else if (resJsonObject.getString("status").equals("failed")) {
            throw new ServiceException(resJsonObject.getString("error_msg"));
        }
        return false;
    }
    /**
     * 创建支付订单
     *
     * @param orderNo    订单号
     * @param payChannel 支付渠道 1:支付宝 2:微信
     * @param payAmt     支付金额
     * @param goodsTitle 商品标题
     * @param goodsDesc  商品描述信息,微信小程序和微信公众号该字段 最大长度 42 个字符
     * @param deviceIp   设备支付公网ip
     * @param openId     微信openId
     * @param notifyUrl  回调地址
     * @return JSONObject
     * @throws Exception
     */
    public JSONObject createPayment(String orderNo, Integer payChannel, String payAmt,
            String goodsTitle, String goodsDesc, String deviceIp, String openId,
            String notifyUrl) throws Exception {
        JSONObject data = new JSONObject();
        JSONObject expand = new JSONObject();
        data.put("order_no", orderNo);
        data.put("app_id", APP_ID);
        data.put("mer_id", MER_ID);
        if (payChannel == 1) {
            data.put("pay_channel", PAY_CHANNEL_ALIPAY);
        } else {
            data.put("pay_channel", PAY_CHANNEL_WECHAT);
            expand.put("open_id", openId);
            expand.put("wx_app_id", WX_APP_ID);
            data.put("expand", expand);
        }
        data.put("pay_amt", payAmt);
        data.put("goods_title", goodsTitle);
        data.put("goods_desc", goodsDesc);
        Map<String, Object> deviceInfo = new HashMap<>();
        deviceInfo.put("device_ip", deviceIp);
        data.put("device_info", deviceInfo);
        data.put("notify_url", notifyUrl);
        JSONObject body = new JSONObject();
        body.put("merId", MER_ID);
        body.put("sign", sign(JSONObject.toJSONString(data)));
        body.put("reqCipher",
                EncryptUtils.aes256ECBPkcs7PaddingEncrypt(JSONObject.toJSONString(data), AES_KEY));
        body.put("reqTime", DateUtils.dateTimeNow("yyyyMMddHHmmssfff"));
        String result = sendPost(BASE_URL + CREATE_PAYMENT_INTERFACE, body);
        JSONObject resJsonObject = JSONObject.parseObject(result);
        if (resJsonObject.getString("status").equals("failed")) {
            throw new ServiceException(resJsonObject.getString("error_msg"));
        }
        if (resJsonObject.getString("status").equals("succeeded")) {
            return resJsonObject.getJSONObject("expand");
        }
        return null;
    }
    /**
     * 申请退款
     *
     * @param paymentId 支付单号
     * @param orderNo   订单号
     * @return
     */
    public boolean refund(String paymentId, String orderNo) throws Exception {
        JSONObject data = new JSONObject();
        data.put("payment_id", paymentId);
        data.put("order_no", orderNo);
        JSONObject body = new JSONObject();
        body.put("merId", MER_ID);
        body.put("sign", sign(JSONObject.toJSONString(data)));
        body.put("reqCipher",
                EncryptUtils.aes256ECBPkcs7PaddingEncrypt(JSONObject.toJSONString(data), AES_KEY));
        body.put("reqTime", DateUtils.dateTimeNow("yyyyMMddHHmmssfff"));
        String result = sendPost(BASE_URL + PAYMENT_REFUND_INTERFACE, body);
        JSONObject resJsonObject = JSONObject.parseObject(result);
        if (resJsonObject.getString("status").equals("succeeded")) {
            return true;
        } else if (resJsonObject.getString("status").equals("failed")) {
            throw new ServiceException(resJsonObject.getString("error_msg"));
        }
        return false;
    }
    /**
     * 商户付款
     *
     * @param orderNo  订单号
     * @param payAmt   交易金额
     * @param cardName 收款银行开户名
     * @param cardId   收款银行卡号
     * @param cardType 收款银行账户类型 DEBIT_CARD:借记卡 CREDIT_CARD:贷记卡 76 QUASI_CREDIT_CARD:准贷卡 PASSBOOK:存折
     *                 UNIT_SETTLE_CARD:单位结算卡 PUBLIC_CARD:对公卡
     * @return
     * @throws Exception
     */
    public boolean merchantPay(String orderNo, String payAmt, String cardName, String cardId,
            String cardType)
            throws Exception {
        JSONObject data = new JSONObject();
        data.put("order_no", orderNo);
        data.put("pay_amt", payAmt);
        data.put("card_name", cardName);
        data.put("card_id", cardId);
        JSONObject body = new JSONObject();
        body.put("merId", MER_ID);
        body.put("sign", sign(JSONObject.toJSONString(data)));
        body.put("reqCipher",
                EncryptUtils.aes256ECBPkcs7PaddingEncrypt(JSONObject.toJSONString(data), AES_KEY));
        body.put("reqTime", DateUtils.dateTimeNow("yyyyMMddHHmmssfff"));
        String result = sendPost(BASE_URL + MERCHANT_PAY_INTERFACE, body);
        JSONObject resJsonObject = JSONObject.parseObject(result);
        if (resJsonObject.getString("status").equals("succeeded")) {
            return true;
        } else if (resJsonObject.getString("status").equals("failed")) {
            throw new ServiceException(resJsonObject.getString("error_msg"));
        }
        return false;
    }
    /**
     * 发送post请求
     *
     * @param url  请求地址
     * @param body 请求体
     * @return
     */
    private static String sendPost(String url, JSONObject body) {
        HttpRequest post = HttpUtil.createPost(url);
        Map<String, String> headers = new HashMap<>();
        headers.put("Content-type", "application/json; charset=utf-8");
        headers.put("Accept", "application/json");
        post.addHeaders(headers);
        post.body(body.toJSONString());
        HttpResponse execute = post.execute();
        String result = execute.body();
        execute.close();
        if (StringUtils.isBlank(result)) {
            throw new ServiceException("请求第三方支付平台异常");
        }
        return result;
    }
    /**
     * RSA私钥签名:签名方式SHA256WithRSA
     *
     * @param data 待签名字符串
     * @return 签名byte[]
     * @throws Exception
     */
    public static String sign(String data) {
        // 先对该json对象数据按照参数字典顺序(参数名ASCII码从小到大排序,参数名区分大小写)排序生成字符串,再进行加签和验签。
        data = JSON.toJSONString(JSONObject.parseObject(data, TreeMap.class));
        // Base64 --> Key
        try {
            byte[] bytes = Base64.getDecoder().decode(MERCHANT_PRIVATE_KEY);
            PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(bytes);
            KeyFactory keyFactory;
            keyFactory = KeyFactory.getInstance("RSA");
            PrivateKey privateKey = keyFactory.generatePrivate(keySpec);
            // Sign
            Signature signature = Signature.getInstance("SHA256WithRSA");
            signature.initSign(privateKey);
            signature.update(data.getBytes("UTF-8"));
            return Base64.getEncoder().encodeToString(signature.sign());
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
}
xinquan-modules/xinquan-order/src/main/resources/mapper/order/OrderMapper.xml
New file
@@ -0,0 +1,38 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.xinquan.order.mapper.OrderMapper">
    <!-- 通用查询映射结果 -->
    <resultMap id="BaseResultMap" type="com.xinquan.order.domain.Order">
        <id column="id" property="id" />
        <result column="del_flag" property="delFlag" />
        <result column="create_by" property="createBy" />
        <result column="create_time" property="createTime" />
        <result column="update_by" property="updateBy" />
        <result column="update_time" property="updateTime" />
        <result column="biz_order_no" property="bizOrderNo" />
        <result column="app_user_id" property="appUserId" />
        <result column="business_id" property="businessId" />
        <result column="order_from" property="orderFrom" />
        <result column="payment_status" property="paymentStatus" />
        <result column="total_amount" property="totalAmount" />
        <result column="real_pay_amount" property="realPayAmount" />
        <result column="commission_amount" property="commissionAmount" />
        <result column="change_price" property="changePrice" />
        <result column="change_price_time" property="changePriceTime" />
        <result column="change_price_operator" property="changePriceOperator" />
        <result column="payment_time" property="paymentTime" />
        <result column="cancel_time" property="cancelTime" />
    </resultMap>
    <!-- 通用查询结果列 -->
    <sql id="Base_Column_List">
        del_flag,
        create_by,
        create_time,
        update_by,
        update_time,
        id, biz_order_no, app_user_id, business_id, order_from, payment_status, total_amount, real_pay_amount, commission_amount, change_price, change_price_time, change_price_operator, payment_time, cancel_time
    </sql>
</mapper>
xinquan-modules/xinquan-order/src/main/resources/mapper/order/OrderPaymentRecordMapper.xml
copy from xinquan-modules/xinquan-meditation/src/main/resources/mapper/meditation/MeditationUserOperationMapper.xml copy to xinquan-modules/xinquan-order/src/main/resources/mapper/order/OrderPaymentRecordMapper.xml
File was copied from xinquan-modules/xinquan-meditation/src/main/resources/mapper/meditation/MeditationUserOperationMapper.xml
@@ -1,19 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.xinquan.meditation.mapper.MeditationUserOperationMapper">
<mapper namespace="com.xinquan.order.mapper.OrderPaymentRecordMapper">
    <!-- 通用查询映射结果 -->
    <resultMap id="BaseResultMap" type="com.xinquan.meditation.domain.MeditationUserOperation">
    <resultMap id="BaseResultMap" type="com.xinquan.order.domain.OrderPaymentRecord">
        <id column="id" property="id" />
        <result column="del_flag" property="delFlag" />
        <result column="create_by" property="createBy" />
        <result column="create_time" property="createTime" />
        <result column="update_by" property="updateBy" />
        <result column="update_time" property="updateTime" />
        <result column="like" property="like" />
        <result column="favorite" property="favorite" />
        <result column="meditation_id" property="meditationId" />
        <result column="app_user_id" property="appUserId" />
        <result column="order_id" property="orderId" />
        <result column="payment_type" property="paymentType" />
        <result column="pay_amount" property="payAmount" />
        <result column="pay_order_no" property="payOrderNo" />
    </resultMap>
    <!-- 通用查询结果列 -->
@@ -23,7 +23,7 @@
        create_time,
        update_by,
        update_time,
        id, like, favorite, meditation_id, app_user_id
        id, order_id, payment_type, pay_amount, pay_order_no
    </sql>
</mapper>
xinquan-modules/xinquan-system/pom.xml
@@ -126,19 +126,6 @@
            <version>1.2.47</version>
        </dependency>
        <dependency>
            <groupId>cn.afterturn</groupId>
            <artifactId>easypoi-spring-boot-starter</artifactId>
            <version>4.0.0</version>
            <exclusions>
                <exclusion>
                    <artifactId>guava</artifactId>
                    <groupId>com.google.guava</groupId>
                </exclusion>
            </exclusions>
        </dependency>
       <!-- <dependency>
            <groupId>com.github.xiaoymin</groupId>
            <artifactId>knife4j-spring-boot-starter</artifactId>
xinquan-modules/xinquan-system/src/main/java/com/xinquan/system/controller/SysRoleController.java
@@ -29,7 +29,6 @@
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;
import javax.annotation.Resource;
@@ -131,7 +130,7 @@
        role.setMenuIds(dto.getMenuIds().toArray((new Long[dto.getMenuIds().size()])));
        // 添加角色
        role.setCreateBy(SecurityUtils.getUsername());
        role.setCreateTime(new Date());
        // role.setCreateTime(new Date());
        roleService.insertRole(role);
//        ArrayList<SysRoleMenu> sysRoleMenus = new ArrayList<>();
//        List<Long> menuIds = dto.getMenuIds();
@@ -235,7 +234,7 @@
        }
        // 编辑角色
        role.setUpdateBy(SecurityUtils.getUsername());
        role.setUpdateTime(new Date());
        // role.setUpdateTime(new Date());
        role.setRoleId(dto.getRoleId());
        roleService.updateRole(role);
        ArrayList<SysRoleMenu> sysRoleMenus = new ArrayList<>();
xinquan-modules/xinquan-system/src/main/java/com/xinquan/system/controller/SysUserController.java
@@ -28,7 +28,6 @@
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import java.io.IOException;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.Set;
@@ -167,8 +166,8 @@
            user.setUserName(user.getPhonenumber());
        }
        user.setPassword(null);
        user.setUpdateBy(SecurityUtils.getUsername());
        user.setUpdateTime(new Date());
        // user.setUpdateBy(SecurityUtils.getUsername());
        // user.setUpdateTime(new Date());
        return toAjax(userService.updateUser(user));
    }
@@ -205,8 +204,8 @@
    @PostMapping("/updateSysUser")
    public R<Boolean> updateSysUser(@RequestBody SysUser sysUser) {
        try {
            sysUser.setUpdateBy(SecurityUtils.getUsername());
            sysUser.setUpdateTime(new Date());
            // sysUser.setUpdateBy(SecurityUtils.getUsername());
            // sysUser.setUpdateTime(new Date());
            userService.updateUser(sysUser);
            return R.ok(true);
        } catch (Exception e) {
@@ -290,7 +289,7 @@
            return R.fail("保存用户'" + username + "'失败,注册账号已存在");
        }
        //添加用户
        userService.save(sysUser);
        userService.registerUser(sysUser);
        return R.ok(sysUser);
    }
    /**
xinquan-modules/xinquan-system/src/main/java/com/xinquan/system/controller/client/PublicController.java
@@ -1,6 +1,7 @@
package com.xinquan.system.controller.client;
import com.xinquan.common.core.web.domain.AjaxResult;
import com.xinquan.common.core.domain.R;
import com.xinquan.system.domain.vo.ContentSettingVO;
import com.xinquan.system.service.PublicService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
@@ -31,8 +32,8 @@
    @ApiImplicitParams({
            @ApiImplicitParam(name = "key", value = "用户内容 1=用户协议 2=隐私协议 3=关于心泉 4=新手冥想指南 5=课程/冥想音频购买协议",
                    required = true, type = "Integer", paramType = "query")})
    public AjaxResult getContent(
    public R<ContentSettingVO> getContent(
            @RequestParam(value = "key", required = true) Integer key) {
        return AjaxResult.success(publicService.getContent(key));
        return R.ok(publicService.getContent(key));
    }
}
xinquan-modules/xinquan-system/src/main/java/com/xinquan/system/controller/inner/InnerBannerController.java
New file
@@ -0,0 +1,40 @@
package com.xinquan.system.controller.inner;
import com.xinquan.common.core.domain.R;
import com.xinquan.common.security.annotation.InnerAuth;
import com.xinquan.system.api.domain.vo.BannerVO;
import com.xinquan.system.service.BannerService;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
 * <p>
 * 轮播图控制器
 * </p>
 *
 * @author mitao
 * @since 2024-08-21
 */
@RestController
@RequiredArgsConstructor
@RequestMapping("/inner/system/banner")
public class InnerBannerController {
    private final BannerService bannerService;
    /**
     * 获取轮播图列表
     *
     * @return 轮播图列表
     */
    @InnerAuth
    @GetMapping("/getBannerList")
    public R<List<BannerVO>> getBannerList() {
        return R.ok(bannerService.getBannerList());
    }
}
xinquan-modules/xinquan-system/src/main/java/com/xinquan/system/controller/inner/InnerHotWordsController.java
New file
@@ -0,0 +1,39 @@
package com.xinquan.system.controller.inner;
import com.xinquan.common.core.domain.R;
import com.xinquan.common.security.annotation.InnerAuth;
import com.xinquan.system.service.HotWordsService;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
 * <p>
 * 热词表     前端控制器
 * </p>
 *
 * @author mitao
 * @since 2024-08-21
 */
@RestController
@RequiredArgsConstructor
@RequestMapping("/inner/system/hot-words")
public class InnerHotWordsController {
    private final HotWordsService hotWordsService;
    /**
     * 获取热词列表
     *
     * @return 热词列表
     */
    @InnerAuth
    @GetMapping("/getHotWordList")
    public R<List<String>> getHotWordList() {
        return R.ok(hotWordsService.getHotWordList());
    }
}
xinquan-modules/xinquan-system/src/main/java/com/xinquan/system/domain/HotWords.java
@@ -1,10 +1,10 @@
package com.xinquan.system.domain;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.annotation.IdType;
import com.xinquan.common.core.web.domain.BaseModel;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.xinquan.common.core.web.domain.BaseModel;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
xinquan-modules/xinquan-system/src/main/java/com/xinquan/system/domain/SysMenus.java
@@ -1,20 +1,11 @@
package com.xinquan.system.domain;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.xinquan.common.core.web.domain.BaseEntity;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import lombok.Data;
/**
 * 菜单权限表 sys_menu
@@ -75,26 +66,6 @@
    /** 权限字符串 */
    private String perms;
    /** 菜单图标 */
    private String icon;
    @TableField("create_by")
    private String createBy;
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @TableField("create_time")
    private Date createTime;
    /** 更新者 */
    @ApiModelProperty(value = "记录修改人,前端忽略")
    //@JsonIgnore
    @TableField("update_by")
    private String updateBy;
    /** 更新时间 */
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @TableField("update_time")
    private Date updateTime;
    /** 备注 */
    private String remark;
xinquan-modules/xinquan-system/src/main/java/com/xinquan/system/mapper/HotWordsMapper.java
@@ -1,7 +1,7 @@
package com.xinquan.system.mapper;
import com.xinquan.system.domain.HotWords;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.xinquan.system.domain.HotWords;
/**
 * <p>
xinquan-modules/xinquan-system/src/main/java/com/xinquan/system/service/BannerService.java
@@ -1,7 +1,9 @@
package com.xinquan.system.service;
import com.xinquan.system.domain.Banner;
import com.baomidou.mybatisplus.extension.service.IService;
import com.xinquan.system.api.domain.vo.BannerVO;
import com.xinquan.system.domain.Banner;
import java.util.List;
/**
 * <p>
@@ -13,4 +15,10 @@
 */
public interface BannerService extends IService<Banner> {
    /**
     * 获取轮播图列表
     *
     * @return 轮播图列表
     */
    List<BannerVO> getBannerList();
}
xinquan-modules/xinquan-system/src/main/java/com/xinquan/system/service/HotWordsService.java
@@ -1,7 +1,8 @@
package com.xinquan.system.service;
import com.xinquan.system.domain.HotWords;
import com.baomidou.mybatisplus.extension.service.IService;
import com.xinquan.system.domain.HotWords;
import java.util.List;
/**
 * <p>
@@ -13,4 +14,10 @@
 */
public interface HotWordsService extends IService<HotWords> {
    /**
     * 获取热词列表
     *
     * @return 热词列表
     */
    List<String> getHotWordList();
}
xinquan-modules/xinquan-system/src/main/java/com/xinquan/system/service/impl/BannerServiceImpl.java
@@ -1,9 +1,12 @@
package com.xinquan.system.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xinquan.common.core.utils.page.BeanUtils;
import com.xinquan.system.api.domain.vo.BannerVO;
import com.xinquan.system.domain.Banner;
import com.xinquan.system.mapper.BannerMapper;
import com.xinquan.system.service.BannerService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import java.util.List;
import org.springframework.stereotype.Service;
/**
@@ -17,4 +20,14 @@
@Service
public class BannerServiceImpl extends ServiceImpl<BannerMapper, Banner> implements BannerService {
    /**
     * 获取轮播图列表
     *
     * @return 轮播图列表
     */
    @Override
    public List<BannerVO> getBannerList() {
        List<Banner> list = this.lambdaQuery().orderByDesc(Banner::getSortNum).list();
        return BeanUtils.copyList(list, BannerVO.class);
    }
}
xinquan-modules/xinquan-system/src/main/java/com/xinquan/system/service/impl/HotWordsServiceImpl.java
@@ -1,9 +1,12 @@
package com.xinquan.system.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xinquan.common.core.utils.page.CollUtils;
import com.xinquan.system.domain.HotWords;
import com.xinquan.system.mapper.HotWordsMapper;
import com.xinquan.system.service.HotWordsService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import java.util.List;
import java.util.stream.Collectors;
import org.springframework.stereotype.Service;
/**
@@ -17,4 +20,18 @@
@Service
public class HotWordsServiceImpl extends ServiceImpl<HotWordsMapper, HotWords> implements HotWordsService {
    /**
     * 获取热词列表
     *
     * @return 热词列表
     */
    @Override
    public List<String> getHotWordList() {
        List<String> hotWordList = CollUtils.emptyList();
        List<HotWords> list = this.lambdaQuery().orderByDesc(HotWords::getSortNum).list();
        if (CollUtils.isNotEmpty(list)) {
            hotWordList = list.stream().map(HotWords::getWordName).collect(Collectors.toList());
        }
        return hotWordList;
    }
}
xinquan-modules/xinquan-system/src/main/resources/mapper/system/SysUserMapper.xml
@@ -157,6 +157,7 @@
             <if test="status != null and status != ''">status,</if>
             <if test="createBy != null and createBy != ''">create_by,</if>
             <if test="remark != null and remark != ''">remark,</if>
        <if test="userType != null and userType != ''">user_type,</if>
             create_time
         )values(
             <if test="userId != null and userId != ''">#{userId},</if>
@@ -171,6 +172,7 @@
             <if test="status != null and status != ''">#{status},</if>
             <if test="createBy != null and createBy != ''">#{createBy},</if>
             <if test="remark != null and remark != ''">#{remark},</if>
        <if test="userType != null and userType != ''">#{user_type},</if>
             sysdate()
         )
    </insert>
xinquan-modules/xinquan-user/pom.xml
@@ -120,20 +120,7 @@
      <artifactId>fastjson</artifactId>
      <version>1.2.47</version>
    </dependency>
    <dependency>
      <groupId>cn.afterturn</groupId>
      <artifactId>easypoi-spring-boot-starter</artifactId>
      <version>4.0.0</version>
      <exclusions>
        <exclusion>
          <artifactId>guava</artifactId>
          <groupId>com.google.guava</groupId>
        </exclusion>
      </exclusions>
    </dependency>
  </dependencies>
  <build>
xinquan-modules/xinquan-user/src/main/java/com/xinquan/user/controller/client/ClientAppUserController.java
@@ -1,8 +1,21 @@
package com.xinquan.user.controller.client;
import com.xinquan.common.core.domain.R;
import com.xinquan.user.domain.dto.UserAnswerDTO;
import com.xinquan.user.domain.vo.AppUserVO;
import com.xinquan.user.domain.vo.TagVO;
import com.xinquan.user.service.AppUserService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import java.util.List;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.annotation.Validated;
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;
/**
@@ -13,9 +26,52 @@
 * @author mitao
 * @since 2024-08-21
 */
@Api(tags = {"用户相关接口"})
@Slf4j
@RestController
@RequestMapping("/client/app-user")
@RequiredArgsConstructor
public class ClientAppUserController {
    private final AppUserService appUserService;
    /**
     * 获取当前登录用户信息
     *
     * @return 用户信息
     * @see com.xinquan.user.domain.vo.AppUserVO
     */
    @PostMapping("/getCurrentUser")
    @ApiOperation(value = "获取当前用户信息", tags = {"用户端-用户信息相关接口"})
    public R<AppUserVO> getCurrentUser() {
        return R.ok(appUserService.getCurrentUser());
    }
    /**
     * 获取问题二的标签列表
     *
     * @return List<TagVO>
     */
    @GetMapping("/getTagList")
    @ApiOperation(value = "问题二获取用户标签列表", tags = {"用户端-计划引导相关接口"})
    public R<List<TagVO>> getTagList() {
        List<TagVO> voList = appUserService.getTagList();
        return R.ok(voList);
    }
    /**
     * 保存计划引导页用户的答案
     *
     * @param dto 用户计划引导答案数据传输对象
     * @return
     */
    @PostMapping("/saveUserAnswers")
    @ApiOperation(value = "保存计划引导页用户的答案", tags = {"用户端-计划引导相关接口"})
    public R<?> saveUserAnswers(@Validated @RequestBody UserAnswerDTO dto) {
        appUserService.saveUserAnswers(dto);
        return R.ok();
    }
}
xinquan-modules/xinquan-user/src/main/java/com/xinquan/user/controller/client/ClientAppUserViewingHistoryController.java
New file
@@ -0,0 +1,50 @@
package com.xinquan.user.controller.client;
import com.xinquan.common.core.domain.R;
import com.xinquan.user.service.AppUserViewingHistoryService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
/**
 * <p>
 * 用户观看历史 前端控制器
 * </p>
 *
 * @author mitao
 * @since 2024-09-06
 */
@Api(tags = {"用户端-观看历史相关接口"})
@RestController
@RequiredArgsConstructor
@RequestMapping("/user/app-user-viewing-history")
public class ClientAppUserViewingHistoryController {
    private final AppUserViewingHistoryService appUserViewingHistoryService;
    /**
     * 记录用户观看记录
     *
     * @param bizId       业务id
     * @param viewingType 观看类型 1=疗愈 2=课程
     */
    @PostMapping("/saveViewingHistory")
    @ApiOperation(value = "记录用户观看记录", tags = {"用户端-用户相关接口"})
    @ApiImplicitParams({
            @ApiImplicitParam(name = "bizId", value = "业务id", dataType = "Long", required = true),
            @ApiImplicitParam(name = "viewingType", value = "观看类型 1=疗愈 2=课程", dataType = "Integer", required = true)
    })
    public R<?> saveViewingRecord(@RequestParam("bizId") Long bizId,
            @RequestParam("viewingType") Integer viewingType) {
        appUserViewingHistoryService.saveViewingRecord(bizId, viewingType);
        return R.ok();
    }
}
xinquan-modules/xinquan-user/src/main/java/com/xinquan/user/controller/inner/InnerAppUserController.java
@@ -3,14 +3,14 @@
import com.xinquan.common.core.domain.R;
import com.xinquan.common.security.annotation.InnerAuth;
import com.xinquan.system.api.model.AppCaptchaBody;
import com.xinquan.system.api.model.AppLoginUser;
import com.xinquan.system.api.model.AppVerifyCellPhoneBody;
import com.xinquan.system.api.model.AppWXLoginBody;
import com.xinquan.system.api.model.AppleLoginUser;
import com.xinquan.system.api.model.LoginUser;
import com.xinquan.system.api.model.WXLoginUser;
import com.xinquan.user.api.domain.AppUser;
import com.xinquan.user.api.domain.dto.AppUserDTO;
import com.xinquan.user.service.AppUserService;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@@ -50,7 +50,7 @@
     */
    @InnerAuth
    @PostMapping("/registerAppUser")
    R<Boolean> registerAppUser(@RequestBody AppUserDTO appUserDTO){
    R<AppUser> registerAppUser(@RequestBody AppUserDTO appUserDTO) {
        return R.ok(appUserService.registerAppUser(appUserDTO));
    }
@@ -62,7 +62,7 @@
     */
    @InnerAuth
    @PostMapping("/wxLogin")
    R<WXLoginUser> wxLogin(@RequestBody AppWXLoginBody body) {
    R<AppLoginUser> wxLogin(@RequestBody AppWXLoginBody body) {
        return R.ok(appUserService.wxLogin(body));
    }
@@ -75,7 +75,7 @@
     */
    @InnerAuth
    @PostMapping("/appleLogin")
    R<AppleLoginUser> appleLogin(@RequestParam("appleId") String appleId) {
    R<AppLoginUser> appleLogin(@RequestParam("appleId") String appleId) {
        return R.ok(appUserService.appleLogin(appleId));
    }
@@ -87,7 +87,7 @@
     */
    @InnerAuth
    @PostMapping("/captchaLogin")
    R<LoginUser> appCaptchaLogin(@RequestBody AppCaptchaBody body) {
    R<AppLoginUser> appCaptchaLogin(@RequestBody AppCaptchaBody body) {
        return R.ok(appUserService.appCaptchaLogin(body));
    }
@@ -99,7 +99,32 @@
     */
    @InnerAuth
    @PostMapping("/verifyCellPhone")
    R<WXLoginUser> verifyCellPhone(@RequestBody AppVerifyCellPhoneBody body) {
    R<AppLoginUser> verifyCellPhone(@RequestBody AppVerifyCellPhoneBody body) {
        return R.ok(appUserService.verifyCellPhone(body));
    }
    /**
     * 更新APP用户信息
     *
     * @param dto APP用户数据传输对象
     */
    @InnerAuth
    @PostMapping("/updateAppUser")
    R<?> updateAppUser(@RequestBody AppUserDTO dto) {
        appUserService.updateAppUser(dto);
        return R.ok();
    }
    /**
     * 获取用户列表
     *
     * @param appUserIdSet 用户id列表
     * @param source       请求来源
     * @return
     */
    @InnerAuth
    @PostMapping("/getAppUserList")
    R<List<AppUser>> getAppUserList(@RequestBody Collection<Long> appUserIdSet) {
        return R.ok(appUserService.listByIds(appUserIdSet));
    }
}
xinquan-modules/xinquan-user/src/main/java/com/xinquan/user/controller/management/MgtAppUserViewingHistoryController.java
New file
@@ -0,0 +1,20 @@
package com.xinquan.user.controller.management;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
 * <p>
 * 用户观看历史 前端控制器
 * </p>
 *
 * @author mitao
 * @since 2024-09-06
 */
@RestController
@RequestMapping("/user/app-user-viewing-history")
public class MgtAppUserViewingHistoryController {
}
xinquan-modules/xinquan-user/src/main/java/com/xinquan/user/domain/AppUserQuestion.java
@@ -1,9 +1,9 @@
package com.xinquan.user.domain;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.xinquan.common.core.web.domain.BaseModel;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableField;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@@ -35,7 +35,11 @@
    @ApiModelProperty(value = "回答 1:是2:否3:偶尔有")
    @TableField("answer")
    private Integer answer;
    private String answer;
    @ApiModelProperty(value = "用户id")
    @TableField("app_user_id")
    private Long appUserId;
}
xinquan-modules/xinquan-user/src/main/java/com/xinquan/user/domain/AppUserViewingHistory.java
New file
@@ -0,0 +1,45 @@
package com.xinquan.user.domain;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.xinquan.common.core.web.domain.BaseModel;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
 * <p>
 * 用户观看历史
 * </p>
 *
 * @author mitao
 * @since 2024-09-06
 */
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("t_app_user_viewing_history")
@ApiModel(value = "AppUserViewingHistory对象", description = "用户观看历史")
public class AppUserViewingHistory extends BaseModel {
    private static final long serialVersionUID = 1L;
    @ApiModelProperty(value = "用户树苗id")
    @TableId("id")
    private Long id;
    @ApiModelProperty(value = "用户id")
    @TableField("app_user_id")
    private Long appUserId;
    @ApiModelProperty(value = "业务id ")
    @TableField("biz_id")
    private Long bizId;
    @ApiModelProperty(value = "观看类型 1:疗愈 2:课程")
    @TableField("viewing_type")
    private Integer viewingType;
}
xinquan-modules/xinquan-user/src/main/java/com/xinquan/user/domain/Tag.java
@@ -30,6 +30,10 @@
    @TableId(value = "id", type = IdType.AUTO)
    private Long id;
    @ApiModelProperty(value = "标签类型")
    @TableField(value = "tag_type")
    private Integer tagType;
    @ApiModelProperty(value = "标签名称")
    @TableField("tag_name")
    private String tagName;
xinquan-modules/xinquan-user/src/main/java/com/xinquan/user/domain/dto/UserAnswerDTO.java
New file
@@ -0,0 +1,29 @@
package com.xinquan.user.domain.dto;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import java.io.Serializable;
import java.util.List;
import javax.validation.Valid;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotEmpty;
import lombok.Data;
/**
 * @author mitao
 * @date 2024/8/27
 */
@Data
@ApiModel("用户计划引导答案数据传输对象")
public class UserAnswerDTO implements Serializable {
    private static final long serialVersionUID = -5037991196273819118L;
    @Valid
    @ApiModelProperty(value = "问题一答案", required = true)
    @NotEmpty(message = "问题一答案不能为空")
    private List<UserAnswerOneDTO> userAnswerOneDTOList;
    @ApiModelProperty(value = "问题二 多个tagId使用英文逗号拼接字符串", required = true)
    @NotBlank(message = "问题二答案不能为空")
    private String tagIds;
}
xinquan-modules/xinquan-user/src/main/java/com/xinquan/user/domain/dto/UserAnswerOneDTO.java
New file
@@ -0,0 +1,27 @@
package com.xinquan.user.domain.dto;
import io.swagger.annotations.ApiModelProperty;
import java.io.Serializable;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import lombok.Data;
/**
 * @author mitao
 * @date 2024/8/28
 */
@Data
public class UserAnswerOneDTO implements Serializable {
    private static final long serialVersionUID = -8938108350024740351L;
    @ApiModelProperty("问题一 问题类型 1:您是否内心纠结敏感多疑?2:您是否辗转反侧彻夜难眠?3:您是否情绪失控暴躁易怒?"
            + "4:您是否沉迷情感倍受伤害?5:您是否人生迷茫踌躇彷徨?6:其它补充说明")
    @NotNull(message = "问题类型不能为空")
    private Integer type;
    @ApiModelProperty("问题一 答案 1:是2:否3:偶尔,其他补充说明")
    @NotBlank(message = "答案不能为空")
    private String answer;
}
xinquan-modules/xinquan-user/src/main/java/com/xinquan/user/domain/vo/AppUserVO.java
New file
@@ -0,0 +1,109 @@
package com.xinquan.user.domain.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import lombok.Data;
/**
 * @author mitao
 * @date 2024/9/7
 */
@Data
@ApiModel(value = "用户信息VO")
public class AppUserVO implements Serializable {
    private static final long serialVersionUID = -6948932379350815750L;
    @ApiModelProperty(value = "App用户id")
    private Long id;
    @ApiModelProperty(value = "系统用户id")
    private Long userId;
    @ApiModelProperty(value = "手机号码")
    private String cellPhone;
    @ApiModelProperty(value = "苹果授权登录唯一标识")
    private String appleId;
    @ApiModelProperty(value = "微信openId")
    private String wxOpenId;
    @ApiModelProperty(value = "头像")
    private String avatar;
    @ApiModelProperty(value = "昵称")
    private String nickname;
    @ApiModelProperty(value = "签名")
    private String signature;
    @ApiModelProperty(value = "性别 1=男 2=女 3=保密")
    private Integer gender;
    @ApiModelProperty(value = "生日")
    private String birthday;
    @ApiModelProperty(value = "学历")
    private String education;
    @ApiModelProperty(value = "行业")
    private String industry;
    @ApiModelProperty(value = "公司")
    private String company;
    @ApiModelProperty(value = "职业")
    private String occupation;
    @ApiModelProperty(value = "所在地")
    private String location;
    @ApiModelProperty(value = "故乡")
    private String hometown;
    @ApiModelProperty(value = "邮箱")
    private String email;
    @ApiModelProperty(value = "会员到期时间")
    private LocalDateTime vipExpireTime;
    @ApiModelProperty(value = "用户状态 1=正常 2=冻结 3=注销")
    private Integer userStatus;
    @ApiModelProperty(value = "冻结原因")
    private String freezingReason;
    @ApiModelProperty(value = "冻结操作人")
    private String freezingOperator;
    @ApiModelProperty(value = "梵文权限 1=是 2否")
    private Integer sanskritFlag;
    @ApiModelProperty(value = "邀请人id")
    private Long inviteUserId;
    @ApiModelProperty(value = "余额")
    private BigDecimal balance;
    @ApiModelProperty(value = "收益")
    private BigDecimal income;
    @ApiModelProperty(value = "当前累计能量值")
    private Integer totalEnergyValue;
    @ApiModelProperty(value = "注册时间")
    private LocalDateTime registerTime;
    @ApiModelProperty(value = "注销时间")
    private LocalDateTime logoutTime;
    @ApiModelProperty(value = "用户等级id")
    private Integer levelSettingId;
    @ApiModelProperty(value = "标签id,多个id使用英文逗号拼接")
    private String tagId;
}
xinquan-modules/xinquan-user/src/main/java/com/xinquan/user/domain/vo/TagVO.java
New file
@@ -0,0 +1,20 @@
package com.xinquan.user.domain.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
 * @author mitao
 * @date 2024/9/4
 */
@Data
@ApiModel("问题二用户标签视图对象")
public class TagVO {
    @ApiModelProperty(value = "标签id")
    private Long id;
    @ApiModelProperty(value = "标签名称")
    private String tagName;
}
xinquan-modules/xinquan-user/src/main/java/com/xinquan/user/mapper/AppUserViewingHistoryMapper.java
New file
@@ -0,0 +1,16 @@
package com.xinquan.user.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.xinquan.user.domain.AppUserViewingHistory;
/**
 * <p>
 * 用户观看历史 Mapper 接口
 * </p>
 *
 * @author mitao
 * @since 2024-09-06
 */
public interface AppUserViewingHistoryMapper extends BaseMapper<AppUserViewingHistory> {
}
xinquan-modules/xinquan-user/src/main/java/com/xinquan/user/service/AppUserService.java
@@ -2,13 +2,15 @@
import com.baomidou.mybatisplus.extension.service.IService;
import com.xinquan.system.api.model.AppCaptchaBody;
import com.xinquan.system.api.model.AppLoginUser;
import com.xinquan.system.api.model.AppVerifyCellPhoneBody;
import com.xinquan.system.api.model.AppWXLoginBody;
import com.xinquan.system.api.model.AppleLoginUser;
import com.xinquan.system.api.model.LoginUser;
import com.xinquan.system.api.model.WXLoginUser;
import com.xinquan.user.api.domain.AppUser;
import com.xinquan.user.api.domain.dto.AppUserDTO;
import com.xinquan.user.domain.dto.UserAnswerDTO;
import com.xinquan.user.domain.vo.AppUserVO;
import com.xinquan.user.domain.vo.TagVO;
import java.util.List;
import java.util.Optional;
/**
@@ -33,7 +35,7 @@
     * @param appUserDTO
     * @return
     */
    Boolean registerAppUser(AppUserDTO appUserDTO);
    AppUser registerAppUser(AppUserDTO appUserDTO);
    /**
     * APP微信登录
@@ -41,7 +43,7 @@
     * @param body
     * @return
     */
    WXLoginUser wxLogin(AppWXLoginBody body);
    AppLoginUser wxLogin(AppWXLoginBody body);
    /**
     * 苹果登录
@@ -50,7 +52,7 @@
     * @param source
     * @return
     */
    AppleLoginUser appleLogin(String appleId);
    AppLoginUser appleLogin(String appleId);
    /**
     * 验证码登录
@@ -58,7 +60,7 @@
     * @param body
     * @return
     */
    LoginUser appCaptchaLogin(AppCaptchaBody body);
    AppLoginUser appCaptchaLogin(AppCaptchaBody body);
    /**
     * 微信苹果登录验证手机号码操作
@@ -66,5 +68,34 @@
     * @param body
     * @return
     */
    WXLoginUser verifyCellPhone(AppVerifyCellPhoneBody body);
    AppLoginUser verifyCellPhone(AppVerifyCellPhoneBody body);
    /**
     * 保存计划引导页用户的答案
     *
     * @param dto 用户计划引导答案数据传输对象
     */
    void saveUserAnswers(UserAnswerDTO dto);
    /**
     * 获取问题二的标签列表
     *
     * @return List<TagVO>
     */
    List<TagVO> getTagList();
    /**
     * 更新APP用户信息
     *
     * @param dto APP用户数据传输对象
     */
    void updateAppUser(AppUserDTO dto);
    /**
     * 获取当前登录用户信息
     *
     * @return 用户信息
     * @see com.xinquan.user.domain.vo.AppUserVO
     */
    AppUserVO getCurrentUser();
}
xinquan-modules/xinquan-user/src/main/java/com/xinquan/user/service/AppUserViewingHistoryService.java
New file
@@ -0,0 +1,23 @@
package com.xinquan.user.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.xinquan.user.domain.AppUserViewingHistory;
/**
 * <p>
 * 用户观看历史 服务类
 * </p>
 *
 * @author mitao
 * @since 2024-09-06
 */
public interface AppUserViewingHistoryService extends IService<AppUserViewingHistory> {
    /**
     * 记录用户观看记录
     *
     * @param bizId       业务id
     * @param viewingType 观看类型 1=疗愈 2=课程
     */
    void saveViewingRecord(Long bizId, Integer viewingType);
}
xinquan-modules/xinquan-user/src/main/java/com/xinquan/user/service/impl/AppUserServiceImpl.java
@@ -1,36 +1,48 @@
package com.xinquan.user.service.impl;
import cn.hutool.core.util.IdUtil;
import cn.hutool.crypto.SecureUtil;
import com.alibaba.nacos.common.utils.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xinquan.common.core.constant.Constants;
import com.xinquan.common.core.constant.SecurityConstants;
import com.xinquan.common.core.domain.R;
import com.xinquan.common.core.enums.AnswerTagEnum;
import com.xinquan.common.core.enums.AppUserStatusEnum;
import com.xinquan.common.core.enums.DisabledEnum;
import com.xinquan.common.core.enums.TreeLevelEnum;
import com.xinquan.common.core.exception.ServiceException;
import com.xinquan.common.core.utils.page.BeanUtils;
import com.xinquan.common.core.utils.page.CollUtils;
import com.xinquan.common.security.utils.SecurityUtils;
import com.xinquan.system.api.RemoteUserService;
import com.xinquan.system.api.domain.SysUser;
import com.xinquan.system.api.feignClient.SysUserClient;
import com.xinquan.system.api.model.AppCaptchaBody;
import com.xinquan.system.api.model.AppLoginUser;
import com.xinquan.system.api.model.AppVerifyCellPhoneBody;
import com.xinquan.system.api.model.AppWXLoginBody;
import com.xinquan.system.api.model.AppleLoginUser;
import com.xinquan.system.api.model.LoginUser;
import com.xinquan.system.api.model.WXLoginUser;
import com.xinquan.user.api.domain.AppUser;
import com.xinquan.user.api.domain.dto.AppUserDTO;
import com.xinquan.user.domain.AppUserQuestion;
import com.xinquan.user.domain.AppUserTree;
import com.xinquan.user.domain.Tag;
import com.xinquan.user.domain.dto.UserAnswerDTO;
import com.xinquan.user.domain.vo.AppUserVO;
import com.xinquan.user.domain.vo.TagVO;
import com.xinquan.user.mapper.AppUserMapper;
import com.xinquan.user.service.AppUserQuestionService;
import com.xinquan.user.service.AppUserService;
import com.xinquan.user.service.AppUserTreeService;
import com.xinquan.user.service.TagService;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@@ -50,6 +62,8 @@
    private final SysUserClient sysUserClient;
    private final RemoteUserService remoteUserService;
    private final AppUserTreeService appUserTreeService;
    private final AppUserQuestionService appUserQuestionService;
    private final TagService tagService;
    /**
     * 获取用户信息
     *
@@ -74,9 +88,10 @@
     * @return
     */
    @Override
    public Boolean registerAppUser(AppUserDTO appUserDTO) {
    public AppUser registerAppUser(AppUserDTO appUserDTO) {
        AppUser appUser = BeanUtils.copyBean(appUserDTO, AppUser.class);
        return this.save(appUser);
        this.save(appUser);
        return appUser;
    }
    /**
@@ -86,33 +101,34 @@
     * @return
     */
    @Override
    public WXLoginUser wxLogin(AppWXLoginBody body) {
        WXLoginUser wxLoginUser = new WXLoginUser();
    public AppLoginUser wxLogin(AppWXLoginBody body) {
        AppLoginUser appLoginUser = new AppLoginUser();
        Optional<AppUser> optionalAppUser = this.getUserByCondition(
                AppUserDTO.builder().wxOpenId(body.getWxOpenId()).build());
        AppUser appUser;
        SysUser sysUser;
        // 存在账户,查询关联系统用户
        if (optionalAppUser.isPresent()) {
            AppUser appUser = optionalAppUser.get();
            SysUser sysUser = sysUserClient.getSysUser(appUser.getUserId()).getData();
            wxLoginUser.setSysUser(sysUser);
            if (Objects.nonNull(sysUser.getUserName())) {
                wxLoginUser.setBindStatus(1);
                wxLoginUser.setCellPhone(appUser.getCellPhone());
            appUser = optionalAppUser.get();
            if (appUser.getUserStatus().equals(AppUserStatusEnum.FROZEN.getCode())) {
                throw new ServiceException("账号已被冻结");
            }
            sysUser = sysUserClient.getSysUser(appUser.getUserId()).getData();
        } else {
            // 不存在账户,创建账户
            // 注册用户信息
            SysUser sysUser = new SysUser();
            sysUser = new SysUser();
            sysUser.setNickName(body.getNickname());
            sysUser.setAvatar(body.getHeadImgUrl());
            sysUser.setUserName(body.getNickname());
            sysUser.setUserType("01"); // app用户
            R<SysUser> registerResult = remoteUserService.registerAppUserInfo(sysUser,
                    SecurityConstants.INNER);
            if (registerResult.getCode() == Constants.FAIL) {
                throw new ServiceException(registerResult.getMsg());
            }
            SysUser sysUserRes = registerResult.getData();
            AppUser appUser = new AppUser();
            appUser = new AppUser();
            appUser.setUserId(sysUserRes.getUserId());
            appUser.setWxOpenId(body.getWxOpenId());
            appUser.setNickname(body.getNickname());
@@ -128,41 +144,45 @@
            this.save(appUser);
            // 初始化用户树苗
            initUserTree(appUser.getId());
            wxLoginUser.setSysUser(null);
            wxLoginUser.setBindStatus(DisabledEnum.NO.getCode());
        }
        return wxLoginUser;
        appLoginUser.setUserid(sysUser.getUserId());
        appLoginUser.setUsername(appUser.getNickname());
        appLoginUser.setAppUserId(appUser.getId());
        appLoginUser.setSysUser(sysUser);
        if (Objects.nonNull(sysUser.getUserName())) {
            appLoginUser.setBindStatus(DisabledEnum.YES.getCode());
            appLoginUser.setCellPhone(appUser.getCellPhone());
        }
        return appLoginUser;
    }
    @Override
    public AppleLoginUser appleLogin(String appleId) {
        AppleLoginUser appleLoginUser = new AppleLoginUser();
    public AppLoginUser appleLogin(String appleId) {
        AppLoginUser appLoginUser = new AppLoginUser();
        Optional<AppUser> optionalAppUser = this.getUserByCondition(
                AppUserDTO.builder().appleId(appleId).build());
        SysUser sysUser;
        AppUser appUser;
        // 存在账户,查询关联系统用户
        if (optionalAppUser.isPresent()) {
            AppUser appUser = optionalAppUser.get();
            SysUser sysUser = sysUserClient.getSysUser(appUser.getUserId()).getData();
            appleLoginUser.setSysUser(sysUser);
            if (Objects.nonNull(sysUser.getUserName())) {
                appleLoginUser.setBindStatus(1);
                appleLoginUser.setCellPhone(appUser.getCellPhone());
            }
            appUser = optionalAppUser.get();
            sysUser = sysUserClient.getSysUser(appUser.getUserId()).getData();
        } else {
            // 不存在账户,创建账户
            // 注册用户信息
            SysUser sysUser = new SysUser();
            sysUser = new SysUser();
            String nickname = "用户" + IdUtil.fastSimpleUUID().substring(0, 6);
            sysUser.setNickName(nickname);
            sysUser.setAvatar("qwer");
            sysUser.setUserName(nickname);
            sysUser.setUserType("01"); // app用户
            R<SysUser> registerResult = remoteUserService.registerAppUserInfo(sysUser,
                    SecurityConstants.INNER);
            if (registerResult.getCode() == Constants.FAIL) {
                throw new ServiceException(registerResult.getMsg());
            }
            SysUser sysUserRes = registerResult.getData();
            AppUser appUser = new AppUser();
            appUser = new AppUser();
            appUser.setUserId(sysUserRes.getUserId());
            appUser.setNickname(nickname);
            appUser.setAvatar("qwer"); // TODO 待完善默认头像
@@ -176,11 +196,16 @@
            this.save(appUser);
            // 初始化用户树苗
            initUserTree(appUser.getId());
            appleLoginUser.setSysUser(null);
            appleLoginUser.setBindStatus(DisabledEnum.NO.getCode());
        }
        return appleLoginUser;
        appLoginUser.setUserid(sysUser.getUserId());
        appLoginUser.setUsername(appUser.getNickname());
        appLoginUser.setAppUserId(appUser.getId());
        appLoginUser.setSysUser(sysUser);
        if (Objects.nonNull(sysUser.getUserName())) {
            appLoginUser.setBindStatus(DisabledEnum.YES.getCode());
            appLoginUser.setCellPhone(appUser.getCellPhone());
        }
        return appLoginUser;
    }
    /**
@@ -191,21 +216,23 @@
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public LoginUser appCaptchaLogin(AppCaptchaBody body) {
        LoginUser loginUser = new LoginUser();
    public AppLoginUser appCaptchaLogin(AppCaptchaBody body) {
        AppLoginUser appLoginUser = new AppLoginUser();
        Optional<AppUser> optionalAppUser = this.getUserByCondition(
                AppUserDTO.builder().cellPhone(body.getCellPhone()).build());
        AppUser appUser;
        SysUser sysUser;
        if (optionalAppUser.isPresent()) {
            AppUser appUser = optionalAppUser.get();
            SysUser sysUser = sysUserClient.getSysUser(appUser.getUserId()).getData();
            loginUser.setSysUser(sysUser);
            appUser = optionalAppUser.get();
            sysUser = sysUserClient.getSysUser(appUser.getUserId()).getData();
        } else {
            // 注册用户信息
            SysUser sysUser = new SysUser();
            sysUser = new SysUser();
            String nickname = "用户" + IdUtil.fastSimpleUUID().substring(0, 6);
            sysUser.setNickName(nickname);
            sysUser.setAvatar("qwer");
            sysUser.setUserName(body.getCellPhone());
            sysUser.setUserType("01"); // app用户
            // 设置默认密码123456
            sysUser.setPassword(SecurityUtils.encryptPassword(Constants.DEFAULT_PASSWORD));
@@ -215,7 +242,7 @@
                throw new ServiceException(registerResult.getMsg());
            }
            SysUser sysUserRes = registerResult.getData();
            AppUser appUser = new AppUser();
            appUser = new AppUser();
            appUser.setUserId(sysUserRes.getUserId());
            appUser.setNickname(nickname);
            appUser.setCellPhone(body.getCellPhone());
@@ -230,8 +257,14 @@
            this.save(appUser);
            // 初始化用户树苗
            initUserTree(appUser.getId());
        }
        return loginUser;
        appLoginUser.setUserid(sysUser.getUserId());
        appLoginUser.setUsername(appUser.getNickname());
        appLoginUser.setAppUserId(appUser.getId());
        appLoginUser.setSysUser(sysUser);
        appLoginUser.setCellPhone(appUser.getCellPhone());
        return appLoginUser;
    }
    /**
@@ -256,7 +289,7 @@
     * @return
     */
    @Override
    public WXLoginUser verifyCellPhone(AppVerifyCellPhoneBody body) {
    public AppLoginUser verifyCellPhone(AppVerifyCellPhoneBody body) {
        Integer loginType = body.getLoginType();
        String wxOrAppleId = body.getWxOrAppleId();
        SysUser sysUser;
@@ -296,29 +329,21 @@
            }
            sysUser.setUserName(body.getCellPhone());
            sysUser.setPassword(
                    SecurityUtils.encryptPassword(Constants.DEFAULT_PASSWORD)); // 默认密码为123456
                    SecurityUtils.encryptPassword(
                            SecureUtil.md5(Constants.DEFAULT_PASSWORD).toUpperCase(
                                    Locale.ROOT)))
            ; // 默认密码为123456
            sysUser.setPhonenumber(body.getCellPhone());
            sysUserClient.updateSysUser(sysUser);
        }
        // 更新用户信息
        this.updateById(appUser);
        if (loginType.equals(1)) {
            // 微信登录
            WXLoginUser wxLoginUser = new WXLoginUser();
            wxLoginUser.setBindStatus(1);
            wxLoginUser.setCellPhone(body.getCellPhone());
            wxLoginUser.setSysUser(sysUser);
            return wxLoginUser;
        } else {
            // 苹果登录
            AppleLoginUser appleLoginUser = new AppleLoginUser();
            appleLoginUser.setBindStatus(1);
            appleLoginUser.setCellPhone(body.getCellPhone());
            appleLoginUser.setSysUser(sysUser);
            return appleLoginUser;
        }
        AppLoginUser appLoginUser = new AppLoginUser();
        appLoginUser.setBindStatus(1);
        appLoginUser.setCellPhone(body.getCellPhone());
        appLoginUser.setSysUser(sysUser);
        return appLoginUser;
    }
    // 定义一个方法来处理用户不存在的情况
    private void handleUserNotPresent(Optional<AppUser> userOptional, String errorMessage) {
        if (!userOptional.isPresent()) {
@@ -337,7 +362,104 @@
        return this.lambdaQuery()
                .eq(loginType.equals(1), AppUser::getWxOpenId, wxOrAppleId)
                .eq(loginType.equals(2), AppUser::getAppleId, wxOrAppleId)
                .ne(AppUser::getUserStatus, AppUserStatusEnum.LOGOUT)
                .ne(AppUser::getUserStatus, AppUserStatusEnum.LOGOUT.getCode())
                .oneOpt();
    }
    /**
     * 保存计划引导页用户的答案
     *
     * @param dto 用户计划引导答案数据传输对象
     */
    @Override
    public void saveUserAnswers(UserAnswerDTO dto) {
        // 获取当前登录用户
        Long userId = SecurityUtils.getUserId();
        AppUser appUser = this.getUserBySysUserId(userId);
        List<AppUserQuestion> appUserQuestionList = BeanUtils.copyList(
                dto.getUserAnswerOneDTOList(), AppUserQuestion.class);
        // 查询用户已填写的答案
        List<AppUserQuestion> baseAppUserQuestionList = appUserQuestionService.lambdaQuery()
                .eq(AppUserQuestion::getAppUserId, appUser.getUserId())
                .list();
        if (CollUtils.isNotEmpty(baseAppUserQuestionList)) {
            // 根据类型更新答案
            for (AppUserQuestion appUserQuestion : appUserQuestionList) {
                Optional<AppUserQuestion> baseAppUserQuestionOptional = baseAppUserQuestionList.stream()
                        .filter(baseAppUserQuestion -> baseAppUserQuestion.getType()
                                .equals(appUserQuestion.getType())).findFirst();
                baseAppUserQuestionOptional.ifPresent(
                        userQuestion -> appUserQuestion.setId(userQuestion.getId()));
            }
        }
        appUserQuestionService.saveOrUpdateBatch(appUserQuestionList);
        // 筛选答案为是和偶尔有的问题,为用户设置对应的标签
        List<AppUserQuestion> appUserQuestionYesList = appUserQuestionList.stream()
                .filter(appUserQuestion -> appUserQuestion.getAnswer().equals("1")
                        || appUserQuestion.getAnswer().equals("3")).collect(Collectors.toList());
        StringBuilder sb = new StringBuilder();
        // 使用 StringBuilder 提高字符串拼接效率
        if (CollUtils.isNotEmpty(appUserQuestionYesList)) {
            List<Long> tagIdList = appUserQuestionYesList.stream()
                    .map(appUserQuestion -> AnswerTagEnum.getEnumByCode(appUserQuestion.getType())
                            .getTagId()).collect(Collectors.toList());
            if (CollUtils.isNotEmpty(tagIdList)) {
                sb.append(CollUtils.join(tagIdList, ","));
            }
        }
        sb.append(",");
        sb.append(dto.getTagIds());
        appUser.setTagId(sb.toString());
        this.updateById(appUser);
    }
    /**
     * 获取AppUser
     *
     * @param userId 系统用户userId
     * @return AppUser
     */
    private AppUser getUserBySysUserId(Long userId) {
        Optional<AppUser> appUserOptional = this.lambdaQuery().eq(AppUser::getUserId, userId)
                .oneOpt();
        if (!appUserOptional.isPresent()) {
            throw new ServiceException("获取用户信息失败,请重新登录");
        }
        return appUserOptional.get();
    }
    /**
     * 获取问题二的标签列表
     *
     * @return List<TagVO>
     */
    @Override
    public List<TagVO> getTagList() {
        List<Tag> list = tagService.list(Wrappers.lambdaQuery(Tag.class).eq(Tag::getTagType, 2));
        return BeanUtils.copyList(list, TagVO.class);
    }
    /**
     * 更新APP用户信息
     *
     * @param dto APP用户数据传输对象
     */
    @Override
    public void updateAppUser(AppUserDTO dto) {
        // 拷贝数据
        AppUser appUser = BeanUtils.copyBean(dto, AppUser.class);
        this.updateById(appUser);
    }
    /**
     * 获取当前登录用户信息
     *
     * @return 用户信息
     * @see com.xinquan.user.domain.vo.AppUserVO
     */
    @Override
    public AppUserVO getCurrentUser() {
        AppUser appUser = this.getById(SecurityUtils.getUserId());
        return BeanUtils.copyBean(appUser, AppUserVO.class);
    }
}
xinquan-modules/xinquan-user/src/main/java/com/xinquan/user/service/impl/AppUserViewingHistoryServiceImpl.java
New file
@@ -0,0 +1,35 @@
package com.xinquan.user.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xinquan.user.domain.AppUserViewingHistory;
import com.xinquan.user.mapper.AppUserViewingHistoryMapper;
import com.xinquan.user.service.AppUserViewingHistoryService;
import org.springframework.stereotype.Service;
/**
 * <p>
 * 用户观看历史 服务实现类
 * </p>
 *
 * @author mitao
 * @since 2024-09-06
 */
@Service
public class AppUserViewingHistoryServiceImpl extends
        ServiceImpl<AppUserViewingHistoryMapper, AppUserViewingHistory> implements
        AppUserViewingHistoryService {
    /**
     * 记录用户观看记录
     *
     * @param bizId       业务id
     * @param viewingType 观看类型 1=疗愈 2=课程
     */
    @Override
    public void saveViewingRecord(Long bizId, Integer viewingType) {
        AppUserViewingHistory appUserViewingHistory = new AppUserViewingHistory();
        appUserViewingHistory.setBizId(bizId);
        appUserViewingHistory.setViewingType(viewingType);
        this.save(appUserViewingHistory);
    }
}
xinquan-modules/xinquan-user/src/main/resources/mapper/user/AppUserMapper.xml
@@ -38,6 +38,7 @@
        <result column="register_time" property="registerTime" />
        <result column="logout_time" property="logoutTime" />
        <result column="level_setting_id" property="levelSettingId" />
        <result column="tag_id" property="tagId"/>
    </resultMap>
    <!-- 通用查询结果列 -->
@@ -47,7 +48,7 @@
        create_time,
        update_by,
        update_time,
        id, user_id, cell_phone, apple_id, open_id, avatar, nickname, signature, gender, birthday, education, industry, company, occupation, location, hometown, email, vip_expire_time, user_status, freezing_reason, freezing_operator, sanskrit_flag, invite_user_id, balance, total_energy_value, register_time, logout_time, level_setting_id
        id, user_id, cell_phone, apple_id, open_id, avatar, nickname, signature, gender, birthday, education, industry, company, occupation, location, hometown, email, vip_expire_time, user_status, freezing_reason, freezing_operator, sanskrit_flag, invite_user_id, balance, total_energy_value, register_time, logout_time, level_setting_id,tag_id
    </sql>
</mapper>
xinquan-modules/xinquan-user/src/main/resources/mapper/user/AppUserViewingHistoryMapper.xml
File was renamed from xinquan-modules/xinquan-meditation/src/main/resources/mapper/meditation/MeditationUserOperationMapper.xml
@@ -1,19 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.xinquan.meditation.mapper.MeditationUserOperationMapper">
<mapper namespace="com.xinquan.user.mapper.AppUserViewingHistoryMapper">
    <!-- 通用查询映射结果 -->
    <resultMap id="BaseResultMap" type="com.xinquan.meditation.domain.MeditationUserOperation">
    <resultMap id="BaseResultMap" type="com.xinquan.user.domain.AppUserViewingHistory">
        <id column="id" property="id" />
        <result column="del_flag" property="delFlag" />
        <result column="create_by" property="createBy" />
        <result column="create_time" property="createTime" />
        <result column="update_by" property="updateBy" />
        <result column="update_time" property="updateTime" />
        <result column="like" property="like" />
        <result column="favorite" property="favorite" />
        <result column="meditation_id" property="meditationId" />
        <result column="app_user_id" property="appUserId" />
        <result column="biz_id" property="bizId" />
        <result column="viewing_type" property="viewingType" />
    </resultMap>
    <!-- 通用查询结果列 -->
@@ -23,7 +22,7 @@
        create_time,
        update_by,
        update_time,
        id, like, favorite, meditation_id, app_user_id
        id, app_user_id, biz_id, viewing_type
    </sql>
</mapper>
xinquan-modules/xinquan-user/src/main/resources/mapper/user/TagMapper.xml
@@ -5,6 +5,7 @@
    <!-- 通用查询映射结果 -->
    <resultMap id="BaseResultMap" type="com.xinquan.user.domain.Tag">
        <id column="id" property="id" />
        <id column="tag_type" property="tagType"/>
        <result column="del_flag" property="delFlag" />
        <result column="create_by" property="createBy" />
        <result column="create_time" property="createTime" />
@@ -20,7 +21,7 @@
        create_time,
        update_by,
        update_time,
        id, tag_name
        id, tag_name,tag_type
    </sql>
</mapper>