From cfc8dd9e5d557324f814093f6d523fa8b16a8b73 Mon Sep 17 00:00:00 2001 From: laixingyu Date: Tue, 18 Jul 2023 22:14:52 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=91=B8=E9=87=91=E9=A3=8E?= =?UTF-8?q?=E5=90=91=E6=A0=87=E5=90=8E=E7=AB=AF=E7=B3=BB=E7=BB=9F=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 48 + LICENSE | 20 + README.md | 0 pom.xml | 225 ++ ssdmn-chat-gpt-api/pom.xml | 227 ++ .../src/main/java/com/ssdmn/WebMain.java | 19 + .../com/ssdmn/aliyun/AliYunProperties.java | 119 + .../java/com/ssdmn/aliyun/AliYunService.java | 259 ++ .../main/java/com/ssdmn/aliyun/ImageInfo.java | 21 + .../com/ssdmn/aliyun/OssPolicyResult.java | 46 + .../mapper/ChatModelConfigMapper.java | 18 + .../mapper/ChatModelConfigMapper.xml | 20 + .../pojo/domain/ChatModelConfig.java | 50 + .../service/ChatModelConfigService.java | 13 + .../impl/ChatModelConfigServiceImpl.java | 22 + .../ssdmn/biz/config/constants/Constants.java | 15 + .../biz/config/mapper/SysConfigMapper.java | 18 + .../biz/config/mapper/SysConfigMapper.xml | 26 + .../biz/config/pojo/domain/SysConfig.java | 72 + .../biz/config/service/SysConfigService.java | 17 + .../service/impl/SysConfigServiceImpl.java | 37 + .../com/ssdmn/biz/contants/Constants.java | 2832 +++++++++++++++++ .../com/ssdmn/biz/contants/HttpStatus.java | 89 + .../ssdmn/biz/contants/ScheduleConstants.java | 56 + .../com/ssdmn/biz/contants/SysConstants.java | 100 + .../com/ssdmn/biz/contants/UserContants.java | 20 + .../biz/file/Base64DecodedMultipartFile.java | 59 + .../com/ssdmn/biz/file/FileProperties.java | 27 + .../biz/file/mapper/FileManageMapper.java | 15 + .../biz/file/pojo/entity/FileManage.java | 61 + .../ssdmn/biz/file/pojo/vo/UploadUrlVO.java | 25 + .../biz/file/service/FileManageService.java | 54 + .../biz/file/service/impl/FileManageImpl.java | 324 ++ .../gupiao/controller/ImportController.java | 179 ++ .../controller/MyCollectController.java | 66 + .../gupiao/controller/StockController.java | 153 + .../TransactionRecordController.java | 105 + .../DongCaiSecondaryIndustryListener.java | 87 + .../HighLowOfThreeHundredListener.java | 72 + .../listener/OriginalIssueStockListener.java | 99 + .../listener/TransactionRecordListener.java | 112 + .../DongCaiSecondaryIndustryMapper.java | 33 + .../mapper/DongCaiSecondaryIndustryMapper.xml | 47 + .../mapper/HighLowOfThreeHundredMapper.java | 9 + .../biz/gupiao/mapper/MyCollectMapper.java | 38 + .../biz/gupiao/mapper/MyCollectMapper.xml | 88 + .../mapper/OriginalIssueStockMapper.java | 35 + .../mapper/OriginalIssueStockMapper.xml | 76 + .../biz/gupiao/mapper/StockNameMapper.java | 9 + .../mapper/TransactionRecordMapper.java | 9 + .../mapper/TransactionRecordTempMapper.java | 20 + .../mapper/TransactionRecordTempMapper.xml | 25 + .../pojo/domain/DongCaiSecondaryIndustry.java | 132 + .../pojo/domain/HighLowOfThreeHundred.java | 95 + .../biz/gupiao/pojo/domain/MyCollect.java | 41 + .../pojo/domain/OriginalIssueStock.java | 133 + .../biz/gupiao/pojo/domain/StockName.java | 56 + .../gupiao/pojo/domain/TransactionRecord.java | 69 + .../pojo/domain/TransactionRecordTemp.java | 60 + .../pojo/request/BigStockDataRequest.java | 18 + .../pojo/request/BrokenLineRequest.java | 21 + .../request/DongCaiRiseLossesRequest.java | 39 + .../DongCaiSecondaryIndustryRequest.java | 31 + .../pojo/request/EarningsLineRequest.java | 39 + .../request/HighLowOfThreeHundredRequest.java | 14 + .../biz/gupiao/pojo/request/KLineRequest.java | 19 + .../gupiao/pojo/request/MyCollectRequest.java | 20 + .../request/OriginalIssueStockDetail.java | 33 + .../request/OriginalIssueStockDetailPage.java | 40 + .../request/OriginalIssueStockRequest.java | 33 + .../OriginalIssueStockRequestPage.java | 46 + .../pojo/request/ProfitAndLossRequest.java | 46 + .../pojo/request/QueryIsCollectRequest.java | 23 + .../request/QueryMyCollectPageRequest.java | 40 + .../pojo/request/SectorTrendsDataRequest.java | 20 + .../pojo/request/TransactionRecordImport.java | 52 + .../request/TransactionRecordPageRequest.java | 48 + .../gupiao/pojo/response/AppBigDetailVO.java | 40 + .../pojo/response/AppRiseLossesResponse.java | 19 + .../response/AppRiseLossesStatistics.java | 31 + .../response/BigStockDataDetailResponse.java | 75 + .../BigStockDataDetailResponseBak.java | 75 + .../pojo/response/BigStockDataResponse.java | 31 + .../biz/gupiao/pojo/response/DayAndPlate.java | 23 + .../DongCaiSecondaryIndustryResponse.java | 60 + .../pojo/response/EarningsLineResponse.java | 25 + .../pojo/response/MyCollectPageResponse.java | 26 + .../OriginalIssueStockDetailResponse.java | 18 + .../response/OriginalIssueStockResponse.java | 44 + .../OriginalIssueStockSortResponseChange.java | 19 + .../gupiao/pojo/response/QueryBuyOrSell.java | 42 + .../response/SectorTrendsDataResponse.java | 30 + .../pojo/response/StockKLineResponse.java | 22 + .../gupiao/pojo/response/StockKLineVO.java | 41 + .../pojo/response/TotalEarningsResponse.java | 22 + .../DongCaiSecondaryIndustryService.java | 60 + .../service/HighLowOfThreeHundredService.java | 16 + .../biz/gupiao/service/MyCollectService.java | 32 + .../service/OriginalIssueStockService.java | 119 + .../biz/gupiao/service/StockNameService.java | 9 + .../service/TransactionRecordService.java | 70 + .../service/TransactionRecordTempService.java | 17 + .../DongCaiSecondaryIndustryServiceImpl.java | 394 +++ .../HighLowOfThreeHundredServiceImpl.java | 43 + .../service/impl/MyCollectServiceImpl.java | 176 + .../impl/OriginalIssueStockServiceImpl.java | 564 ++++ .../service/impl/StockNameServiceImpl.java | 24 + .../impl/TransactionRecordServiceImpl.java | 280 ++ .../TransactionRecordTempServiceImpl.java | 120 + .../ssdmn/biz/login/constants/Constants.java | 43 + .../biz/login/controller/LoginController.java | 98 + .../biz/login/controller/SmsController.java | 71 + .../biz/login/enums/sms/SmsRedisEnum.java | 74 + .../login/enums/sms/SmsTemplateCodeEnum.java | 81 + .../biz/login/enums/sms/SmsTemplateEnum.java | 75 + .../biz/login/exception/BaseException.java | 98 + .../biz/login/exception/BizException.java | 86 + .../login/exception/CommissionException.java | 86 + .../login/exception/FileUploadException.java | 85 + .../exception/PartnerIntegralException.java | 86 + .../biz/login/exception/TaskException.java | 34 + .../user/UserBindLoginException.java | 86 + .../exception/user/UserLoginException.java | 81 + .../ssdmn/biz/login/mapper/SUserMapper.java | 10 + .../biz/login/mapper/UserLoginMapper.java | 10 + .../login/mapper/UserLoginRecordMapper.java | 15 + .../biz/login/mapper/UserLoginWayMapper.java | 13 + .../biz/login/pojo/dto/BasePageRequest.java | 26 + .../pojo/dto/ChatRecordDetailRequest.java | 26 + .../biz/login/pojo/dto/DatePageRequest.java | 27 + .../login/pojo/dto/FillUserInfoRequest.java | 59 + .../biz/login/pojo/dto/LoginBaseDTO.java | 26 + .../biz/login/pojo/dto/LoginRequest.java | 21 + .../login/pojo/dto/MiniAppFirstPhoneDTO.java | 43 + .../login/pojo/dto/MiniAppSecondPhoneDTO.java | 31 + .../pojo/dto/MiniAppUserUserInfoDTO.java | 39 + .../login/pojo/dto/PageChatRecordRequest.java | 13 + .../biz/login/pojo/dto/SendSmsRequest.java | 15 + .../ssdmn/biz/login/pojo/dto/SmsCodeDTO.java | 26 + .../biz/login/pojo/dto/UserLoginDTO.java | 53 + .../login/pojo/dto/UserSmsCodeLoginDTO.java | 23 + .../com/ssdmn/biz/login/pojo/entity/User.java | 72 + .../biz/login/pojo/entity/UserLogin.java | 61 + .../login/pojo/entity/UserLoginRecord.java | 41 + .../biz/login/pojo/entity/UserLoginWay.java | 38 + .../ssdmn/biz/login/pojo/vo/BaseUserVO.java | 47 + .../ssdmn/biz/login/pojo/vo/HomeInfoVO.java | 20 + .../com/ssdmn/biz/login/pojo/vo/LoginVO.java | 14 + .../login/pojo/vo/PageUserAndPartnerVO.java | 81 + .../ssdmn/biz/login/pojo/vo/UserCouponVO.java | 32 + .../biz/login/pojo/vo/UserIntegralVO.java | 26 + .../biz/login/pojo/vo/UserInviteInfo.java | 11 + .../ssdmn/biz/login/pojo/vo/UserLoginVO.java | 50 + .../ssdmn/biz/login/pojo/vo/UserPartVO.java | 28 + .../ssdmn/biz/login/pojo/vo/UserRedisVO.java | 18 + .../com/ssdmn/biz/login/pojo/vo/UserVO.java | 21 + .../ssdmn/biz/login/pojo/vo/UserWxInfoVO.java | 32 + .../biz/login/pojo/vo/WxOAuth2UserInfoVO.java | 51 + .../ssdmn/biz/login/service/LoginService.java | 52 + .../ssdmn/biz/login/service/SUserService.java | 40 + .../login/service/impl/LoginServiceImpl.java | 372 +++ .../login/service/impl/SUserServiceImpl.java | 369 +++ .../service/impl/UserLoginRecordService.java | 37 + .../biz/tool/controller/ToolController.java | 34 + .../ssdmn/biz/tool/mapper/GptToolMapper.java | 18 + .../ssdmn/biz/tool/mapper/GptToolMapper.xml | 24 + .../ssdmn/biz/tool/pojo/domain/GptTool.java | 74 + .../biz/tool/service/GptToolService.java | 17 + .../tool/service/impl/GptToolServiceImpl.java | 34 + .../biz/user/mapper/UserOperLogMapper.java | 18 + .../biz/user/mapper/UserOperLogMapper.xml | 39 + .../biz/user/pojo/domain/UserOperLog.java | 123 + .../biz/user/service/UserOperLogService.java | 13 + .../service/impl/UserOperLogServiceImpl.java | 22 + .../boot/SpringBootApplication.java | 18 + .../combination/PathRestController.java | 27 + .../swagger/SwaggerFiledExplain.java | 15 + .../common/annotation/valid/EnumType.java | 56 + .../ssdmn/common/annotation/valid/Phone.java | 53 + .../common/annotation/valid/SmsCode.java | 61 + .../ssdmn/common/annotation/valid/Url.java | 44 + .../com/ssdmn/common/contants/Constants.java | 96 + .../com/ssdmn/common/contants/HttpStatus.java | 89 + .../common/contants/ScheduleConstants.java | 56 + .../common/contants/SpringConstants.java | 24 + .../com/ssdmn/common/date/DateConverter.java | 27 + .../common/date/DateConverterConfig.java | 74 + .../com/ssdmn/common/date/DateFactory.java | 98 + .../java/com/ssdmn/common/date/DateUtil.java | 106 + .../ssdmn/common/date/LocalDateConverter.java | 24 + .../LocalDateDeserializationConverter.java | 22 + .../date/LocalDateSerializationConverter.java | 22 + .../common/date/LocalDateTimeConverter.java | 23 + ...LocalDateTimeDeserializationConverter.java | 26 + .../LocalDateTimeSerializationConverter.java | 22 + .../ssdmn/common/date/LocalTimeConverter.java | 23 + .../LocalTimeDeserializationConverter.java | 22 + .../date/LocalTimeSerializationConverter.java | 22 + .../ssdmn/common/exception/BizException.java | 86 + .../com/ssdmn/common/html/EscapeUtil.java | 156 + .../com/ssdmn/common/html/HTMLFilter.java | 566 ++++ .../com/ssdmn/common/http/HttpHelper.java | 56 + .../java/com/ssdmn/common/http/HttpUtils.java | 265 ++ .../interceptor/AuthUserHandlerResolver.java | 74 + .../interceptor/RepeatSubmitInterceptor.java | 43 + .../ssdmn/common/interceptor/UserCache.java | 15 + .../interceptor/annotation/AuthUser.java | 17 + .../interceptor/annotation/RepeatSubmit.java | 26 + .../impl/SameUrlDataInterceptor.java | 111 + .../com/ssdmn/common/ip/AddressUtils.java | 43 + .../java/com/ssdmn/common/ip/IpUtils.java | 173 + .../java/com/ssdmn/common/jwt/JWTUtil.java | 49 + .../java/com/ssdmn/common/lang/Assert.java | 66 + .../com/ssdmn/common/page/Comparison.java | 43 + .../java/com/ssdmn/common/page/PageList.java | 39 + .../java/com/ssdmn/common/page/PageModel.java | 58 + .../java/com/ssdmn/common/page/PageUtils.java | 68 + .../com/ssdmn/common/page/QueryField.java | 18 + .../java/com/ssdmn/common/page/Querys.java | 300 ++ .../common/qrCode/service/QRCodeService.java | 48 + .../service/impl/QRCodeServiceImpl.java | 74 + .../common/redis/annotation/RedisLock.java | 26 + .../common/redis/aop/RedisLockAspect.java | 150 + .../common/redis/service/RedisConfig.java | 130 + .../common/redis/service/RedisService.java | 693 ++++ .../enums/BlacklistOrWhiteListEnum.java | 6 + .../restrict/enums/RestrictTypeEnum.java | 6 + .../interceptor/RestrictInterceptor.java | 57 + .../interceptor/annotation/Restrict.java | 23 + .../restrict/strategy/RestrictStrategy.java | 10 + .../impl/RepeatSubmitRestrictStrategy.java | 97 + .../ssdmn/common/result/ResponseResult.java | 56 + .../java/com/ssdmn/common/result/Result.java | 90 + .../ssdmn/common/result/ResultConstants.java | 377 +++ .../java/com/ssdmn/common/result/Results.java | 159 + .../com/ssdmn/common/result/SysConstants.java | 100 + .../ssdmn/common/sql/MyMetaObjectHandler.java | 44 + .../com/ssdmn/common/sql/SqlInterceptor.java | 96 + .../java/com/ssdmn/common/str/Convert.java | 1008 ++++++ .../com/ssdmn/common/str/StrFormatter.java | 90 + .../com/ssdmn/common/str/StringUtils.java | 577 ++++ .../com/ssdmn/common/utils/Base64Utils.java | 59 + .../java/com/ssdmn/common/utils/BigNum.java | 587 ++++ .../ssdmn/common/utils/CronPatternUtil.java | 38 + .../java/com/ssdmn/common/utils/CronUtil.java | 10 + .../com/ssdmn/common/utils/FileTypeUtil.java | 262 ++ .../java/com/ssdmn/common/utils/FileUtil.java | 282 ++ .../com/ssdmn/common/utils/MapperUtil.java | 24 + .../com/ssdmn/common/utils/MessageUtils.java | 25 + .../com/ssdmn/common/utils/RandomUtils.java | 157 + .../com/ssdmn/common/utils/ServletUtil.java | 154 + .../com/ssdmn/common/utils/SpringUtil.java | 24 + .../com/ssdmn/common/utils/SpringUtils.java | 146 + .../com/ssdmn/common/utils/StringUtil.java | 138 + .../com/ssdmn/common/utils/UserAgentUtil.java | 20 + .../com/ssdmn/common/utils/UserRedis.java | 99 + .../ssdmn/config/GlobalExceptionHandler.java | 139 + .../com/ssdmn/config/MybatisPlusConfig.java | 64 + .../com/ssdmn/config/ParamConvertConfig.java | 25 + .../com/ssdmn/config/ParamsInterceptor.java | 79 + .../java/com/ssdmn/config/SwaggerConfig.java | 85 + .../com/ssdmn/config/SwaggerShowMonitor.java | 111 + .../java/com/ssdmn/config/WebConfigurer.java | 75 + .../config/banary/WxMpConfiguration.java | 112 + .../ssdmn/config/banary/WxMpProperties.java | 59 + .../config/filter/ActivityInfoFilter.java | 93 + .../com/ssdmn/config/filter/FilterConfig.java | 108 + .../ssdmn/config/filter/RepeatableFilter.java | 49 + .../filter/RepeatedlyRequestWrapper.java | 77 + .../com/ssdmn/config/filter/XssFilter.java | 72 + .../filter/XssHttpServletRequestWrapper.java | 112 + .../ssdmn/framework/handler/HandlerUtil.java | 42 + .../DefaultHandlerInterceptor.java | 14 + .../framework/handler/interfaces/Count.java | 6 + .../framework/handler/interfaces/Getter.java | 16 + .../framework/handler/interfaces/Handler.java | 14 + .../framework/handler/interfaces/List.java | 15 + .../framework/handler/interfaces/Page.java | 15 + .../framework/handler/interfaces/Sum.java | 6 + .../framework/handler/interfaces/Valid.java | 15 + .../ssdmn/system/annatation/Violation.java | 50 + .../com/ssdmn/system/aop/UserOperAspect.java | 83 + .../java/com/ssdmn/system/bean/Message.java | 46 + .../component/captcha/CaptchaProperties.java | 20 + .../component/captcha/NECaptchaVerifier.java | 93 + .../system/component/sensitive/Sensitive.java | 34 + .../sensitive/SensitiveJsonSerializer.java | 41 + .../sensitive/SensitiveStrategies.java | 52 + .../sensitive/SensitiveStrategy.java | 14 + .../strategy/impl/AddressStrategy.java | 16 + .../strategy/impl/BankCardStrategy.java | 16 + .../strategy/impl/EmailStrategy.java | 18 + .../strategy/impl/IdCardStrategy.java | 18 + .../strategy/impl/PhoneStrategy.java | 17 + .../valid/BadWordValidComponent.java | 51 + .../com/ssdmn/system/contants/Constant.java | 12 + .../com/ssdmn/system/enums/MessageType.java | 4 + .../ssdmn/system/handler/MessageHandler.java | 7 + .../ssdmn/system/manager/AsyncFactory.java | 142 + .../ssdmn/system/manager/AsyncManager.java | 52 + .../ssdmn/system/manager/ShutdownManager.java | 33 + .../system/manager/ThreadPoolConfig.java | 66 + .../com/ssdmn/system/manager/Threads.java | 71 + .../ssdmn/system/mapper/IgnoreBaseMapper.java | 83 + .../pojo/dto/AddRestrictInfoRequest.java | 37 + .../impl/CaptchaRestrictStrategy.java | 42 + .../strategy/impl/IpRestrictStrategy.java | 24 + .../strategy/impl/PhoneRestrictStrategy.java | 20 + .../strategy/impl/SignRestrictStrategy.java | 70 + .../ssdmn/web/pojo/dto/login/LoginDTO.java | 28 + .../web/pojo/dto/login/WxLoginRedisDTO.java | 16 + ...itional-spring-configuration-metadata.json | 8 + .../src/main/resources/application-dev.yml | 111 + .../src/main/resources/application-prod.yml | 104 + .../src/main/resources/application-test.yml | 104 + .../src/main/resources/application.yml | 98 + .../src/main/resources/ck/1.txt | 0 .../src/main/resources/logback-spring.xml | 84 + 318 files changed, 25892 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 README.md create mode 100644 pom.xml create mode 100644 ssdmn-chat-gpt-api/pom.xml create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/WebMain.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/aliyun/AliYunProperties.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/aliyun/AliYunService.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/aliyun/ImageInfo.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/aliyun/OssPolicyResult.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/chatmodel/mapper/ChatModelConfigMapper.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/chatmodel/mapper/ChatModelConfigMapper.xml create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/chatmodel/pojo/domain/ChatModelConfig.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/chatmodel/service/ChatModelConfigService.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/chatmodel/service/impl/ChatModelConfigServiceImpl.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/config/constants/Constants.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/config/mapper/SysConfigMapper.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/config/mapper/SysConfigMapper.xml create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/config/pojo/domain/SysConfig.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/config/service/SysConfigService.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/config/service/impl/SysConfigServiceImpl.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/contants/Constants.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/contants/HttpStatus.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/contants/ScheduleConstants.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/contants/SysConstants.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/contants/UserContants.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/file/Base64DecodedMultipartFile.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/file/FileProperties.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/file/mapper/FileManageMapper.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/file/pojo/entity/FileManage.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/file/pojo/vo/UploadUrlVO.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/file/service/FileManageService.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/file/service/impl/FileManageImpl.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/controller/ImportController.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/controller/MyCollectController.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/controller/StockController.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/controller/TransactionRecordController.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/listener/DongCaiSecondaryIndustryListener.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/listener/HighLowOfThreeHundredListener.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/listener/OriginalIssueStockListener.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/listener/TransactionRecordListener.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/mapper/DongCaiSecondaryIndustryMapper.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/mapper/DongCaiSecondaryIndustryMapper.xml create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/mapper/HighLowOfThreeHundredMapper.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/mapper/MyCollectMapper.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/mapper/MyCollectMapper.xml create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/mapper/OriginalIssueStockMapper.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/mapper/OriginalIssueStockMapper.xml create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/mapper/StockNameMapper.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/mapper/TransactionRecordMapper.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/mapper/TransactionRecordTempMapper.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/mapper/TransactionRecordTempMapper.xml create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/domain/DongCaiSecondaryIndustry.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/domain/HighLowOfThreeHundred.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/domain/MyCollect.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/domain/OriginalIssueStock.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/domain/StockName.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/domain/TransactionRecord.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/domain/TransactionRecordTemp.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/request/BigStockDataRequest.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/request/BrokenLineRequest.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/request/DongCaiRiseLossesRequest.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/request/DongCaiSecondaryIndustryRequest.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/request/EarningsLineRequest.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/request/HighLowOfThreeHundredRequest.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/request/KLineRequest.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/request/MyCollectRequest.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/request/OriginalIssueStockDetail.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/request/OriginalIssueStockDetailPage.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/request/OriginalIssueStockRequest.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/request/OriginalIssueStockRequestPage.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/request/ProfitAndLossRequest.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/request/QueryIsCollectRequest.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/request/QueryMyCollectPageRequest.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/request/SectorTrendsDataRequest.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/request/TransactionRecordImport.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/request/TransactionRecordPageRequest.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/response/AppBigDetailVO.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/response/AppRiseLossesResponse.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/response/AppRiseLossesStatistics.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/response/BigStockDataDetailResponse.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/response/BigStockDataDetailResponseBak.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/response/BigStockDataResponse.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/response/DayAndPlate.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/response/DongCaiSecondaryIndustryResponse.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/response/EarningsLineResponse.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/response/MyCollectPageResponse.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/response/OriginalIssueStockDetailResponse.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/response/OriginalIssueStockResponse.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/response/OriginalIssueStockSortResponseChange.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/response/QueryBuyOrSell.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/response/SectorTrendsDataResponse.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/response/StockKLineResponse.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/response/StockKLineVO.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/response/TotalEarningsResponse.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/service/DongCaiSecondaryIndustryService.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/service/HighLowOfThreeHundredService.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/service/MyCollectService.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/service/OriginalIssueStockService.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/service/StockNameService.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/service/TransactionRecordService.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/service/TransactionRecordTempService.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/service/impl/DongCaiSecondaryIndustryServiceImpl.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/service/impl/HighLowOfThreeHundredServiceImpl.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/service/impl/MyCollectServiceImpl.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/service/impl/OriginalIssueStockServiceImpl.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/service/impl/StockNameServiceImpl.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/service/impl/TransactionRecordServiceImpl.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/service/impl/TransactionRecordTempServiceImpl.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/constants/Constants.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/controller/LoginController.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/controller/SmsController.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/enums/sms/SmsRedisEnum.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/enums/sms/SmsTemplateCodeEnum.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/enums/sms/SmsTemplateEnum.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/exception/BaseException.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/exception/BizException.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/exception/CommissionException.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/exception/FileUploadException.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/exception/PartnerIntegralException.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/exception/TaskException.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/exception/user/UserBindLoginException.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/exception/user/UserLoginException.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/mapper/SUserMapper.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/mapper/UserLoginMapper.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/mapper/UserLoginRecordMapper.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/mapper/UserLoginWayMapper.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/pojo/dto/BasePageRequest.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/pojo/dto/ChatRecordDetailRequest.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/pojo/dto/DatePageRequest.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/pojo/dto/FillUserInfoRequest.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/pojo/dto/LoginBaseDTO.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/pojo/dto/LoginRequest.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/pojo/dto/MiniAppFirstPhoneDTO.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/pojo/dto/MiniAppSecondPhoneDTO.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/pojo/dto/MiniAppUserUserInfoDTO.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/pojo/dto/PageChatRecordRequest.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/pojo/dto/SendSmsRequest.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/pojo/dto/SmsCodeDTO.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/pojo/dto/UserLoginDTO.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/pojo/dto/UserSmsCodeLoginDTO.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/pojo/entity/User.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/pojo/entity/UserLogin.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/pojo/entity/UserLoginRecord.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/pojo/entity/UserLoginWay.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/pojo/vo/BaseUserVO.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/pojo/vo/HomeInfoVO.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/pojo/vo/LoginVO.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/pojo/vo/PageUserAndPartnerVO.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/pojo/vo/UserCouponVO.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/pojo/vo/UserIntegralVO.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/pojo/vo/UserInviteInfo.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/pojo/vo/UserLoginVO.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/pojo/vo/UserPartVO.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/pojo/vo/UserRedisVO.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/pojo/vo/UserVO.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/pojo/vo/UserWxInfoVO.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/pojo/vo/WxOAuth2UserInfoVO.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/service/LoginService.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/service/SUserService.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/service/impl/LoginServiceImpl.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/service/impl/SUserServiceImpl.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/service/impl/UserLoginRecordService.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/tool/controller/ToolController.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/tool/mapper/GptToolMapper.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/tool/mapper/GptToolMapper.xml create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/tool/pojo/domain/GptTool.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/tool/service/GptToolService.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/tool/service/impl/GptToolServiceImpl.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/user/mapper/UserOperLogMapper.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/user/mapper/UserOperLogMapper.xml create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/user/pojo/domain/UserOperLog.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/user/service/UserOperLogService.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/user/service/impl/UserOperLogServiceImpl.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/annotation/boot/SpringBootApplication.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/annotation/combination/PathRestController.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/annotation/swagger/SwaggerFiledExplain.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/annotation/valid/EnumType.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/annotation/valid/Phone.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/annotation/valid/SmsCode.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/annotation/valid/Url.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/contants/Constants.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/contants/HttpStatus.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/contants/ScheduleConstants.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/contants/SpringConstants.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/date/DateConverter.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/date/DateConverterConfig.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/date/DateFactory.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/date/DateUtil.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/date/LocalDateConverter.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/date/LocalDateDeserializationConverter.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/date/LocalDateSerializationConverter.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/date/LocalDateTimeConverter.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/date/LocalDateTimeDeserializationConverter.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/date/LocalDateTimeSerializationConverter.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/date/LocalTimeConverter.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/date/LocalTimeDeserializationConverter.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/date/LocalTimeSerializationConverter.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/exception/BizException.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/html/EscapeUtil.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/html/HTMLFilter.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/http/HttpHelper.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/http/HttpUtils.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/interceptor/AuthUserHandlerResolver.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/interceptor/RepeatSubmitInterceptor.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/interceptor/UserCache.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/interceptor/annotation/AuthUser.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/interceptor/annotation/RepeatSubmit.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/interceptor/impl/SameUrlDataInterceptor.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/ip/AddressUtils.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/ip/IpUtils.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/jwt/JWTUtil.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/lang/Assert.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/page/Comparison.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/page/PageList.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/page/PageModel.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/page/PageUtils.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/page/QueryField.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/page/Querys.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/qrCode/service/QRCodeService.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/qrCode/service/impl/QRCodeServiceImpl.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/redis/annotation/RedisLock.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/redis/aop/RedisLockAspect.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/redis/service/RedisConfig.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/redis/service/RedisService.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/restrict/enums/BlacklistOrWhiteListEnum.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/restrict/enums/RestrictTypeEnum.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/restrict/interceptor/RestrictInterceptor.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/restrict/interceptor/annotation/Restrict.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/restrict/strategy/RestrictStrategy.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/restrict/strategy/impl/RepeatSubmitRestrictStrategy.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/result/ResponseResult.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/result/Result.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/result/ResultConstants.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/result/Results.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/result/SysConstants.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/sql/MyMetaObjectHandler.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/sql/SqlInterceptor.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/str/Convert.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/str/StrFormatter.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/str/StringUtils.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/utils/Base64Utils.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/utils/BigNum.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/utils/CronPatternUtil.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/utils/CronUtil.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/utils/FileTypeUtil.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/utils/FileUtil.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/utils/MapperUtil.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/utils/MessageUtils.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/utils/RandomUtils.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/utils/ServletUtil.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/utils/SpringUtil.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/utils/SpringUtils.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/utils/StringUtil.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/utils/UserAgentUtil.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/utils/UserRedis.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/config/GlobalExceptionHandler.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/config/MybatisPlusConfig.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/config/ParamConvertConfig.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/config/ParamsInterceptor.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/config/SwaggerConfig.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/config/SwaggerShowMonitor.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/config/WebConfigurer.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/config/banary/WxMpConfiguration.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/config/banary/WxMpProperties.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/config/filter/ActivityInfoFilter.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/config/filter/FilterConfig.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/config/filter/RepeatableFilter.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/config/filter/RepeatedlyRequestWrapper.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/config/filter/XssFilter.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/config/filter/XssHttpServletRequestWrapper.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/framework/handler/HandlerUtil.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/framework/handler/interceptor/DefaultHandlerInterceptor.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/framework/handler/interfaces/Count.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/framework/handler/interfaces/Getter.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/framework/handler/interfaces/Handler.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/framework/handler/interfaces/List.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/framework/handler/interfaces/Page.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/framework/handler/interfaces/Sum.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/framework/handler/interfaces/Valid.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/system/annatation/Violation.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/system/aop/UserOperAspect.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/system/bean/Message.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/system/component/captcha/CaptchaProperties.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/system/component/captcha/NECaptchaVerifier.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/system/component/sensitive/Sensitive.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/system/component/sensitive/SensitiveJsonSerializer.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/system/component/sensitive/SensitiveStrategies.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/system/component/sensitive/SensitiveStrategy.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/system/component/sensitive/strategy/impl/AddressStrategy.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/system/component/sensitive/strategy/impl/BankCardStrategy.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/system/component/sensitive/strategy/impl/EmailStrategy.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/system/component/sensitive/strategy/impl/IdCardStrategy.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/system/component/sensitive/strategy/impl/PhoneStrategy.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/system/component/valid/BadWordValidComponent.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/system/contants/Constant.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/system/enums/MessageType.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/system/handler/MessageHandler.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/system/manager/AsyncFactory.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/system/manager/AsyncManager.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/system/manager/ShutdownManager.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/system/manager/ThreadPoolConfig.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/system/manager/Threads.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/system/mapper/IgnoreBaseMapper.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/system/restrict/pojo/dto/AddRestrictInfoRequest.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/system/restrict/strategy/impl/CaptchaRestrictStrategy.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/system/restrict/strategy/impl/IpRestrictStrategy.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/system/restrict/strategy/impl/PhoneRestrictStrategy.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/system/restrict/strategy/impl/SignRestrictStrategy.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/web/pojo/dto/login/LoginDTO.java create mode 100644 ssdmn-chat-gpt-api/src/main/java/com/ssdmn/web/pojo/dto/login/WxLoginRedisDTO.java create mode 100644 ssdmn-chat-gpt-api/src/main/resources/META-INF/additional-spring-configuration-metadata.json create mode 100644 ssdmn-chat-gpt-api/src/main/resources/application-dev.yml create mode 100644 ssdmn-chat-gpt-api/src/main/resources/application-prod.yml create mode 100644 ssdmn-chat-gpt-api/src/main/resources/application-test.yml create mode 100644 ssdmn-chat-gpt-api/src/main/resources/application.yml create mode 100644 ssdmn-chat-gpt-api/src/main/resources/ck/1.txt create mode 100644 ssdmn-chat-gpt-api/src/main/resources/logback-spring.xml diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..88f71dd --- /dev/null +++ b/.gitignore @@ -0,0 +1,48 @@ +###################################################################### +# Build Tools + +.gradle +/build/ +!gradle/wrapper/gradle-wrapper.jar + +target/ +!.mvn/wrapper/maven-wrapper.jar + +###################################################################### +# IDE + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +nbproject/private/ +build/* +nbbuild/ +dist/ +nbdist/ +.nb-gradle/ + +###################################################################### +# Others +*.log +*.xml.versionsBackup +*.swp + +!*/build/*.java +!*/build/*.html +!*/build/*.xml +/log/ + +package-lock.json +/bin/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..8564f29 --- /dev/null +++ b/LICENSE @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2018 RuoYi + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..e69de29 diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..45df5f4 --- /dev/null +++ b/pom.xml @@ -0,0 +1,225 @@ + + + 4.0.0 + + + org.springframework.boot + spring-boot-starter-parent + 2.6.3 + + + com.ssdmn + ssdmn + 1.0.0 + + ssdmn + ssdmnBoot系统 + + + UTF-8 + UTF-8 + 1.8 + 3.1.1 + 1.2.6 + 1.21 + 3.0.0 + 2.3.2 + 3.4.2 + 1.3.1 + 1.2.83 + 5.8.0 + 5.8.0 + 2.11.0 + 1.4 + 3.2.2 + 4.1.2 + 1.7 + 0.9.1 + 1.1.55.RELEASE + 1.9 + + + + + + + + + com.alibaba + druid-spring-boot-starter + ${druid.version} + + + + + eu.bitwalker + UserAgentUtils + ${bitwalker.version} + + + + + com.baomidou + mybatis-plus-boot-starter + ${mybatis-plus-boot-starter.version} + + + com.baomidou + mybatis-plus-extension + ${mybatis-plus-boot-starter.version} + + + + + com.github.pagehelper + pagehelper-spring-boot-starter + ${pagehelper.boot.version} + + + + + com.github.oshi + oshi-core + ${oshi.version} + + + + net.java.dev.jna + jna + ${jna.version} + + + + net.java.dev.jna + jna-platform + ${jna.version} + + + + + io.springfox + springfox-boot-starter + ${swagger.version} + + + io.swagger + swagger-models + + + + + + + commons-io + commons-io + ${commons.io.version} + + + org.apache.commons + commons-text + ${commons-text.version} + + + com.ibeetl + beetl-framework-starter + ${beetl.version} + + + + + commons-fileupload + commons-fileupload + ${commons.fileupload.version} + + + + + org.apache.poi + poi-ooxml + ${poi.version} + + + + + + commons-collections + commons-collections + ${commons.collections.version} + + + + + com.alibaba + fastjson + ${fastjson.version} + + + + + io.jsonwebtoken + jjwt + ${jwt.version} + + + + + + + ssdmn-chat-gpt-api + + pom + + + + + src/main/java + + **/*.xml + + + + src/main/resources + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.1 + + ${java.version} + ${java.version} + ${project.build.sourceEncoding} + + + + + + + + public + aliyun nexus + http://maven.aliyun.com/nexus/content/groups/public/ + + true + + + + + + + public + aliyun nexus + http://maven.aliyun.com/nexus/content/groups/public/ + + true + + + false + + + + \ No newline at end of file diff --git a/ssdmn-chat-gpt-api/pom.xml b/ssdmn-chat-gpt-api/pom.xml new file mode 100644 index 0000000..2bb4a47 --- /dev/null +++ b/ssdmn-chat-gpt-api/pom.xml @@ -0,0 +1,227 @@ + + + + ssdmn + com.ssdmn + 1.0.0 + + 4.0.0 + + ssdmn-api + jar + 3.7.0 + + + 8 + 8 + + + + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-starter-validation + + + + org.apache.tomcat.embed + tomcat-embed-core + + + org.springframework.boot + spring-boot-test + test + + + + org.springframework + spring-context + + + org.springframework + spring-tx + + + org.springframework + spring-aspects + + + org.springframework + spring-context-support + + + org.springframework + spring-web + provided + + + + mysql + mysql-connector-java + + + + cn.hutool + hutool-all + 5.7.15 + + + + + com.github.xiaoymin + knife4j-spring-boot-starter + 3.0.3 + + + + + com.baomidou + mybatis-plus-boot-starter + + + com.baomidou + mybatis-plus-extension + + + + org.projectlombok + lombok + + + + com.alibaba + fastjson + + + + commons-io + commons-io + 2.4 + + + + + redis.clients + jedis + + + org.springframework.boot + spring-boot-starter-data-redis + + + + + + + + + + + + + + + + + + + + + + + + com.alibaba.cloud + aliyun-oss-spring-boot-starter + 1.0.0 + + + com.alibaba.cloud + aliyun-sms-spring-boot-starter + 1.0.0 + + + org.springframework + spring-mock + 2.0.8 + compile + + + + com.google.zxing + core + 3.3.3 + + + + com.github.javen205 + IJPay-All + 2.9.6 + + + + com.github.binarywang + weixin-java-open + 4.0.0 + + + com.github.binarywang + weixin-java-mp + 4.0.0 + + + + + + + + com.unfbx + chatgpt-java + 1.0.8 + + + + + com.alibaba + easyexcel + 3.1.1 + + + + + + + org.springframework.boot + spring-boot-maven-plugin + 2.1.1.RELEASE + + true + + + + + repackage + + + + + + org.apache.maven.plugins + maven-war-plugin + 3.1.0 + + false + ${project.artifactId} + + + + ${project.artifactId} + + + \ No newline at end of file diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/WebMain.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/WebMain.java new file mode 100644 index 0000000..2ac0651 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/WebMain.java @@ -0,0 +1,19 @@ +package com.ssdmn; + + +import com.ssdmn.common.annotation.boot.SpringBootApplication; +import org.springframework.boot.SpringApplication; + +/** + * SpringBoot入口 + * + * @author 郭世旭 + * @version 1.0 + * @createTime 2021-04-10 09:58:30 + */ +@SpringBootApplication +public class WebMain { + public static void main(String[] args) { + SpringApplication.run(WebMain.class,args); + } +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/aliyun/AliYunProperties.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/aliyun/AliYunProperties.java new file mode 100644 index 0000000..d762f0a --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/aliyun/AliYunProperties.java @@ -0,0 +1,119 @@ +package com.ssdmn.aliyun; + +import lombok.Data; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; + +/** + * 阿里云相关服务类 + * + * @author 郭世旭 + * @version 1.0 + * @createTime 2021-0-15 09:58:30 + */ +@Data +@Configuration +@ConfigurationProperties(prefix = "alibaba.cloud") +@ConditionalOnProperty(prefix = "alibaba.cloud", value = {"access-key", "secret-key"}) +public class AliYunProperties { + + /** + * OSS配置 + */ + private final Oss oss = new Oss(); + /** + * 短信配置 + */ + private final Sms sms = new Sms(); + /** + * MQ配置 + */ + private final Rocketmq rocketmq = new Rocketmq(); + /** + * accessKey + */ + private String accessKey; + /** + * secretKey + */ + private String secretKey; + /** + * 地区 + */ + private String regionId; + + @Data + public static class Oss { + /** + * 实例名 + */ + private String bucketName; + /** + * 文件访问地址 + */ + private String domain; + /** + * 回调地址 + */ + private String callback; + /** + * 上传地址 + */ + private String endpoint; + /** + * 永久文件路径 + */ + private String everlastDir; + /** + * 临时文件路径 + */ + private String tempDir; + + /** + * 到期时间(s) + */ + private Long expireTime; + } + + @Data + public static class Sms { + /** + * 短信签名 + */ + private String signName; + /** + * 短信模板 + */ + private String templateCode; + /** + * 验证码长度 + */ + private Integer length; + /** + * 有效时长 + */ + private Long validTime; + /** + * Report queue name. + */ + private String reportQueueName; + + /** + * Up queue name. + */ + private String upQueueName; + } + + @Data + public static class Rocketmq { + private String nameSrvAddr; + private String topic; + private String groupId; + private String tag; + private String orderTopic; + private String orderGroupId; + private String orderTag; + } + +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/aliyun/AliYunService.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/aliyun/AliYunService.java new file mode 100644 index 0000000..bf21550 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/aliyun/AliYunService.java @@ -0,0 +1,259 @@ +package com.ssdmn.aliyun; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.RandomUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.http.HttpUtil; +import com.alibaba.cloud.spring.boot.sms.ISmsService; +import com.alibaba.fastjson.JSONObject; +import com.aliyun.oss.HttpMethod; +import com.aliyun.oss.OSSClient; +import com.aliyun.oss.common.utils.BinaryUtil; +import com.aliyun.oss.model.*; +import com.aliyuncs.dysmsapi.model.v20170525.SendBatchSmsRequest; +import com.aliyuncs.dysmsapi.model.v20170525.SendBatchSmsResponse; +import com.aliyuncs.dysmsapi.model.v20170525.SendSmsRequest; +import com.aliyuncs.dysmsapi.model.v20170525.SendSmsResponse; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; +import org.springframework.context.annotation.Configuration; + +import javax.annotation.Resource; +import java.io.InputStream; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.time.Instant; +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; +import java.util.Arrays; +import java.util.Date; +import java.util.List; +import java.util.stream.Collectors; + + +/** + * @author 郭世旭 + */ +@Slf4j +@Configuration +@ConditionalOnBean(AliYunProperties.class) +public class AliYunService { + + @Resource + private AliYunProperties aliyunProperties; + + @Resource + private OSSClient ossClient; + + @Resource + private ISmsService smsService; + + /** + * OSS永久文件上传 + * + * @param in 文件流 + * @param fileName 文件名 + * @return 文件访问地址 + * @Param type 1-图片 + */ + public String ossUploadEverlast(InputStream in, String fileName, int type) { + //文件存放目录 + String dir = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd")); + //文件完整路径 files/everlast/ + String filePath = aliyunProperties.getOss().getEverlastDir() + "/" + dir + "/" + fileName; + //执行上传操作 + ossClient.putObject(aliyunProperties.getOss().getBucketName(), filePath, in); + //5年 + Date expiration = new Date(System.currentTimeMillis() + 5L * 365 * 24 * 60 * 60 * 1000); + String url = ossClient.generatePresignedUrl(aliyunProperties.getOss().getBucketName(), filePath, expiration).toString(); + log.info("=====OSS永久文件上传成功(带密钥)=====文件路径:" + url); + //获取没有密钥的url + url = StrUtil.subBefore(url, "?", false); + log.info("=====OSS永久文件上传成功(不带密钥)=====文件路径:" + url); + //请求获取图片信息 + if (ObjectUtil.equals(type, 1)) { + String imgInfoUrl = StrUtil.addSuffixIfNot(url, "?x-oss-process=image/info"); + String s = HttpUtil.get(imgInfoUrl); + ImageInfo imageInfo = JSONObject.parseObject(s, ImageInfo.class); + + url = StrUtil.format("{}?w={}&h={}", url, imageInfo.getImageWidth().get("value"), imageInfo.getImageHeight().get("value")); + } + log.info("=====OSS永久文件上传成功=====最终返回文件路径:" + url); + + return url; + } + + /** + * 临时文件上传 + * + * @param in 文件流 + * @param fileName 文件名 + * @return 文件访问地址 + */ + public String ossUploadTemp(InputStream in, String fileName) { + //文件完整路径 + String filePath = aliyunProperties.getOss().getTempDir() + "/" + fileName; + //执行上传操作 + ossClient.putObject(aliyunProperties.getOss().getBucketName(), filePath, in); + log.info("=====OSS临时文件上传成功=====文件路径:" + filePath); + return aliyunProperties.getOss().getDomain() + "/" + filePath; + } + + /** + * 根据文件名删除文件(批量) + * + * @param fileNames 文件名(绝对路径) + * 示例:https://youimg1.c-ctrip.com/target/100f0t000000iie24023B_D_256_180.jpg + * 或target/100f0t000000iie24023B_D_256_180.jpg + * @return 删除失败的文件列表 如为空则全部删除成功 + */ + public List ossDeleteFile(String... fileNames) { + + List files = Arrays.stream(fileNames).map(str -> { + if (str.startsWith("http")) { + return str.replace(aliyunProperties.getOss().getDomain() + "/", ""); + } else { + return str; + } + }).collect(Collectors.toList()); + + DeleteObjectsResult result = ossClient.deleteObjects( + new DeleteObjectsRequest(aliyunProperties.getOss().getBucketName()) + .withQuiet(false) + .withKeys(files) + ); + + //删除失败的文件 + List failFiles = result.getDeletedObjects(); + if (CollUtil.isEmpty(failFiles)) { + log.info("=====OSS文件删除成功=====文件名:" + Arrays.asList(fileNames)); + } else { + log.info("=====OSS文件删除部分失败=====文件名:" + failFiles); + } + return result.getDeletedObjects(); + } + + /** + * 前端文件直传获取签名 + * + * @return 签名信息 + */ + public OssPolicyResult ossPolicy() { + + + String accessKeyId = ossClient.getCredentialsProvider().getCredentials().getAccessKeyId(); + AliYunProperties.Oss ossConfig = aliyunProperties.getOss(); + // 用户上传文件时指定的前缀 + String dir = ossConfig.getEverlastDir() + "/" + LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd")) + "/"; + long expireEndTime = Instant.now().toEpochMilli() + ossConfig.getExpireTime() * 1000; + Date expiration = new Date(expireEndTime); + // PostObject请求最大可支持的文件大小为5 GB,即CONTENT_LENGTH_RANGE为5*1024*1024*1024。 + PolicyConditions policyConds = new PolicyConditions(); + policyConds.addConditionItem(PolicyConditions.COND_CONTENT_LENGTH_RANGE, 0, 5 * 1024 * 1024 * 1024L); + policyConds.addConditionItem(MatchMode.StartWith, PolicyConditions.COND_KEY, dir); + + String postPolicy = ossClient.generatePostPolicy(expiration, policyConds); + byte[] binaryData = postPolicy.getBytes(StandardCharsets.UTF_8); + String encodedPolicy = BinaryUtil.toBase64String(binaryData); + String postSignature = ossClient.calculatePostSignature(postPolicy); + OssPolicyResult result = new OssPolicyResult(); + result.setAccessid(accessKeyId); + result.setPolicy(encodedPolicy); + result.setDir(dir); + result.setHost(ossConfig.getDomain()); + result.setSignature(postSignature); + result.setExpire(ossConfig.getExpireTime()); + return result; + } + + /** + * 发送短信 + * + * @param sendContent 模板替换内容 示例:{"code":"666666"} + * @param phoneNumbers 手机号码 多个用,隔开 + * @return 发送结果 + */ + @SneakyThrows + public boolean smsSend(String sendContent, String phoneNumbers, String templateCode) { + // 组装请求对象-具体描述见控制台-文档部分内容 + SendSmsRequest request = new SendSmsRequest(); + // 必填:待发送手机号 + request.setPhoneNumbers(phoneNumbers); + // 必填:短信签名-可在短信控制台中找到 + request.setSignName(aliyunProperties.getSms().getSignName()); + // 必填:短信模板-可在短信控制台中找到 + request.setTemplateCode(templateCode); + // 可选:模板中的变量替换JSON串 + request.setTemplateParam(sendContent); + SendSmsResponse sendSmsResponse = smsService.sendSmsRequest(request); + log.info("==短信发送结果=>{}", sendSmsResponse); + if (sendSmsResponse.getCode() != null && sendSmsResponse.getCode().equals("OK")) { + //请求成功 + return true; + } + + return false; + } + + /** + * 发送短信带扩展属性 + * + * @param sendContent 模板替换内容JSON串 与手机号个数保持一致 + * @param phoneNumbers 手机号码 多个用,隔开 + * @param traceId 业务扩展字段 + * @param extendCode 上行短信扩展码 + * @return 发送结果 + */ + @SneakyThrows + public SendSmsResponse smsSendExtend(String sendContent, String phoneNumbers, String traceId, String extendCode) { + // 组装请求对象-具体描述见控制台-文档部分内容 + SendSmsRequest request = new SendSmsRequest(); + // 必填:待发送手机号 + request.setPhoneNumbers(phoneNumbers); + // 必填:短信签名-可在短信控制台中找到 + request.setSignName(aliyunProperties.getSms().getSignName()); + // 必填:短信模板-可在短信控制台中找到 + request.setTemplateCode(aliyunProperties.getSms().getTemplateCode()); + // 可选:模板中的变量替换JSON串 + request.setTemplateParam(sendContent); + + // 选填-上行短信扩展码(无特殊需求用户请忽略此字段) + request.setSmsUpExtendCode(extendCode); + + // 可选:outId为提供给业务方扩展字段,最终在短信回执消息中将此值带回给调用者 + request.setOutId(traceId); + SendSmsResponse sendSmsResponse = smsService.sendSmsRequest(request); + log.info("==短信发送结果=>{}", sendSmsResponse); + return sendSmsResponse; + } + + /** + * 批量发送短信 + * + * @param sendContent 模板替换内容 + * @param phoneNumbers 手机号码 多个用,隔开 + * @return 是否成功 + */ + @SneakyThrows + public SendBatchSmsResponse smsSendBatch(String sendContent, String phoneNumbers) { + // 组装请求对象 + SendBatchSmsRequest request = new SendBatchSmsRequest(); + // 必填:待发送手机号。支持JSON格式的批量调用,批量上限为100个手机号码,批量调用相对于单条调用及时性稍有延迟,验证码类型的短信推荐使用单条调用的方式 + request.setPhoneNumberJson("[" + phoneNumbers + "]"); + // 必填:短信签名-支持不同的号码发送不同的短信签名 + request.setSignNameJson("[" + aliyunProperties.getSms().getSignName() + "]"); + // 必填:短信模板-可在短信控制台中找到 + request.setTemplateCode(aliyunProperties.getSms().getTemplateCode()); + // 必填:模板中的变量替换JSON串 + // 友情提示:如果JSON中需要带换行符,请参照标准的JSON协议对换行符的要求,比如短信内容中包含\r\n的情况在JSON中需要表示成\\r\\n,否则会导致JSON在服务端解析失败 + request.setTemplateParamJson(sendContent); + // 可选-上行短信扩展码(扩展码字段控制在7位或以下,无特殊需求用户请忽略此字段) + // request.setSmsUpExtendCodeJson("[\"90997\",\"90998\"]"); + SendBatchSmsResponse sendSmsResponse = smsService.sendSmsBatchRequest(request); + return sendSmsResponse; + } + +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/aliyun/ImageInfo.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/aliyun/ImageInfo.java new file mode 100644 index 0000000..69a83c3 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/aliyun/ImageInfo.java @@ -0,0 +1,21 @@ +package com.ssdmn.aliyun; + +import lombok.Data; + +import java.util.Map; + +/** + * @description: 阿里云图片信息对象 + * @author: 郭世旭 + * @create: 2021-06-23 11:32 + **/ +@Data +public class ImageInfo { + private Map fileSize; + private Map format; + private Map imageHeight; + private Map imageWidth; + private Map resolutionUnit; + private Map xResolution; + private Map yResolution; +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/aliyun/OssPolicyResult.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/aliyun/OssPolicyResult.java new file mode 100644 index 0000000..696ad68 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/aliyun/OssPolicyResult.java @@ -0,0 +1,46 @@ +package com.ssdmn.aliyun; + +import lombok.Data; + +/** + * 阿里云OSS签名返回实体 + * + * @author 郭世旭 + * @version 1.0 + * @createTime 2021-03-22 09:58:30 + */ +@Data +public class OssPolicyResult { + /** + * accessId + */ + private String accessid; + /** + * 加密数据 + */ + private String policy; + /** + * 签名 + */ + private String signature; + /** + * 文件路径 + */ + private String dir; + /** + * 访问地址 + */ + private String host; + /** + * 回调地址 + */ + private String callback; + /** + * 前端回调方法 + */ + private String callbackFun; + /** + * 到期时间 + */ + private Long expire; +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/chatmodel/mapper/ChatModelConfigMapper.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/chatmodel/mapper/ChatModelConfigMapper.java new file mode 100644 index 0000000..e80418c --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/chatmodel/mapper/ChatModelConfigMapper.java @@ -0,0 +1,18 @@ +package com.ssdmn.biz.chatmodel.mapper; + +import com.ssdmn.biz.chatmodel.pojo.domain.ChatModelConfig; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +/** +* @author Administrator +* @description 针对表【chat_model_config】的数据库操作Mapper +* @createDate 2023-02-28 13:38:34 +* @Entity com.ssdmn.biz.chatmodel.pojo.domain.ChatModelConfig +*/ +public interface ChatModelConfigMapper extends BaseMapper { + +} + + + + diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/chatmodel/mapper/ChatModelConfigMapper.xml b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/chatmodel/mapper/ChatModelConfigMapper.xml new file mode 100644 index 0000000..2ef2cf6 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/chatmodel/mapper/ChatModelConfigMapper.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + id,name,integral, + model_id,logo,description + + diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/chatmodel/pojo/domain/ChatModelConfig.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/chatmodel/pojo/domain/ChatModelConfig.java new file mode 100644 index 0000000..fba1211 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/chatmodel/pojo/domain/ChatModelConfig.java @@ -0,0 +1,50 @@ +package com.ssdmn.biz.chatmodel.pojo.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 java.io.Serializable; +import lombok.Data; + +/** + * + * @TableName chat_model_config + */ +@TableName(value ="chat_model_config") +@Data +public class ChatModelConfig implements Serializable { + /** + * id + */ + @TableId(type = IdType.AUTO) + private Integer id; + + /** + * 名称 + */ + private String name; + + /** + * 积分/字 + */ + private Double integral; + + /** + * openai模型id + */ + private String modelId; + + /** + * logo + */ + private String logo; + + /** + * 描述 + */ + private String description; + + @TableField(exist = false) + private static final long serialVersionUID = 1L; +} \ No newline at end of file diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/chatmodel/service/ChatModelConfigService.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/chatmodel/service/ChatModelConfigService.java new file mode 100644 index 0000000..760afec --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/chatmodel/service/ChatModelConfigService.java @@ -0,0 +1,13 @@ +package com.ssdmn.biz.chatmodel.service; + +import com.ssdmn.biz.chatmodel.pojo.domain.ChatModelConfig; +import com.baomidou.mybatisplus.extension.service.IService; + +/** +* @author Administrator +* @description 针对表【chat_model_config】的数据库操作Service +* @createDate 2023-02-28 13:38:34 +*/ +public interface ChatModelConfigService extends IService { + +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/chatmodel/service/impl/ChatModelConfigServiceImpl.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/chatmodel/service/impl/ChatModelConfigServiceImpl.java new file mode 100644 index 0000000..68ce637 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/chatmodel/service/impl/ChatModelConfigServiceImpl.java @@ -0,0 +1,22 @@ +package com.ssdmn.biz.chatmodel.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.ssdmn.biz.chatmodel.pojo.domain.ChatModelConfig; +import com.ssdmn.biz.chatmodel.service.ChatModelConfigService; +import com.ssdmn.biz.chatmodel.mapper.ChatModelConfigMapper; +import org.springframework.stereotype.Service; + +/** +* @author Administrator +* @description 针对表【chat_model_config】的数据库操作Service实现 +* @createDate 2023-02-28 13:38:34 +*/ +@Service +public class ChatModelConfigServiceImpl extends ServiceImpl + implements ChatModelConfigService{ + +} + + + + diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/config/constants/Constants.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/config/constants/Constants.java new file mode 100644 index 0000000..3f59ae9 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/config/constants/Constants.java @@ -0,0 +1,15 @@ +package com.ssdmn.biz.config.constants; + +public class Constants { + + public static final String SIGN_IN_INTEGRAL = "SIGN_IN_INTEGRAL"; + + public static final String FILL_INFO_INTEGRAL = "FILL_INFO_INTEGRAL"; + + public static final String INVITE_INTEGRAL = "INVITE_INTEGRAL"; + + public static final String FIRST_RECHARGE_INTEGRAL = "FIRST_RECHARGE_INTEGRAL"; + + public static final String REGISTER_INTEGRAL = "FIRST_RECHARGE_INTEGRAL"; + public static final String EXTRA_INTEGRAL = "EXTRA_INTEGRAL"; +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/config/mapper/SysConfigMapper.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/config/mapper/SysConfigMapper.java new file mode 100644 index 0000000..be916e3 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/config/mapper/SysConfigMapper.java @@ -0,0 +1,18 @@ +package com.ssdmn.biz.config.mapper; + +import com.ssdmn.biz.config.pojo.domain.SysConfig; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +/** +* @author Administrator +* @description 针对表【sys_config(参数配置表)】的数据库操作Mapper +* @createDate 2023-03-01 13:22:21 +* @Entity com.ssdmn.biz.config.pojo.domain.SysConfig +*/ +public interface SysConfigMapper extends BaseMapper { + +} + + + + diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/config/mapper/SysConfigMapper.xml b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/config/mapper/SysConfigMapper.xml new file mode 100644 index 0000000..c820e86 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/config/mapper/SysConfigMapper.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + config_id,config_name,config_key, + config_value,config_type,create_by, + create_time,update_by,update_time, + remark + + diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/config/pojo/domain/SysConfig.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/config/pojo/domain/SysConfig.java new file mode 100644 index 0000000..0879a60 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/config/pojo/domain/SysConfig.java @@ -0,0 +1,72 @@ +package com.ssdmn.biz.config.pojo.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 java.io.Serializable; +import java.time.LocalDateTime; +import java.util.Date; +import lombok.Data; + +/** + * 参数配置表 + * @TableName sys_config + */ +@TableName(value ="sys_config") +@Data +public class SysConfig implements Serializable { + /** + * 参数主键 + */ + @TableId(type = IdType.AUTO) + private Integer configId; + + /** + * 参数名称 + */ + private String configName; + + /** + * 参数键名 + */ + private String configKey; + + /** + * 参数键值 + */ + private String configValue; + + /** + * 系统内置(Y是 N否) + */ + private String configType; + + /** + * 创建者 + */ + private String createBy; + + /** + * 创建时间 + */ + private LocalDateTime createTime; + + /** + * 更新者 + */ + private String updateBy; + + /** + * 更新时间 + */ + private LocalDateTime updateTime; + + /** + * 备注 + */ + private String remark; + + @TableField(exist = false) + private static final long serialVersionUID = 1L; +} \ No newline at end of file diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/config/service/SysConfigService.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/config/service/SysConfigService.java new file mode 100644 index 0000000..324d8ce --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/config/service/SysConfigService.java @@ -0,0 +1,17 @@ +package com.ssdmn.biz.config.service; + +import com.ssdmn.biz.config.pojo.domain.SysConfig; +import com.baomidou.mybatisplus.extension.service.IService; + +/** +* @author Administrator +* @description 针对表【sys_config(参数配置表)】的数据库操作Service +* @createDate 2023-03-01 13:22:21 +*/ +public interface SysConfigService extends IService { + + SysConfig getByKey(String key); + + String getValueByKey(String key); + +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/config/service/impl/SysConfigServiceImpl.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/config/service/impl/SysConfigServiceImpl.java new file mode 100644 index 0000000..36fd85f --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/config/service/impl/SysConfigServiceImpl.java @@ -0,0 +1,37 @@ +package com.ssdmn.biz.config.service.impl; + +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.ssdmn.biz.config.pojo.domain.SysConfig; +import com.ssdmn.biz.config.service.SysConfigService; +import com.ssdmn.biz.config.mapper.SysConfigMapper; +import com.ssdmn.common.lang.Assert; +import org.springframework.stereotype.Service; + +/** + * @author Administrator + * @description 针对表【sys_config(参数配置表)】的数据库操作Service实现 + * @createDate 2023-03-01 13:22:21 + */ +@Service +public class SysConfigServiceImpl extends ServiceImpl + implements SysConfigService { + + @Override + public SysConfig getByKey(String key) { + return getOne(Wrappers.lambdaQuery(SysConfig.class).eq(SysConfig::getConfigKey, key)); + } + + @Override + public String getValueByKey(String key) { + SysConfig sysConfig = getByKey(key); + Assert.notNull(sysConfig, "配置不存在"); + Assert.notBlank(sysConfig.getConfigValue(), "配置不存在"); + + return sysConfig.getConfigValue(); + } +} + + + + diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/contants/Constants.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/contants/Constants.java new file mode 100644 index 0000000..04b4411 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/contants/Constants.java @@ -0,0 +1,2832 @@ +package com.ssdmn.biz.contants; + + + +import com.ssdmn.common.annotation.swagger.SwaggerFiledExplain; + +import java.math.BigDecimal; +import java.util.regex.Pattern; + +/** + * 常量 + * + * @author 郭世旭 + * @version 1.0 + * @createTime 2021-03-22 09:58:30 + */ +public interface Constants { + + /** + * 表数据状态 + */ + interface DataState { + /** + * 有效 + */ + Integer VALID = 1; + + /** + * 无效 + */ + Integer INVALID = 0; + + } + + /** + * 数据正则表达式 + */ + interface REGULAR { + + /** + * 整数,一位小数,两位小数 + */ + Pattern PATTERN = Pattern.compile("^[0-9]+(\\.[0-9]{1,2})?$"); + + /** + * 整数 + */ + Pattern INTEGER = Pattern.compile("^[0-9]+$"); + /** + * 一位小数 + */ + Pattern ONE_DOUBLE = Pattern.compile("^[0-9]+(\\.[0-9]{1}){1}$"); + } + + /** + * 用户登录方式 + */ + interface UserLoginWay { + /** + * 手机号登录 + */ + int PHONE = 1; + /** + * 账号密码登录 + */ + int PASSWORD = 3; + /** + * 公众号微信登录 + */ + int PUBLIC_NUMBER_WX = 4; + /** + * APP微信登录 + */ + int APP_WX = 6; + /** + * 小程序登录 + */ + int MINI_WX = 7; + /** + * 苹果登录 + */ + int IOS = 8; + } + + /** + * 验证码类型 + */ + interface SmsCodeType { + /** + * 登录 + */ + int LOGIN = 1; + /** + * 绑定微信手机号验证码 + */ + int WX_BIND_PHONE = 2; + + /** + * 拼桌成功 + */ + int TABLE_TOGETHER_SUCCESS = 3; + + /** + * 拼桌支付成功 + */ + int TABLE_TOGETHER_PAY_SUCCESS = 4; + } + + /** + * 收藏的类型 + */ + interface CollectionType { + /** + * 商品 + */ + int PRODUCT = 1; + + /** + * 店铺 + */ + int STORE = 2; + + /** + * 套餐 + */ + int COMBO = 3; + } + + /** + * 吃饭的时间 + */ + interface openDamType { + /** + * 早 + */ + Integer MORNING = 1; + + /** + * 中 + */ + Integer NOON = 2; + + /** + * 晚 + */ + Integer NIGHT = 3; + } + + /** + * 拼桌的类型 + */ + interface openDamStatus { + /** + * 进行中 + */ + Integer DOING = 1; + + /** + * 完成 + */ + Integer DONE = 2; + + /** + * 取消 + */ + Integer CANCEL = 3; + } + + /** + * 支付方式 + */ + interface PayWay { + /** + * 公众号 + */ + int PUBLISH_NUMBER = 101; + + /** + * 小程序 + */ + int MINI = 102; + + /** + * app + */ + int APP = 103; + + /** + * h5 + */ + int H5 = 104; + } + + interface PayType { + /** + * 普通下单 + */ + int ORDINARY_ORDER = 1; + + /** + * 充值 + */ + int RECHARGE = 2; + } + + /** + * 商城 + */ + interface Market { + /** + * 首页每个商品分类下显示商品个数 + */ + Integer GOODS_DISPLAY_NUM_ON_CATEGORY = 6; + /** + * 库存/销量修改方式-下单 + */ + int TYPE_PLACE_ORDER = 1; + /** + * 库存/销量修改方式-回滚 + */ + int TYPE_ROLLBACK_ORDER = 2; + /** + * 完整性检测类型-sku + */ + String COMPLETENESS_TYPE_SKU = "sku"; + /** + * 完整性检测类型-goods + */ + String COMPLETENESS_TYPE_GOODS = "goods"; + + /** + * 已上架 + */ + Integer ON_SALE = 1; + + /** + * 未上架 + */ + Integer NOT_ON_SALE = 0; + } + + /** + * 文件存储路径 + */ + interface FilePath { + String ALIYUN_UPLOAD = "aliyun/upload"; + + } + + interface FileUploadType { + Integer ALIYUN_UPLOAD_TYPE = 1; + } + + /** + * 轮播图 + */ + interface BannerLocation { + /** + * 首页banner轮播图 + */ + Integer HOME_PAGE = 111; + /** + * 首页banner + */ + Integer HOME_PAGE_BANNER = 121; + /** + * 赶场商城首页Banner location + */ + Integer MARKET_HEAD_PAGE_BANNER = 315; + } + + /** + * bannerLocation字典表类型 + */ + interface BannerLocationType { + /** + * 显示位置 + */ + Integer SHOW_LOCATION = 1; + + /** + * 跳转类型 + */ + Integer JUMP_TYPE = 2; + } + + /** + * 是否默认 + */ + interface Default { + + Integer DEFAULT = 1; + + Integer NOT_DEFAULT = 0; + } + + /** + * 是否删除 + */ + interface Delete { + + Integer DELETE = 1; + + Integer NOT_DELETE = 0; + } + + + /** + * 订单状态 + */ + interface OrderState { + /** + * 无效订单 + */ + Integer ORDER_INVALID = -1; + /** + * 待支付 + */ + Integer ORDER_WAIT_PAY = 888; + /** + * 待发货 + */ + Integer ORDER_WAIT_DELIVERY = 1; + /** + * 已发货 + */ + Integer ORDER_DELIVERED = 2; + /** + * 已取消订单(订单已取消) + */ + Integer ORDER_CANCELLED = 3; + /** + * 已确认收货(订单已完成) + */ + Integer ORDER_CONFIRM_RECEIPT = 4; + /** + * 申请退款 + */ + Integer ORDER_APPLY_REFUND = 5; + /** + * 等待退款 + */ + Integer ORDER_WAIT_REFUND = 6; + /** + * 拒绝退款 + */ + Integer ORDER_REFUSE_REFUND = 7; + /** + * 已退款 + */ + Integer ORDER_REFUNDED = 8; + } + + /** + * 订单完成后流程状态 + */ + interface OverState { + /** + * 100.已完成 + */ + Integer COMPLETED = 100; + /** + * 101.待核销 + */ + Integer WAIT_EXCHANGE = 101; + /** + * 102.待评价(已核销) + */ + Integer WAIT_COMMENT = 102; + /** + * 103.待追评(已评价) + */ + Integer WAIT_COMMENT_AGAIN = 103; + /** + * 104.已追评 + */ + Integer ALREADY_COMMENTED = 104; + } + + /** + * 订单类型 + */ + interface OrderType { + /** + * 订单类型-普通商城订单 + */ + int ORDER_TYPE_MARKET = 801; + /** + * 订单类型-冠名合伙人订单 + */ + int ORDER_TYPE_VIP_PACKAGE = 802; + /** + * 订单类型-火锅套餐订单 + */ + int ORDER_TYPE_HOT_POT_PACKAGE = 803; + /** + * 拼付订单 + */ + int ORDER_TYPE_PAY_TOGETHER = 804; + /** + * 订单类型-拼团订单 + */ + int ORDER_TYPE_LEG_UP_GROUP = 806; + /** + * 订单类型-拼桌订单 + */ + int ORDER_TYPE_TABLE_TOGETHER = 807; + /** + * 订单类型-共享合伙人套餐订单 + */ + int SHARED_PARTNER_SET_MEAL = 808; + /** + * 火锅达人升级套餐 + */ + int ORDER_TYPE_HM_LV_UP = 809; + + /** + * 预存订单(新共享合伙人订单) + */ + int SHARED_PARTNER_SET_MEAL_NEW = 810; + + /** + * 客如云订单 + */ + int ORDER_TYPE_KRY = 811; + + /** + * 预定订单 + */ + int ORDER_TYPE_DEPOSIT = 812; + } + + /** + * 订单是在H5管理平台否查询 + */ + interface OrderStateIsQuery { + /** + * 不可查询 + */ + int CAN_NOT_QUERY = 0; + /** + * 可查询 + */ + int CAN_QUERY = 1; + } + +// /** +// * 活动类型 +// */ +// interface ActivitiesType { +// /** +// * 活动类型-拼桌 +// */ +// int ACTIVITIES_TABLE_TOGETHER = 101; +// +// /** +// * 活动类型-拼团 +// */ +// int ACTIVITIES_FRIENDS_LEG_UP = 102; +// } + + /** + * 配送方式 + */ + interface DelivertMode { + /** + * 物流配送 + */ + Integer LOGISTICS = 1; + + /** + * 门店自提 + */ + Integer PICKED_UP_IN_STORE = 2; + + /** + * 无需处理 + */ + Integer NONE = 3; + } + + /** + * redis前缀 + */ + interface RedisPrefix { + + /** + * 用户登录 + */ + String REDIS_PREFIX_USER_LOGIN = "USER:LOGIN:{}"; + + /** + * 验证码登录 + */ + String REDIS_PREFIX_SMS_LOGIN = "SMS:LOGIN:{}"; + + /** + * 微信code + */ + String WX_APP_CODE = "WX:APP:CODE:{}"; + + /** + * 小程序登录 + */ + String WX_MINI_LOG = "WX:MINI:CODE:{}"; + + /** + * 日志 + */ + String TRACE_ID_VALUE = "TRACE:ID:{}"; + + /** + * 需要推送的开发人员 + */ + String PUSH_DEVELOPER = "PUSH:DEVELOPER:{}"; + + /** + * 测试手机号 + */ + String TEST_PHONE = "TEST:PHONE"; + + /** + * 数据库配置表表名 + */ + String CONFIG_TABLE = "CONFIG:TABLE:{}"; + } + + interface OrderConfig { + + /** + * 贵宾套餐价格 + */ + BigDecimal VIP_GOODS_PRICE = new BigDecimal("0"); + /** + * 贵宾套餐运费 + */ + BigDecimal VIP_POSTAGE_MONEY = new BigDecimal("0"); + + /** + * 批量下单订单名称 + */ + String SHOPPING_CART_ORDER_NAME = "大路餐饮订单"; + + + } + + interface AscOrDesc { + /** + * 升序 + */ + Integer ASC = 0; + + /** + * 降序 + */ + Integer DESC = 1; + } + + /** + * 门店类型 + */ + interface StoreType { + /** + * 总公司店 + */ + int COMPANY = 0; + + /** + * 旗舰店 + */ + int FLAGSHIP = 1; + + /** + * 中心店 + */ + int CENTER = 2; + + /** + * 社区店 + */ + int COMMUNITY = 3; + + /** + * 双品牌店 + */ + int DUAL_BRAND = 10; + } + + /** + * 门店状态 + */ + interface StoreState { + int OPEN = 1; + String OPEN_NAME = "开业"; + + int CLOSE = 0; + String CLOSE_NAME = "未开业"; + } + + + /** + * 是否能没有桌子开业 0-不能 1-能 + */ + interface NoTableOpen { + + /** + * 能 + */ + int CAN_NO_TABLE_OPEN = 1; + + /** + * 不能 + */ + int CAN_NOT_NO_TABLE_OPEN = 0; + } + + /** + * 卡卷 + */ + interface Virtual { + + /** + * 待领取 + */ + Integer NOT_GET = 1; + + /** + * 待使用 + */ + Integer NO_USE = 2; + + /** + * 已使用 + */ + Integer USED = 3; + } + + /** + * 门店订单状态 + */ + interface StoreOrderState { + + /** + * 下单完成 + */ + Integer ORDERED = 1; + + /** + * 进行中 + */ + Integer DOING = 2; + + /** + * 完成 + */ + Integer DONE = 3; + + /** + * 取消 + */ + Integer CANCEL = 4; + } + + /** + * 桌子 + */ + interface Table { + /** + * 包间 + */ + Integer IN = 1; + + /** + * 大厅 + */ + Integer OUT = 2; + } + + /** + * 桌子预选状态 状态(1.可选 2.已预选 3.已选) + */ + interface TableState { + /** + * 可选 + */ + int OPTIONAL = 1; + + /** + * 已预选 + */ + int PRESELECTED = 2; + + /** + * 已选 + */ + int SELECTED = 3; + } + + /** + * 申请状态(0.已拒绝 1.待审核 2.已通过) + */ + interface TableApplyState { + /** + * 已拒绝 + */ + Integer REJECTED = 1; + + /** + * 待审核 + */ + Integer PENDING_REVIEW = 2; + + /** + * 已通过 + */ + Integer PASSED = 3; + } + + /** + * 桌子所属楼层 + */ + interface TableFloor { + /** + * 2楼 + */ + Integer FLOOR_2ND = 2; + + /** + * 3楼 + */ + Integer FLOOR_3ND = 3; + } + + interface TablePlace { + /** + * 包间 + */ + String HALL = "A"; + + /** + * 大厅 + */ + String ROOM = "S"; + } + + + /** + * 桌子座位状态 + */ + interface TableSeat { + + /** + * 空位 + */ + Integer EMPTY = 1; + + /** + * 预定 + */ + Integer RESERVE = 2; + + /** + * 客人就位 + */ + Integer ARRIVE = 3; + + /** + * 座位损坏 + */ + Integer BAD = 4; + + + } + + /** + * 门店订单类型 + */ + interface StoreOrderType { + + /** + * 普通 + */ + Integer COMMON = 1; + + /** + * 套餐 + */ + Integer MORE = 2; + } + + /** + * 核销码类型 + */ + interface VerificationCodeTypeInt { + + /** + * 二维码 + */ + int QR_CODE = 1; + + /** + * 条形码 + */ + int BAR_CODE = 2; + /** + * 编号 + */ + int NUMBER = 3; + + } + + /** + * 核销方式 + */ + interface VerificationConfigId { + + /** + * 1-鸭肠 + */ + int DUCK_INTESTINES = 1; + + /** + * 2咖啡 + */ + int COFFEE = 2; + + /** + * 3-超值套餐 + */ + int COMBO_ORDER = 3; + + /** + * 4-体验套餐 + */ + int EXPERIENCE_COMBO = 4; + + /** + * 5-股东券 + */ + int STOCKHOLDER_COUPON = 5; + + /** + * 6-平台券 + */ + int PLATFORM_COUPON = 6; + + /** + * 7-会议券 + */ + int MEETING_COUPON = 7; + + /** + * 8-通用券 + */ + int COMMON_USE_COUPON = 8; + + /** + * 9-终身免费券 + */ + int FREE_COUPON = 9; + + /** + * 10-女神节毛肚券 + */ + int HAIRY_BELLY_COUPON = 10; + + /** + * 11-女神节绵绵冰 + */ + int MIANMIAN_ICE_COUPON = 11; + + /** + * 12-女神节暴打柠檬茶 + */ + int LEMON_TEA_COUPON = 12; + + /** + * 13-女神节冰激凌油条 + */ + int ICE_CREAM_FRITTERS_COUPON = 13; + + /** + * 14-定金 + */ + int ORDER_EARNEST = 14; + + /** + * 15-扫码赠送100元券 + */ + int SCAN_CODE_GIVE_AWAY = 15; + + /** + * 17-煎蛋 + */ + int FRIED_EGG = 17; + } + + /** + * 核销码状态 + */ + interface VerificationCodeTypeState { + + /** + * 进行中 + */ + Integer DOING = 1; + + /** + * 完成 + */ + Integer DONE = 2; + + } + + + /** + * 常规配置 + */ + interface Config { + /** + * 是 + */ + Integer YES = 1; + /** + * 否 + */ + Integer NO = 0; + } + + /** + * 工作域配置 + */ + interface Quartz { + /** + * 好友助力拼团-用于工作域Group等 + */ + String LEG_UP_GROUP = "legUpGroup"; + + /** + * 拼桌-用于工作域Group等 + */ + String TABLE_TOGETHER = "tableTogether"; + + /** + * 订单-用于工作域Group等 + */ + String ORDER = "order"; + + /** + * 拼团接口别名 + */ + String LEG_UP_GROUP_SERVICE = "legUpGroupService"; + /** + * 拼团接口超时Quartz回调接口.方法名 + */ + String LEG_UP_GROUP_QUARTZ_CALL_BACK_NAME = LEG_UP_GROUP_SERVICE + ".handleTimeOut"; + + /** + * 订单接口别名 + */ + String ORDER_SERVICE = "orderService"; + + /** + * 订单接口支付超时Quartz回调接口.方法名 + */ + String ORDER_QUARTZ_CALL_BACK_NAME = ORDER_SERVICE + ".handlePayTimeOut"; + + /** + * 拼团接口团长Quartz回调接口.方法名 + */ + String LEG_UP_GROUP_ORGANIZER_QUARTZ_CALL_BACK_NAME = LEG_UP_GROUP_SERVICE + ".handleOrganizerReceiveTimeOut"; + + /** + * 拼桌接口别名 + */ + String TABLE_TOGETHER_SERVICE = "tableTogetherService"; + + /** + * 拼桌接口Quartz 超时回调接口.方法名 + */ + String TABLE_TOGETHER_QUARTZ_CALL_BACK_NAME = TABLE_TOGETHER_SERVICE + ".handleTimeOut"; + + /** + * 拼桌接口Quartz 支付超时回调接口.方法名 + */ + String TABLE_TOGETHER_QUARTZ_PAY_CALL_BACK_NAME = TABLE_TOGETHER_SERVICE + ".handlePayTimeOut"; + } + + /** + * 卡券类型 + */ + interface VirtualUseType { + /** + * 4万冠名权 + */ + Integer NAMING_RIGHT_THREE = 1; + + /** + * 6万冠名权 + */ + Integer NAMING_RIGHT_FIVE = 2; + + /** + * 8万冠名权 + */ + Integer NAMING_RIGHT_SEVEN = 3; + + } + + /** + * 卡券状态 + */ + interface VirtualType { + /** + * 待领取 + */ + Integer WAITING_GET = 1; + + /** + * 待使用 + */ + Integer WAITING_USE = 2; + + /** + * 已使用 + */ + Integer HAS_USE = 3; + } + + /** + * 卡券状态 + */ + interface CouponState { + /** + * 无效 + */ + Integer INVALID = 1; + + /** + * 待领取 + */ + Integer WAITING_GET = 1; + + /** + * 待使用 + */ + Integer WAITING_USE = 2; + + /** + * 已使用 + */ + Integer HAS_USE = 3; + + /** + * 已过期 + */ + Integer EXPIRED = 4; + } + + /** + * 卡券类型关系表 + */ + interface VirtualWorkRelation { + + /** + * 合伙人身份 + */ + Integer PARTNER_STATUS = 1; + + } + + /** + * 系统消息 + */ + interface SysInfo { + + interface BaseType { + /** + * 系统消息 + */ + int SYSTEM = 1; + } + + /** + * 小类型 + */ + interface Type { + /** + * 预订座位 + */ + int BOOK_SEAT = 1; + + /** + * 拼桌 + */ + int JOIN_TABLE = 2; + + /** + * 拼团 + */ + int JOIN_GROUP = 3; + + /** + * 我的团队 + */ + int MY_TEAM = 4; + + /** + * 分享明细 + */ + int SHARE_DETAILS = 5; + } + + /** + * 消息内容 + */ + interface Info { + String INFO_BOOKING_SUCCEED = "您在{}的预订桌位已成功受理,到店后出示预订信息即可!"; + } + + /** + * 消息标题 + */ + interface Title { + String TITLE_BOOKING_SUCCEED = "预订座位成功"; + } + } + + /** + * 支付状态 + */ + interface PayState { + /** + * 待支付 + */ + Integer UNPAID = 0; + + /** + * 已支付 + */ + Integer PAID = 1; + + /** + * 已退款 + */ + Integer REFUNDED = 2; + } + + /** + * 活动 + */ + interface Activities { + + /** + * 身份 + */ + interface Identity { + /** + * 团长 + */ + Integer ORGANIZER = 1; + /** + * 团员 + */ + Integer MEMBER = 2; + + /** + * 未参团 什么也不是 + */ + Integer NON = 3; + } + + /** + * 拼桌 + */ + interface TableTogetherState { + /** + * 待成团 + */ + Integer WAIT_TOGETHER = 201; + /** + * 已成团-待支付 + */ + Integer TOGETHER_SUCCESS = 202; + /** + * 拼桌已取消 + */ + Integer TABLE_CANCEL = 203; + /** + * 拼桌成功-所有人支付完成 + */ + Integer TABLE_SUCCESS = 204; + /** + * 拼桌失败 + */ + Integer TABLE_FAIL = 205; + + } + + /** + * 好友助力拼团状态: + */ + interface LegUpGroupState { + /** + * 未成立(几乎不可能出现) + */ + Integer LEG_UP_CREATE_FAIL = -1; + /** + * 等待好友助力(活动中) + */ + Integer LEG_UP_ON_WAIT = 1; + /** + * 助力成功 + */ + Integer LEG_UP_SUCCESS = 2; + /** + * 助力失败 超时 + */ + Integer LEG_UP_FAIL = 3; + } + + /** + * 好友助力团长状态: + */ + interface LegUpOrganizerState { + /** + * 无法购买/领取 + */ + Integer CAN_NOT_RECEIVE = 104; + + /** + * 可折扣购买 + */ + Integer CAN_DISCOUNT_BUY = 105; + + /** + * 可免费领取 + */ + Integer FREE_RECEIVE = 106; + + /** + * 已领取|已购买 + */ + Integer ALREADY_RECEIVED = 107; + } + + /** + * 活动通知 + */ + interface Notice { + /** + * 拼团 + */ + Integer LEG_UP_GROUP = 1; + + /** + * 拼桌 + */ + Integer TABLE_TOGETHER = 2; + } + + /** + * 拼团阶段 + */ + interface LegUpGroupStage { + /** + * 一阶段达成 + */ + int ONE_STAGE_REACHED = 1; + /** + * 二阶段达成 + */ + int TWO_STAGE_REACHED = 2; + } + } + + /** + * 是否是超级管理员 + */ + interface IsAdmin { + + /** + * 1表示超级管理员 + */ + Integer ADMIN = 1; + + /** + * 2表示普通管理员 + */ + Integer COMMON_ADMIN = 2; + + /** + * 3普通成员 + */ + Integer COMMON = 3; + + } + + interface EmployeeRole { + /** + * 店长 + */ + Long SHOT_MANAGER = 1L; + } + + + /** + * 城市等级 + */ + interface CityLevel { + /** + * 国 + */ + Integer COUNTRY = 0; + + /** + * 省 + */ + Integer PROVINCE = 1; + + /** + * 市 + */ + Integer CITY = 2; + + /** + * 区 + */ + Integer AREA = 3; + } + + /** + * 性别 + */ + interface Sex { + + Integer N = 0; + + Integer M = 1; + + Integer F = 2; + + } + + /** + * 使用状态 + */ + interface IsUsed { + + /** + * 可用 + */ + Integer USED = 1; + + /** + * 禁用 + */ + Integer NOT_USED = 2; + + /** + * 黑名单 + */ + Integer BLACK = 3; + + } + + /** + * 微信支付相关常量 + */ + interface WxPay { + interface WxType { + /** + * 支付 + */ + Integer PAY = 1; + /** + * 退款 + */ + Integer REFUND = 2; + + } + + interface WxChannel { + //微信支付类型常量值 + /** + * 公众号 + */ + int PUBLISH_NUMBER = 101; + + /** + * 小程序 + */ + int MINI = 102; + + /** + * app + */ + int APP = 103; + + /** + * h5 + */ + int H5 = 104; + } + + + /** + * 微信支付描述前缀 + */ + String WAP_NAME_PREFIX = "大路火锅订单:"; + + /** + * 微信退款成功标识 + */ + String OK = "OK"; + } + + interface OrderStatus { + /** + * 支付失败 + */ + Integer PAY_FAIL = 0; + + /** + * 支付成功 + */ + Integer PAY_SUCCESS = 1; + } + + interface Version { + /** + * IOS版本类型 + */ + Long IOS_VERSION_TYPE = 2101L; + + /** + * /** + * 安卓版本类型 + */ + Long ANDROID_VERSION_TYPE = 2102L; + } + + /** + * 配置表枚举 + */ + interface ConfigTable { + /** + * 用户默认头像 + */ + Long AVATAR = 1L; + + /** + * 自动确认收货时间(天) + */ + Long LAST_RETURN_TIME = 2L; + + /** + * 订单支付期限字段名 + */ + Long ORDER_PAY_TERM = 3L; + + /** + * 团长领取期限 + */ + Long ORGANIZER_RECEIVE_TERM = 4L; + + /** + * 团长每个商品最大开团数 + */ + Long CREATE_LEG_UP_MAX_LIMIT = 5L; + + /** + * 门店桌位最大预订人数 + */ + Long ORDER_TABLE_NUM = 6L; + + /** + * 门店桌位最大预订人数 + */ + Long ORDER_TABLE_DAY = 7L; + + /** + * 用户二维码访问地址 + */ + Long QR_CODE_URL = 8L; + + /** + * 直接佣金比例 + */ + Long DIRECT_COMMISSION_RATIO = 9L; + + /** + * 三万合伙人管理奖提成比例 + */ + Long JUNIOR_LEVEL_PARTNER_POINT = 10L; + + /** + * 五万合伙人管理奖提成比例 + */ + Long MID_LEVEL_PARTNER_POINT = 11L; + + /** + * 七万合伙人管理奖提成比例 + */ + Long SENIOR_LEVEL_PARTNER_POINT = 12L; + + /** + * 分公司奖提成比例 + */ + Long COMPANY_AWARD_POINT = 13L; + + /** + * 分公司扶持奖比例 + */ + Long COMPANY_SUPPORT_AWARD_POINT = 14L; + + /** + * 分佣日期 + */ + Long COMMISSION_DATE = 15L; + + /** + * 升级分公司直接业绩条件 + */ + Long DIRECT_PERFORMANCE_SUPPORT = 16L; + + /** + * 升级分公司团队业绩条件 + */ + Long TEAM_PERFORMANCE_SUPPORT = 17L; + + /** + * 大额提现税点 + */ + Long LARGE_AMOUNT_WITHDRAWAL_TAX_POINT = 18L; + + /** + * 最大提现金额 + */ + Long MAXIMUM_WITHDRAWAL_AMOUNT = 19L; + + /** + * 小程序内容显示开关(提交) + */ + Long MINI_CONTENT_VISIBLE = 20L; + + /** + * 客服电话号码 + */ + Long CONSUMER_SERVICE_PHONE = 21L; + + /** + * 公众上级电话(周总) + */ + Long PARTNER_OBJECT = 22L; + + /** + * 员工提成分佣比例 + */ + Long COMMISSION_RATIO = 23L; + + /** + * 店长扶持比例 + */ + Long STORE_MANAGER_SUPPORT = 24L; + + /** + * 台桌利润分佣 + */ + Long TABLE_PROFIT_SHARING_COMMISSION = 25L; + + /** + * 共享合伙人套餐分佣比例 + */ + Long COMMISSION_SHARE_RATIO = 26L; + + /** + * 双品牌门店最多上传图片的张数 + */ + Long STORE_DOUBLE_IMAGE_MAX = 27L; + + /** + * 最大提现金额 + */ + Long SMALL_AMOUNT_MAXIMUM_WITHDRAWAL_AMOUNT = 28L; + + /** + * 小额提现税点 + */ + Long SMALL_AMOUNT_WITHDRAWAL_TAX_POINT = 29L; + + /** + * 门店桌子利润分成比例 % + */ + Long STORE_TABLE_PROFIT_POINT = 30L; + } + + interface PageModel { + /** + * 默认页数 + */ + Integer PAGE_NO = 1; + + /** + * 默认长度 + */ + Integer PAGE_SIZE = 20; + } + + + /** + * 富文本类型 + */ + interface RichTextType { + + /** + * 提现交易规则 + */ + Integer TRANSACTION = 1; + + /** + * 合伙人升级规则 + */ + Integer UP = 2; + + /** + * 产品佣金推荐规则 + */ + Integer COMMISSION = 3; + + /** + * 合伙加盟双品牌门店介绍 + */ + Integer JOIN = 4; + /** + * 基金规则 + */ + Integer FUND = 5; + + /** + * 用户协议 + */ + Integer USER = 6; + + /** + * 隐私政策 + */ + Integer PRIVATE = 7; + + } + + + /** + * 订单状态 + */ + interface FoodsOrderState { + /** + * 待支付 + */ + Integer ORDER_WAIT_PAY = 888; + /** + * 待使用 + */ + Integer ORDER_WAIT_USE = 1; + /** + * 已完成(已核销) + */ + Integer ORDER_USED = 0; + } + + /** + * 合伙人 + */ + interface Partner { + + /** + * 业绩类型 + */ + interface PerformanceType { + /** + * 直接业绩 + */ + int DIRECT_PERFORMANCE = 1; + + /** + * 间接业绩 + */ + int INDIRECT_PERFORMANCE = 2; + } + + /** + * 收支状态 + */ + interface IncomeExpenditureState { + /** + * 无到账状态 + */ + int NO_STATE = 0; + /** + * 待到账 + */ + int TO_BE_ACCOUNTED = 1; + + /** + * 已到账 + */ + int HAS_ARRIVED = 2; + + /** + * 已退款 + */ + int HAS_REFUNDED = 3; + } + + /** + * 分佣收支类型 + */ + interface IncomeExpenditureType { + /** + * 收入 + */ + int INCOME = 1; + /** + * 支出 + */ + int EXPENDITURE = 0; + } + + /** + * 分佣结算类型 + */ + interface SettlementType { + /** + * 无需结算 + */ + int NO_NEED_SETTLEMENT = 0; + /** + * 秒结秒发 + */ + int RIGHT_NOW_SETTLEMENT = 1; + /** + * 秒结月发 + */ + int MONTHLY_SETTLEMENT = 2; + /** + * 月结月发 + */ + int MONTH_END_MONTH_HAIR = 3; + } + + /** + * 分佣规则 + */ + interface CommissionRule { + /** + * 按照商品自带比例分佣 + */ + int GOODS_PROPORTION = 1; + + /** + * 特殊比例分佣 + */ + int SPECIAL_PROPORTION = 2; + + /** + * 按照身份配置比例分佣 + */ + int IDENTITY_PROPORTION = 3; + + /** + * 按照固定值分佣 + */ + int FIXED_VALUE = 4; + + /** + * 级差分佣 + */ + int DIFFERENTIAL_COMMISSION = 5; + + /** + * 多种奖项之和实现级差分佣 + */ + int ALL_DIFFERENTIAL_COMMISSION = 6; + } + + /** + * 分佣规则 + */ + interface IdentityCommissionRule { + + /** + * 特殊比例分佣 + */ + int SPECIAL_PROPORTION = 2; + + /** + * 按照固定值分佣 + */ + int FIXED_VALUE = 4; + } + + /** + * 门店统计收支类型 + */ + interface StatisticsIncomeType { + /** + * 订单收入 + */ + int ORDER_INCOME = 101; + + /** + * 用户充值收入 + */ + int USER_RECHARGE_INCOME = 102; + + /** + * 门店收入 + */ + int SHOP_ORDER_INCOME = 103; + + /** + * 用户退款支出 + */ + int USER_REIMBURSE_EXPENDITURE = 201; + + /** + * 用户提现支出 + */ + int USER_WITHDRAW_EXPENDITURE = 202; + + /** + * 门店退款支出 + */ + int SHOP_ORDER_EXPENDITURE = 203; + } + + /** + * 税务类型 + */ + interface TaxType { + /** + * 提现收税 + */ + int WITHDRAW = 1; + + /** + * 双品牌收税 + */ + int DUAL_BRAND = 2; + } + + /** + * 收支类型 + */ + interface IncomeType { + + /** + * 后台增加 + */ + int ADMIN_ADD = 100; + + /** + * 直接推荐 + */ + int DIRECT_RECOMMENDATION = 101; + + /** + * 间接推荐 + */ + int INDIRECT_RECOMMENDATION = 102; + + /** + * 冠名合伙人管理奖 + */ + int NAMED_MANAGEMENT_AWARD = 103; + + /** + * 共享合伙人管理奖 + */ + int SHARED_MANAGEMENT_AWARD = 104; + + /** + * 分公司奖收入 + */ + int COMPANY_AWARD = 105; + + /** + * 分公司扶持政策收入 + */ + int COMPANY_SUPPORT = 106; + + /** + * 双品牌门店推荐 + */ + int RECOMMENDED_DUAL_BRAND_STORES = 107; + + /** + * 推荐奖 + */ + int REFERRAL_AWARD = 108; + + /** + * 爱敏团队业绩收入 + */ + int AM_PERFORMANCE_INCOME = 131; + + /** + * 爱敏团队达人直接推荐收入 + */ + int AM_HOTPOT_MASTER_DIRECT_INCOME = 132; + + /** + * 爱敏团队达人直接推荐收入 + */ + int AM_HOTPOT_MASTER_INDIRECT_INCOME = 133; + + /** + * 充值 + */ + int TOP_UP_INCOME = 151; + + /** + * 退款 + */ + int SELF_REFUND_INCOME = 152; + + /** + * 消费金转换 + */ + int CONSUMPTION_CONVERSION = 153; + + /** + * 后台扣除 + */ + int ADMIN_EXPENSES = 200; + + /** + * 小额提现支出 + */ + int SMALL_WITHDRAWAL_EXPENSES = 201; + + /** + * 大额提现支出 + */ + int LARGE_WITHDRAWAL_EXPENSES = 202; + + /** + * 消费支出 + */ + int PAY_EXPENDITURES = 203; + + /** + * 转账支出 + */ + int TRANSFER_EXPENSES = 204; + + /** + * 转换为抵扣金 + */ + int CONVERSION_DEDUCTION = 254; + } + + /** + * 申请提现类型 + */ + interface ApplicationWithdrawType { + /** + * 小额提现 + */ + int SMALL_WITHDRAWAL = 1; + + /** + * 大额提现 + */ + int LARGE_WITHDRAWAL = 2; + } + + /** + * 创建方式 + */ + interface CreateWay { + /** + * 后台创建 + */ + Integer MANAGER_CREAT = 0; + /** + * 用户注册 + */ + Integer USER_REGISTRATION = 1; + + /** + * 通过二维码分享加入 + */ + Integer QR_SHARE = 2; + + /** + * 商品一键分享加入 + */ + Integer COMMODITY_SHARE = 3; + + /** + * 拼桌分享加入 + */ + Integer JOIN_TABLE_SHARE = 4; + + /** + * 拼团分享加入 + */ + Integer JOIN_GROUP = 5; + + /** + * 拼桌分享 + */ + Integer TABLE = 6; + + /** + * 火锅达人身份升级套餐分享创建 + */ + Integer THIRD_PARTY = 7; + + /** + * 火锅达人身份升级套餐分享创建 + */ + Integer HOT_POT_MASTER_SHARE = 8; + + /** + * 文章分享创建 + */ + Integer ARTICLE_SHARE = 9; + + /** + * 商家联盟活动创建 + */ + Integer MERCHANT_ALLIANCE_ACTIVITY = 10; + + /** + * 分享礼品页创建 + */ + Integer SHARE_GIFT_PAGE = 11; + } + + /** + * 合伙人类型 + */ + interface AccessoryType { + @SwaggerFiledExplain("游客") + int ORDINARY_PARTNER = 0; + + @SwaggerFiledExplain("火锅达人") + int HOT_POT_MASTER = 1; + + @SwaggerFiledExplain("新共享合伙人") + int SHARE_ACCESSORY_NEW = 2; + + @SwaggerFiledExplain("共享合伙人") + int SHARE_ACCESSORY = 3; + + @SwaggerFiledExplain("冠名合伙人") + int NAMED_ACCESSORY = 51; + } + + /** + * 提现方式 + */ + interface PartnerWithdrawWay { + @SwaggerFiledExplain("微信") + int WX_WITHDRAW = 1; + @SwaggerFiledExplain("支付宝") + int ALI_PAY = 2; + } + + /** + * 是否分佣 + */ + interface IsCommission { + + @SwaggerFiledExplain("分佣") + int COMMISSION = 1; + + @SwaggerFiledExplain("不分佣") + int NO_COMMISSION = 0; + } + + /** + * 共享合伙人身份 + */ + interface PartnerIdentity { + /** + * 游客 + */ + String TOURIST = "T"; + + /** + * 火锅达人 + */ + String HOT_POT_MASTER = "H"; + + /** + * 初级合伙人 + */ + String JUNIOR_LEVEL_PARTNER = "J"; + + /** + * 中级合伙人 + */ + String MID_LEVEL_PARTNER = "M"; + + /** + * 高级合伙人 + */ + String SENIOR_LEVEL_PARTNER = "S"; + /** + * 初创合伙人 + */ + String START_UP_PARTNER = "A"; + + /** + * 钻石合伙人 + */ + String DIAMOND_PARTNER = "B"; + + /** + * 万创合伙人 + */ + String W_C_PARTNER = "C"; + + /** + * 城市合伙人 + */ + String CITY_PARTNER = "D"; + + /** + * 分公司 + */ + String BRANCH_COMPANY = "X"; + + /** + * 游客 + */ + String TOURIST_NEW = "TN"; + + /** + * 火锅达人 + */ + String HOT_POT_MASTER_NEW = "HN"; + } + + interface TeamType { + + /** + * 普通队伍 + */ + int ORDINARY_TEAM = 0; + + /** + * 爱敏团队 + */ + int AM_TEAM = 1; + } + } + + /** + * 身份录入申请 + */ + interface SubscribedApply { + + /** + * 状态(1、待录入,2、已录入,0、废弃) + */ + interface ApplyState { + /** + * 待录入 + */ + int TO_BE_ENTERED = 1; + /** + * 已录入 + */ + int ENTERED = 2; + /** + * 废弃 + */ + int CLOSE = 0; + } + + /** + * 是否完款 + */ + interface IsPaymentCompleted { + /** + * 已完款 + */ + int COMPLETED = 1; + + /** + * 未完款 + */ + int TO_BE_COMPLETED = 0; + + + } + + } + + /** + * 微信常量 + */ + interface Wx { + /** + * 小程序常量 + */ + interface Mini { + /** + * 小程序原始ID + */ + String GH = "gh_5bcf7dd9daa0"; + + /** + * 商品详情路径 + */ + String GOODS_DETAIL_URL = "pageA/theFair/goodsDetail/goodsDetail"; + + /** + * 拼桌路径 + */ + String TABLE_TOGETHER_URL = "pageA/hotPot/spell/details/details"; + + /** + * 拼团路径 + */ + String LEG_UP_GROUP_URL = "pageA/theFair/spellDetails/spellDetails"; + + /** + * 火锅套餐路径 + */ + String FOODS_URL = "pageA/valueMeal/foodsDetail/foodsDetail"; + + /** + * VIP套餐路径 + */ + String HOT_POT_MASTER_LV_UP_URL = "pageA/hotPot/recruit/recruit"; + + /** + * 文章详情路径 + */ + String ARTICLE_DETAILS_URL = "pageB/article/details/details"; + + /** + * 分享路径 + */ + String SHARE_URL = "/pages/share/share"; + + /** + * 小程序登录临时code过期时间 + */ + Long EXPIRE_DATE = 5 * 60L; + + } + } + + interface KryOrderState { + /** + * 订单创建 + */ + Integer ORDER_CREATION = 1; + + /** + * 订单确认(接受订单) + */ + Integer CONFIRM_ORDER = 13; + + /** + * 拒绝订单 + */ + Integer REJECT_THE_ORDER = 14; + + /** + * 订单取消 + */ + Integer CANCEL_THE_ORDER = 15; + + /** + * 订单作废 + */ + Integer ORDER_VOIDED = 16; + + /** + * 退货 + */ + Integer RETURN_OF_GOODS = 19; + + + /** + * 退款 + */ + Integer REIMBURSE = 20; + + /** + * 反结账 + */ + Integer ANTI_CHECKOUT = 21; + + /** + * 订单完成 + */ + Integer ORDER_COMPLETED = 26; + + /** + * 合单 + */ + Integer COMBINED_ORDER = 91; + + /** + * 挂账 + */ + Integer OPEN_ACCOUNT = 92; + + /** + * 支付完成 + */ + Integer PAYMENT_COMPLETED = 93; + + } + + /** + * 支付状态 + */ + interface TradePayStatus { + + /** + * 未支付 + */ + Integer UNPAID = 1; + + /** + * 支付中 + */ + Integer PAYMENTS = 2; + /** + * 已支付 + */ + Integer PAID = 3; + /** + * 退款中 + */ + Integer REFUNDING = 4; + /** + * 已退款 + */ + Integer REFUNDED = 5; + /** + * 退款失败 + */ + Integer REFUND_FAILED = 6; + /** + * 预支付 + */ + Integer PREPAID = 7; + /** + * 等待退款 + */ + Integer WAITING_FOR_REFUND = 8; + /** + * 支付失败 + */ + Integer PAYMENT_FAILED = 9; + + } + + /** + * 订单状态 + */ + interface TradeStatus { + /** + * 未处理 + */ + Integer UNTREATED = 1; + /** + * 挂单 + */ + Integer PENDING_ORDER = 2; + /** + * 已确认 + */ + Integer CONFIRMED = 3; + /** + * 已完成(全部支付) + */ + Integer COMPLETED = 4; + /** + * 已退货 + */ + Integer RETURNED = 5; + /** + * 已作废 + */ + Integer ABOLISHED = 6; + /** + * 已拒绝 + */ + Integer REJECTED = 7; + /** + * 已取消 + */ + Integer CANCELLED = 8; + /** + * 已反结账 + */ + Integer ANTI_CHECKOUT = 10; + + /** + * 待清账 + */ + Integer TO_BE_CLEARED = 13; + } + + /** + * 支付方式 + */ + interface PayMethod { + /** + * 无需支付 + */ + Integer NO_NEED_PAY = 0; + /** + * 1.线下支付 + */ + Integer PAY_OFF_LINE = 1; + /** + * 2.余额支付 + */ + Integer PAY_BY_BALANCE = 2; + /** + * 3.微信支付 + */ + Integer PAY_BY_WX = 3; + /** + * 4.全积分支付 + */ + Integer PAY_BY_CONSUMPTION_MONEY = 4; + } + + /** + * 提现审核状态 + */ + interface TransferApplicationState { + + /** + * 待审核 + */ + int PENDING_REVIEW = 1; + + /** + * 审核通过 + */ + int EXAMINATION_PASSED = 2; + + /** + * 审核拒绝 + */ + int REVIEW_REJECTED = 3; + } + + /** + * 分享类型 + */ + interface ShareType { + /** + * 赶场商城分享类型 + */ + Integer MARKET_SHARE_TYPE = 101; + /** + * 火锅套餐分享类型 + */ + Integer FOODS_SHARE_TYPE = 102; + /** + * 拼团分享类型 + */ + Integer GROUP_SHARE_TYPE = 103; + /** + * 拼桌分享类型 + */ + Integer TABLE_TOGETHER_SHARE_TYPE = 104; + } + + /** + * 员工工作状态 + */ + interface EmployeeWorkState { + + /** + * 当月入职 + */ + int ENROLLED_IN_THE_MONTH = 1; + + /** + * 正常在职 + */ + int NORMAL_JOB = 2; + + /** + * 下月离职 + */ + int RESIGNATION_NEXT_MONTH = 3; + + /** + * 当月离职 + */ + int RESIGNATION_IN_THE_MONTH = 4; + + /** + * 离职 + */ + int RESIGN = 5; + + } + + + /** + * 员工类型 1、自营门店员工 2、双品牌员工 + */ + interface EmployeeType { + /** + * 自营店员工 + */ + Integer SELF_EMPLOYED_STORE = 1; + + /** + * 双品牌员工 + */ + Integer DOUBLE_STORE = 2; + } + + /** + * 拼付状态 + */ + interface PayTogetherState { + /** + * 拼付中 + */ + Integer ON_PAY_TOGETHER = 1; + + /** + * 拼付完成 + */ + Integer PAY_TOGETHER_FINISH = 2; + + /** + * 拼付取消 + */ + Integer PAY_TOGETHER_CANCEL = 3; + + } + + interface kryOrderStatisticsType { + /** + * 天 + */ + int DAY = 1; + /** + * 月 + */ + int MONTH = 2; + /** + * 年 + */ + int YEAR = 3; + /** + * 季度 + */ + int QUARTERLY = 4; + + /** + * 小时 + */ + int HOUR = 5; + + /** + * 分钟 + */ + int MINUTE = 6; + } + + interface StoreIncomeAndExpenditure { + /** + * 收入 + */ + Integer INCOME = 1; + + /** + * 支出 + */ + Integer EXPENDITURE = 4; + } + + /** + * 二维码类型(前端扫码类型标识) + */ + interface QrCodeType { + /** + * 火锅拼付 + */ + Integer PAY_TOGETHER = 701; + } + + /** + * 支出审核类型 + */ + interface ExpenditureState { + + /** + * 审核中 + */ + Integer CHECK_DOING = 1; + + /** + * 审核通过 + */ + Integer PASSED = 2; + + /** + * 审核失败 + */ + Integer CHECK_FAILURE = 3; + + } + + /** + * 门店支出类型是否可写 + */ + interface ExpenditureStateCanWrite { + /** + * 可写 + */ + Integer CAN_WRITE = 1; + + /** + * 不可写 + */ + Integer CAN_NOT_WRITE = 0; + } + + /** + * 露天坝 + */ + interface OpenDam { + interface JumpType { + /** + * 无跳转类型 + */ + Integer JUMP_TYPE_NON = 0; + + /** + * 跳转类型-动态详情 + */ + Integer JUMP_TYPE_DYNAMIC = 101; + + /** + * 跳转类型 -拼桌详情 + */ + Integer JUMP_TYPE_TABLE_TOGETHER = 102; + + /** + * 跳转类型 视频 + */ + Integer JUMP_TYPE_VIDEO = 103; + } + + /** + * 关注状态 + */ + interface AttentionState { + /** + * 用户是自己 + */ + Integer MYSELF = 0; + + /** + * 未关注 + */ + Integer NO_ATTENTION = 1; + + /** + * 已关注 + */ + Integer ALREADY_ATTENTION = 2; + } + + /** + * 动态类型 + */ + interface DynamicType { + /** + * 图片 + */ + Integer IMG = 1; + + /** + * 视频 + */ + Integer VIDEO = 2; + } + } + + /** + * 豪华套餐类型 + */ + interface VIPGoodsTyp { + + /** + * vip套餐 + */ + Integer VIP_PACKAGE = 1; + + /** + * 共享合伙人套餐 + */ + Integer SHARED_PARTNER = 2; + + } + + /** + * 用户积分类型 + */ + interface PartnerIntegral { + Long VIP = 1L; + + Long CONSUMPTION_MONEY = 3L; + + Long DEDUCTION = 2L; + + Long STORE_POINT = 4L; + } + + /** + * 操作平台类型 + */ + interface OperatingPlatform { + /** + * 管理平台 + */ + Integer MANAGEMENT_PLATFORM = 1; + + /** + * 员工端 + */ + Integer EMPLOYEE_PLATFORM = 2; + + /** + * 用户端 + */ + Integer USER_PLATFORM = 3; + } + + /** + * 访问类型(谁看过我) + */ + interface VisitingType { + /** + * 文章 + */ + int ARTICLE = 101; + } + + /** + * 范围/源 + */ + interface Scope { + /** + * vip商城 + */ + Long SCOPE_VIP = 1L; + + /** + * 豪华套餐 + */ + Long SCOPE_FOODS = 2L; + + /** + * 赶场商品 + */ + Long SCOPE_MARKET = 3L; + + /** + * 火锅达人升级套餐商城 + */ + Long SCOPE_HOT_POT = 4L; + + /** + * 线下火锅店 + */ + Long SCOPE_STORE = 5L; + + } + + /** + * 艾敏团队 推广身份配置 + */ + interface SpreadIdentity { + /** + * 总号 + */ + long TOTAL_NUMBER = 1L; + + /** + * 大队长 + */ + long BIG_TEAM_LEADER = 2L; + + /** + * 小队长 + */ + long LITTLE_TEAM_LEADER = 3L; + + /** + * 团员 + */ + long MEMBER = 4L; + } + + /** + * 范围/源 + */ + interface Lock { + + /** + * 优惠券锁 + */ + String COUPON_UPDATE = "COUPON_UPDATE_LOCK:"; + + } + +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/contants/HttpStatus.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/contants/HttpStatus.java new file mode 100644 index 0000000..a54846f --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/contants/HttpStatus.java @@ -0,0 +1,89 @@ +package com.ssdmn.biz.contants; + +/** + * 返回状态码 + * + * @author 郭世旭 + */ +public class HttpStatus +{ + /** + * 操作成功 + */ + public static final int SUCCESS = 200; + + /** + * 对象创建成功 + */ + public static final int CREATED = 201; + + /** + * 请求已经被接受 + */ + public static final int ACCEPTED = 202; + + /** + * 操作已经执行成功,但是没有返回数据 + */ + public static final int NO_CONTENT = 204; + + /** + * 资源已被移除 + */ + public static final int MOVED_PERM = 301; + + /** + * 重定向 + */ + public static final int SEE_OTHER = 303; + + /** + * 资源没有被修改 + */ + public static final int NOT_MODIFIED = 304; + + /** + * 参数列表错误(缺少,格式不匹配) + */ + public static final int BAD_REQUEST = 400; + + /** + * 未授权 + */ + public static final int UNAUTHORIZED = 401; + + /** + * 访问受限,授权过期 + */ + public static final int FORBIDDEN = 403; + + /** + * 资源,服务未找到 + */ + public static final int NOT_FOUND = 404; + + /** + * 不允许的http方法 + */ + public static final int BAD_METHOD = 405; + + /** + * 资源冲突,或者资源被锁 + */ + public static final int CONFLICT = 409; + + /** + * 不支持的数据,媒体类型 + */ + public static final int UNSUPPORTED_TYPE = 415; + + /** + * 系统内部错误 + */ + public static final int ERROR = 500; + + /** + * 接口未实现 + */ + public static final int NOT_IMPLEMENTED = 501; +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/contants/ScheduleConstants.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/contants/ScheduleConstants.java new file mode 100644 index 0000000..5f69e7c --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/contants/ScheduleConstants.java @@ -0,0 +1,56 @@ +package com.ssdmn.biz.contants; + +/** + * 常量 + * + * @author 郭世旭 + * @version 1.0 + * @createTime 2021-03-22 09:58:30 + */ +public interface ScheduleConstants { + String TASK_CLASS_NAME = "TASK_CLASS_NAME"; + + /** 执行目标key */ + String TASK_PROPERTIES = "TASK_PROPERTIES"; + + /** 默认 */ + String MISFIRE_DEFAULT = "0"; + + /** 立即触发执行 */ + String MISFIRE_IGNORE_MISFIRES = "1"; + + /** 触发一次执行 */ + String MISFIRE_FIRE_AND_PROCEED = "2"; + + /** 不触发立即执行 */ + String MISFIRE_DO_NOTHING = "3"; + + /** 允许并发 */ + String CONCURRENT_YES = "1"; + + /** 不允许并发执行 */ + String CONCURRENT_NO = "0"; + + enum Status{ + /** + * 正常 + */ + NORMAL("0"), + /** + * 暂停 + */ + PAUSE("1"); + + private String value; + + private Status(String value) + { + this.value = value; + } + + public String getValue() + { + return value; + } + } +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/contants/SysConstants.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/contants/SysConstants.java new file mode 100644 index 0000000..e1cdb51 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/contants/SysConstants.java @@ -0,0 +1,100 @@ +package com.ssdmn.biz.contants; + +import java.math.BigDecimal; + +/** + * 常量 + * + * @author 郭世旭 + * @version 1.0 + * @createTime 2021-03-22 09:58:30 + */ +public interface SysConstants { + /** + * 成功 200 + */ + int SUCCESS_CODE = 200; + /** + * 成功 0 + */ + String SUCCESS_MESSAGE = "success"; + /** + * 请求成功消息 + */ + String RESPONSE_SUCCESS_MESSAGE = "true"; + /** + * 失败 -1 + */ + int ERROR_CODE = -1; + /** + * 失败 -1 + */ + String ERROR_MESSAGE = "error"; + /** + * 登录相关错误代码 + */ + int LOGIN_CODE = 401; + /** + * 请求头token键 + */ + String TOKEN_KEY = "token"; + /** + * 有效 + */ + @Deprecated + Integer ONE = 1; + /** + * 无效 + */ + @Deprecated + Integer ZERO = 0; + /** + * 有效 + */ + Integer VALID = 1; + /** + * 无效 + */ + Integer INVALID = 0; + /** + * 已删除 + */ + Integer DELETED = 1; + /** + * 未删除 + */ + Integer NOT_DELETED =0; + /** + * 上架 + */ + Integer ON_SALE = 1; + /** + * 未上架 + */ + Integer NOT_ON_SALE =0; + + /** + * session有效时间 + */ + Integer SESSION_KEEP_TIME_SECONDS = 10; + + /** + * 一百 + */ + BigDecimal ONE_HUNDRED = new BigDecimal("100"); + + /** + * 员工默认密码 + */ + String DEFAULT_PWD = "123456"; + + /** + * 定义临时文件存放的位置 + */ + String FILE_PATH_UPLOAD = "/opt/hot-pot/houduan/temp/temporary-hotPot-fileUpload/"; + + /** + * 门店图片 + */ + int STORE_IMAGE_NUMBER = 5; +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/contants/UserContants.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/contants/UserContants.java new file mode 100644 index 0000000..2e9cb50 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/contants/UserContants.java @@ -0,0 +1,20 @@ +package com.ssdmn.biz.contants; + + +/** + * 常量 + * + * @author 郭世旭 + * @version 1.0 + * @createTime 2021-03-22 09:58:30 + */ +public interface UserContants { + + interface UserLevel { + int COMMON_VIP = 0; + int DAY_VIP = 1; + int WEEK_VIP = 2; + int MONTH_VIP = 3; + int YEAR_VIP = 4; + } +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/file/Base64DecodedMultipartFile.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/file/Base64DecodedMultipartFile.java new file mode 100644 index 0000000..9b024f3 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/file/Base64DecodedMultipartFile.java @@ -0,0 +1,59 @@ +package com.ssdmn.biz.file; + +import lombok.Data; +import org.springframework.web.multipart.MultipartFile; + +import java.io.*; + +@Data +public class Base64DecodedMultipartFile implements MultipartFile { + + private final byte[] imgContent; + private final String header; + + public Base64DecodedMultipartFile(byte[] imgContent, String header) { + this.imgContent = imgContent; + this.header = header.split(";")[0]; + } + + @Override + public String getName() { + return System.currentTimeMillis() + Math.random() + "." + header.split("/")[1]; + } + + @Override + public String getOriginalFilename() { + return System.currentTimeMillis() + (int)Math.random() * 10000 + "." + header.split("/")[1]; + } + + @Override + public String getContentType() { + return header.split(":")[1]; + } + + @Override + public boolean isEmpty() { + return imgContent == null || imgContent.length == 0; + } + + @Override + public long getSize() { + return imgContent.length; + } + + @Override + public byte[] getBytes() { + return imgContent; + } + + @Override + public InputStream getInputStream() { + return new ByteArrayInputStream(imgContent); + } + + @Override + public void transferTo(File dest) throws IOException, IllegalStateException { + new FileOutputStream(dest).write(imgContent); + } + +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/file/FileProperties.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/file/FileProperties.java new file mode 100644 index 0000000..5610873 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/file/FileProperties.java @@ -0,0 +1,27 @@ +package com.ssdmn.biz.file; + +import lombok.Data; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; + +/** + * 文件属性类 yml配置文件属性读取 + * @author 郭世旭 + */ +@Configuration +@ConfigurationProperties(prefix = "file") +@ConditionalOnProperty(prefix = "file", value = {"path"}) +@Data +public class FileProperties { + + private final Qr qr = new Qr(); + + private String path; + + @Data + public static class Qr{ + private String path; + private String fileName; + } +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/file/mapper/FileManageMapper.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/file/mapper/FileManageMapper.java new file mode 100644 index 0000000..8df03c4 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/file/mapper/FileManageMapper.java @@ -0,0 +1,15 @@ +package com.ssdmn.biz.file.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.ssdmn.biz.file.pojo.entity.FileManage; +import org.apache.ibatis.annotations.Mapper; + +/** + * @Description: + * @Author: fan + * @Data: 2021/5/8 9:56 + * @Version: 1.0v + */ +@Mapper +public interface FileManageMapper extends BaseMapper { +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/file/pojo/entity/FileManage.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/file/pojo/entity/FileManage.java new file mode 100644 index 0000000..c0d91f9 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/file/pojo/entity/FileManage.java @@ -0,0 +1,61 @@ +package com.ssdmn.biz.file.pojo.entity; + +import com.baomidou.mybatisplus.annotation.*; +import com.fasterxml.jackson.annotation.JsonIgnore; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.time.LocalDateTime; +import java.util.Date; + +/** + * @Description: + * @Author: fan + * @Data: 2021/5/8 9:50 + * @Version: 1.0v + */ +@Data +@TableName("file_manage") +public class FileManage { + + @TableId(type = IdType.AUTO) + @ApiModelProperty(value="主键") + private Long id; + + @ApiModelProperty(value="入参时的文件名") + private String name; + + @ApiModelProperty(value="上传时的文件名") + private String uploadName; + + @ApiModelProperty(value="文件类型") + private String type; + + @ApiModelProperty(value="文件后缀") + private String suffix; + + @ApiModelProperty(value="是否是图片 0-不是 1-是") + private Integer isPicture; + + @ApiModelProperty(value="文件大小") + private Long size; + + @ApiModelProperty(value="文件来源") + private String src; + + @ApiModelProperty(value="文件访问路径") + private String accessPath; + + @ApiModelProperty(value="md5") + private String md5; + + @ApiModelProperty(value="创建时间") + @TableField(fill = FieldFill.INSERT) + @JsonIgnore + private LocalDateTime createTime; + + @ApiModelProperty(value="修改时间") + @JsonIgnore + private LocalDateTime updateTime; + +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/file/pojo/vo/UploadUrlVO.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/file/pojo/vo/UploadUrlVO.java new file mode 100644 index 0000000..fff8048 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/file/pojo/vo/UploadUrlVO.java @@ -0,0 +1,25 @@ +package com.ssdmn.biz.file.pojo.vo; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.net.URL; + +/** + * @author 余洲 + * @version 0.0.1 + * @description uploadUrlVO + * @since 2021/11/9 10:41 + */ +@Data +public class UploadUrlVO { + @ApiModelProperty("请求路径") + URL url; + + @ApiModelProperty("请求头类型") + String contentType; + + @ApiModelProperty("上传后的文件地址") + String filePath; + +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/file/service/FileManageService.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/file/service/FileManageService.java new file mode 100644 index 0000000..ce4f258 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/file/service/FileManageService.java @@ -0,0 +1,54 @@ +package com.ssdmn.biz.file.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.ssdmn.biz.file.pojo.entity.FileManage; +import org.springframework.web.multipart.MultipartFile; + +import java.io.File; +import java.io.IOException; +import java.util.List; + +/** + * @Description: + * @Author: fan + * @Data: 2021/5/8 9:58 + * @Version: 1.0v + */ +public interface FileManageService extends IService { + + /** + * 上传文件 + * @param file 文件 + * @return 访问路径 + */ + String fileUpload(MultipartFile file); + + /** + * 上传文件 + * @param file 文件 + * @return 访问路径 + */ + String fileUpload(File file); + + /** + * 通过网络路径上传文件 + * @param url 文件网格路径 + * @return 访问路径 + */ + String fileUpload(String url); + + /** + * 通过base64上传文件 + * @param base64 文件base64 + * @return 访问路径 + */ + String fileUploadByBase64(String base64) throws IOException; + + /** + * 批量上传本地文件 + * @param files 多个文件 + * @return 访问路径集合 + */ + List fileUploadBatch(MultipartFile[] files); + +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/file/service/impl/FileManageImpl.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/file/service/impl/FileManageImpl.java new file mode 100644 index 0000000..4070184 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/file/service/impl/FileManageImpl.java @@ -0,0 +1,324 @@ +package com.ssdmn.biz.file.service.impl; + +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.crypto.SecureUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.ssdmn.aliyun.AliYunService; +import com.ssdmn.common.exception.BizException; +import com.ssdmn.biz.file.mapper.FileManageMapper; +import com.ssdmn.biz.file.pojo.entity.FileManage; +import com.ssdmn.biz.file.service.FileManageService; +import com.ssdmn.common.result.SysConstants; +import com.ssdmn.common.utils.FileTypeUtil; +import com.ssdmn.common.utils.FileUtil; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; + +import javax.annotation.Resource; +import java.io.BufferedInputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.HttpURLConnection; +import java.net.URL; +import java.net.URLConnection; +import java.util.ArrayList; +import java.util.List; + + +/** + * @Description: + * @Author: fan + * @Data: 2021/5/8 9:59 + * @Version: 1.0v + */ +@Service +@Slf4j +public class FileManageImpl extends ServiceImpl implements FileManageService { + + /** + * java判断文件是否相同的的方式 + * 1:计算文件的 MD5 值 根据MD5值 判断文件是否是同一个文件 + * 2:计算文件的 SHA-1 值 根据SHA-1值 判断文件是否是同一个文件 + * 3:直接判断内容是否相同 读取文件后,将其转换成字节流: + * 4: 校验base64 位字符串形式的文件是否相等 + * 5:判断是不是同一个网络路径 + */ + private final String aliyunFilePath = SysConstants.FILE_PATH_UPLOAD + "aliyun/upload"; + + @Resource + private AliYunService aliYunService; + + + @Override + public String fileUpload(MultipartFile file) { + + log.info("file->{}", file); + log.info("fileName->{}", file.getOriginalFilename()); + //判断为空 + if (file.isEmpty()) { + throw new BizException("文件不存在"); + } + try { + log.info("fileName->{}", file.getOriginalFilename()); + //操作文件 包括上传文件,存储数据库等 + File tempFile = FileUtil.multipartFileToFile(file, aliyunFilePath); + return doFile(tempFile, false); + } catch (Exception e) { + e.printStackTrace(); + log.error("文件上传失败->e:{}", e.getMessage()); + throw new BizException("文件上传失败"); + } + + } + + @Override + public String fileUpload(File file) { + log.info("file->{}", file); + //判断为空 + try { + log.info("fileName->{}", file.getName()); + //操作文件 包括上传文件,存储数据库等 + return doFile(file); + } catch (Exception e) { + throw new BizException("文件上传失败"); + } + } + + /** + * 通过网络路径上传文件 + * + * @param url 文件网格路径 + * @return 访问路径 + */ + @Override + public String fileUpload(String url) { + if (ObjectUtil.isEmpty(url)) { + throw new BizException("文件路径不能为空"); + } + + //不是以https 开头的 + if (!url.startsWith("http")) { + throw new BizException("网络路径必须以http开始"); + } + + // 进来第一步判断有不有相同的网络路径 + FileManage fileManage = getOne(Wrappers.lambdaQuery(FileManage.class).eq(FileManage::getSrc, url).last("limit 1")); + if (ObjectUtil.isNotEmpty(fileManage)) { + // 图片访问路径 + return fileManage.getAccessPath(); + } + + try { + //建立远程连接 + HttpURLConnection httpUrlConnection = doConnection(url); + BufferedInputStream bin = new BufferedInputStream(httpUrlConnection.getInputStream()); + + //取文件后缀名 + String fileSufName = FileTypeUtil.getSuffixFromUrl(url); + if (ObjectUtil.isEmpty(fileSufName)) { + fileSufName = getFileSufName(new URL(url).openStream()); + } + if (ObjectUtil.isEmpty(fileSufName)) { + fileSufName = FileTypeUtil.getMIMETypeFromUrl(url); + } + if (ObjectUtil.isEmpty(fileSufName)) { + fileSufName = FileTypeUtil.getMIMETypeFromInputStream(bin); + } + if (ObjectUtil.isEmpty(fileSufName)) { + fileSufName = url.substring(url.lastIndexOf(".") + 1); + } + + if (ObjectUtil.isEmpty(fileSufName)) { + throw new BizException("FILE_URL_ERROR"); + } + //网络文件名不方便取也不重要,用路径拼接后缀代替文件名 + //filePath: 自定义文件存位置 + String fileName = aliyunFilePath + FileUtil.getRandomNumber(5) + "." + fileSufName; + + //创建文件 + File file = cn.hutool.core.io.FileUtil.touch(new File(fileName)); + file = cn.hutool.core.io.FileUtil.writeFromStream(bin, file); + + return doFile(file); + } catch (Exception e) { + e.printStackTrace(); + throw new BizException("文件上传失败"); + } + } + + @Override + public String fileUploadByBase64(String base64) throws IOException { + + return fileUpload(FileUtil.base64ToMultipartFile(base64)); + } + + @Override + public List fileUploadBatch(MultipartFile[] files) { + + List urlList = new ArrayList<>(); + //判断为空 + if (ObjectUtil.isEmpty(files)) { + throw new BizException("文件不存在"); + } + try { + for (MultipartFile file : files) { + //判断为空 + if (file.isEmpty()) { + throw new BizException("文件不存在"); + } + String path = doFile(FileUtil.multipartFileToFile(file, aliyunFilePath)); + + urlList.add(path); + } + + return urlList; + } catch (Exception e) { + throw new BizException("文件上传失败"); + } + } + + /** + * @param contextPath 网格路径 + * @return 真实地址 + * @throws Exception 连接失败 + */ + private HttpURLConnection doConnection(String contextPath) throws Exception { + // 统一资源 + URL url = new URL(contextPath); + // 连接类的父类,抽象类 + URLConnection urlConnection = url.openConnection(); + // http的连接类 + HttpURLConnection httpUrlConnection = (HttpURLConnection) urlConnection; + //设置超时 + httpUrlConnection.setConnectTimeout(1000 * 5); + //设置请求方式,默认是GET + httpUrlConnection.setRequestMethod("GET"); + // 设置字符编码 + httpUrlConnection.setRequestProperty("Charset", "UTF-8"); + + return httpUrlConnection; + } + + private String getFileSufName(InputStream is) throws IOException { + byte[] b = new byte[3]; + is.read(b, 0, b.length); + //头文件 + String headCode = FileUtil.bytesToHexString(b); + headCode = headCode.toUpperCase(); + log.info("头文件代码:" + headCode); + String sufName = FileUtil.checkType(headCode); + log.info("文件后缀:" + sufName); + + return sufName; + } + + /** + * 文件流上传 + * + * @param file 文件 + * @return 文件访问地址 + */ + private String doFile(File file) { + return doFile(file, false); + } + + /** + * 文件流上传 + * + * @param file 文件 + * @param isEndurance 持久化 + * @return 文件访问地址 + */ + private String doFile(File file, boolean isEndurance) { + if (ObjectUtil.isEmpty(file)) { + throw new BizException("FILE_URL_ERROR"); + } + + if (true) { + return "https://fileupload.cqdlcy.com/files/everlast/20220411/2022-04-11_15%7E06%7E44_75423.png?w=461&h=461"; + } + //判断是否有相同文件已经上传 把文件转成Md5 + String md5 = SecureUtil.md5(file); + // 数据库有相同的文件 云查的数据库 + String path = validFileIsExist(md5); + if (!ObjectUtil.isNull(path)) { + return path; + } + + //重命名文件 + String fileName = file.getName(); + log.info("fileName->{}", fileName); + int index = fileName.lastIndexOf("."); + if (index == -1) { + // 根据文件流的头部信息获得文件类型 + fileName += "." + cn.hutool.core.io.FileUtil.getType(file); + } + index = fileName.lastIndexOf("."); + if (index == -1) { + throw new BizException("UPLOAD_TYPE_ERROR"); + } + String newFileName = FileUtil.getRandomNumber(5) + fileName.substring(index); + + InputStream inputStream = cn.hutool.core.io.FileUtil.getInputStream(file); + log.info("inputStream->{},newFileName->{}", inputStream, newFileName); + //上传文件 + Integer integer = booleanToInteger(FileUtil.getImg(file)); + + String imgUrl = aliYunService.ossUploadEverlast(inputStream, newFileName, integer); + log.info("imgUrl->{}", imgUrl); + if (isEndurance) { + //将上传成功的文件存入数据库 + FileManage fileManage = new FileManage(); + fileManage.setName(fileName); + fileManage.setUploadName(newFileName); + fileManage.setType(cn.hutool.core.io.FileUtil.getType(file)); + fileManage.setSize(cn.hutool.core.io.FileUtil.size(file)); + fileManage.setSuffix(fileName.substring(fileName.lastIndexOf("."))); + fileManage.setMd5(md5); + fileManage.setAccessPath(imgUrl); + fileManage.setIsPicture(integer); + log.info("存入数据库的实例 fileManage->{}", fileManage); + save(fileManage); + } + cn.hutool.core.io.FileUtil.del(file); + return imgUrl; + + } + + /** + * 通过文件的md5 判断是否有相同的文件 + * + * @param md5 文件md5内容 + * @return 有就返回访问路径,没有返回null + */ + private String validFileIsExist(String md5) { + log.info("md5->{}", md5); + + FileManage manage = getOne(new LambdaQueryWrapper<>(FileManage.class).eq(FileManage::getMd5, md5).last("limit 1")); + + log.info("md5查询相同数据->{}", md5); + if (ObjectUtil.isNull(manage)) { + return null; + } + // 图片访问路径 + return manage.getAccessPath(); + } + + /** + * 将布尔值转换为整型 + * + * @param bl Boolean类型 + * @return true返回1 false返回0 + */ + private Integer booleanToInteger(boolean bl) { + if (bl) { + return 1; + } + + return 0; + } +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/controller/ImportController.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/controller/ImportController.java new file mode 100644 index 0000000..5a148a0 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/controller/ImportController.java @@ -0,0 +1,179 @@ +package com.ssdmn.biz.gupiao.controller; + +import cn.hutool.core.date.DateUtil; +import com.alibaba.excel.EasyExcel; +import com.ssdmn.biz.gupiao.listener.DongCaiSecondaryIndustryListener; +import com.ssdmn.biz.gupiao.listener.HighLowOfThreeHundredListener; +import com.ssdmn.biz.gupiao.listener.OriginalIssueStockListener; +import com.ssdmn.biz.gupiao.listener.TransactionRecordListener; +import com.ssdmn.biz.gupiao.pojo.domain.DongCaiSecondaryIndustry; +import com.ssdmn.biz.gupiao.pojo.domain.HighLowOfThreeHundred; +import com.ssdmn.biz.gupiao.pojo.domain.OriginalIssueStock; +import com.ssdmn.biz.gupiao.pojo.request.TransactionRecordImport; +import com.ssdmn.biz.gupiao.service.DongCaiSecondaryIndustryService; +import com.ssdmn.biz.gupiao.service.HighLowOfThreeHundredService; +import com.ssdmn.biz.gupiao.service.OriginalIssueStockService; +import com.ssdmn.biz.gupiao.service.TransactionRecordService; +import com.ssdmn.common.annotation.combination.PathRestController; +import com.ssdmn.common.exception.BizException; +import com.ssdmn.common.interceptor.UserCache; +import com.ssdmn.common.interceptor.annotation.AuthUser; +import com.ssdmn.common.result.Result; +import com.ssdmn.common.result.ResultConstants; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestPart; +import org.springframework.web.multipart.MultipartFile; + +import javax.annotation.Resource; +import java.io.InputStream; +import java.util.Date; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import static com.ssdmn.common.result.ResultConstants.FAIL; +import static com.ssdmn.common.result.Results.ok; + +/** + * @Description: 数据导入模块 + * @Author: fan + * @Version: 1.0 + * @CreateTime: 2023/5/7 17:15 + */ +@Api(tags = "数据导入模块") +@PathRestController("/import") +@Slf4j +public class ImportController { + + @Resource + DongCaiSecondaryIndustryService dongCaiSecondaryIndustryService; + + @Resource + HighLowOfThreeHundredService highLowOfThreeHundredService; + + @Resource + OriginalIssueStockService originalIssueStockService; + + @Resource + TransactionRecordService transactionRecordService; + + + @PostMapping(value = "/dongCaiSecondary") + @ApiOperation("东财二级行业指数每日成交情况导入") + public Result dongCaiSecondary(@RequestPart("file") MultipartFile file) { + Date date = checkFileType(file); + + try { + InputStream is = file.getInputStream(); + EasyExcel.read(is, DongCaiSecondaryIndustry.class, new DongCaiSecondaryIndustryListener(dongCaiSecondaryIndustryService, date)) + .sheet() + .headRowNumber(1) + .doRead(); + + } catch (Exception e) { + e.printStackTrace(); + throw new BizException(FAIL,e.getMessage()); + } + + return ok("导入成功"); + } + + @PostMapping(value = "/highLow") + @ApiOperation("创300天新高新低导入") + public Result highLow(@RequestPart("file") MultipartFile file) { + Date date = checkFileType(file); + + try { + InputStream is = file.getInputStream(); + EasyExcel.read(is, HighLowOfThreeHundred.class, new HighLowOfThreeHundredListener(highLowOfThreeHundredService, date)) + .sheet() + .headRowNumber(1) + .doRead(); + + } catch (Exception e) { + e.printStackTrace(); + throw new BizException(FAIL,e.getMessage()); + } + + return ok("导入成功"); + } + + @PostMapping(value = "/originalIssueStock") + @ApiOperation("动量原始股导入") + public Result originalIssueStock(@RequestPart("file") MultipartFile file) { + + Date date = checkFileType(file); + + try { + InputStream is = file.getInputStream(); + EasyExcel.read(is, OriginalIssueStock.class, new OriginalIssueStockListener(originalIssueStockService, date)) + .sheet() + .headRowNumber(1) + .doRead(); + + } catch (Exception e) { + e.printStackTrace(); + throw new BizException(FAIL,e.getMessage()); + } + + return ok("导入成功"); + } + + + @PostMapping(value = "/transactionRecord") + @ApiOperation("交易记录导入") + public Result transactionRecord(@RequestPart("file") MultipartFile file, @AuthUser UserCache userCache) { + + String fileName = file.getOriginalFilename(); + if (!fileName.matches("^.+\\.(?i)(xls)$") && !fileName.matches("^.+\\.(?i)(xlsx)$")) { + throw new BizException(ResultConstants.SYSTEM_PARAM_FAIL); + } + + try { + InputStream is = file.getInputStream(); + EasyExcel.read(is, TransactionRecordImport.class, new TransactionRecordListener(transactionRecordService,userCache)) + .sheet() + .headRowNumber(1) + .doRead(); + + } catch (Exception e) { + e.printStackTrace(); + throw new BizException(FAIL,e.getMessage()); + } + + return ok("导入成功"); + + } + + /** + * 检查文件类型 + * + * @param file 文件 + * @return 日期 + */ + private Date checkFileType(MultipartFile file) { + String fileName = file.getOriginalFilename(); + if (!fileName.matches("^.+\\.(?i)(xls)$") && !fileName.matches("^.+\\.(?i)(xlsx)$")) { + throw new BizException(ResultConstants.SYSTEM_PARAM_FAIL); + } + + Pattern p = Pattern.compile("(\\d{8})"); + Matcher m = p.matcher(fileName); + if (m.find()) { + if(!m.group(0).startsWith("20")){ + throw new BizException(500,"文件名不符合规范"); + } + String group = m.group(0); + String year = group.substring(0, 4); + String month = group.substring(4, 6); + String day = group.substring(6, 8); + + return DateUtil.parseDate(year +"-"+ month + "-"+day); + } + + throw new BizException(500,"文件名不符合规范"); + + } +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/controller/MyCollectController.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/controller/MyCollectController.java new file mode 100644 index 0000000..0401557 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/controller/MyCollectController.java @@ -0,0 +1,66 @@ +package com.ssdmn.biz.gupiao.controller; + +import com.ssdmn.biz.gupiao.pojo.request.MyCollectRequest; +import com.ssdmn.biz.gupiao.pojo.request.QueryIsCollectRequest; +import com.ssdmn.biz.gupiao.pojo.request.QueryMyCollectPageRequest; +import com.ssdmn.biz.gupiao.pojo.response.MyCollectPageResponse; +import com.ssdmn.biz.gupiao.service.MyCollectService; +import com.ssdmn.common.annotation.combination.PathRestController; +import com.ssdmn.common.interceptor.UserCache; +import com.ssdmn.common.interceptor.annotation.AuthUser; +import com.ssdmn.common.page.PageList; +import com.ssdmn.common.result.Result; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; + +import javax.annotation.Resource; + +import static com.ssdmn.common.result.Results.ok; + +/** + * @Description: + * @Author: fan + * @Version: 1.0 + * @CreateTime: 2023/5/9 11:49 + */ +@Api(tags = "我的收藏") +@PathRestController("/collect") +@Slf4j +public class MyCollectController { + + @Resource + MyCollectService myCollectService; + + @PostMapping(value = "/add") + @ApiOperation("添加我的收藏") + public Result addMyCollect(@AuthUser UserCache userCache,@RequestBody MyCollectRequest myCollectRequest) { + + myCollectService.addMyCollect(userCache,myCollectRequest); + return ok(); + } + + @PostMapping(value = "/del") + @ApiOperation("删除我的收藏") + public Result delMyCollect(@AuthUser UserCache userCache,@RequestBody MyCollectRequest myCollectRequest) { + + myCollectService.removeMyCollect(userCache,myCollectRequest); + return ok(); + } + + @PostMapping(value = "/query") + @ApiOperation("查询我的收藏") + public Result> queryMyCollect(@AuthUser UserCache userCache,@RequestBody QueryMyCollectPageRequest queryMyCollectPageRequest) { + + return ok(myCollectService.queryMyCollect(userCache,queryMyCollectPageRequest)); + } + + @PostMapping(value = "/queryIsCollect") + @ApiOperation("查询是否收藏某个code") + public Result queryIsCollect(@AuthUser UserCache userCache,@RequestBody QueryIsCollectRequest queryIsCollectRequest) { + + return ok(myCollectService.queryIsCollect(userCache,queryIsCollectRequest)); + } +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/controller/StockController.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/controller/StockController.java new file mode 100644 index 0000000..9343664 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/controller/StockController.java @@ -0,0 +1,153 @@ +package com.ssdmn.biz.gupiao.controller; + +import com.ssdmn.biz.gupiao.pojo.domain.OriginalIssueStock; +import com.ssdmn.biz.gupiao.pojo.request.*; +import com.ssdmn.biz.gupiao.pojo.response.*; +import com.ssdmn.biz.gupiao.service.DongCaiSecondaryIndustryService; +import com.ssdmn.biz.gupiao.service.OriginalIssueStockService; +import com.ssdmn.common.annotation.combination.PathRestController; +import com.ssdmn.common.interceptor.UserCache; +import com.ssdmn.common.interceptor.annotation.AuthUser; +import com.ssdmn.common.page.PageList; +import com.ssdmn.common.page.PageModel; +import com.ssdmn.common.result.Result; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiImplicitParams; +import io.swagger.annotations.ApiOperation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; +import java.util.Date; + +import static com.ssdmn.common.result.Results.ok; + +@Api(tags = "股票模块") +@PathRestController("/stock") +@Slf4j +public class StockController { + + @Resource + OriginalIssueStockService originalIssueStockService; + + @Resource + DongCaiSecondaryIndustryService dongCaiSecondaryIndustryService; + + + @PostMapping(value = "/trendPlate") + @ApiOperation("趋势板块") + public Result> trendPlate(@RequestBody DongCaiSecondaryIndustryRequest dongCaiSecondaryIndustryRequest) { + + + return ok(dongCaiSecondaryIndustryService.trendPlate(dongCaiSecondaryIndustryRequest)); + } + + + @PostMapping(value = "/riseLossesStatistics") + @ApiOperation("首页-涨跌统计") + public Result riseLossesStatistics(@AuthUser UserCache userCache, @RequestBody OriginalIssueStockRequest originalIssueStockRequest) { + + return ok(originalIssueStockService.riseLossesStatistics(userCache,originalIssueStockRequest)); + } + + @PostMapping(value = "/appRiseLossesStatistics") + @ApiOperation("app-大盘信息-个股信息涨跌统计") + public Result appRiseLossesStatistics(@AuthUser UserCache userCache, @RequestBody OriginalIssueStockRequest originalIssueStockRequest) { + + return ok(originalIssueStockService.appRiseLossesStatistics(userCache,originalIssueStockRequest)); + } + + @PostMapping(value = "/appBigDetail") + @ApiOperation("app首页-大盘信息-详情") + public Result appBigDetail(@RequestBody OriginalIssueStockRequest originalIssueStockRequest) { + + + return ok(originalIssueStockService.appBigDetail(originalIssueStockRequest)); + } + + @PostMapping(value = "/kLine") + @ApiOperation("首页-k线图") + public Result kLine(@RequestBody KLineRequest kLineRequest) { + + return ok(originalIssueStockService.kLine(kLineRequest.getSecurityType(),kLineRequest.getSecurityCode())); + } + + @PostMapping(value = "/stockDetails") + @ApiOperation("首页-个股详情") + public Result> stockDetails(@RequestBody PageModel pageModel) { + + + return ok(originalIssueStockService.stockDetails(pageModel)); + } + + @PostMapping(value = "/riseLossesDetail") + @ApiOperation("个股涨跌分页查询") + public Result> riseLossesDetail(@RequestBody OriginalIssueStockRequestPage originalIssueStockRequestPage) { + + + return ok(originalIssueStockService.riseLossesDetail(originalIssueStockRequestPage)); + } + + @PostMapping(value = "/originalIssueStockDetail") + @ApiOperation("个股详情分页查询") + public Result> originalIssueStockDetail(@RequestBody OriginalIssueStockDetailPage originalIssueStockDetailPage) { + + return ok(originalIssueStockService.originalIssueStockDetail(originalIssueStockDetailPage)); + } + + @PostMapping(value = "/getByCode") + @ApiOperation("每日个股详情") + public Result getByCode(@AuthUser UserCache userCache,@RequestBody OriginalIssueStockDetail originalIssueStockDetail) { + + return ok(originalIssueStockService.getByCode(userCache,originalIssueStockDetail)); + } + + @GetMapping(value = "/stockSortChange/{securityCode}") + @ApiOperation("个股排名变化") + public Result stockSortChange(@PathVariable("securityCode") String securityCode) { + + + return ok(originalIssueStockService.stockSortChange(securityCode)); + } + + /*******************************块板*************************************/ + + @GetMapping(value = "/dongCaiSortChange/{securityCode}") + @ApiOperation("块板排名变化") + public Result dongCaiSortChange(@PathVariable("securityCode") String securityCode) { + + return ok(dongCaiSecondaryIndustryService.dongCaiSortChange(securityCode)); + } + + @GetMapping(value = "/dongCaiSortChange/kLine/{securityCode}") + @ApiOperation("块板指数(k线图)") + public Result dongCaiKLine(@PathVariable("securityCode") String securityCode) { + + + return ok(dongCaiSecondaryIndustryService.dongCaiKLine(securityCode)); + } + + @GetMapping(value = "/dongCaiSortChange/riseLossesStatistics") + @ApiOperation("块板涨跌区域统计") + @ApiImplicitParams({ + @ApiImplicitParam(name = "securityCode", value = "code", required = true, dataType = "string"), + @ApiImplicitParam(name = "day", value = "日期", required = false, dataType = "date",format = "yyyy-MM-dd" ) + }) + public Result dongCaiRiseLossesStatistics(@AuthUser UserCache userCache,String securityCode,Date day) { + + + return ok(dongCaiSecondaryIndustryService.dongCaiRiseLossesStatistics(userCache,securityCode,day)); + } + + @GetMapping(value = "/dongCaiSortChange/dongCaiRiseLosses") + @ApiOperation("块板涨/跌停个股") + public Result> dongCaiRiseLosses(DongCaiRiseLossesRequest dongCaiRiseLossesRequest) { + + return ok(dongCaiSecondaryIndustryService.dongCaiRiseLosses(dongCaiRiseLossesRequest)); + } + + +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/controller/TransactionRecordController.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/controller/TransactionRecordController.java new file mode 100644 index 0000000..b9c3ee7 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/controller/TransactionRecordController.java @@ -0,0 +1,105 @@ +package com.ssdmn.biz.gupiao.controller; + +import cn.hutool.core.util.ObjectUtil; +import com.ssdmn.biz.gupiao.pojo.domain.TransactionRecord; +import com.ssdmn.biz.gupiao.pojo.domain.TransactionRecordTemp; +import com.ssdmn.biz.gupiao.pojo.request.EarningsLineRequest; +import com.ssdmn.biz.gupiao.pojo.request.ProfitAndLossRequest; +import com.ssdmn.biz.gupiao.pojo.request.TransactionRecordPageRequest; +import com.ssdmn.biz.gupiao.pojo.response.EarningsLineResponse; +import com.ssdmn.biz.gupiao.pojo.response.TotalEarningsResponse; +import com.ssdmn.biz.gupiao.service.TransactionRecordService; +import com.ssdmn.common.annotation.combination.PathRestController; +import com.ssdmn.common.date.DateUtil; +import com.ssdmn.common.interceptor.UserCache; +import com.ssdmn.common.interceptor.annotation.AuthUser; +import com.ssdmn.common.page.PageList; +import com.ssdmn.common.result.Result; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiImplicitParams; +import io.swagger.annotations.ApiOperation; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; +import java.util.Date; +import java.util.List; + +import static com.ssdmn.common.result.Results.ok; + +/** + * @Description: 交易记录 + * @Author: fan + * @Version: 1.0 + * @CreateTime: 2023/5/9 21:02 + */ +@Api(tags = "交易记录") +@PathRestController("/transaction") +@Slf4j +public class TransactionRecordController { + + @Resource + TransactionRecordService transactionRecordService; + + @PostMapping(value = "/saveOrUpdate") + @ApiOperation("保存或修改交易记录") + public Result saveOrUpdate(@AuthUser UserCache userCache, @RequestBody TransactionRecord transactionRecord) { + transactionRecord.setUserId(userCache.getUserId()); + transactionRecordService.saveOrUpdateTransactionRecord(transactionRecord); + + return ok("操作成功"); + } + + @PostMapping(value = "/delByIds") + @ApiOperation("批量删除交易记录") + @ApiImplicitParams({ + @ApiImplicitParam(name = "ids", value = "ID集合", required = true, allowMultiple = true, dataType = "long", paramType = "query") + }) + public Result delByIds(@AuthUser UserCache userCache, @RequestParam("ids") List ids) { + + transactionRecordService.delByIds(userCache,ids); + + return ok("操作成功"); + } + + @GetMapping(value = "/getDetailById/{id:\\d+}") + @ApiOperation("查看交易详情") + public Result getDetailById(@PathVariable("id") Long id) { + + return ok(transactionRecordService.getDetailById(id)); + } + + @PostMapping(value = "/findPage") + @ApiOperation("分页查询") + public Result> findPage(@RequestBody TransactionRecordPageRequest transactionRecordPageRequest) { + if(ObjectUtil.isNotEmpty(transactionRecordPageRequest.getNum())) { + transactionRecordPageRequest.setEndTime(new Date()); + transactionRecordPageRequest.setStartTime(DateUtil.offsetDay(new Date(), transactionRecordPageRequest.getNum()* -1)); + } + return ok(transactionRecordService.findPage(transactionRecordPageRequest)); + } + + + @PostMapping(value = "/totalEarnings") + @ApiOperation("总收益") + public Result totalEarnings(@AuthUser UserCache userCache, String securityCode) { + + return ok(transactionRecordService.totalEarnings(userCache,securityCode)); + } + + @PostMapping(value = "/earningsLine") + @ApiOperation("收益折线图") + public Result earningsLine(@AuthUser UserCache userCache, @RequestBody EarningsLineRequest earningsLineRequest) { + + return ok(transactionRecordService.EarningsLine(userCache,earningsLineRequest)); + } + + @PostMapping(value = "/profitOrLoss") + @ApiOperation("盈亏个股分页查询") + public Result> profitOrLoss(@AuthUser UserCache userCache , @RequestBody ProfitAndLossRequest profitAndLossRequest) { + + return ok(transactionRecordService.profitOrLoss(userCache,profitAndLossRequest)); + } + +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/listener/DongCaiSecondaryIndustryListener.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/listener/DongCaiSecondaryIndustryListener.java new file mode 100644 index 0000000..81f8a89 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/listener/DongCaiSecondaryIndustryListener.java @@ -0,0 +1,87 @@ +package com.ssdmn.biz.gupiao.listener; + +import cn.hutool.log.Log; +import cn.hutool.log.LogFactory; +import com.alibaba.excel.context.AnalysisContext; +import com.alibaba.excel.exception.ExcelDataConvertException; +import com.alibaba.excel.metadata.data.ReadCellData; +import com.alibaba.excel.read.listener.ReadListener; +import com.ssdmn.biz.gupiao.pojo.domain.DongCaiSecondaryIndustry; +import com.ssdmn.biz.gupiao.service.DongCaiSecondaryIndustryService; +import com.ssdmn.common.date.DateUtil; +import com.ssdmn.common.exception.BizException; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Map; + +/** + * @Description: + * @Author: fan + * @Version: 1.0 + * @CreateTime: 2023/5/7 19:21 + */ +public class DongCaiSecondaryIndustryListener implements ReadListener { + + Log log = LogFactory.get(DongCaiSecondaryIndustryListener.class); + + DongCaiSecondaryIndustryService dongCaiSecondaryIndustryService; + Date date; + + /** + * 缓存数据 + */ + private final List cachedDataList = new ArrayList<>(); + + public DongCaiSecondaryIndustryListener(DongCaiSecondaryIndustryService dongCaiSecondaryIndustryService,Date date) { + this.dongCaiSecondaryIndustryService = dongCaiSecondaryIndustryService; + this.date = date; + } + + @Override + public void invoke(DongCaiSecondaryIndustry data, AnalysisContext context) { + + // 数据来源:东方财富Choice数据 <--脏数据 + if(!data.getSecurityCode().endsWith("EI")){ + return; + } + + + data.setBusinessDate(date); + data.setSort(context.readRowHolder().getRowIndex()); + + String securityCode = data.getSecurityCode(); + String[] split = securityCode.split("\\."); + + data.setSecurityCode(split[0]); + if(split.length>1){ + data.setSecurityType(split[1]); + } + + + cachedDataList.add(data); + } + + @Override + public void doAfterAllAnalysed(AnalysisContext analysisContext) { + + dongCaiSecondaryIndustryService.importExcel(cachedDataList,date); + + log.info("{}条数据,开始存储数据库!", cachedDataList.size()); + } + + @Override + public void onException(Exception exception, AnalysisContext context) throws Exception { + + throw new BizException(500,"第"+(context.readRowHolder().getRowIndex() + 1 )+"行" + + ((ExcelDataConvertException) exception).getColumnIndex() +"列" + + exception.getMessage()); + } + + @Override + public boolean hasNext(AnalysisContext context) { + System.out.println(context); + return true; + } +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/listener/HighLowOfThreeHundredListener.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/listener/HighLowOfThreeHundredListener.java new file mode 100644 index 0000000..bb804f2 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/listener/HighLowOfThreeHundredListener.java @@ -0,0 +1,72 @@ +package com.ssdmn.biz.gupiao.listener; + +import cn.hutool.log.Log; +import cn.hutool.log.LogFactory; +import com.alibaba.excel.context.AnalysisContext; +import com.alibaba.excel.exception.ExcelDataConvertException; +import com.alibaba.excel.read.listener.ReadListener; +import com.ssdmn.biz.gupiao.pojo.domain.HighLowOfThreeHundred; +import com.ssdmn.biz.gupiao.service.HighLowOfThreeHundredService; +import com.ssdmn.common.date.DateUtil; +import com.ssdmn.common.exception.BizException; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +/** + * @Description: + * @Author: fan + * @Version: 1.0 + * @CreateTime: 2023/5/7 19:21 + */ +public class HighLowOfThreeHundredListener implements ReadListener { + + Log log = LogFactory.get(HighLowOfThreeHundredListener.class); + + HighLowOfThreeHundredService highLowOfThreeHundredService; + Date date; + + /** + * 缓存数据 + */ + private final List cachedDataList = new ArrayList<>(); + + public HighLowOfThreeHundredListener(HighLowOfThreeHundredService highLowOfThreeHundredService,Date date) { + this.highLowOfThreeHundredService = highLowOfThreeHundredService; + this.date = date; + } + + @Override + public void invoke(HighLowOfThreeHundred data, AnalysisContext analysisContext) { + + data.setBusinessDate(date); + String securityCode = data.getSecurityCode(); + String[] split = securityCode.split("\\."); + + data.setSecurityCode(split[0]); + if(split.length>1){ + data.setSecurityType(split[1]); + } + + cachedDataList.add(data); + } + + @Override + public void doAfterAllAnalysed(AnalysisContext analysisContext) { + highLowOfThreeHundredService.importExcel(cachedDataList,date); + log.info("{}条数据,开始存储数据库!", cachedDataList.size()); + } + + @Override + public void onException(Exception exception, AnalysisContext context) throws Exception { + throw new BizException(500,"第"+(context.readRowHolder().getRowIndex() + 1 )+"行" + + ((ExcelDataConvertException) exception).getColumnIndex() +"列" + + exception.getMessage()); + } + + @Override + public boolean hasNext(AnalysisContext context) { + return true; + } +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/listener/OriginalIssueStockListener.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/listener/OriginalIssueStockListener.java new file mode 100644 index 0000000..7f52065 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/listener/OriginalIssueStockListener.java @@ -0,0 +1,99 @@ +package com.ssdmn.biz.gupiao.listener; + +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.log.Log; +import cn.hutool.log.LogFactory; +import com.alibaba.excel.context.AnalysisContext; +import com.alibaba.excel.exception.ExcelDataConvertException; +import com.alibaba.excel.metadata.data.CellData; +import com.alibaba.excel.read.listener.ReadListener; +import com.alibaba.excel.read.metadata.holder.ReadHolder; +import com.alibaba.excel.read.metadata.holder.xlsx.XlsxReadSheetHolder; +import com.ssdmn.biz.gupiao.pojo.domain.OriginalIssueStock; +import com.ssdmn.biz.gupiao.service.OriginalIssueStockService; +import com.ssdmn.common.date.DateUtil; +import com.ssdmn.common.exception.BizException; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +/** + * @Description: + * @Author: fan + * @Version: 1.0 + * @CreateTime: 2023/5/7 17:39 + */ +public class OriginalIssueStockListener implements ReadListener { + + Log log = LogFactory.get(OriginalIssueStockListener.class); + + OriginalIssueStockService originalIssueStockService; + Date date; + + /** + * 缓存数据 + */ + private final List cachedDataList = new ArrayList<>(); + + public OriginalIssueStockListener(OriginalIssueStockService originalIssueStockService,Date date) { + this.originalIssueStockService = originalIssueStockService; + this.date = date; + } + + @Override + public void onException(Exception exception, AnalysisContext context) throws Exception { + + throw new BizException(500,"第"+(context.readRowHolder().getRowIndex() + 1 )+"行" + + ((ExcelDataConvertException) exception).getColumnIndex() +"列" + + exception.getMessage()); + } + + @Override + public void invoke(OriginalIssueStock data, AnalysisContext context) { + + /* + 上证:后缀.SH的股票 + 深证:后缀.SZ的股票 + 创业板:30开始的股票 + 科创板:688开始的股票 + */ + data.setBusinessDate(date); + data.setSort(context.readRowHolder().getRowIndex()); + String securityCode = data.getSecurityCode(); + String[] split = securityCode.split("\\."); + data.setSecurityCode(split[0]); + if(securityCode.startsWith("688")){ + data.setSecurityType("688"); + } else if (securityCode.startsWith("30")) { + data.setSecurityType("30"); + }else { + + if(split.length>1){ + data.setSecurityType(split[1]); + } + } + + /* + 上证指数 000001.SH + 创业板 399006.SZ + 深证成指 399001.SZ + 科创板 000688.SH + */ + + cachedDataList.add(data); + } + + + @Override + public void doAfterAllAnalysed(AnalysisContext context) { + + originalIssueStockService.importExcel(cachedDataList, date); + log.info("{}条数据,开始存储数据库!", cachedDataList.size()); + } + + @Override + public boolean hasNext(AnalysisContext context) { + return true; + } +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/listener/TransactionRecordListener.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/listener/TransactionRecordListener.java new file mode 100644 index 0000000..48f34ec --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/listener/TransactionRecordListener.java @@ -0,0 +1,112 @@ +package com.ssdmn.biz.gupiao.listener; + +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.log.Log; +import cn.hutool.log.LogFactory; +import com.alibaba.excel.context.AnalysisContext; +import com.alibaba.excel.exception.ExcelDataConvertException; +import com.alibaba.excel.read.listener.ReadListener; +import com.ssdmn.biz.gupiao.pojo.domain.TransactionRecord; +import com.ssdmn.biz.gupiao.pojo.request.TransactionRecordImport; +import com.ssdmn.biz.gupiao.service.TransactionRecordService; +import com.ssdmn.common.date.DateUtil; +import com.ssdmn.common.exception.BizException; +import com.ssdmn.common.interceptor.UserCache; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.List; + +/** + * @Description: 交易记录导入 + * @Author: fan + * @Version: 1.0 + * @CreateTime: 2023/5/9 19:33 + */ +public class TransactionRecordListener implements ReadListener { + + Log log = LogFactory.get(TransactionRecordListener.class); + + // -1 + BigDecimal NEGATIVE_ONE = new BigDecimal("-1"); + + + /** + * 缓存数据 + */ + private final List cachedDataList = new ArrayList<>(); + + private final TransactionRecordService transactionRecordService; + private final UserCache userCache; + + + public TransactionRecordListener(TransactionRecordService transactionRecordService,UserCache userCache) { + this.transactionRecordService = transactionRecordService; + this.userCache = userCache; + } + + @Override + public void invoke(TransactionRecordImport data, AnalysisContext analysisContext) { + TransactionRecord record = new TransactionRecord(); + record.setUserId(userCache.getUserId()); + + // 股票代码 + String securityCode = data.getSecurityCode(); + + if(ObjectUtil.isEmpty(securityCode) || !securityCode.contains(".")){ + throw new BizException(500,"股票代码不能为空"); + } + String[] split = securityCode.split("\\."); + + record.setSecurityCode(split[0]); + if(split.length>1){ + record.setSecurityType(split[1]); + } + + record.setTransactionDate(DateUtil.parse(data.getTransactionDate(),"yyyy-MM-dd")); + record.setTransactionTime(DateUtil.parse(data.getTransactionDate(),"yyyy-MM-dd HH:mm:ss")); + + // 手续费 14520(5%) + String premium = data.getPremium(); + String[] split1 = premium.split("\\("); + + record.setPremium(new BigDecimal(split1[0])); + if(split1.length>1){ + record.setPremiumRatio(new BigDecimal(split1[1].replaceAll("%","").replaceAll("\\)",""))); + } + + if("证券买入".equals(data.getTransactionCategory())){ + + record.setTransactionAmount(NEGATIVE_ONE.multiply(data.getTransactionAmount())); + }else{ + record.setTransactionAmount(data.getTransactionAmount()); + } + + record.setTransactionCategory(data.getTransactionCategory()); + record.setSecurityName(data.getSecurityName()); + record.setTransactionPrice(data.getTransactionPrice()); + record.setVolume(data.getVolume()); + + cachedDataList.add(record); + } + + @Override + public void doAfterAllAnalysed(AnalysisContext analysisContext) { + + transactionRecordService.importExcel(cachedDataList); + log.info("{}条数据,开始存储数据库!", cachedDataList.size()); + } + + @Override + public void onException(Exception exception, AnalysisContext context) throws Exception { + + throw new BizException(500,"第"+(context.readRowHolder().getRowIndex() + 1 )+"行" + + ((ExcelDataConvertException) exception).getColumnIndex() +"列" + + exception.getMessage()); + } + + @Override + public boolean hasNext(AnalysisContext context) { + return true; + } +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/mapper/DongCaiSecondaryIndustryMapper.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/mapper/DongCaiSecondaryIndustryMapper.java new file mode 100644 index 0000000..146c47e --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/mapper/DongCaiSecondaryIndustryMapper.java @@ -0,0 +1,33 @@ +package com.ssdmn.biz.gupiao.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.ssdmn.biz.gupiao.pojo.domain.DongCaiSecondaryIndustry; +import com.ssdmn.biz.gupiao.pojo.request.QueryMyCollectPageRequest; +import com.ssdmn.biz.gupiao.pojo.response.MyCollectPageResponse; +import com.ssdmn.common.page.PageModel; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.Date; + +@Mapper +public interface DongCaiSecondaryIndustryMapper extends BaseMapper { + + + /** + * 趋势板块查询 + * @param pageInfo 分页 + * @param firstDate 第一个日期 + * @param secondDate 第二个日期 + * @param sortSort 排序的排序 + * @param sortChangeSort 排序变化的排序 + */ + Page trendPlate(@Param("page") Page pageInfo, + @Param("firstDate")Date firstDate, + @Param("secondDate")Date secondDate, + @Param("sortSort") String sortSort, + @Param("sortChangeSort")String sortChangeSort); + + +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/mapper/DongCaiSecondaryIndustryMapper.xml b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/mapper/DongCaiSecondaryIndustryMapper.xml new file mode 100644 index 0000000..c7c2ba3 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/mapper/DongCaiSecondaryIndustryMapper.xml @@ -0,0 +1,47 @@ + + + + + + + + + diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/mapper/HighLowOfThreeHundredMapper.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/mapper/HighLowOfThreeHundredMapper.java new file mode 100644 index 0000000..731c3f6 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/mapper/HighLowOfThreeHundredMapper.java @@ -0,0 +1,9 @@ +package com.ssdmn.biz.gupiao.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.ssdmn.biz.gupiao.pojo.domain.HighLowOfThreeHundred; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface HighLowOfThreeHundredMapper extends BaseMapper { +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/mapper/MyCollectMapper.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/mapper/MyCollectMapper.java new file mode 100644 index 0000000..0ffac16 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/mapper/MyCollectMapper.java @@ -0,0 +1,38 @@ +package com.ssdmn.biz.gupiao.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.ssdmn.biz.gupiao.pojo.domain.MyCollect; +import com.ssdmn.biz.gupiao.pojo.request.QueryMyCollectPageRequest; +import com.ssdmn.biz.gupiao.pojo.response.MyCollectPageResponse; +import com.ssdmn.common.page.PageModel; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +@Mapper +public interface MyCollectMapper extends BaseMapper { + + /** + * 个人收藏个股查询 + * + * @param pageInfo + * @param queryMyCollectPageRequest + * @return + */ + Page myStockCollectPage( + @Param("page") Page pageInfo, + @Param("queryMyCollectPageRequest") QueryMyCollectPageRequest queryMyCollectPageRequest, + @Param("userId") Long userId); + + /** + * 个人收藏板块查询 + * + * @param pageInfo + * @param queryMyCollectPageRequest + * @return + */ + Page myBigCollectPage( + @Param("page") Page pageInfo, + @Param("queryMyCollectPageRequest") QueryMyCollectPageRequest queryMyCollectPageRequest, + @Param("userId") Long userId); +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/mapper/MyCollectMapper.xml b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/mapper/MyCollectMapper.xml new file mode 100644 index 0000000..d32e45b --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/mapper/MyCollectMapper.xml @@ -0,0 +1,88 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/mapper/OriginalIssueStockMapper.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/mapper/OriginalIssueStockMapper.java new file mode 100644 index 0000000..63cd758 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/mapper/OriginalIssueStockMapper.java @@ -0,0 +1,35 @@ +package com.ssdmn.biz.gupiao.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.ssdmn.biz.gupiao.pojo.domain.OriginalIssueStock; +import com.ssdmn.biz.gupiao.pojo.response.DayAndPlate; +import com.ssdmn.common.page.PageModel; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.Date; +import java.util.List; +import java.util.Set; + +@Mapper +public interface OriginalIssueStockMapper extends BaseMapper { + + + /** + * 统计几天几板 + * @param businessDate 日期 + * @param securityCodeSet 股票code集合 + */ + List statisticsDayAndPlate(@Param("businessDate") Date businessDate , @Param("securityCodeSet") Set securityCodeSet); + + + Page findPage(@Param("page") Page pageInfo, + @Param("businessDate") Date businessDate, + @Param("securityType") String securityType, + @Param("riseOrLosses") Integer riseOrLosses, + @Param("dayAndPlateSort") Integer dayAndPlateSort, + @Param("dongCaiIndustryIndexCode2") String dongCaiIndustryIndexCode2, + @Param("keyWord") String keyWord); + +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/mapper/OriginalIssueStockMapper.xml b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/mapper/OriginalIssueStockMapper.xml new file mode 100644 index 0000000..1bafb17 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/mapper/OriginalIssueStockMapper.xml @@ -0,0 +1,76 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/mapper/StockNameMapper.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/mapper/StockNameMapper.java new file mode 100644 index 0000000..aa6691e --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/mapper/StockNameMapper.java @@ -0,0 +1,9 @@ +package com.ssdmn.biz.gupiao.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.ssdmn.biz.gupiao.pojo.domain.StockName; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface StockNameMapper extends BaseMapper { +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/mapper/TransactionRecordMapper.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/mapper/TransactionRecordMapper.java new file mode 100644 index 0000000..7b6360a --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/mapper/TransactionRecordMapper.java @@ -0,0 +1,9 @@ +package com.ssdmn.biz.gupiao.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.ssdmn.biz.gupiao.pojo.domain.TransactionRecord; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface TransactionRecordMapper extends BaseMapper { +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/mapper/TransactionRecordTempMapper.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/mapper/TransactionRecordTempMapper.java new file mode 100644 index 0000000..02af51b --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/mapper/TransactionRecordTempMapper.java @@ -0,0 +1,20 @@ +package com.ssdmn.biz.gupiao.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.ssdmn.biz.gupiao.pojo.domain.TransactionRecordTemp; +import com.ssdmn.biz.gupiao.pojo.response.QueryBuyOrSell; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.Date; +import java.util.List; + +@Mapper +public interface TransactionRecordTempMapper extends BaseMapper { + + List queryBuyOrSell( @Param("userId") Long userId, + @Param("extreme") String extreme, + @Param("transactionCategory") String transactionCategory, + @Param("startDate") Date startDate, + @Param("endDate") Date endDate); +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/mapper/TransactionRecordTempMapper.xml b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/mapper/TransactionRecordTempMapper.xml new file mode 100644 index 0000000..768d676 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/mapper/TransactionRecordTempMapper.xml @@ -0,0 +1,25 @@ + + + + + + + \ No newline at end of file diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/domain/DongCaiSecondaryIndustry.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/domain/DongCaiSecondaryIndustry.java new file mode 100644 index 0000000..7a7902f --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/domain/DongCaiSecondaryIndustry.java @@ -0,0 +1,132 @@ +package com.ssdmn.biz.gupiao.pojo.domain; + +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +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.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.math.BigDecimal; +import java.util.Date; + +/** + * @Description: 东财二级行业指数每日成交情况 + * @Author: fan + * @Version: 1.0 + * @CreateTime: 2023/5/7 15:20 + */ +@TableName("dong_cai_secondary_industry") +@Data +@ExcelIgnoreUnannotated +public class DongCaiSecondaryIndustry { + + @TableId(type = IdType.AUTO) + private Long id; + + @ApiModelProperty("业务日期") + @JsonFormat(pattern = "yyyy-MM-dd") + private Date businessDate; + + @ApiModelProperty("排名(序号)") + private Integer sort; + + @ApiModelProperty("证券代码") + @ExcelProperty(index = 0) + private String securityCode; + + @ApiModelProperty("证券名称") + @ExcelProperty(index = 1) + private String securityName; + + @ApiModelProperty("证券类型") + private String securityType; + + @ApiModelProperty("成份个数") + @ExcelProperty(index = 2) + private Integer numberIngredients; + + @ApiModelProperty("开盘价") + @ExcelProperty(index = 3) + private BigDecimal openingPrice; + + @ApiModelProperty("收盘价") + @ExcelProperty(index = 4) + private BigDecimal closingPrice; + + @ApiModelProperty("成交量(股)") + @ExcelProperty(index = 5) + private BigDecimal volume; + + @ApiModelProperty("成交额(百万元)") + @ExcelProperty(index = 6) + private BigDecimal turnover; + + @ApiModelProperty("总市值(百万元)") + @ExcelProperty(index = 7) + private BigDecimal totalCapitalization; + + @ApiModelProperty("自由流通市值(百万元)") + @ExcelProperty(index = 8) + private BigDecimal freeCapitalization; + + @ApiModelProperty("涨跌幅(%)") + @ExcelProperty(index = 9) + private BigDecimal changePercentage; + + @ApiModelProperty("最高价") + @ExcelProperty(index = 10) + private BigDecimal highestPrice; + + @ApiModelProperty("最低价") + @ExcelProperty(index = 11) + private BigDecimal lowestPrice; + + @ApiModelProperty("上涨家数") + @ExcelProperty(index = 12) + private String riseNumber; + + @ApiModelProperty("下跌家数") + @ExcelProperty(index = 13) + private String lossesNumber; + + @ApiModelProperty("平盘家数") + @ExcelProperty(index = 14) + private String flatNumber; + + @ApiModelProperty("涨停家数") + @ExcelProperty(index = 15) + private String riseStopNumber; + + @ApiModelProperty("跌停家数") + @ExcelProperty(index = 16) + private String lossesStopNumber; + + @ApiModelProperty("停牌家数") + @ExcelProperty(index = 17) + private String stopNumber; + + @ApiModelProperty("近期创历史新高") + @ExcelProperty(index = 18) + private String recentlyHigh; + + @ApiModelProperty("近期创历史新低") + @ExcelProperty(index = 19) + private String recentlyLow; + + @ApiModelProperty("市盈率PE(TTM)") + @ExcelProperty(index = 20) + private String peTtm; + + @ApiModelProperty("市盈率PE中位值(TTM)") + @ExcelProperty(index = 21) + private String peCenterTtm; + + @ApiModelProperty("排序变化") + @TableField(exist=false) + private Integer sortChange; + +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/domain/HighLowOfThreeHundred.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/domain/HighLowOfThreeHundred.java new file mode 100644 index 0000000..e119dd5 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/domain/HighLowOfThreeHundred.java @@ -0,0 +1,95 @@ +package com.ssdmn.biz.gupiao.pojo.domain; + +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.math.BigDecimal; +import java.util.Date; + +/** + * @Description: 创300天新高新低 + * @Author: fan + * @Version: 1.0 + * @CreateTime: 2023/5/7 15:39 + */ +@TableName("high_low_of_three_hundred") +@Data +@ExcelIgnoreUnannotated +public class HighLowOfThreeHundred { + + @TableId(type = IdType.AUTO) + private Long id; + + @ApiModelProperty("业务日期") + private Date businessDate; + + @ApiModelProperty("证券代码") + @ExcelProperty(index = 0) + private String securityCode; + + @ApiModelProperty("证券名称") + @ExcelProperty(index = 1) + private String securityName; + + @ApiModelProperty("证券类型") + private String securityType; + + + @ApiModelProperty("开盘价") + @ExcelProperty(index = 2) + private BigDecimal openingPrice; + + @ApiModelProperty("收盘价") + @ExcelProperty(index = 3) + private BigDecimal closingPrice; + + @ApiModelProperty("最高价") + @ExcelProperty(index = 4) + private BigDecimal highestPrice; + + + @ApiModelProperty("涨跌幅(%)") + @ExcelProperty(index = 5) + private BigDecimal changePercentage; + + @ApiModelProperty("成交量(股)") + @ExcelProperty(index = 6) + private String volume; + + @ApiModelProperty("近期创阶段新高最新[近1日内]") + @ExcelProperty(index = 7) + private String recentlyHigh; + + @ApiModelProperty("东财行业指数2级") + @ExcelProperty(index = 8) + private String dongCaiIndustryIndexLevel2; + + @ApiModelProperty("区间最高价日") + @ExcelProperty(index = 9) + private String highestPriceOfRange; + + @ApiModelProperty("上市天数") + @ExcelProperty(index = 10) + private Integer dayOnMarket; + + @ApiModelProperty("近期创阶段新低") + @ExcelProperty(index = 11) + private String hitLowRecently; + + @ApiModelProperty("区间最低价日") + @ExcelProperty(index = 12) + private String lowestPriceRange; + + @ApiModelProperty("是否为ST股票 1-是 0-否") + @ExcelProperty(index = 13) + private String isStStock; + + @ApiModelProperty("是否为*ST股票 1-是 0-否") + @ExcelProperty(index = 14) + private String isStarStStock; +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/domain/MyCollect.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/domain/MyCollect.java new file mode 100644 index 0000000..ae1fb6f --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/domain/MyCollect.java @@ -0,0 +1,41 @@ +package com.ssdmn.biz.gupiao.pojo.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.time.LocalDateTime; +import java.util.Date; + +/** + * @Description: 我的收藏 + * @Author: fan + * @Version: 1.0 + * @CreateTime: 2023/5/9 6:04 + */ +@Data +@TableName("my_collect") +public class MyCollect { + + @TableId(type = IdType.AUTO) + private Long id; + + @ApiModelProperty("用户Id") + private Long userId; + + @ApiModelProperty("股票Code") + private String securityCode; + + @ApiModelProperty("股票名称") + private String securityName; + + @ApiModelProperty("证券类型") + private String securityType; + + @ApiModelProperty("收藏日期") + private Date collectDate; + + private LocalDateTime createTime; +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/domain/OriginalIssueStock.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/domain/OriginalIssueStock.java new file mode 100644 index 0000000..0b50a91 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/domain/OriginalIssueStock.java @@ -0,0 +1,133 @@ +package com.ssdmn.biz.gupiao.pojo.domain; + +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +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.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.math.BigDecimal; +import java.util.Date; +import java.util.Objects; + +/** + * @Description: 动量原始股 + * @Author: fan + * @Version: 1.0 + * @CreateTime: 2023/5/7 14:59 + */ +@Data +@TableName("original_issue_stock") +@ExcelIgnoreUnannotated +public class OriginalIssueStock { + + @TableId(type = IdType.AUTO) + private Long id; + + @ApiModelProperty("业务日期") + @JsonFormat(pattern = "yyyy-MM-dd",timezone="GMT+8" ) + private Date businessDate; + + @ApiModelProperty("排名(序号)") + private Integer sort; + + @ApiModelProperty("证券代码") + @ExcelProperty(index = 0) + private String securityCode; + + @ApiModelProperty("证券名称") + @ExcelProperty(index = 1) + private String securityName; + + @ApiModelProperty("证券类型") + private String securityType; + + @ApiModelProperty("开盘价") + @ExcelProperty(index = 2) + private BigDecimal openingPrice; + + @ApiModelProperty("收盘价") + @ExcelProperty(index = 3) + private BigDecimal closingPrice; + + @ApiModelProperty("最高价") + @ExcelProperty(index = 4) + private BigDecimal highestPrice; + + @ApiModelProperty("最低价") + @ExcelProperty(index = 5) + private BigDecimal lowestPrice; + + @ApiModelProperty("当日涨跌幅") + @ExcelProperty(index = 6) + private BigDecimal riseLossesCurrentDay; + + @ApiModelProperty("东财行业指数2级") + @ExcelProperty(index = 7) + private String dongCaiIndustryIndexLevel2; + + @ApiModelProperty("20日区间涨跌幅") + @ExcelProperty(index = 8) + private BigDecimal rangeRiseLosses20; + + @ApiModelProperty("10日区间涨跌幅") + @ExcelProperty(index = 9) + private BigDecimal rangeRiseLosses10; + + @ApiModelProperty("60日区间涨跌幅") + @ExcelProperty(index = 10) + private BigDecimal rangeRiseLosses60; + + @ApiModelProperty("首发上市日期") + @ExcelProperty(index = 11) + private Date initialListingDate; + + @ApiModelProperty("可交易日数") + @ExcelProperty(index = 12) + private Integer numberDaysAvailable; + + @ApiModelProperty("成交量") + @ExcelProperty(index = 13) + private String volume; + + @ApiModelProperty("成交额") + @ExcelProperty(index = 14) + private String turnover; + + @ApiModelProperty("所属东财行业指数代码[行业类别]2级") + @ExcelProperty(index = 15) + private String dongCaiIndustryIndexCode2; + + @ApiModelProperty("自由流通市值") + @ExcelProperty(index = 16) + private String freeCapitalization; + + @ApiModelProperty("20日区间平均成交量") + @ExcelProperty(index = 17) + private String averageVolumeDay20; + + @ApiModelProperty("机构持仓合计") + @ExcelProperty(index = 18) + private String totalInstitutionalPositions; + + @ApiModelProperty("东财行业指数3级") + @ExcelProperty(index = 19) + private String dongCaiIndustryIndexLevel3; + + @ApiModelProperty("是否涨停 1-是 0-否") + @ExcelProperty(index = 20) + private String riseStop; + + @ApiModelProperty("是否跌停 1-是 0-否") + @ExcelProperty(index = 21) + private String lossesStop; + + @ApiModelProperty("几天几板") + @TableField(exist = false) + private String dayAndPlate; + +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/domain/StockName.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/domain/StockName.java new file mode 100644 index 0000000..2f706af --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/domain/StockName.java @@ -0,0 +1,56 @@ +package com.ssdmn.biz.gupiao.pojo.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.time.LocalDateTime; +import java.util.Objects; + +/** + * @Description: 股票表 + * @Author: fan + * @Version: 1.0 + * @CreateTime: 2023/5/9 6:05 + */ +@TableName("stock_name") +@Data +public class StockName { + + @TableId(type = IdType.AUTO) + private Long id; + + @ApiModelProperty("证券代码") + private String securityCode; + + @ApiModelProperty("证券名称") + private String securityName; + + @ApiModelProperty("证券类型") + private String securityType; + + @ApiModelProperty("'来源数据库表名'") + private String tableName; + + @ApiModelProperty("东财") + private String dongCaiSecurityCode; + private String dongCaiSecurityName; + + private LocalDateTime createTime; + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + StockName that = (StockName) o; + return securityCode.equals(that.securityCode) && securityType.equals(that.securityType); + } + + @Override + public int hashCode() { + return Objects.hash(securityCode, securityType); + } + +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/domain/TransactionRecord.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/domain/TransactionRecord.java new file mode 100644 index 0000000..9b3a469 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/domain/TransactionRecord.java @@ -0,0 +1,69 @@ +package com.ssdmn.biz.gupiao.pojo.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.Date; + +/** + * @Description: 交易记录 + * @Author: fan + * @Version: 1.0 + * @CreateTime: 2023/5/9 19:19 + */ +@TableName("transaction_record") +@Data +public class TransactionRecord { + + @TableId(type = IdType.AUTO) + private Long id; + + @ApiModelProperty("用户Id") + private Long userId; + + @ApiModelProperty("交易日期") + @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8") + @DateTimeFormat(pattern = "yyyy-MM-dd") + private Date transactionDate; + + @ApiModelProperty("交易时间") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date transactionTime; + + @ApiModelProperty("证券代码") + private String securityCode; + + @ApiModelProperty("证券名称") + private String securityName; + + @ApiModelProperty("证券类型") + private String securityType; + + @ApiModelProperty("成交价") + private BigDecimal transactionPrice; + + @ApiModelProperty("成交量") + private BigDecimal volume; + + @ApiModelProperty("成交金额") + private BigDecimal transactionAmount; + + @ApiModelProperty("手续费") + private BigDecimal premium; + + @ApiModelProperty("手续费率") + private BigDecimal premiumRatio; + + @ApiModelProperty("交易类型") + private String transactionCategory; + + private LocalDateTime createTime; +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/domain/TransactionRecordTemp.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/domain/TransactionRecordTemp.java new file mode 100644 index 0000000..f12596d --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/domain/TransactionRecordTemp.java @@ -0,0 +1,60 @@ +package com.ssdmn.biz.gupiao.pojo.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.math.BigDecimal; +import java.util.Date; + +/** + * @Description: 个人盈亏临时表 + * @Author: fan + * @Version: 1.0 + * @CreateTime: 2023/5/10 21:31 + */ +@Data +@TableName("transaction_record_temp") +public class TransactionRecordTemp { + + @TableId(type = IdType.AUTO) + private Long id; + + @ApiModelProperty("用户Id") + private Long userId; + + @ApiModelProperty("证券代码") + private String securityCode; + + @ApiModelProperty("证券名称") + private String securityName; + + @ApiModelProperty("证券类型") + private String securityType; + + @ApiModelProperty("东财行业指数2级") + private String dongCaiIndustryIndexLevel2; + + @ApiModelProperty("所属东财行业指数代码[行业类别]2级") + private String dongCaiIndustryIndexCode2; + + @ApiModelProperty("总收益") + private BigDecimal totalMoney; + + @ApiModelProperty("买入日期") + @JsonFormat(pattern = "yyyy-MM-dd",timezone="GMT+8" ) + private Date buyDate; + + @ApiModelProperty("买入价格") + private BigDecimal buyPrice; + + @ApiModelProperty("卖出日期") + @JsonFormat(pattern = "yyyy-MM-dd",timezone="GMT+8" ) + private Date sellDate; + + @ApiModelProperty("卖出价格") + private BigDecimal sellPrice; +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/request/BigStockDataRequest.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/request/BigStockDataRequest.java new file mode 100644 index 0000000..513294f --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/request/BigStockDataRequest.java @@ -0,0 +1,18 @@ +package com.ssdmn.biz.gupiao.pojo.request; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.time.LocalDate; + +/** + * @Author 余洲 + * @Date 2023/5/6 8:28 + * @Version 1.0 + */ +@Data +public class BigStockDataRequest { + + @ApiModelProperty("日期") + private LocalDate localDate; +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/request/BrokenLineRequest.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/request/BrokenLineRequest.java new file mode 100644 index 0000000..2617d27 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/request/BrokenLineRequest.java @@ -0,0 +1,21 @@ +package com.ssdmn.biz.gupiao.pojo.request; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.util.Date; + +/** + * @Description: 收益折线图 + * @Author: fan + * @Version: 1.0 + * @CreateTime: 2023/5/11 20:02 + */ +@Data +public class BrokenLineRequest { + + + + + +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/request/DongCaiRiseLossesRequest.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/request/DongCaiRiseLossesRequest.java new file mode 100644 index 0000000..88a0534 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/request/DongCaiRiseLossesRequest.java @@ -0,0 +1,39 @@ +package com.ssdmn.biz.gupiao.pojo.request; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.ssdmn.common.page.PageModel; +import com.ssdmn.common.page.QueryField; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; + +import java.util.Date; + +/** + * @Description: + * @Author: fan + * @Version: 1.0 + * @CreateTime: 2023/5/30 20:30 + */ +@Data +public class DongCaiRiseLossesRequest { + + PageModel pageModel; + + @ApiModelProperty("业务日期") + @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8") + @DateTimeFormat(pattern = "yyyy-MM-dd") + @QueryField + Date businessDate; + + + @ApiModelProperty("证券代码") + @QueryField + private String securityCode; + + @ApiModelProperty("涨停 - 1 ,跌停 - 0") + Integer riseOrLosses; + + @ApiModelProperty("几天几板排序:升序-1 ,降序 - 0") + private Integer dayAndPlateSort; +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/request/DongCaiSecondaryIndustryRequest.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/request/DongCaiSecondaryIndustryRequest.java new file mode 100644 index 0000000..24850c5 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/request/DongCaiSecondaryIndustryRequest.java @@ -0,0 +1,31 @@ +package com.ssdmn.biz.gupiao.pojo.request; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.ssdmn.common.page.PageModel; +import com.ssdmn.common.page.QueryField; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; + +import java.util.Date; + +/** + * @Description: + * @Author: fan + * @Version: 1.0 + * @CreateTime: 2023/5/7 23:59 + */ +@Data +public class DongCaiSecondaryIndustryRequest { + + PageModel pageModel; + + @ApiModelProperty("业务日期") + @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8") + @DateTimeFormat(pattern = "yyyy-MM-dd") + @QueryField + Date businessDate; + + @ApiModelProperty("趋势类型 1-当日趋势 5-趋势 10-趋势 15-趋势 20-趋势 30-趋势") + Integer trendType; +} \ No newline at end of file diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/request/EarningsLineRequest.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/request/EarningsLineRequest.java new file mode 100644 index 0000000..95562b2 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/request/EarningsLineRequest.java @@ -0,0 +1,39 @@ +package com.ssdmn.biz.gupiao.pojo.request; + +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; + +import java.util.Date; + +/** + * @Description: 收益折线图 + * @Author: fan + * @Version: 1.0 + * @CreateTime: 2023/5/10 23:03 + */ +@Data +public class EarningsLineRequest { + + @ApiModelProperty("证券代码") + private String securityCode; + + @ApiModelProperty(value = "收益率-1,收益金额-2",required = true) + private Integer type; + + @ApiModelProperty("时间类型 1-三个月 2-一年") + private Integer timeType; + + @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8") + @DateTimeFormat(pattern = "yyyy-MM-dd") + private Date startDate; + + @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8") + @DateTimeFormat(pattern = "yyyy-MM-dd") + private Date endDate; + + + + +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/request/HighLowOfThreeHundredRequest.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/request/HighLowOfThreeHundredRequest.java new file mode 100644 index 0000000..8529a32 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/request/HighLowOfThreeHundredRequest.java @@ -0,0 +1,14 @@ +package com.ssdmn.biz.gupiao.pojo.request; + +import com.ssdmn.common.page.PageModel; +import lombok.Data; + +/** + * @Description: + * @Author: fan + * @Version: 1.0 + * @CreateTime: 2023/5/7 23:59 + */ +@Data +public class HighLowOfThreeHundredRequest extends PageModel { +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/request/KLineRequest.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/request/KLineRequest.java new file mode 100644 index 0000000..e115601 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/request/KLineRequest.java @@ -0,0 +1,19 @@ +package com.ssdmn.biz.gupiao.pojo.request; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * @Description: + * @Author: fan + * @Version: 1.0 + * @CreateTime: 2023/5/11 10:52 + */ +@Data +public class KLineRequest { + + @ApiModelProperty("股票类型:上证-SZ, 深证-SH,创业板-30,科创版-688") + String securityType; + @ApiModelProperty("股票Code") + String securityCode; +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/request/MyCollectRequest.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/request/MyCollectRequest.java new file mode 100644 index 0000000..b653dba --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/request/MyCollectRequest.java @@ -0,0 +1,20 @@ +package com.ssdmn.biz.gupiao.pojo.request; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * @Description: 我的收藏 + * @Author: fan + * @Version: 1.0 + * @CreateTime: 2023/5/9 12:25 + */ +@Data +public class MyCollectRequest { + + @ApiModelProperty("证券代码") + private String securityCode; + + @ApiModelProperty("证券类型") + private String securityType; +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/request/OriginalIssueStockDetail.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/request/OriginalIssueStockDetail.java new file mode 100644 index 0000000..95b78fe --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/request/OriginalIssueStockDetail.java @@ -0,0 +1,33 @@ +package com.ssdmn.biz.gupiao.pojo.request; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.ssdmn.common.page.QueryField; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; + +import java.util.Date; + +/** + * @Description: 个股详情查询 + * @Author: fan + * @Version: 1.0 + * @CreateTime: 2023/5/9 6:37 + */ +@Data +public class OriginalIssueStockDetail { + + @ApiModelProperty("股票代码或名称") + @QueryField + private String securityCode; + + @ApiModelProperty("股票代码或名称") + @QueryField + private String securityType; + + @ApiModelProperty("业务日期") + @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8") + @DateTimeFormat(pattern = "yyyy-MM-dd") + @QueryField + Date businessDate; +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/request/OriginalIssueStockDetailPage.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/request/OriginalIssueStockDetailPage.java new file mode 100644 index 0000000..4277de4 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/request/OriginalIssueStockDetailPage.java @@ -0,0 +1,40 @@ +package com.ssdmn.biz.gupiao.pojo.request; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.ssdmn.common.page.PageModel; +import com.ssdmn.common.page.QueryField; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; + +import java.util.Date; + +/** + * @Description: 个股详情分页查询 + * @Author: fan + * @Version: 1.0 + * @CreateTime: 2023/5/8 22:49 + */ +@Data +public class OriginalIssueStockDetailPage { + + PageModel pageModel; + + @ApiModelProperty("股票代码或名称") + private String keyWord; + + @ApiModelProperty("业务日期") + @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8") + @DateTimeFormat(pattern = "yyyy-MM-dd") + @QueryField + Date businessDate; + + @ApiModelProperty("证券类型") + @QueryField + private String securityType; + + @ApiModelProperty("几天几板排序:升序-1 ,降序 - 0") + private Integer dayAndPlateSort; + + +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/request/OriginalIssueStockRequest.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/request/OriginalIssueStockRequest.java new file mode 100644 index 0000000..d1df95c --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/request/OriginalIssueStockRequest.java @@ -0,0 +1,33 @@ +package com.ssdmn.biz.gupiao.pojo.request; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.ssdmn.common.page.QueryField; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; + +import java.util.Date; + +/** + * @Description: + * @Author: fan + * @Version: 1.0 + * @CreateTime: 2023/5/7 23:58 + */ +@Data +public class OriginalIssueStockRequest { + + @ApiModelProperty("业务日期") + @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8") + @DateTimeFormat(pattern = "yyyy-MM-dd") + @QueryField + Date businessDate; + + @ApiModelProperty("证券代码") + @QueryField + private String securityCode; + + @ApiModelProperty("股票类型 上证-SZ, 深证-SH,创业板-30,科创版-688 ") + @QueryField + String securityType; +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/request/OriginalIssueStockRequestPage.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/request/OriginalIssueStockRequestPage.java new file mode 100644 index 0000000..3e45784 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/request/OriginalIssueStockRequestPage.java @@ -0,0 +1,46 @@ +package com.ssdmn.biz.gupiao.pojo.request; + +import com.alibaba.excel.annotation.ExcelProperty; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.ssdmn.common.page.PageModel; +import com.ssdmn.common.page.QueryField; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; + +import java.util.Date; + +/** + * @Description: 分页查询原始服 + * @Author: fan + * @Version: 1.0 + * @CreateTime: 2023/5/8 22:36 + */ +@Data +public class OriginalIssueStockRequestPage { + + PageModel pageModel; + + @ApiModelProperty("业务日期") + @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8") + @DateTimeFormat(pattern = "yyyy-MM-dd") + @QueryField + Date businessDate; + + @ApiModelProperty("股票类型 上证-SZ, 深证-SH,创业板-30,科创版-688") + @QueryField + String securityType; + + @ApiModelProperty("证券代码") + @QueryField + private String securityCode; + + @ApiModelProperty("涨停 - 1 ,跌停 - 0") + Integer riseOrLosses; + + @ApiModelProperty("几天几板排序:升序-1 ,降序 - 0") + private Integer dayAndPlateSort; + + + +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/request/ProfitAndLossRequest.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/request/ProfitAndLossRequest.java new file mode 100644 index 0000000..8af282b --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/request/ProfitAndLossRequest.java @@ -0,0 +1,46 @@ +package com.ssdmn.biz.gupiao.pojo.request; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.ssdmn.common.page.PageModel; +import com.ssdmn.common.page.QueryField; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; + +import java.util.Date; + +/** + * @Description: 盈亏查询 + * @Author: fan + * @Version: 1.0 + * @CreateTime: 2023/5/10 22:10 + */ +@Data +public class ProfitAndLossRequest { + + PageModel pageModel; + + @ApiModelProperty("关键字查询") + private String keyWord; + + @ApiModelProperty("证券类型") + @QueryField + private String securityType; + + @ApiModelProperty("盈利-1 亏损 -0") + private Integer type; + + @ApiModelProperty("开始时间") + @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8") + @DateTimeFormat(pattern = "yyyy-MM-dd") + Date startDate; + + @ApiModelProperty("结束时间") + @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8") + @DateTimeFormat(pattern = "yyyy-MM-dd") + Date endDate; + + @ApiModelProperty(value = "金额排序 1-升序 0-降序",example = "1") + private Integer sortAmount; + +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/request/QueryIsCollectRequest.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/request/QueryIsCollectRequest.java new file mode 100644 index 0000000..3810f6f --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/request/QueryIsCollectRequest.java @@ -0,0 +1,23 @@ +package com.ssdmn.biz.gupiao.pojo.request; + +import com.ssdmn.common.page.QueryField; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * @Description: + * @Author: fan + * @Version: 1.0 + * @CreateTime: 2023/5/9 6:13 + */ +@Data +public class QueryIsCollectRequest { + + @ApiModelProperty("股票code") + @QueryField + private String securityCode; + + @ApiModelProperty("股票类型") + @QueryField + private String securityType; +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/request/QueryMyCollectPageRequest.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/request/QueryMyCollectPageRequest.java new file mode 100644 index 0000000..cd5d00e --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/request/QueryMyCollectPageRequest.java @@ -0,0 +1,40 @@ +package com.ssdmn.biz.gupiao.pojo.request; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.ssdmn.common.page.PageModel; +import com.ssdmn.common.page.QueryField; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; + +import java.util.Date; + +/** + * @Description: + * @Author: fan + * @Version: 1.0 + * @CreateTime: 2023/5/9 6:13 + */ +@Data +public class QueryMyCollectPageRequest { + + PageModel pageModel; + + @ApiModelProperty("股票代码或名称") + private String keyWord; + + @ApiModelProperty("业务日期") + @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8") + @DateTimeFormat(pattern = "yyyy-MM-dd") + @QueryField + Date businessDate; + + @ApiModelProperty("所属于大盘") + private String market; + + @ApiModelProperty(value = "板块-1 个股-2",required = true) + private Integer plateOrA; + + @ApiModelProperty("几天几板排序:升序-1 ,降序 - 0") + private Integer dayAndPlateSort; +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/request/SectorTrendsDataRequest.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/request/SectorTrendsDataRequest.java new file mode 100644 index 0000000..726ee17 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/request/SectorTrendsDataRequest.java @@ -0,0 +1,20 @@ +package com.ssdmn.biz.gupiao.pojo.request; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + + +/** + * @Author 余洲 + * @Date 2023/5/6 8:28 + * @Version 1.0 + */ +@Data +public class SectorTrendsDataRequest { + + @ApiModelProperty("当日趋势传1,5日趋势传5") + private Integer day; + + @ApiModelProperty("排行数量") + private Integer rankNum; +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/request/TransactionRecordImport.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/request/TransactionRecordImport.java new file mode 100644 index 0000000..0c0e229 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/request/TransactionRecordImport.java @@ -0,0 +1,52 @@ +package com.ssdmn.biz.gupiao.pojo.request; + +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.math.BigDecimal; +import java.util.Date; + +/** + * @Description: 交易记录导入实体类 + * @Author: fan + * @Version: 1.0 + * @CreateTime: 2023/5/9 21:41 + */ +@Data +@ExcelIgnoreUnannotated +public class TransactionRecordImport { + + @ApiModelProperty("交易日期") + @ExcelProperty(index = 0) + private String transactionDate; + + @ApiModelProperty("证券代码") + @ExcelProperty(index = 1) + private String securityCode; + + @ApiModelProperty("证券名称") + @ExcelProperty(index = 2) + private String securityName; + + @ApiModelProperty("成交价") + @ExcelProperty(index = 3) + private BigDecimal transactionPrice; + + @ApiModelProperty("成交量") + @ExcelProperty(index = 4) + private BigDecimal volume; + + @ApiModelProperty("成交金额") + @ExcelProperty(index = 5) + private BigDecimal transactionAmount; + + @ApiModelProperty("手续费") + @ExcelProperty(index = 6) + private String premium; + + @ApiModelProperty("交易类型") + @ExcelProperty(index = 7) + private String transactionCategory; +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/request/TransactionRecordPageRequest.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/request/TransactionRecordPageRequest.java new file mode 100644 index 0000000..e3a0d0d --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/request/TransactionRecordPageRequest.java @@ -0,0 +1,48 @@ +package com.ssdmn.biz.gupiao.pojo.request; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.ssdmn.common.page.PageModel; +import com.ssdmn.common.page.QueryField; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; + +import java.util.Date; + +/** + * @Description: 交易记录分页查询 + * @Author: fan + * @Version: 1.0 + * @CreateTime: 2023/5/9 20:02 + */ +@Data +public class TransactionRecordPageRequest { + + PageModel pageModel; + + @ApiModelProperty("股票代码或名称") + private String keyWord; + + @ApiModelProperty("交易日期") + @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8") + @DateTimeFormat(pattern = "yyyy-MM-dd") + @QueryField + private Date transactionDate; + + @ApiModelProperty("交易类型") + @QueryField + private String transactionCategory; + + @ApiModelProperty("起止时间") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date startTime; + + @ApiModelProperty("截止时间") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date endTime; + + @ApiModelProperty("近几天") + private Integer num; +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/response/AppBigDetailVO.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/response/AppBigDetailVO.java new file mode 100644 index 0000000..1e594ff --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/response/AppBigDetailVO.java @@ -0,0 +1,40 @@ +package com.ssdmn.biz.gupiao.pojo.response; + +import com.ssdmn.biz.gupiao.pojo.domain.DongCaiSecondaryIndustry; +import com.ssdmn.biz.gupiao.pojo.domain.OriginalIssueStock; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * @Description: app大盘信息详情 + * @Author: fan + * @Version: 1.0 + * @CreateTime: 2023/5/18 22:47 + */ +@Data +public class AppBigDetailVO { + + DongCaiSecondaryIndustry dongCaiSecondaryIndustry; + + @ApiModelProperty("成份个数") + int size ; + + @ApiModelProperty("涨") + long rise ; + + @ApiModelProperty("跌") + long losses ; + + @ApiModelProperty("平") + long flat ; + + @ApiModelProperty("涨停") + long riseStop; + + @ApiModelProperty("跌停") + long lossesStop; + + @ApiModelProperty("停牌") + long suspension; + +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/response/AppRiseLossesResponse.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/response/AppRiseLossesResponse.java new file mode 100644 index 0000000..bfb9562 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/response/AppRiseLossesResponse.java @@ -0,0 +1,19 @@ +package com.ssdmn.biz.gupiao.pojo.response; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.util.List; + +/** + * @Description: app-大盘信息-个股信息涨跌统计 + * @Author: fan + * @Version: 1.0 + * @CreateTime: 2023/5/11 22:13 + */ +@Data +public class AppRiseLossesResponse { + + @ApiModelProperty("结果集") + List list; +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/response/AppRiseLossesStatistics.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/response/AppRiseLossesStatistics.java new file mode 100644 index 0000000..a1f4f55 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/response/AppRiseLossesStatistics.java @@ -0,0 +1,31 @@ +package com.ssdmn.biz.gupiao.pojo.response; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.math.BigDecimal; + +/** + * @Description: app-大盘信息-个股信息涨跌统计 + * @Author: fan + * @Version: 1.0 + * @CreateTime: 2023/5/11 22:13 + */ +@Data +public class AppRiseLossesStatistics { + + @ApiModelProperty("涨") + private Integer riseNum; + + @ApiModelProperty("跌") + private Integer lossesNum; + + @ApiModelProperty("平") + private Integer equalNum; + + @ApiModelProperty("总金额") + private BigDecimal totalAmount; + + @ApiModelProperty("证券名称") + private String securityName; +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/response/BigStockDataDetailResponse.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/response/BigStockDataDetailResponse.java new file mode 100644 index 0000000..24593de --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/response/BigStockDataDetailResponse.java @@ -0,0 +1,75 @@ +package com.ssdmn.biz.gupiao.pojo.response; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.time.LocalDate; + +/** + * @Author 余洲 + * @Date 2023/5/6 8:34 + * @Version 1.0 + */ +@Data +public class BigStockDataDetailResponse { + + @ApiModelProperty("值") + private String value = "3238.68"; + + @ApiModelProperty("值改变类型 0-下跌 1-上升") + private Integer valueChangeType = 1; + + @ApiModelProperty("改变的值") + private Double valueChangeValue = 6.7D; + + @ApiModelProperty("值改变类型 0-下跌 1-上升") + private Integer percentageChangeType = 1; + + @ApiModelProperty("百分比改变的值") + private Double percentageChangeValue = 0.21; + + @ApiModelProperty("开盘价") + private Double openPrice = 3213.62; + + @ApiModelProperty("收盘价") + private Double closePrice = 3238.68; + + @ApiModelProperty("量") + private Double num = 1.48; + + @ApiModelProperty("额") + private Double price = 1919D; + + @ApiModelProperty("证券代码") + private String securityCode = "000001"; + + @ApiModelProperty("交易日期") + private LocalDate tradeDate = LocalDate.now(); + + @ApiModelProperty("最高价") + private Double maxPrice = 3270.54; + + @ApiModelProperty("最低价") + private Double minPrice = 3210.54; + + @ApiModelProperty("涨跌幅") + private Double chg = 5D; + + @ApiModelProperty("10日涨跌幅") + private Double chgWith10 = 10D; + + @ApiModelProperty("20日涨跌幅") + private Double chgWith20 = 20D; + + @ApiModelProperty("60日涨跌幅") + private Double chgWith60 = 30D; + + @ApiModelProperty("流通") + private Double circulate = 42.46; + + @ApiModelProperty("20日区间平均交易量") + private Double averageTradeWith20 = 60D; + + @ApiModelProperty("是否收藏 0-否 1-是") + private Integer isCollect = 0; +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/response/BigStockDataDetailResponseBak.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/response/BigStockDataDetailResponseBak.java new file mode 100644 index 0000000..50e79ff --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/response/BigStockDataDetailResponseBak.java @@ -0,0 +1,75 @@ +package com.ssdmn.biz.gupiao.pojo.response; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.time.LocalDate; + +/** + * @Author 余洲 + * @Date 2023/5/6 8:34 + * @Version 1.0 + */ +@Data +public class BigStockDataDetailResponseBak { + + @ApiModelProperty("值") + private String value; + + @ApiModelProperty("值改变类型 0-下跌 1-上升") + private Integer valueChangeType; + + @ApiModelProperty("改变的值") + private Double valueChangeValue; + + @ApiModelProperty("值改变类型 0-下跌 1-上升") + private Integer percentageChangeType; + + @ApiModelProperty("百分比改变的值") + private Double percentageChangeValue; + + @ApiModelProperty("开盘价") + private Double openPrice; + + @ApiModelProperty("收盘价") + private Double closePrice; + + @ApiModelProperty("量") + private Integer num; + + @ApiModelProperty("额") + private Integer price; + + @ApiModelProperty("证券代码") + private String securityCode; + + @ApiModelProperty("交易日期") + private LocalDate tradeDate; + + @ApiModelProperty("最高价") + private Double maxPrice; + + @ApiModelProperty("最低价") + private Double minPrice; + + @ApiModelProperty("涨跌幅") + private Double chg; + + @ApiModelProperty("10日涨跌幅") + private Double chgWith10; + + @ApiModelProperty("20日涨跌幅") + private Double chgWith20; + + @ApiModelProperty("60日涨跌幅") + private Double chgWith60; + + @ApiModelProperty("流通") + private Double circulate; + + @ApiModelProperty("20日区间平均交易量") + private Double averageTradeWith20; + + @ApiModelProperty("是否收藏 0-否 1-是") + private Integer isCollect; +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/response/BigStockDataResponse.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/response/BigStockDataResponse.java new file mode 100644 index 0000000..1af3ea2 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/response/BigStockDataResponse.java @@ -0,0 +1,31 @@ +package com.ssdmn.biz.gupiao.pojo.response; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.util.List; + +/** + * @Author 余洲 + * @Date 2023/5/6 8:34 + * @Version 1.0 + */ +@Data +public class BigStockDataResponse { + + @ApiModelProperty("涨") + private Integer riseNum; + + @ApiModelProperty("跌") + private Integer lossesNum; + + @ApiModelProperty("平") + private Integer equalNum; + + private List xAxis; + + private List data; + + @ApiModelProperty("是否收藏 0-否 1-是") + private Integer isCollect; +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/response/DayAndPlate.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/response/DayAndPlate.java new file mode 100644 index 0000000..c0c652d --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/response/DayAndPlate.java @@ -0,0 +1,23 @@ +package com.ssdmn.biz.gupiao.pojo.response; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * @Description: 几天几版 + * @Author: fan + * @Version: 1.0 + * @CreateTime: 2023/5/11 19:03 + */ +@Data +public class DayAndPlate { + + @ApiModelProperty("证券代码") + private String securityCode; + + @ApiModelProperty("天数") + private String dayNumber; + + @ApiModelProperty("板数") + private String plate; +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/response/DongCaiSecondaryIndustryResponse.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/response/DongCaiSecondaryIndustryResponse.java new file mode 100644 index 0000000..d9d1783 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/response/DongCaiSecondaryIndustryResponse.java @@ -0,0 +1,60 @@ +package com.ssdmn.biz.gupiao.pojo.response; + +import cn.hutool.core.util.ObjectUtil; +import com.alibaba.excel.annotation.ExcelProperty; +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; + +import java.util.Date; + +/** + * @Description: + * @Author: fan + * @Version: 1.0 + * @CreateTime: 2023/5/8 0:23 + */ +@Data +public class DongCaiSecondaryIndustryResponse { + + private Long id; + + @ApiModelProperty("业务日期") + @JsonFormat(pattern = "yyyy-MM-dd",timezone="GMT+8" ) + private Date businessDate; + + @ApiModelProperty("排名") + private Integer sort; + + @ApiModelProperty("板块排名变化") + private Integer plateUpOrDown; + + @ApiModelProperty("排名变化") + private Integer sortChange; + + @ApiModelProperty("证券代码") + private String securityCode; + + @ApiModelProperty("证券名称") + private String securityName; + + @ApiModelProperty("证券类型") + private String securityType; + + public Integer getPlateUpOrDown() { + if(ObjectUtil.isEmpty(plateUpOrDown)){ + return 0; + } + return plateUpOrDown; + } + + public Integer getSortChange() { + if(ObjectUtil.isEmpty(sortChange)){ + return 0; + } + return sortChange; + } +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/response/EarningsLineResponse.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/response/EarningsLineResponse.java new file mode 100644 index 0000000..0cb86e2 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/response/EarningsLineResponse.java @@ -0,0 +1,25 @@ +package com.ssdmn.biz.gupiao.pojo.response; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.math.BigDecimal; +import java.util.List; + +/** + * @Description: 收益拆线图 + * @Author: fan + * @Version: 1.0 + * @CreateTime: 2023/5/10 23:03 + */ +@Data +public class EarningsLineResponse { + +// @ApiModelProperty("日期列表") +// List businessDateList; +// +// @ApiModelProperty("数据列表") +// List valueList; + + +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/response/MyCollectPageResponse.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/response/MyCollectPageResponse.java new file mode 100644 index 0000000..3c7c097 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/response/MyCollectPageResponse.java @@ -0,0 +1,26 @@ +package com.ssdmn.biz.gupiao.pojo.response; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.ssdmn.biz.gupiao.pojo.domain.OriginalIssueStock; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.util.Date; + +/** + * @Description: + * @Author: fan + * @Version: 1.0 + * @CreateTime: 2023/5/9 6:24 + */ +@Data +public class MyCollectPageResponse extends OriginalIssueStock { + + @ApiModelProperty("股票ID") + private String stockId; + + @ApiModelProperty("收藏日期") + @JsonFormat(pattern = "yyyy-MM-dd",timezone="GMT+8" ) + private Date collectDate; + +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/response/OriginalIssueStockDetailResponse.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/response/OriginalIssueStockDetailResponse.java new file mode 100644 index 0000000..c84acb6 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/response/OriginalIssueStockDetailResponse.java @@ -0,0 +1,18 @@ +package com.ssdmn.biz.gupiao.pojo.response; + +import com.ssdmn.biz.gupiao.pojo.domain.OriginalIssueStock; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * @Description: + * @Author: fan + * @Version: 1.0 + * @CreateTime: 2023/5/10 15:11 + */ +@Data +public class OriginalIssueStockDetailResponse extends OriginalIssueStock { + + @ApiModelProperty("是否收藏 1-是") + private Integer isCollect; +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/response/OriginalIssueStockResponse.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/response/OriginalIssueStockResponse.java new file mode 100644 index 0000000..557ae26 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/response/OriginalIssueStockResponse.java @@ -0,0 +1,44 @@ +package com.ssdmn.biz.gupiao.pojo.response; + +import com.alibaba.excel.annotation.ExcelProperty; +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.math.BigDecimal; +import java.util.Date; + +/** + * @Description: + * @Author: fan + * @Version: 1.0 + * @CreateTime: 2023/5/8 0:47 + */ +@Data +public class OriginalIssueStockResponse { + + @TableId(type = IdType.AUTO) + private Long id; + + @ApiModelProperty("业务日期") + @JsonFormat(pattern = "yyyy-MM-dd",timezone="GMT+8" ) + private Date businessDate; + + @ApiModelProperty("证券代码") + private String securityCode; + + @ApiModelProperty("证券名称") + private String securityName; + + @ApiModelProperty("开盘价") + private BigDecimal openingPrice; + + @ApiModelProperty("当日涨跌幅") + private BigDecimal riseLossesCurrentDay; + + @ApiModelProperty("东财行业指数2级") + private String dongCaiIndustryIndexLevel2; + +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/response/OriginalIssueStockSortResponseChange.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/response/OriginalIssueStockSortResponseChange.java new file mode 100644 index 0000000..7ee585c --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/response/OriginalIssueStockSortResponseChange.java @@ -0,0 +1,19 @@ +package com.ssdmn.biz.gupiao.pojo.response; + +import lombok.Data; + +import java.util.Date; +import java.util.List; + +/** + * @Description: 个股排名变化 + * @Author: fan + * @Version: 1.0 + * @CreateTime: 2023/5/9 6:46 + */ +@Data +public class OriginalIssueStockSortResponseChange { + + private List xAxis; + private List yAxis; +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/response/QueryBuyOrSell.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/response/QueryBuyOrSell.java new file mode 100644 index 0000000..f6974e0 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/response/QueryBuyOrSell.java @@ -0,0 +1,42 @@ +package com.ssdmn.biz.gupiao.pojo.response; + +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; + +import java.math.BigDecimal; +import java.util.Date; + +/** + * @Description: + * @Author: fan + * @Version: 1.0 + * @CreateTime: 2023/5/10 21:47 + */ +@Data +public class QueryBuyOrSell { + + @ApiModelProperty("交易日期") + private Date transactionDate; + + @ApiModelProperty("证券代码") + private String securityCode; + + @ApiModelProperty("证券名称") + private String securityName; + + @ApiModelProperty("证券类型") + private String securityType; + + @ApiModelProperty("买入总数") + private BigDecimal buyMoney; + + @ApiModelProperty("成交价") + private BigDecimal transactionPrice; + + @ApiModelProperty("东财") + private String dongCaiSecurityCode; + private String dongCaiSecurityName; + +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/response/SectorTrendsDataResponse.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/response/SectorTrendsDataResponse.java new file mode 100644 index 0000000..f309399 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/response/SectorTrendsDataResponse.java @@ -0,0 +1,30 @@ +package com.ssdmn.biz.gupiao.pojo.response; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.time.LocalDate; + +/** + * @Author 余洲 + * @Date 2023/5/6 8:34 + * @Version 1.0 + */ +@Data +public class SectorTrendsDataResponse { + + @ApiModelProperty("交易日期") + private LocalDate tradeDate; + + @ApiModelProperty("板块名称") + private String blockName; + + @ApiModelProperty("板块排名") + private Integer blockRankNum; + + @ApiModelProperty("改变类型 0-下跌 1-上升") + private Integer changeType; + + @ApiModelProperty("改变的值") + private Integer changeValue; +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/response/StockKLineResponse.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/response/StockKLineResponse.java new file mode 100644 index 0000000..2b4c38b --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/response/StockKLineResponse.java @@ -0,0 +1,22 @@ +package com.ssdmn.biz.gupiao.pojo.response; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.util.List; + +/** + * @Description: + * @Author: fan + * @Version: 1.0 + * @CreateTime: 2023/5/10 19:34 + */ +@Data +public class StockKLineResponse { + + @ApiModelProperty("数据") + List valueCollect; + + @ApiModelProperty("日期") + List keyCollect; +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/response/StockKLineVO.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/response/StockKLineVO.java new file mode 100644 index 0000000..ca0fafa --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/response/StockKLineVO.java @@ -0,0 +1,41 @@ +package com.ssdmn.biz.gupiao.pojo.response; + +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.math.BigDecimal; +import java.util.Date; + +/** + * @Description: 首页k线图 + * @Author: fan + * @Version: 1.0 + * @CreateTime: 2023/5/10 19:04 + */ +@Data +public class StockKLineVO { + + @ApiModelProperty("业务日期") + @JsonFormat(pattern = "yyyy-MM-dd",timezone="GMT+8" ) + private Date businessDate; + + @ApiModelProperty("开盘价") + private BigDecimal openingPrice; + + @ApiModelProperty("收盘价") + private BigDecimal closingPrice; + + @ApiModelProperty("最高价") + private BigDecimal highestPrice; + + @ApiModelProperty("最低价") + private BigDecimal lowestPrice; + + @ApiModelProperty("当日涨跌幅") + private BigDecimal riseLossesCurrentDay; + + @ApiModelProperty("成交额") + private String turnover; + +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/response/TotalEarningsResponse.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/response/TotalEarningsResponse.java new file mode 100644 index 0000000..c8c99f4 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/pojo/response/TotalEarningsResponse.java @@ -0,0 +1,22 @@ +package com.ssdmn.biz.gupiao.pojo.response; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.math.BigDecimal; + +/** + * @Description: 总收益 + * @Author: fan + * @Version: 1.0 + * @CreateTime: 2023/5/10 12:30 + */ +@Data +public class TotalEarningsResponse { + + @ApiModelProperty("总盈亏") + private BigDecimal totalEarnings; + + @ApiModelProperty("收益率") + private BigDecimal earningsRate; +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/service/DongCaiSecondaryIndustryService.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/service/DongCaiSecondaryIndustryService.java new file mode 100644 index 0000000..69a8967 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/service/DongCaiSecondaryIndustryService.java @@ -0,0 +1,60 @@ +package com.ssdmn.biz.gupiao.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.ssdmn.biz.gupiao.pojo.domain.DongCaiSecondaryIndustry; +import com.ssdmn.biz.gupiao.pojo.domain.OriginalIssueStock; +import com.ssdmn.biz.gupiao.pojo.request.DongCaiRiseLossesRequest; +import com.ssdmn.biz.gupiao.pojo.request.DongCaiSecondaryIndustryRequest; +import com.ssdmn.biz.gupiao.pojo.response.BigStockDataResponse; +import com.ssdmn.biz.gupiao.pojo.response.DongCaiSecondaryIndustryResponse; +import com.ssdmn.biz.gupiao.pojo.response.OriginalIssueStockSortResponseChange; +import com.ssdmn.common.interceptor.UserCache; +import com.ssdmn.common.page.PageList; + +import java.util.Date; +import java.util.List; + +public interface DongCaiSecondaryIndustryService extends IService { + + + /** + * 导入数据 + * + * @param cachedDataList + */ + void importExcel(List cachedDataList,Date businessDate); + + /** + * 趋势板块 + * + * @param dongCaiSecondaryIndustryRequest + * @return + */ + PageList trendPlate(DongCaiSecondaryIndustryRequest dongCaiSecondaryIndustryRequest); + + /** + * 板块排名变化 + * @param securityCode code + * @return + */ + OriginalIssueStockSortResponseChange dongCaiSortChange(String securityCode); + + /** + * 块板k线图 + * @param securityCode 板块code + * @return + */ + Object[] dongCaiKLine(String securityCode); + + /** + * 块板涨跌区域统计 + * @param securityCode + * @return + */ + BigStockDataResponse dongCaiRiseLossesStatistics(UserCache userCache,String securityCode, Date day); + + /** + * 块板涨/跌停个股 + */ + PageList dongCaiRiseLosses(DongCaiRiseLossesRequest dongCaiRiseLossesRequest); +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/service/HighLowOfThreeHundredService.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/service/HighLowOfThreeHundredService.java new file mode 100644 index 0000000..3e327b0 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/service/HighLowOfThreeHundredService.java @@ -0,0 +1,16 @@ +package com.ssdmn.biz.gupiao.service; + +import com.ssdmn.biz.gupiao.pojo.domain.HighLowOfThreeHundred; + +import java.util.Date; +import java.util.List; + +public interface HighLowOfThreeHundredService { + + /** + * 导入数据 + * + * @param cachedDataList + */ + void importExcel(List cachedDataList, Date businessDate); +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/service/MyCollectService.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/service/MyCollectService.java new file mode 100644 index 0000000..075d7f1 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/service/MyCollectService.java @@ -0,0 +1,32 @@ +package com.ssdmn.biz.gupiao.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.ssdmn.biz.gupiao.pojo.domain.MyCollect; +import com.ssdmn.biz.gupiao.pojo.request.MyCollectRequest; +import com.ssdmn.biz.gupiao.pojo.request.QueryIsCollectRequest; +import com.ssdmn.biz.gupiao.pojo.request.QueryMyCollectPageRequest; +import com.ssdmn.biz.gupiao.pojo.response.MyCollectPageResponse; +import com.ssdmn.common.interceptor.UserCache; +import com.ssdmn.common.page.PageList; + +public interface MyCollectService extends IService { + + /** + * 添加我的收藏 + */ + void addMyCollect(UserCache userCache,MyCollectRequest myCollectRequest); + + /** + * 添加我的收藏 + */ + void removeMyCollect(UserCache userCache,MyCollectRequest myCollectRequest); + + /** + * 查询我的收藏 + * @param queryMyCollectPageRequest + * @return + */ + PageList queryMyCollect(UserCache userCache,QueryMyCollectPageRequest queryMyCollectPageRequest); + + Integer queryIsCollect(UserCache userCache, QueryIsCollectRequest queryIsCollectRequest); +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/service/OriginalIssueStockService.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/service/OriginalIssueStockService.java new file mode 100644 index 0000000..46919d3 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/service/OriginalIssueStockService.java @@ -0,0 +1,119 @@ +package com.ssdmn.biz.gupiao.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.ssdmn.biz.gupiao.pojo.domain.OriginalIssueStock; +import com.ssdmn.biz.gupiao.pojo.request.OriginalIssueStockDetail; +import com.ssdmn.biz.gupiao.pojo.request.OriginalIssueStockDetailPage; +import com.ssdmn.biz.gupiao.pojo.request.OriginalIssueStockRequest; +import com.ssdmn.biz.gupiao.pojo.request.OriginalIssueStockRequestPage; +import com.ssdmn.biz.gupiao.pojo.response.*; +import com.ssdmn.common.interceptor.UserCache; +import com.ssdmn.common.page.PageList; +import com.ssdmn.common.page.PageModel; + +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.Set; + +public interface OriginalIssueStockService extends IService { + + /** + * 导入数据 + * + * @param cachedDataList 数据 + * @param businessDate 日期 + */ + void importExcel(List cachedDataList, Date businessDate); + + /** + * 涨跌统计 + * + * @param userCache + * @param originalIssueStockRequest + * @return + */ + BigStockDataResponse riseLossesStatistics(UserCache userCache ,OriginalIssueStockRequest originalIssueStockRequest); + + /** + * app-大盘信息-个股信息涨跌统计 + * @param userCache + * @param originalIssueStockRequest + * @return + */ + AppRiseLossesResponse appRiseLossesStatistics(UserCache userCache, OriginalIssueStockRequest originalIssueStockRequest); + + /** + * app首页-大盘信息-详情 + * @param originalIssueStockRequest + * @return + */ + AppBigDetailVO appBigDetail(OriginalIssueStockRequest originalIssueStockRequest); + + /** + * 首页-个股详情 + * @param pageModel 分页 + * @return + */ + PageList stockDetails(PageModel pageModel); + + /** + * 个股涨跌分页查询 + * @param originalIssueStockRequestPage 分页 + * @return + */ + PageList riseLossesDetail(OriginalIssueStockRequestPage originalIssueStockRequestPage); + + /** + * 个股详情 + * @param originalIssueStockDetailPage 查询条件 + * @return + */ + PageList originalIssueStockDetail(OriginalIssueStockDetailPage originalIssueStockDetailPage); + + /** + * 每日个股详情 + * + * @param userCache + * @param originalIssueStockDetail + * @return + */ + OriginalIssueStockDetailResponse getByCode(UserCache userCache,OriginalIssueStockDetail originalIssueStockDetail); + + /** + * 个股排名变化 + * @param securityCode 股票代码 + */ + OriginalIssueStockSortResponseChange stockSortChange(String securityCode); + + /** + * @param securityType 股票大类 + * @param securityCode + * @return + */ + Object[] kLine(String securityType,String securityCode); + + /** + * 几天几板 + * @param securityCodeSet 股票代码 + */ + Map getDayAndPlateMap(Set securityCodeSet); + + /** + * @param pageModel 分页 + * @param businessDate 日期 + * @param securityType 类型 + * @param dayAndPlateSort 几天几板排序 + * @param dongCaiIndustryIndexCode2 东行二级code + * @param keyWord 关键字查询 + * @return 个股分页数据 + */ + PageList findPage(PageModel pageModel, + Date businessDate, + String securityType, + Integer riseOrLosses, + Integer dayAndPlateSort, + String dongCaiIndustryIndexCode2, + String keyWord); + +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/service/StockNameService.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/service/StockNameService.java new file mode 100644 index 0000000..f5fb686 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/service/StockNameService.java @@ -0,0 +1,9 @@ +package com.ssdmn.biz.gupiao.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.ssdmn.biz.gupiao.pojo.domain.StockName; + +public interface StockNameService extends IService { + + +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/service/TransactionRecordService.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/service/TransactionRecordService.java new file mode 100644 index 0000000..3137464 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/service/TransactionRecordService.java @@ -0,0 +1,70 @@ +package com.ssdmn.biz.gupiao.service; + +import com.ssdmn.biz.gupiao.pojo.domain.TransactionRecord; +import com.ssdmn.biz.gupiao.pojo.domain.TransactionRecordTemp; +import com.ssdmn.biz.gupiao.pojo.request.EarningsLineRequest; +import com.ssdmn.biz.gupiao.pojo.request.ProfitAndLossRequest; +import com.ssdmn.biz.gupiao.pojo.request.TransactionRecordPageRequest; +import com.ssdmn.biz.gupiao.pojo.response.TotalEarningsResponse; +import com.ssdmn.common.interceptor.UserCache; +import com.ssdmn.common.page.PageList; + +import java.util.List; + +public interface TransactionRecordService { + + + /** + * 导入 + * @param cachedDataList 数据 + */ + void importExcel(List cachedDataList); + + /** + * 保存或修改交易记录 + * @param transactionRecord + */ + void saveOrUpdateTransactionRecord(TransactionRecord transactionRecord); + + /** + * 批量删除交易记录 + * + * @param userCache + * @param ids 记录Id + */ + void delByIds(UserCache userCache,List ids); + + /** + * 查看交易详情 + * @param id id + * @return + */ + TransactionRecord getDetailById(Long id); + + + /** + * 分页查询 + * @param transactionRecordPageRequest + * @return + */ + PageList findPage(TransactionRecordPageRequest transactionRecordPageRequest); + + /** + * 总收益 + */ + TotalEarningsResponse totalEarnings(UserCache userCache, String securityCode); + + /** + * 收益折线图 + */ + Object[] EarningsLine(UserCache userCache, EarningsLineRequest earningsLineRequest); + + + /** + * 盈亏个股 + * @param profitAndLossRequest 查询条件 + */ + PageList profitOrLoss(UserCache userCache, ProfitAndLossRequest profitAndLossRequest); + + +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/service/TransactionRecordTempService.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/service/TransactionRecordTempService.java new file mode 100644 index 0000000..dd6ff9e --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/service/TransactionRecordTempService.java @@ -0,0 +1,17 @@ +package com.ssdmn.biz.gupiao.service; + +import com.ssdmn.biz.gupiao.pojo.domain.TransactionRecordTemp; +import com.ssdmn.biz.gupiao.pojo.request.ProfitAndLossRequest; +import com.ssdmn.common.interceptor.UserCache; +import com.ssdmn.common.page.PageList; + +public interface TransactionRecordTempService { + + /** + * 个人盈亏 + * @param userCache + * @param profitAndLossRequest + * @return + */ + PageList queryBuyOrSell(UserCache userCache, ProfitAndLossRequest profitAndLossRequest); +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/service/impl/DongCaiSecondaryIndustryServiceImpl.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/service/impl/DongCaiSecondaryIndustryServiceImpl.java new file mode 100644 index 0000000..38d83d6 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/service/impl/DongCaiSecondaryIndustryServiceImpl.java @@ -0,0 +1,394 @@ +package com.ssdmn.biz.gupiao.service.impl; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.date.DateField; +import cn.hutool.core.date.DateTime; +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.ssdmn.biz.gupiao.mapper.DongCaiSecondaryIndustryMapper; +import com.ssdmn.biz.gupiao.pojo.domain.DongCaiSecondaryIndustry; +import com.ssdmn.biz.gupiao.pojo.domain.MyCollect; +import com.ssdmn.biz.gupiao.pojo.domain.OriginalIssueStock; +import com.ssdmn.biz.gupiao.pojo.domain.StockName; +import com.ssdmn.biz.gupiao.pojo.request.DongCaiRiseLossesRequest; +import com.ssdmn.biz.gupiao.pojo.request.DongCaiSecondaryIndustryRequest; +import com.ssdmn.biz.gupiao.pojo.response.*; +import com.ssdmn.biz.gupiao.service.DongCaiSecondaryIndustryService; +import com.ssdmn.biz.gupiao.service.MyCollectService; +import com.ssdmn.biz.gupiao.service.OriginalIssueStockService; +import com.ssdmn.biz.gupiao.service.StockNameService; +import com.ssdmn.common.date.DateUtil; +import com.ssdmn.common.exception.BizException; +import com.ssdmn.common.interceptor.UserCache; +import com.ssdmn.common.page.PageList; +import com.ssdmn.common.page.PageUtils; +import com.ssdmn.common.page.Querys; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import java.util.*; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Function; +import java.util.stream.Collectors; + + +/** + * @Description: 东财二级行业指数每日成交情况 + * @Author: fan + * @Version: 1.0 + * @CreateTime: 2023/5/7 17:12 + */ +@Service +@Slf4j +public class DongCaiSecondaryIndustryServiceImpl extends ServiceImpl + implements DongCaiSecondaryIndustryService { + + @Resource + StockNameService stockNameService; + + @Resource + OriginalIssueStockService originalIssueStockService; + + @Resource + MyCollectService myCollectService; + + @Override + public void importExcel(List cachedDataList, Date businessDate ) { + + if(ObjectUtil.isEmpty(cachedDataList)){ + return; + } + + LambdaQueryWrapper queryWrapper + = new LambdaQueryWrapper().eq(DongCaiSecondaryIndustry::getBusinessDate,businessDate); + + // 先删除当前日期的数据 + remove(queryWrapper); + + // 批量保存 + saveBatch(cachedDataList); + + // 已保存的 + List existStockList = stockNameService + .list(new LambdaQueryWrapper() + .select(StockName::getSecurityCode, StockName::getSecurityType)); + + // 保存股票配置,用于收藏 + List stockNameList = cachedDataList.stream().map(dongCai -> { + StockName stockName = new StockName(); + stockName.setSecurityCode(dongCai.getSecurityCode()); + stockName.setSecurityName(dongCai.getSecurityName()); + stockName.setSecurityType(dongCai.getSecurityType()); + stockName.setDongCaiSecurityCode(dongCai.getSecurityCode()); + stockName.setDongCaiSecurityName(dongCai.getSecurityName()); + + stockName.setTableName("dong_cai_secondary_industry"); + return stockName; + }).collect(Collectors.toList()); + + boolean b = stockNameList.removeAll(existStockList); + log.info("过滤状态:{}", b); + if (!stockNameList.isEmpty()) { + stockNameService.saveBatch(stockNameList); + log.info("新增股条数:{}", stockNameList.size()); + } + + + } + + @Override + public PageList trendPlate(DongCaiSecondaryIndustryRequest dongCaiSecondaryIndustryRequest) { + + // 分页 + Page page = Querys.page(dongCaiSecondaryIndustryRequest.getPageModel()); + + // 类型 + Integer trendType = 2; + + if(ObjectUtil.isNotEmpty(dongCaiSecondaryIndustryRequest.getTrendType()) && dongCaiSecondaryIndustryRequest.getTrendType() > 1){ + + trendType = dongCaiSecondaryIndustryRequest.getTrendType(); + } + + if(ObjectUtil.isEmpty(dongCaiSecondaryIndustryRequest.getBusinessDate())){ + dongCaiSecondaryIndustryRequest.setBusinessDate(DateUtil.parseDate(DateUtil.now())); + } + + + // 查询最后的日期 + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper() + .select(DongCaiSecondaryIndustry::getBusinessDate) + .le(DongCaiSecondaryIndustry::getBusinessDate,dongCaiSecondaryIndustryRequest.getBusinessDate()) + .groupBy(DongCaiSecondaryIndustry::getBusinessDate) + .orderByDesc(DongCaiSecondaryIndustry::getBusinessDate).last("limit " + trendType); + + List dongCaiSecondaryIndustryList = list(queryWrapper); + + Date firstDate = null; + Date secondDate = null; + + if(dongCaiSecondaryIndustryList.size() > 1){ + firstDate = dongCaiSecondaryIndustryList.get(0).getBusinessDate(); + } + + if(dongCaiSecondaryIndustryList.size() > 2){ + secondDate = dongCaiSecondaryIndustryList.get(dongCaiSecondaryIndustryList.size() -1).getBusinessDate(); + } + + String sortField = dongCaiSecondaryIndustryRequest.getPageModel().getSortField(); + String sortWay = dongCaiSecondaryIndustryRequest.getPageModel().getSortWay(); + + // 排名的排序 + String sort = null; + + // 排名变化的排序 + String sortChange = null; + + if(ObjectUtil.isNotEmpty(sortField)){ + + String[] sortFieldArr = sortField.split(","); + String[] sortWayArr = sortWay.split(","); + + for (int i = 0; i < sortFieldArr.length; i++) { + String s = sortFieldArr[i]; + if("sort".equals(s)){ + try{ + sort = sortWayArr[i]; + }catch (Exception e){ + sort = "desc"; + } + + } + + if("sortChange".equals(s)){ + try{ + sortChange = sortWayArr[i]; + }catch (Exception e){ + sortChange = "desc"; + } + + } + } + } + + Page resultPage = baseMapper.trendPlate(new Page<>(dongCaiSecondaryIndustryRequest.getPageModel().getPageNo(), + dongCaiSecondaryIndustryRequest.getPageModel().getPageSize()), + firstDate, + secondDate, + sort, + sortChange ); + + + List collect = resultPage.getRecords().stream().map(dongCaiSecondaryIndustry -> + + BeanUtil.toBean(dongCaiSecondaryIndustry, DongCaiSecondaryIndustryResponse.class) + + ).collect(Collectors.toList()); + + return PageUtils.pageList((int)resultPage.getCurrent(),(int)resultPage.getSize(),(int)resultPage.getTotal(),collect); + } + + + @Override + public OriginalIssueStockSortResponseChange dongCaiSortChange(String securityCode) { + + DateTime dateTime = cn.hutool.core.date.DateUtil.date().offset(DateField.MONTH, -6); + + LambdaQueryWrapper queryWrapper + = new LambdaQueryWrapper() + .select(DongCaiSecondaryIndustry::getBusinessDate, DongCaiSecondaryIndustry::getSort) + .ge(DongCaiSecondaryIndustry::getBusinessDate, dateTime) + .eq(DongCaiSecondaryIndustry::getSecurityCode, securityCode); + + List list = list(queryWrapper); + OriginalIssueStockSortResponseChange change = new OriginalIssueStockSortResponseChange(); + List xAxis = new ArrayList<>(); + List yAxis = new ArrayList<>(); + change.setXAxis(xAxis); + change.setYAxis(yAxis); + list.forEach(stock -> { + xAxis.add(cn.hutool.core.date.DateUtil.format(stock.getBusinessDate(), "yyyy-MM-dd")); + yAxis.add(stock.getSort()); + }); + + return change; + + } + + @Override + public Object[] dongCaiKLine(String securityCode) { + // 两个都为空时 + if(ObjectUtil.isEmpty(securityCode)){ + throw new BizException("code不能为空"); + } + + LambdaQueryWrapper queryWrapper = + new LambdaQueryWrapper().eq(DongCaiSecondaryIndustry::getSecurityCode, securityCode); + + + + // 默认查半年 + DateTime startDate = cn.hutool.core.date.DateUtil.date(cn.hutool.core.date.DateUtil.parse(cn.hutool.core.date.DateUtil.now(),"yyyy-MM-dd").offset(DateField.MONTH, -6)); + + List list = list(queryWrapper.ge(DongCaiSecondaryIndustry::getBusinessDate, startDate).orderByDesc(DongCaiSecondaryIndustry::getBusinessDate)); + + // 日期为key + Map businessDateMap = list.stream().collect(Collectors.toMap(DongCaiSecondaryIndustry::getBusinessDate, Function.identity(), (l1, l2) -> l2)); + + TreeMap treeMap = new TreeMap<>(businessDateMap); + + // 提取数据 + List valueCollect = treeMap.values().stream().map(value -> BeanUtil.toBean(value, StockKLineVO.class)).collect(Collectors.toList()); + + // 提取key + List keyCollect = treeMap.keySet().stream().map(key -> cn.hutool.core.date.DateUtil.format(key, "yyyy-MM-dd")).collect(Collectors.toList()); + StockKLineResponse response = new StockKLineResponse(); + response.setKeyCollect(keyCollect); + response.setValueCollect(valueCollect); + Object[] arr = new Object[keyCollect.size()]; + for (int i = 0; i < keyCollect.size(); i++) { + + Object[] temp = new Object[6]; + temp[0] = keyCollect.get(i); + StockKLineVO stockKLineVO = valueCollect.get(i); + // 第一个日期,第二个开盘价,第三个收盘价,第四个最低价,第五个最高价,第六个成交额 + temp[1] = stockKLineVO.getOpeningPrice(); + temp[2] = stockKLineVO.getClosingPrice(); + temp[3] = stockKLineVO.getLowestPrice(); + temp[4] = stockKLineVO.getHighestPrice(); + temp[5] = Double.parseDouble(stockKLineVO.getTurnover().replaceAll(",", "")); + arr[i] = temp; + } + + return arr; + } + + + @Override + public BigStockDataResponse dongCaiRiseLossesStatistics(UserCache userCache, String securityCode, Date day) { + + if(ObjectUtil.isEmpty(day)){ + day = cn.hutool.core.date.DateUtil.parse(DateUtil.now(),"yyyy-MM-dd"); + } + + BigStockDataResponse response = new BigStockDataResponse(); + List asList = Arrays.asList(">=10%", ">=7%", "7-5%", "5-3%", "3-0%", "平", "0至-3%", "-3至-5%", "-5至-7%", "<=-7%", "<=-10%"); + response.setXAxis(asList); + + List dataList = new ArrayList<>(asList.size()); + for (int i = 0; i < asList.size() ; i++) { + dataList.add(i, 0); + } + + response.setData(dataList); + + AtomicInteger riseNum = new AtomicInteger(0); + AtomicInteger lossesNum = new AtomicInteger(0); + AtomicInteger equalNum = new AtomicInteger(0); + + // 查询当前日期的所有数据 + LambdaQueryWrapper wrapper = new LambdaQueryWrapper() + .eq(OriginalIssueStock::getBusinessDate,day) + .eq(OriginalIssueStock::getDongCaiIndustryIndexCode2,securityCode+".EI"); + List list = originalIssueStockService.list(wrapper); + + // 分类统计 + list.forEach(originalIssueStock -> { + double value = originalIssueStock.getRiseLossesCurrentDay().doubleValue(); + + if (value >= 10) { + riseNum.getAndIncrement(); + dataList.set(0, dataList.get(0) + 1); + } else if (value >= 7) { + riseNum.getAndIncrement(); + dataList.set(1, dataList.get(1) + 1); + } else if (value >= 5) { + riseNum.getAndIncrement(); + dataList.set(2, dataList.get(2) + 1); + } else if (value >= 3) { + riseNum.getAndIncrement(); + riseNum.getAndIncrement(); + dataList.set(3, dataList.get(3) + 1); + } else if (value > 0) { + riseNum.getAndIncrement(); + dataList.set(4, dataList.get(4) + 1); + } else if (value == 0) { + equalNum.getAndIncrement(); + dataList.set(5, dataList.get(5) + 1); + } else if (value < 0 && value >-3) { + lossesNum.getAndIncrement(); + dataList.set(6, dataList.get(6) + 1); + } else if (value <= -3 && value > -5) { + lossesNum.getAndIncrement(); + dataList.set(7, dataList.get(7) + 1); + } else if (value <= -5 && value > -7) { + lossesNum.getAndIncrement(); + dataList.set(8, dataList.get(8) + 1); + } else if (value <= -7 && value > -10) { + lossesNum.getAndIncrement(); + dataList.set(9, dataList.get(9) + 1); + } else { + lossesNum.getAndIncrement(); + dataList.set(10, dataList.get(10) + 1); + } + + }); + + response.setRiseNum(riseNum.get()); + response.setLossesNum(lossesNum.get()); + response.setEqualNum(equalNum.get()); + + // 如果是个股去判断是否有收藏 + MyCollect myCollect = + myCollectService.getOne(new LambdaQueryWrapper().eq(MyCollect::getSecurityCode, securityCode) + .eq(MyCollect::getSecurityType, "EI") + .eq(MyCollect::getUserId, userCache.getUserId())); + if(ObjectUtil.isNotEmpty(myCollect)){ + response.setIsCollect(1); + } + + return response; + } + + @Override + public PageList dongCaiRiseLosses(DongCaiRiseLossesRequest dongCaiRiseLossesRequest) { + + if(ObjectUtil.isEmpty(dongCaiRiseLossesRequest.getBusinessDate())){ + dongCaiRiseLossesRequest.setBusinessDate(cn.hutool.core.date.DateUtil.parse(DateUtil.now(),"yyyy-MM-dd")); + } + if(ObjectUtil.isNotEmpty(dongCaiRiseLossesRequest.getSecurityCode()) && !dongCaiRiseLossesRequest.getSecurityCode().endsWith("EI")){ + dongCaiRiseLossesRequest.setSecurityCode(dongCaiRiseLossesRequest.getSecurityCode() + ".EI"); + } + + + + PageList originalIssueStockPageList = originalIssueStockService.findPage(dongCaiRiseLossesRequest.getPageModel(), + dongCaiRiseLossesRequest.getBusinessDate(), + "", + dongCaiRiseLossesRequest.getRiseOrLosses(), + dongCaiRiseLossesRequest.getDayAndPlateSort(), + dongCaiRiseLossesRequest.getSecurityCode(), + null); + + // 几天几板,最多30天,几板表示在30内涨停的次数 + + if(!originalIssueStockPageList.getList().isEmpty()){ + + Set securityCodeList = originalIssueStockPageList.getList().stream().map(OriginalIssueStock::getSecurityCode).collect(Collectors.toSet()); + + Map dayAndPlateMap = originalIssueStockService.getDayAndPlateMap(securityCodeList); + originalIssueStockPageList.getList().forEach(originalIssueStock -> { + originalIssueStock.setDongCaiIndustryIndexCode2(originalIssueStock.getDongCaiIndustryIndexLevel2()); + DayAndPlate dayAndPlate = dayAndPlateMap.get(originalIssueStock.getSecurityCode()); + originalIssueStock.setDayAndPlate(dayAndPlate.getDayNumber() + "天" + dayAndPlate.getPlate() + "板"); + }); + + } + + return originalIssueStockPageList; + + } +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/service/impl/HighLowOfThreeHundredServiceImpl.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/service/impl/HighLowOfThreeHundredServiceImpl.java new file mode 100644 index 0000000..07cc341 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/service/impl/HighLowOfThreeHundredServiceImpl.java @@ -0,0 +1,43 @@ +package com.ssdmn.biz.gupiao.service.impl; + +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.ssdmn.biz.gupiao.mapper.HighLowOfThreeHundredMapper; +import com.ssdmn.biz.gupiao.pojo.domain.HighLowOfThreeHundred; +import com.ssdmn.biz.gupiao.service.HighLowOfThreeHundredService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.util.Date; +import java.util.List; + +/** + * @Description: 创300天新高新低 + * @Author: fan + * @Version: 1.0 + * @CreateTime: 2023/5/7 17:12 + */ +@Service +@Slf4j +public class HighLowOfThreeHundredServiceImpl extends ServiceImpl + implements HighLowOfThreeHundredService { + + @Override + public void importExcel(List cachedDataList, Date businessDate) { + + if(ObjectUtil.isEmpty(cachedDataList)){ + return; + } + + LambdaQueryWrapper queryWrapper + = new LambdaQueryWrapper().eq(HighLowOfThreeHundred::getBusinessDate,businessDate); + + // 先删除当前日期的数据 + remove(queryWrapper); + + // 批量保存 + saveBatch(cachedDataList); + + } +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/service/impl/MyCollectServiceImpl.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/service/impl/MyCollectServiceImpl.java new file mode 100644 index 0000000..f009254 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/service/impl/MyCollectServiceImpl.java @@ -0,0 +1,176 @@ +package com.ssdmn.biz.gupiao.service.impl; + +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.ssdmn.biz.gupiao.mapper.MyCollectMapper; +import com.ssdmn.biz.gupiao.pojo.domain.MyCollect; +import com.ssdmn.biz.gupiao.pojo.domain.OriginalIssueStock; +import com.ssdmn.biz.gupiao.pojo.domain.StockName; +import com.ssdmn.biz.gupiao.pojo.request.MyCollectRequest; +import com.ssdmn.biz.gupiao.pojo.request.QueryIsCollectRequest; +import com.ssdmn.biz.gupiao.pojo.request.QueryMyCollectPageRequest; +import com.ssdmn.biz.gupiao.pojo.response.DayAndPlate; +import com.ssdmn.biz.gupiao.pojo.response.MyCollectPageResponse; +import com.ssdmn.biz.gupiao.service.MyCollectService; +import com.ssdmn.biz.gupiao.service.OriginalIssueStockService; +import com.ssdmn.biz.gupiao.service.StockNameService; +import com.ssdmn.common.exception.BizException; +import com.ssdmn.common.interceptor.UserCache; +import com.ssdmn.common.page.PageList; +import com.ssdmn.common.page.PageUtils; +import lombok.Data; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * @Description: + * @Author: fan + * @Version: 1.0 + * @CreateTime: 2023/5/9 6:11 + */ +@Service +@Data +public class MyCollectServiceImpl extends ServiceImpl implements MyCollectService { + + @Resource + StockNameService stockNameService; + + @Resource + OriginalIssueStockService originalIssueStockService; + + @Override + public PageList queryMyCollect(UserCache userCache,QueryMyCollectPageRequest queryMyCollectPageRequest) { + + + Page myCollectPageResponsePageList; + + + if(ObjectUtil.isEmpty(queryMyCollectPageRequest.getPlateOrA())){ + + throw new BizException(500,"板块-1 个股-2"); + }else if(queryMyCollectPageRequest.getPlateOrA() == 2){ + + myCollectPageResponsePageList= + baseMapper.myStockCollectPage(new Page<>(queryMyCollectPageRequest.getPageModel().getPageNo(), + queryMyCollectPageRequest.getPageModel().getPageSize()), + queryMyCollectPageRequest,userCache.getUserId()); + + } else if (queryMyCollectPageRequest.getPlateOrA() == 1) { + + myCollectPageResponsePageList = + baseMapper.myBigCollectPage(new Page<>(queryMyCollectPageRequest.getPageModel().getPageNo(), + queryMyCollectPageRequest.getPageModel().getPageSize()), + queryMyCollectPageRequest,userCache.getUserId()); + }else { + throw new BizException(500,"板块-1 个股-2"); + } + + + // 只有个股才有几天几板 + if(queryMyCollectPageRequest.getPlateOrA() == 2){ + // 几天几板,最多30天,几板表示在30内涨停的次数 + if(!myCollectPageResponsePageList.getRecords().isEmpty()){ + + Set securityCodeList = myCollectPageResponsePageList.getRecords().stream().map(OriginalIssueStock::getSecurityCode).collect(Collectors.toSet()); + + Map dayAndPlateMap = originalIssueStockService.getDayAndPlateMap(securityCodeList); + myCollectPageResponsePageList.getRecords().forEach(originalIssueStock -> { + originalIssueStock.setDongCaiIndustryIndexCode2(originalIssueStock.getDongCaiIndustryIndexLevel2()); + DayAndPlate dayAndPlate = dayAndPlateMap.get(originalIssueStock.getSecurityCode()); + if(ObjectUtil.isNotEmpty(dayAndPlate)){ + originalIssueStock.setDayAndPlate(dayAndPlate.getDayNumber() + "天" + dayAndPlate.getPlate() + "板"); + } + + }); + + } + } + + return PageUtils.pageList(myCollectPageResponsePageList); + } + + @Override + public Integer queryIsCollect(UserCache userCache, QueryIsCollectRequest queryIsCollectRequest) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper() + .eq(MyCollect::getUserId, userCache.getUserId()) + .eq(MyCollect::getSecurityCode, queryIsCollectRequest.getSecurityCode()); + + // 四个大盘 + if(ObjectUtil.isEmpty(queryIsCollectRequest.getSecurityType())){ + queryWrapper.isNull(MyCollect::getSecurityType); + }else { + queryWrapper.eq(MyCollect::getSecurityType, queryIsCollectRequest.getSecurityType()); + } + + MyCollect myCollect = getOne(queryWrapper); + if(ObjectUtil.isEmpty(myCollect)) { + + return 0; + } + + return 1; + } + + + @Override + public void addMyCollect(UserCache userCache, MyCollectRequest myCollectRequest) { + + LambdaQueryWrapper wrapper = new LambdaQueryWrapper() + .eq(StockName::getSecurityCode, myCollectRequest.getSecurityCode()); + + if(ObjectUtil.isEmpty(myCollectRequest.getSecurityType())){ + wrapper.and(w->w.eq(StockName::getSecurityType,"").or().isNull(StockName::getSecurityType)); + }else{ + wrapper.eq(StockName::getSecurityType, myCollectRequest.getSecurityType()); + } + + StockName one = + stockNameService.getOne(wrapper); + + if(ObjectUtil.isEmpty(one)){ + throw new BizException(500,"没有找到对应的股票"); + } + + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper() + .eq(MyCollect::getUserId, userCache.getUserId()) + .eq(MyCollect::getSecurityCode, myCollectRequest.getSecurityCode()) + .eq(MyCollect::getSecurityType, myCollectRequest.getSecurityType()); + + List queryMyCollect = list(queryWrapper); + if(ObjectUtil.isNotEmpty(queryMyCollect)){ + return; + } + + MyCollect myCollect = new MyCollect(); + myCollect.setCollectDate(new Date()); + myCollect.setSecurityCode(one.getSecurityCode()); + myCollect.setSecurityName(one.getSecurityName()); + myCollect.setSecurityType(one.getSecurityType()); + myCollect.setUserId(userCache.getUserId()); + save(myCollect); + } + + @Override + public void removeMyCollect(UserCache userCache, MyCollectRequest myCollectRequest) { + + LambdaQueryWrapper wrapper = new LambdaQueryWrapper().eq(MyCollect::getUserId, userCache.getUserId()) + .eq(MyCollect::getSecurityCode, myCollectRequest.getSecurityCode()); + + // 四个大盘为空 + if(ObjectUtil.isEmpty(myCollectRequest.getSecurityType())){ + wrapper.isNull(MyCollect::getSecurityType); + }else { + wrapper.eq(MyCollect::getSecurityType, myCollectRequest.getSecurityType()); + } + + remove(wrapper); + } +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/service/impl/OriginalIssueStockServiceImpl.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/service/impl/OriginalIssueStockServiceImpl.java new file mode 100644 index 0000000..4916d3f --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/service/impl/OriginalIssueStockServiceImpl.java @@ -0,0 +1,564 @@ +package com.ssdmn.biz.gupiao.service.impl; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.date.DateField; +import cn.hutool.core.date.DateTime; +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.ssdmn.biz.gupiao.mapper.OriginalIssueStockMapper; +import com.ssdmn.biz.gupiao.pojo.domain.DongCaiSecondaryIndustry; +import com.ssdmn.biz.gupiao.pojo.domain.MyCollect; +import com.ssdmn.biz.gupiao.pojo.domain.OriginalIssueStock; +import com.ssdmn.biz.gupiao.pojo.domain.StockName; +import com.ssdmn.biz.gupiao.pojo.request.OriginalIssueStockDetail; +import com.ssdmn.biz.gupiao.pojo.request.OriginalIssueStockDetailPage; +import com.ssdmn.biz.gupiao.pojo.request.OriginalIssueStockRequest; +import com.ssdmn.biz.gupiao.pojo.request.OriginalIssueStockRequestPage; +import com.ssdmn.biz.gupiao.pojo.response.*; +import com.ssdmn.biz.gupiao.service.DongCaiSecondaryIndustryService; +import com.ssdmn.biz.gupiao.service.MyCollectService; +import com.ssdmn.biz.gupiao.service.OriginalIssueStockService; +import com.ssdmn.biz.gupiao.service.StockNameService; +import com.ssdmn.common.exception.BizException; +import com.ssdmn.common.interceptor.UserCache; +import com.ssdmn.common.page.PageList; +import com.ssdmn.common.page.PageModel; +import com.ssdmn.common.page.PageUtils; +import com.ssdmn.common.page.Querys; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.*; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Function; +import java.util.stream.Collectors; + +/** + * @Description: 动量原始股 + * @Author: fan + * @Version: 1.0 + * @CreateTime: 2023/5/7 17:11 + */ +@Service +@Slf4j +public class OriginalIssueStockServiceImpl extends ServiceImpl + implements OriginalIssueStockService { + + @Resource + StockNameService stockNameService; + + @Resource + MyCollectService myCollectService; + + @Resource + DongCaiSecondaryIndustryService dongCaiSecondaryIndustryService; + + @Resource + OriginalIssueStockService originalIssueStockService; + + @Override + public void importExcel(List cachedDataList, Date businessDate) { + + if (ObjectUtil.isEmpty(cachedDataList)) { + return; + } + + LambdaQueryWrapper queryWrapper + = new LambdaQueryWrapper().eq(OriginalIssueStock::getBusinessDate, businessDate); + + // 先删除当前日期的数据 + remove(queryWrapper); + + // 批量保存 + saveBatch(cachedDataList); + + // 已保存的 + List existStockList = stockNameService + .list(new LambdaQueryWrapper() + .select(StockName::getSecurityCode, StockName::getSecurityType)); + + // 保存股票配置,用于收藏 + List stockNameList = cachedDataList.stream().map(stock -> { + StockName stockName = new StockName(); + stockName.setSecurityCode(stock.getSecurityCode()); + stockName.setSecurityName(stock.getSecurityName()); + stockName.setSecurityType(stock.getSecurityType()); + stockName.setDongCaiSecurityCode(stock.getDongCaiIndustryIndexCode2().replace(".EI","")); + stockName.setDongCaiSecurityName(stock.getDongCaiIndustryIndexLevel2()); + + stockName.setTableName("original_issue_stock"); + return stockName; + }).collect(Collectors.toList()); + + boolean b = stockNameList.removeAll(existStockList); + log.info("过滤状态:{}", b); + if (!stockNameList.isEmpty()) { + stockNameService.saveBatch(stockNameList); + log.info("新增股条数:{}", stockNameList.size()); + } + + } + + + @Override + public BigStockDataResponse riseLossesStatistics(UserCache userCache, OriginalIssueStockRequest originalIssueStockRequest) { + if (ObjectUtil.isEmpty(originalIssueStockRequest.getBusinessDate())) { + + originalIssueStockRequest.setBusinessDate(DateUtil.parseDate(DateUtil.now())); + } + + BigStockDataResponse response = new BigStockDataResponse(); + List asList = Arrays.asList(">=10%", ">=7%", "7-5%", "5-3%", "3-0%", "平", "0至-3%", "-3至-5%", "-5至-7%", "<=-7%", "<=-10%"); + response.setXAxis(asList); + + List dataList = new ArrayList<>(asList.size()); + for (int i = 0; i < asList.size() ; i++) { + dataList.add(i, 0); + } + + response.setData(dataList); + + AtomicInteger riseNum = new AtomicInteger(0); + AtomicInteger lossesNum = new AtomicInteger(0); + AtomicInteger equalNum = new AtomicInteger(0); + + // 查询当前日期的所有数据 + LambdaQueryWrapper wrapper = Querys.wrapper(originalIssueStockRequest, OriginalIssueStock.class); + List list = list(wrapper); + + // 分类统计 + list.forEach(originalIssueStock -> { + double value = originalIssueStock.getRiseLossesCurrentDay().doubleValue(); + + if (value >= 10) { + riseNum.getAndIncrement(); + dataList.set(0, dataList.get(0) + 1); + } else if (value >= 7) { + riseNum.getAndIncrement(); + dataList.set(1, dataList.get(1) + 1); + } else if (value >= 5) { + riseNum.getAndIncrement(); + dataList.set(2, dataList.get(2) + 1); + } else if (value >= 3) { + riseNum.getAndIncrement(); + riseNum.getAndIncrement(); + dataList.set(3, dataList.get(3) + 1); + } else if (value > 0) { + riseNum.getAndIncrement(); + dataList.set(4, dataList.get(4) + 1); + } else if (value == 0) { + equalNum.getAndIncrement(); + dataList.set(5, dataList.get(5) + 1); + } else if (value < 0 && value >-3) { + lossesNum.getAndIncrement(); + dataList.set(6, dataList.get(6) + 1); + } else if (value <= -3 && value > -5) { + lossesNum.getAndIncrement(); + dataList.set(7, dataList.get(7) + 1); + } else if (value <= -5 && value > -7) { + lossesNum.getAndIncrement(); + dataList.set(8, dataList.get(8) + 1); + } else if (value <= -7 && value > -10) { + lossesNum.getAndIncrement(); + dataList.set(9, dataList.get(9) + 1); + } else { + lossesNum.getAndIncrement(); + dataList.set(10, dataList.get(10) + 1); + } + + }); + + response.setRiseNum(riseNum.get()); + response.setLossesNum(lossesNum.get()); + response.setEqualNum(equalNum.get()); + + // 如果是个股去判断是否有收藏 + if(ObjectUtil.isNotEmpty(originalIssueStockRequest.getSecurityCode())){ + MyCollect myCollect = + myCollectService.getOne(new LambdaQueryWrapper().eq(MyCollect::getSecurityCode, originalIssueStockRequest.getSecurityCode()).eq(MyCollect::getUserId, userCache.getUserId())); + if(ObjectUtil.isNotEmpty(myCollect)){ + response.setIsCollect(1); + } + + } + + return response; + } + + @Override + public AppRiseLossesResponse appRiseLossesStatistics(UserCache userCache, OriginalIssueStockRequest originalIssueStockRequest) { + + AppRiseLossesResponse response = new AppRiseLossesResponse(); + List statisticsList = new ArrayList<>(); + response.setList(statisticsList); + + // 查询当前日期的所有数据 + LambdaQueryWrapper wrapper = Querys.wrapper(originalIssueStockRequest, OriginalIssueStock.class); + + Map> securityTypeMap = list(wrapper).stream().collect(Collectors.groupingBy(OriginalIssueStock::getSecurityType)); + + // 股票类型 上证-SZ, 深证-SH,创业板-30,科创版-688 + List arr = Arrays.asList("SZ","SH","30","688"); + + + AtomicInteger totalRiseNum = new AtomicInteger(); + AtomicInteger totalLossesNum = new AtomicInteger(); + AtomicInteger totalEqualNum = new AtomicInteger(); + AtomicInteger totalAmount = new AtomicInteger(); + + + securityTypeMap.forEach((k,v)->{ + if(!arr.contains(k)){ + return; + } + AppRiseLossesStatistics statistics = new AppRiseLossesStatistics(); + statisticsList.add(statistics); + + int riseNum = v.stream().filter(stock -> stock.getRiseLossesCurrentDay().compareTo(BigDecimal.ZERO) > 0).collect(Collectors.toSet()).size(); + int equalNum = v.stream().filter(stock -> stock.getRiseLossesCurrentDay().compareTo(BigDecimal.ZERO) == 0).collect(Collectors.toSet()).size(); + int lossesNum = v.stream().filter(stock -> stock.getRiseLossesCurrentDay().compareTo(BigDecimal.ZERO) < 0).collect(Collectors.toSet()).size(); + + totalRiseNum.addAndGet(riseNum); + totalEqualNum.addAndGet(equalNum); + totalLossesNum.addAndGet(lossesNum); + statistics.setTotalAmount(new BigDecimal(v.size())); + totalAmount.getAndAdd(v.size()); + + statistics.setLossesNum(lossesNum); + statistics.setEqualNum(equalNum); + statistics.setRiseNum(riseNum); + + if("SZ".equals(k)){ + statistics.setSecurityName("上证指数"); + + } else if ("SH".equals(k)) { + statistics.setSecurityName("深证指数"); + + } else if ("30".equals(k)) { + statistics.setSecurityName("创业板"); + + }else {// 688 + statistics.setSecurityName("科创板"); + } + + }); + + AppRiseLossesStatistics totalStatistics = new AppRiseLossesStatistics(); + totalStatistics.setSecurityName("全部股票"); + + totalStatistics.setRiseNum(totalRiseNum.get()); + totalStatistics.setEqualNum(totalEqualNum.get()); + totalStatistics.setLossesNum(totalLossesNum.get()); + totalStatistics.setTotalAmount(new BigDecimal(totalAmount.get())); + statisticsList.add(totalStatistics); + + return response; + } + + @Override + public AppBigDetailVO appBigDetail(OriginalIssueStockRequest originalIssueStockRequest) { + String dongCaiIndustryIndexCode2 = originalIssueStockRequest.getSecurityCode() + "."+originalIssueStockRequest.getSecurityType(); + + LambdaQueryWrapper wrapper = new LambdaQueryWrapper() + .eq(DongCaiSecondaryIndustry::getSecurityCode, originalIssueStockRequest.getSecurityCode()) + .eq(DongCaiSecondaryIndustry::getSecurityType, originalIssueStockRequest.getSecurityType()) + .eq(DongCaiSecondaryIndustry::getBusinessDate,originalIssueStockRequest.getBusinessDate()); + + DongCaiSecondaryIndustry dongCaiSecondaryIndustry = dongCaiSecondaryIndustryService.getOne(wrapper); + + + // 查询当前日期的所有数据 + AppBigDetailVO appBigDetailVO = new AppBigDetailVO(); + + + appBigDetailVO.setDongCaiSecondaryIndustry(dongCaiSecondaryIndustry); + + List list = list(new LambdaQueryWrapper() + .eq(OriginalIssueStock::getBusinessDate, originalIssueStockRequest.getBusinessDate()) + .eq(OriginalIssueStock::getDongCaiIndustryIndexCode2,dongCaiIndustryIndexCode2)); + + // 成份个数 + int size = list.size(); + // 涨 + long rise = list.stream().filter(stock -> stock.getRiseLossesCurrentDay().doubleValue() > 0).count(); + + // 跌 + long losses = list.stream().filter(stock -> stock.getRiseLossesCurrentDay().doubleValue() < 0).count(); + + // 平 + long flat = list.stream().filter(stock -> stock.getRiseLossesCurrentDay().doubleValue() ==0).count(); + + // 涨停 + long riseStop = list.stream().filter(stock -> "是".equals(stock.getRiseStop())).count(); + + // 跌停 + long lossesStop = list.stream().filter(stock -> "是".equals(stock.getLossesStop())).count(); + + appBigDetailVO.setSize(size); + appBigDetailVO.setRise(rise); + appBigDetailVO.setLosses(losses); + appBigDetailVO.setFlat(flat); + appBigDetailVO.setRiseStop(riseStop); + appBigDetailVO.setLossesStop(lossesStop); + + return appBigDetailVO; + } + + @Override + public PageList stockDetails(PageModel pageModel) { + + Page page = Querys.page(pageModel); + + LambdaQueryWrapper queryWrapper = + new LambdaQueryWrapper() + .orderByAsc(OriginalIssueStock::getId); + + Page resultPage = page(page, queryWrapper); + + List collect = resultPage.getRecords() + .stream() + .map(originalIssueStock -> { + OriginalIssueStockResponse originalIssueStockResponse = BeanUtil.toBean(originalIssueStock, OriginalIssueStockResponse.class); + if (ObjectUtil.isNotEmpty(originalIssueStockResponse.getRiseLossesCurrentDay())) { + // 保留两位小数 + originalIssueStockResponse.setRiseLossesCurrentDay(originalIssueStockResponse.getRiseLossesCurrentDay().setScale(2, RoundingMode.CEILING)); + } + + return originalIssueStockResponse; + }).collect(Collectors.toList()); + + return PageUtils.pageList((int) resultPage.getCurrent(), (int) resultPage.getSize(), (int) resultPage.getTotal(), collect); + } + + @Override + public PageList riseLossesDetail(OriginalIssueStockRequestPage originalIssueStockRequestPage) { + + // 几天几板,最多30天,几板表示在30内涨停的次数 + PageList originalIssueStockPageList = findPage(originalIssueStockRequestPage.getPageModel(), + originalIssueStockRequestPage.getBusinessDate(), + originalIssueStockRequestPage.getSecurityType(), + originalIssueStockRequestPage.getRiseOrLosses(), + originalIssueStockRequestPage.getDayAndPlateSort(), + null, null); + + + if(!originalIssueStockPageList.getList().isEmpty()){ + + Set securityCodeList = originalIssueStockPageList.getList().stream().map(OriginalIssueStock::getSecurityCode).collect(Collectors.toSet()); + + Map dayAndPlateMap = getDayAndPlateMap(securityCodeList); + originalIssueStockPageList.getList().forEach(originalIssueStock -> { + originalIssueStock.setDongCaiIndustryIndexCode2(originalIssueStock.getDongCaiIndustryIndexLevel2()); + DayAndPlate dayAndPlate = dayAndPlateMap.get(originalIssueStock.getSecurityCode()); + originalIssueStock.setDayAndPlate(dayAndPlate.getDayNumber() + "天" + dayAndPlate.getPlate() + "板"); + }); + + } + + return originalIssueStockPageList; + } + + @Override + public PageList originalIssueStockDetail(OriginalIssueStockDetailPage originalIssueStockDetailPage) { + + // 几天几板,最多30天,几板表示在30内涨停的次数 + PageList originalIssueStockPageList = findPage(originalIssueStockDetailPage.getPageModel(), + originalIssueStockDetailPage.getBusinessDate(), + originalIssueStockDetailPage.getSecurityType(), + null, + originalIssueStockDetailPage.getDayAndPlateSort(), + null, + originalIssueStockDetailPage.getKeyWord()); + + + originalIssueStockPageList.getList().forEach(originalIssueStock -> { + // 字段前端取反了 + originalIssueStock.setDongCaiIndustryIndexCode2(originalIssueStock.getDongCaiIndustryIndexLevel2()); + }); + + // 几天几板,最多30天,几板表示在30内涨停的次数 + if(!originalIssueStockPageList.getList().isEmpty()){ + + Set securityCodeList = originalIssueStockPageList.getList().stream().map(OriginalIssueStock::getSecurityCode).collect(Collectors.toSet()); + + Map dayAndPlateMap = getDayAndPlateMap(securityCodeList); + originalIssueStockPageList.getList().forEach(originalIssueStock -> { + originalIssueStock.setDongCaiIndustryIndexCode2(originalIssueStock.getDongCaiIndustryIndexLevel2()); + DayAndPlate dayAndPlate = dayAndPlateMap.get(originalIssueStock.getSecurityCode()); + originalIssueStock.setDayAndPlate(dayAndPlate.getDayNumber() + "天" + dayAndPlate.getPlate() + "板"); + }); + + } + + return originalIssueStockPageList; + } + + @Override + public OriginalIssueStockDetailResponse getByCode(UserCache userCache, OriginalIssueStockDetail originalIssueStockDetail) { + LambdaQueryWrapper wrapper = + Querys.wrapper(originalIssueStockDetail, OriginalIssueStock.class) + .last("limit 1"); + OriginalIssueStock one = getOne(wrapper); + if(ObjectUtil.isEmpty(one)){ + throw new BizException(500,"没有这个股票"); + } + MyCollect myCollect = + myCollectService.getOne(new LambdaQueryWrapper().eq(MyCollect::getSecurityCode, originalIssueStockDetail.getSecurityCode()).eq(MyCollect::getUserId, userCache.getUserId())); + + + OriginalIssueStockDetailResponse originalIssueStockDetailResponse = BeanUtil.toBean(one, OriginalIssueStockDetailResponse.class); + // 查询是否为收藏 + if(ObjectUtil.isNotEmpty(myCollect)){ + originalIssueStockDetailResponse.setIsCollect(1); + } + + return originalIssueStockDetailResponse; + } + + @Override + public OriginalIssueStockSortResponseChange stockSortChange(String securityCode) { + DateTime dateTime = DateUtil.date().offset(DateField.MONTH, -6); + + LambdaQueryWrapper queryWrapper + = new LambdaQueryWrapper() + .select(OriginalIssueStock::getBusinessDate, OriginalIssueStock::getSort) + .ge(OriginalIssueStock::getBusinessDate, dateTime) + .eq(OriginalIssueStock::getSecurityCode, securityCode); + + List list = list(queryWrapper); + OriginalIssueStockSortResponseChange change = new OriginalIssueStockSortResponseChange(); + List xAxis = new ArrayList<>(); + List yAxis = new ArrayList<>(); + change.setXAxis(xAxis); + change.setYAxis(yAxis); + list.forEach(stock -> { + xAxis.add(DateUtil.format(stock.getBusinessDate(), "yyyy-MM-dd")); + yAxis.add(stock.getSort()); + }); + + return change; + } + + @Override + public Object[] kLine(String securityType, String securityCode) { + + // 两个都为空时 + if(ObjectUtil.isEmpty(securityType) && ObjectUtil.isEmpty(securityCode)){ + throw new BizException("类型和code不能同时为空"); + } + + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + + if(ObjectUtil.isNotEmpty(securityType)){ + + String securityName; + + if("SZ".equals(securityType)){ + securityName = "上证指数"; + } else if ("SH".equals(securityType)) { + securityName = "深证指数"; + } else if ("30".equals(securityType)) { + securityName = "创业板"; + }else if ("688".equals(securityType)){ + securityName = "科创板"; + }else { + throw new BizException(500,"暂不支持其他类型的查询"); + } + + queryWrapper.eq(OriginalIssueStock::getSecurityName, securityName); + + }else{ + + queryWrapper.eq(OriginalIssueStock::getSecurityCode, securityCode); + } + + // 默认查半年 + DateTime startDate = DateUtil.date(DateUtil.parse(DateUtil.now(),"yyyy-MM-dd").offset(DateField.MONTH, -6)); + + List list = list(queryWrapper.ge(OriginalIssueStock::getBusinessDate, startDate).orderByDesc(OriginalIssueStock::getBusinessDate)); + + // 日期为key + Map businessDateMap = list.stream().collect(Collectors.toMap(OriginalIssueStock::getBusinessDate, Function.identity(), (l1, l2) -> l2)); + + TreeMap treeMap = new TreeMap<>(businessDateMap); + + // 提取数据 + List valueCollect = treeMap.values().stream().map(value -> BeanUtil.toBean(value, StockKLineVO.class)).collect(Collectors.toList()); + + // 提取key + List keyCollect = treeMap.keySet().stream().map(key -> DateUtil.format(key, "yyyy-MM-dd")).collect(Collectors.toList()); + StockKLineResponse response = new StockKLineResponse(); + response.setKeyCollect(keyCollect); + response.setValueCollect(valueCollect); + Object[] arr = new Object[keyCollect.size()]; + for (int i = 0; i < keyCollect.size(); i++) { + + Object[] temp = new Object[6]; + temp[0] = keyCollect.get(i); + StockKLineVO stockKLineVO = valueCollect.get(i); + // 第一个日期,第二个开盘价,第三个收盘价,第四个最低价,第五个最高价,第六个成交额 + temp[1] = stockKLineVO.getOpeningPrice(); + temp[2] = stockKLineVO.getClosingPrice(); + temp[3] = stockKLineVO.getLowestPrice(); + temp[4] = stockKLineVO.getHighestPrice(); + try{ + temp[5] = Double.parseDouble(stockKLineVO.getTurnover().replaceAll(",", "")); + }catch (Exception e){ + temp[5] = Double.parseDouble("0"); + } + + arr[i] = temp; + } + + return arr; + } + + public Map getDayAndPlateMap(Set securityCodeSet){ + + if(securityCodeSet.isEmpty()){ + return null; + } + + DateTime offset = DateUtil.date().offset(DateField.DAY_OF_YEAR, -30); + return baseMapper.statisticsDayAndPlate(offset, securityCodeSet) + .stream().collect(Collectors.toMap(DayAndPlate::getSecurityCode, Function.identity(),(l1,l2)->l2)); + } + + @Override + public PageList findPage(PageModel pageModel, + Date businessDate, + String securityType, + Integer riseOrLosses, + Integer dayAndPlateSort, + String dongCaiIndustryIndexCode2, + String keyWord) { + + Page page = baseMapper.findPage(new Page<>(pageModel.getPageNo(), + pageModel.getPageSize()), businessDate, securityType, riseOrLosses, dayAndPlateSort, dongCaiIndustryIndexCode2,keyWord); + + return PageUtils.pageList(page); + + } +} + + + + + + + + + + + + + + + + diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/service/impl/StockNameServiceImpl.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/service/impl/StockNameServiceImpl.java new file mode 100644 index 0000000..8280486 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/service/impl/StockNameServiceImpl.java @@ -0,0 +1,24 @@ +package com.ssdmn.biz.gupiao.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.ssdmn.biz.gupiao.mapper.StockNameMapper; +import com.ssdmn.biz.gupiao.pojo.domain.StockName; +import com.ssdmn.biz.gupiao.service.StockNameService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +/** + * @Description: + * @Author: fan + * @Version: 1.0 + * @CreateTime: 2023/5/9 6:10 + */ +@Service +@Slf4j +public class StockNameServiceImpl extends ServiceImpl implements StockNameService { + + + + + +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/service/impl/TransactionRecordServiceImpl.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/service/impl/TransactionRecordServiceImpl.java new file mode 100644 index 0000000..ff21c58 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/service/impl/TransactionRecordServiceImpl.java @@ -0,0 +1,280 @@ +package com.ssdmn.biz.gupiao.service.impl; + +import cn.hutool.core.date.DateField; +import cn.hutool.core.date.DateTime; +import cn.hutool.core.date.DateUnit; +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.ssdmn.biz.gupiao.mapper.TransactionRecordMapper; +import com.ssdmn.biz.gupiao.pojo.domain.StockName; +import com.ssdmn.biz.gupiao.pojo.domain.TransactionRecord; +import com.ssdmn.biz.gupiao.pojo.domain.TransactionRecordTemp; +import com.ssdmn.biz.gupiao.pojo.request.EarningsLineRequest; +import com.ssdmn.biz.gupiao.pojo.request.ProfitAndLossRequest; +import com.ssdmn.biz.gupiao.pojo.request.TransactionRecordPageRequest; +import com.ssdmn.biz.gupiao.pojo.response.EarningsLineResponse; +import com.ssdmn.biz.gupiao.pojo.response.TotalEarningsResponse; +import com.ssdmn.biz.gupiao.service.StockNameService; +import com.ssdmn.biz.gupiao.service.TransactionRecordService; +import com.ssdmn.biz.gupiao.service.TransactionRecordTempService; +import com.ssdmn.common.exception.BizException; +import com.ssdmn.common.interceptor.UserCache; +import com.ssdmn.common.page.PageList; +import com.ssdmn.common.page.PageUtils; +import com.ssdmn.common.page.Querys; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.*; +import java.util.stream.Collectors; + +/** + * @Description: 交易记录 + * @Author: fan + * @Version: 1.0 + * @CreateTime: 2023/5/9 19:31 + */ +@Service +@Slf4j +public class TransactionRecordServiceImpl extends ServiceImpl implements TransactionRecordService { + + + @Resource + TransactionRecordTempService transactionRecordTempService; + + @Override + public void importExcel(List cachedDataList) { + + if (ObjectUtil.isEmpty(cachedDataList)) { + return; + } + + saveBatch(cachedDataList); + } + + @Override + public void saveOrUpdateTransactionRecord(TransactionRecord transactionRecord) { + + if("证券买入".equals(transactionRecord.getTransactionCategory())){ + + transactionRecord.setTransactionAmount(new BigDecimal("-1").multiply(transactionRecord.getTransactionAmount())); + } + + String securityCode = transactionRecord.getSecurityCode(); + if(ObjectUtil.isEmpty(securityCode)){ + throw new BizException(500,"股票code不能为空"); + } + + if(securityCode.contains(".")){ + String[] split = securityCode.split("\\."); + transactionRecord.setSecurityCode(split[0]); + transactionRecord.setSecurityType(split[1]); + } + + if(ObjectUtil.isEmpty(transactionRecord.getId())){ + save(transactionRecord); + }else{ + updateById(transactionRecord); + } + } + + @Override + public void delByIds(UserCache userCache, List ids) { + + if(ids.isEmpty() || ObjectUtil.isEmpty(userCache.getUserId())){ + return; + } + + LambdaQueryWrapper queryWrapper = + new LambdaQueryWrapper() + .eq(TransactionRecord::getUserId,userCache.getUserId()) + .in(TransactionRecord::getId,ids); + + remove(queryWrapper); + } + + @Override + public TransactionRecord getDetailById(Long id) { + + TransactionRecord transaction = getById(id); + if(ObjectUtil.isEmpty(transaction)){ + throw new BizException(500,"交易记录不存在或已被删除"); + } + + // 因为买入是负数,取个绝对值 + transaction.setTransactionAmount(transaction.getTransactionAmount().abs()); + + return transaction; + } + + @Override + public PageList findPage(TransactionRecordPageRequest transactionRecordPageRequest) { + + Page page = Querys.page(transactionRecordPageRequest.getPageModel()); + + LambdaQueryWrapper wrapper = Querys.wrapper(transactionRecordPageRequest,TransactionRecord.class); + + // 封装keyword查询 + if(ObjectUtil.isNotEmpty(transactionRecordPageRequest.getKeyWord())){ + wrapper.and(w-> w.like(TransactionRecord::getSecurityCode,transactionRecordPageRequest.getKeyWord()) + .or() + .like(TransactionRecord::getSecurityName,transactionRecordPageRequest.getKeyWord())); + } + + // 时间范围查询 + if(ObjectUtil.isNotEmpty(transactionRecordPageRequest.getStartTime()) && ObjectUtil.isNotEmpty(transactionRecordPageRequest.getEndTime())){ + + wrapper.between(TransactionRecord::getTransactionTime,transactionRecordPageRequest.getStartTime(),transactionRecordPageRequest.getEndTime()); + } + + return PageUtils.pageList(page(page,wrapper)); + } + + @Override + public TotalEarningsResponse totalEarnings(UserCache userCache,String securityCode) { + + + TotalEarningsResponse totalEarningsResponse = new TotalEarningsResponse(); + + LambdaQueryWrapper queryWrapper + = new LambdaQueryWrapper() + .eq(TransactionRecord::getUserId,userCache.getUserId()); + + if(ObjectUtil.isNotEmpty(securityCode)){ + queryWrapper.eq(TransactionRecord::getSecurityCode,securityCode); + } + + + List list = list(queryWrapper); + if(list.isEmpty()){ + + totalEarningsResponse.setEarningsRate(BigDecimal.ZERO); + totalEarningsResponse.setTotalEarnings(BigDecimal.ZERO); + + }else { + + BigDecimal buyMoney = list.stream().filter(transactionRecord -> "证券买入".equals(transactionRecord.getTransactionCategory())) + .map(transactionRecord -> transactionRecord.getTransactionPrice().multiply(transactionRecord.getVolume())).reduce(BigDecimal.ZERO,BigDecimal::add); + + BigDecimal sellMoney = list.stream().filter(transactionRecord -> "证券卖出".equals(transactionRecord.getTransactionCategory())) + .map(transactionRecord -> transactionRecord.getTransactionPrice().multiply(transactionRecord.getVolume())).reduce(BigDecimal.ZERO,BigDecimal::add); + + totalEarningsResponse.setTotalEarnings(sellMoney.subtract(buyMoney)); + if(BigDecimal.ZERO.equals(buyMoney) || BigDecimal.ZERO.equals(sellMoney)){ + + totalEarningsResponse.setEarningsRate(BigDecimal.ZERO); + }else{ + + totalEarningsResponse.setEarningsRate(sellMoney.subtract(buyMoney).divide(buyMoney, 4, RoundingMode.CEILING)); + } + totalEarningsResponse.setTotalEarnings(sellMoney.subtract(buyMoney)); + } + + return totalEarningsResponse; + } + + @Override + public Object[] EarningsLine(UserCache userCache, EarningsLineRequest earningsLineRequest) { + + EarningsLineResponse earningsLineResponse = new EarningsLineResponse(); + + LambdaQueryWrapper queryWrapper = Querys.wrapper(earningsLineRequest,TransactionRecord.class) + .eq(TransactionRecord::getUserId, userCache.getUserId()); + + if(ObjectUtil.isNotEmpty(earningsLineRequest.getStartDate()) && ObjectUtil.isNotEmpty(earningsLineRequest.getEndDate())){ + queryWrapper.between(TransactionRecord::getTransactionDate,earningsLineRequest.getStartDate(),earningsLineRequest.getEndDate()); + }else{ + DateTime date = DateUtil.date(); + DateTime offset; + if(earningsLineRequest.getTimeType() == 1){ + + offset = date.offset(DateField.MONTH, -3); + }else if (earningsLineRequest.getTimeType() == 0){ + + offset = date.offset(DateField.MONTH, -1); + }else { + + offset = date.offset(DateField.YEAR, -1); + } + + queryWrapper.ge(TransactionRecord::getTransactionDate,offset); + } + + List list = list(queryWrapper); + if(list.isEmpty()){ + return new Object[]{}; + } + + Map> collect = list.stream().collect(Collectors.groupingBy(TransactionRecord::getTransactionDate)); + + Integer type = earningsLineRequest.getType(); + + List businessDateList = new ArrayList<>(); + + List valueList = new ArrayList<>(); + + + TreeSet set = new TreeSet<>(collect.keySet()); + long between = DateUtil.between(set.first(), set.last(), DateUnit.DAY); + for (int i = 0; i <= between; i++) { + + // 时间加一天 + DateTime offset = DateUtil.date(set.first()).offset(DateField.DAY_OF_YEAR, i); + businessDateList.add(offset.getTime()); + + List recordList = collect.get(offset); + if(ObjectUtil.isEmpty(recordList)){ + valueList.add(BigDecimal.ZERO); + continue; + } + + BigDecimal buyMoney = recordList.stream().filter(transactionRecord -> "证券买入".equals(transactionRecord.getTransactionCategory())) + .map(transactionRecord -> transactionRecord.getTransactionPrice().multiply(transactionRecord.getVolume())).reduce(BigDecimal.ZERO,BigDecimal::add); + + BigDecimal sellMoney = recordList.stream().filter(transactionRecord -> "证券卖出".equals(transactionRecord.getTransactionCategory())) + .map(transactionRecord -> transactionRecord.getTransactionPrice().multiply(transactionRecord.getVolume())).reduce(BigDecimal.ZERO,BigDecimal::add); + + BigDecimal diff; + + if(BigDecimal.ZERO.equals(buyMoney) || BigDecimal.ZERO.equals(sellMoney) ){ + diff = BigDecimal.ZERO; + }else{ + // 收益率 + if(type == 1){ + diff = sellMoney.subtract(buyMoney).divide(buyMoney, 4, RoundingMode.CEILING); + // 收益金额 + }else{ + diff = sellMoney.subtract(buyMoney); + } + } + + valueList.add(diff); + } + + Object[] arr = new Object[businessDateList.size()]; + for (int i = 0; i < businessDateList.size(); i++) { + Object[] temp = new Object[2]; + temp[0] = businessDateList.get(i); + temp[1] =valueList.get(i); + arr[i] = temp; + } + +// earningsLineResponse.setValueList(valueList); +// earningsLineResponse.setBusinessDateList(businessDateList); + + + return arr; + } + + @Override + public PageList profitOrLoss(UserCache userCache, ProfitAndLossRequest profitAndLossRequest) { + + return transactionRecordTempService.queryBuyOrSell(userCache, profitAndLossRequest); + } +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/service/impl/TransactionRecordTempServiceImpl.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/service/impl/TransactionRecordTempServiceImpl.java new file mode 100644 index 0000000..732d72e --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/gupiao/service/impl/TransactionRecordTempServiceImpl.java @@ -0,0 +1,120 @@ +package com.ssdmn.biz.gupiao.service.impl; + +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.ssdmn.biz.gupiao.mapper.TransactionRecordTempMapper; +import com.ssdmn.biz.gupiao.pojo.domain.TransactionRecordTemp; +import com.ssdmn.biz.gupiao.pojo.request.ProfitAndLossRequest; +import com.ssdmn.biz.gupiao.pojo.response.QueryBuyOrSell; +import com.ssdmn.biz.gupiao.service.TransactionRecordTempService; +import com.ssdmn.common.interceptor.UserCache; +import com.ssdmn.common.page.PageList; +import com.ssdmn.common.page.PageUtils; +import com.ssdmn.common.page.Querys; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; + +/** + * @Description: + * @Author: fan + * @Version: 1.0 + * @CreateTime: 2023/5/10 21:36 + */ +@Service +public class TransactionRecordTempServiceImpl + extends ServiceImpl + implements TransactionRecordTempService { + + + /** + * 个人盈亏股查询 + */ + public PageList queryBuyOrSell(UserCache userCache, ProfitAndLossRequest profitAndLossRequest){ + + synchronized (userCache.getUserId().toString().intern()) { + + Map queryBuyMap = baseMapper.queryBuyOrSell(userCache.getUserId(), + "min", + "证券买入", + profitAndLossRequest.getStartDate(), + profitAndLossRequest.getEndDate()) + .stream().collect(Collectors.toMap(QueryBuyOrSell::getSecurityCode, Function.identity(), (l1, l2) -> l2)); + + Map querySellMap = baseMapper.queryBuyOrSell(userCache.getUserId(), + "max", + "证券卖出", + profitAndLossRequest.getStartDate(), profitAndLossRequest.getEndDate()) + .stream().collect(Collectors.toMap(QueryBuyOrSell::getSecurityCode, Function.identity(), (l1, l2) -> l2)); + + List transactionRecordTempList = new ArrayList<>(); + queryBuyMap.forEach((k, v) -> { + QueryBuyOrSell querySell = querySellMap.get(k); + if (ObjectUtil.isEmpty(querySell)) { + return; + } + TransactionRecordTemp temp = new TransactionRecordTemp(); + + temp.setSecurityCode(v.getSecurityCode()); + temp.setSecurityName(v.getSecurityName()); + temp.setSecurityType(v.getSecurityType()); + temp.setDongCaiIndustryIndexCode2(v.getDongCaiSecurityCode()); + temp.setDongCaiIndustryIndexLevel2(v.getDongCaiSecurityName()); + temp.setBuyDate(v.getTransactionDate()); + temp.setBuyPrice(v.getTransactionPrice()); + temp.setSellDate(querySell.getTransactionDate()); + temp.setSellPrice(querySell.getTransactionPrice()); + temp.setTotalMoney(v.getBuyMoney().add(querySell.getBuyMoney()));// 最后结果可能有正有负 + temp.setUserId(userCache.getUserId()); + + transactionRecordTempList.add(temp); + }); + + // 先删除再保存 + remove(new LambdaQueryWrapper().eq(TransactionRecordTemp::getUserId, userCache.getUserId())); + + saveBatch(transactionRecordTempList); + } + + // 再分页查询 + Page page = Querys.page(profitAndLossRequest.getPageModel()); + LambdaQueryWrapper wrapper = Querys.wrapper(profitAndLossRequest, TransactionRecordTemp.class); + + // 盈利 + if (ObjectUtil.isNotEmpty(profitAndLossRequest.getType())) { + if (profitAndLossRequest.getType() == 1) { + wrapper.gt(TransactionRecordTemp::getTotalMoney, 0); + } else if (profitAndLossRequest.getType() == 0) { + wrapper.lt(TransactionRecordTemp::getTotalMoney, 0); + } + } + + // 关键字查询 + if (ObjectUtil.isNotEmpty(profitAndLossRequest.getKeyWord())) { + wrapper.and(w -> w.like(TransactionRecordTemp::getSecurityCode, profitAndLossRequest.getKeyWord()) + .or().like(TransactionRecordTemp::getSecurityName, profitAndLossRequest.getKeyWord()) + .or().like(TransactionRecordTemp::getDongCaiIndustryIndexLevel2, profitAndLossRequest.getKeyWord()) + .or().like(TransactionRecordTemp::getDongCaiIndustryIndexCode2, profitAndLossRequest.getKeyWord()) + ); + } + + // 金额排序 + if (ObjectUtil.isNotEmpty(profitAndLossRequest.getSortAmount())) { + if (profitAndLossRequest.getSortAmount() == 1) { + wrapper.orderByAsc(TransactionRecordTemp::getTotalMoney); + } else { + wrapper.orderByDesc(TransactionRecordTemp::getTotalMoney); + } + } + + Page resultPage = page(page, wrapper); + + return PageUtils.pageList(resultPage); + } +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/constants/Constants.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/constants/Constants.java new file mode 100644 index 0000000..dc4753b --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/constants/Constants.java @@ -0,0 +1,43 @@ +package com.ssdmn.biz.login.constants; + +public interface Constants { + + public static final String SMS = "USER:SMS:"; + + public static final String INVITE_AES_KEY = "SSDMN-CHATGPTAPI"; + + /** + * 用户登录 + */ + String REDIS_PREFIX_USER_LOGIN = "USER:LOGIN:{}"; + + /** + * 验证码登录 + */ + String REDIS_PREFIX_SMS_LOGIN = "SMS:LOGIN:{}"; + + + /** + * 验证码类型 + */ + interface SmsCodeType { + /** + * 登录 + */ + int LOGIN = 1; + /** + * 绑定微信手机号验证码 + */ + int WX_BIND_PHONE = 2; + + /** + * 拼桌成功 + */ + int TABLE_TOGETHER_SUCCESS = 3; + + /** + * 拼桌支付成功 + */ + int TABLE_TOGETHER_PAY_SUCCESS = 4; + } +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/controller/LoginController.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/controller/LoginController.java new file mode 100644 index 0000000..077d9c4 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/controller/LoginController.java @@ -0,0 +1,98 @@ +package com.ssdmn.biz.login.controller; + + +import com.ssdmn.biz.login.exception.user.UserBindLoginException; +import com.ssdmn.biz.login.pojo.dto.MiniAppFirstPhoneDTO; +import com.ssdmn.biz.login.pojo.dto.MiniAppSecondPhoneDTO; +import com.ssdmn.biz.login.pojo.dto.MiniAppUserUserInfoDTO; +import com.ssdmn.biz.login.pojo.dto.UserSmsCodeLoginDTO; +import com.ssdmn.biz.login.service.LoginService; +import com.ssdmn.common.result.Result; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.extern.slf4j.Slf4j; +import me.chanjar.weixin.common.error.WxErrorException; +import org.springframework.validation.annotation.Validated; +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; + +import javax.annotation.Resource; + +import static com.ssdmn.common.result.Results.fail; +import static com.ssdmn.common.result.Results.ok; + + +/** + * @author 郭世旭 + */ +@Api(tags = "用户模块") +@RestController +@RequestMapping("user/login") +@Slf4j +public class LoginController { + + @Resource + private LoginService loginService; + + + /** + * 验证码登录 + */ + @ApiOperation("验证码登录") + @PostMapping("/smsCodeLogin") + public Result smsCodeLogin(@RequestBody UserSmsCodeLoginDTO userSmsCodeLoginDTO){ + + return ok(loginService.smsCodeLogin(userSmsCodeLoginDTO)); + } + + /** + * 微信小程序授权基本信息登录 + */ + @ApiOperation("微信小程序登录") + @PostMapping("/wxMiniUserInfoLogin") + public Result wxLogin(@Validated MiniAppUserUserInfoDTO miniAppUserUserInfoDTO){ + String token; + try { + token = loginService.wxMiniLogin(miniAppUserUserInfoDTO); + }catch (UserBindLoginException e){ + return fail(e.getCode(),e.getMessage(),e.getData()); + }catch (WxErrorException e){ + log.error("微信登录异常->{}",e.getMessage()); + return fail(e.getMessage()); + } + return ok(token); + } + + + @ApiOperation("微信小程序授权绑定手机号") + @PostMapping("/wxMiniFirstBindPhone") + public Result wxMiniFirstBindPhone(@Validated MiniAppFirstPhoneDTO miniAppFirstPhoneDTO){ + String token; + try { + token = loginService.wxMiniFirstBindPhone(miniAppFirstPhoneDTO); + }catch (UserBindLoginException e){ + return fail(e.getCode(),e.getMessage(),e.getData()); + }catch (Exception e){ + return fail(e.getMessage()); + } + return ok(token); + } + + @ApiOperation("微信小程序验证码绑定手机号") + @PostMapping("/wxMiniSecondBindPhone") + public Result wxMiniSecondBindPhone(@Validated MiniAppSecondPhoneDTO miniAppSecondPhoneDTO){ + String token; + try { + token = loginService.wxMiniSecondBindPhone(miniAppSecondPhoneDTO); + }catch (UserBindLoginException e){ + return fail(e.getCode(),e.getMessage(),e.getData()); + }catch (Exception e){ + return fail(e.getMessage()); + } + + return ok(token); + } + +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/controller/SmsController.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/controller/SmsController.java new file mode 100644 index 0000000..c605e10 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/controller/SmsController.java @@ -0,0 +1,71 @@ +package com.ssdmn.biz.login.controller; + +import cn.hutool.core.util.StrUtil; +import com.ssdmn.aliyun.AliYunProperties; +import com.ssdmn.aliyun.AliYunService; +import com.ssdmn.biz.login.enums.sms.SmsRedisEnum; +import com.ssdmn.biz.login.enums.sms.SmsTemplateCodeEnum; +import com.ssdmn.biz.login.enums.sms.SmsTemplateEnum; +import com.ssdmn.biz.login.pojo.dto.SmsCodeDTO; +import com.ssdmn.common.redis.service.RedisService; +import com.ssdmn.common.result.Result; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.annotation.Resource; + +import static com.ssdmn.common.result.ResultConstants.SMS_SEND_FAIL; +import static com.ssdmn.common.result.Results.fail; +import static com.ssdmn.common.result.Results.ok; +import static com.ssdmn.common.utils.RandomUtils.generateNumberChar; + + +/** + * @author 郭世旭 + */ +@Api(tags = "验证码模块") +@RestController +@RequestMapping("user/sms") +public class SmsController { + + @Resource + private AliYunService aliYunService; + + @Resource + private AliYunProperties aliyunProperties; + + @Resource + private RedisService redisService; + + /** + * 发送验证码短信 + * + * @return 短信验证码 + */ + @ApiOperation("发送短信验证码") + @PostMapping("/send") + public Result smsSend(@Validated SmsCodeDTO smsCodeDTO) { + Integer type = smsCodeDTO.getType(); + //生成验证码 + String code = generateNumberChar(aliyunProperties.getSms().getLength()); + //短信模板 + String smsTemplate = SmsTemplateEnum.getValue(type); + //短信模板code + String smsTemplateCode = SmsTemplateCodeEnum.getValue(type); + //发送短信 + boolean b = aliYunService.smsSend(StrUtil.format(smsTemplate, code), smsCodeDTO.getPhone(), smsTemplateCode); + if (b) { + //存redis + String smsRedis = SmsRedisEnum.getValue(type); + redisService.set(StrUtil.format(smsRedis, smsCodeDTO.getPhone()), code, aliyunProperties.getSms().getValidTime()); + + return ok(); + } + + return fail(SMS_SEND_FAIL); + } +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/enums/sms/SmsRedisEnum.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/enums/sms/SmsRedisEnum.java new file mode 100644 index 0000000..6cfa6d5 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/enums/sms/SmsRedisEnum.java @@ -0,0 +1,74 @@ +package com.ssdmn.biz.login.enums.sms; + + +import lombok.AllArgsConstructor; +import lombok.NoArgsConstructor; + +import java.util.LinkedHashMap; + +import static com.ssdmn.biz.login.constants.Constants.REDIS_PREFIX_SMS_LOGIN; + + +/** + * @description: 短信存储redis模板 + * @Author: 郭世旭 + * @Date: 2021/04/16 + */ +@NoArgsConstructor +@AllArgsConstructor +public enum SmsRedisEnum { + + + /*-----Basic-----*/ + SMS_CODE_LOGIN(1, REDIS_PREFIX_SMS_LOGIN), + + ; + + + public Integer status; + + public String message; + + SmsRedisEnum(int status, String message) { + this.status = status; + this.message = message; + } + + public Integer getStatus() { + return status; + } + + public void setStatus(Integer status) { + this.status = status; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + /** + * 获取所有回复码 + * + * @return + */ + public static LinkedHashMap getArrayMessage() { + LinkedHashMap responseMessages = new LinkedHashMap<>(); + for (SmsRedisEnum statusEnum : SmsRedisEnum.values()) { + responseMessages.put(statusEnum.status, statusEnum.message); + } + return responseMessages; + } + + public static String getValue(Integer status) { + for (SmsRedisEnum ele : values()) { + if (ele.getStatus().equals(status)) { + return ele.getMessage(); + } + } + return null; + } +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/enums/sms/SmsTemplateCodeEnum.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/enums/sms/SmsTemplateCodeEnum.java new file mode 100644 index 0000000..82ac802 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/enums/sms/SmsTemplateCodeEnum.java @@ -0,0 +1,81 @@ +package com.ssdmn.biz.login.enums.sms; + + +import lombok.AllArgsConstructor; +import lombok.NoArgsConstructor; + +import java.util.LinkedHashMap; + +import static com.ssdmn.biz.login.constants.Constants.SmsCodeType.*; + +/** + * @Description: 短信发送模板 + * @Author: 郭世旭 + * @Date: 2021/04/16 + */ +@NoArgsConstructor +@AllArgsConstructor +public enum SmsTemplateCodeEnum { + + + /*-----Basic-----*/ + SMS_CODE_LOGIN(LOGIN, "SMS_152880348"), + + SMS_CODE_WX_MINI(WX_BIND_PHONE, "SMS_217425727"), + + SMS_CODE_TABLE_TOGETHER_SUCCESS(TABLE_TOGETHER_SUCCESS,"SMS_218156155"), + + SMS_CODE_TABLE_TOGETHER_PAY_SUCCESS(TABLE_TOGETHER_PAY_SUCCESS,"SMS_218169791"), + + OPENING_NOTICE(TABLE_TOGETHER_PAY_SUCCESS,"SMS_229470533") + + ; + + + public Integer status; + + public String message; + + SmsTemplateCodeEnum(int status, String message) { + this.status = status; + this.message = message; + } + + public Integer getStatus() { + return status; + } + + public void setStatus(Integer status) { + this.status = status; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + /** + * 获取所有回复码 + * + * @return + */ + public static LinkedHashMap getArrayMessage() { + LinkedHashMap responseMessages = new LinkedHashMap<>(); + for (SmsTemplateCodeEnum statusEnum : SmsTemplateCodeEnum.values()) { + responseMessages.put(statusEnum.status, statusEnum.message); + } + return responseMessages; + } + + public static String getValue(Integer status) { + for (SmsTemplateCodeEnum ele : values()) { + if (ele.getStatus().equals(status)) { + return ele.getMessage(); + } + } + return null; + } +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/enums/sms/SmsTemplateEnum.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/enums/sms/SmsTemplateEnum.java new file mode 100644 index 0000000..0667ec5 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/enums/sms/SmsTemplateEnum.java @@ -0,0 +1,75 @@ +package com.ssdmn.biz.login.enums.sms; + + +import lombok.AllArgsConstructor; +import lombok.NoArgsConstructor; + +import java.util.LinkedHashMap; + +import static com.ssdmn.biz.login.constants.Constants.SmsCodeType.*; + +/** + * @Description: 短信发送模板 + * @Author: 郭世旭 + * @Date: 2021/04/16 + */ +@NoArgsConstructor +@AllArgsConstructor +public enum SmsTemplateEnum { + + + /*-----Basic-----*/ + SMS_CODE_LOGIN(LOGIN, "{\"code\":\"{}\"}"), + + SMS_CODE_WX_MINI(WX_BIND_PHONE, "{\"code\":\"{}\"}"), + + ; + + + public Integer status; + + public String message; + + SmsTemplateEnum(int status, String message) { + this.status = status; + this.message = message; + } + + public Integer getStatus() { + return status; + } + + public void setStatus(Integer status) { + this.status = status; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + /** + * 获取所有回复码 + * + * @return + */ + public static LinkedHashMap getArrayMessage() { + LinkedHashMap responseMessages = new LinkedHashMap<>(); + for (SmsTemplateEnum statusEnum : SmsTemplateEnum.values()) { + responseMessages.put(statusEnum.status, statusEnum.message); + } + return responseMessages; + } + + public static String getValue(Integer status) { + for (SmsTemplateEnum ele : values()) { + if (ele.getStatus().equals(status)) { + return ele.getMessage(); + } + } + return null; + } +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/exception/BaseException.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/exception/BaseException.java new file mode 100644 index 0000000..28bffb8 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/exception/BaseException.java @@ -0,0 +1,98 @@ +package com.ssdmn.biz.login.exception; + + +import com.ssdmn.common.str.StringUtils; +import com.ssdmn.common.utils.MessageUtils; + +/** + * 基础异常 + * + * @author 郭世旭 + */ +public class BaseException extends RuntimeException +{ + private static final long serialVersionUID = 1L; + + /** + * 所属模块 + */ + private String module; + + /** + * 错误码 + */ + private String code; + + /** + * 错误码对应的参数 + */ + private Object[] args; + + /** + * 错误消息 + */ + private String defaultMessage; + + public BaseException(String module, String code, Object[] args, String defaultMessage) + { + this.module = module; + this.code = code; + this.args = args; + this.defaultMessage = defaultMessage; + } + + public BaseException(String module, String code, Object[] args) + { + this(module, code, args, null); + } + + public BaseException(String module, String defaultMessage) + { + this(module, null, null, defaultMessage); + } + + public BaseException(String code, Object[] args) + { + this(null, code, args, null); + } + + public BaseException(String defaultMessage) + { + this(null, null, null, defaultMessage); + } + + @Override + public String getMessage() + { + String message = null; + if (!StringUtils.isEmpty(code)) + { + message = MessageUtils.message(code, args); + } + if (message == null) + { + message = defaultMessage; + } + return message; + } + + public String getModule() + { + return module; + } + + public String getCode() + { + return code; + } + + public Object[] getArgs() + { + return args; + } + + public String getDefaultMessage() + { + return defaultMessage; + } +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/exception/BizException.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/exception/BizException.java new file mode 100644 index 0000000..43591fe --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/exception/BizException.java @@ -0,0 +1,86 @@ +package com.ssdmn.biz.login.exception; + + +import com.ssdmn.common.result.ResultConstants; + +/** + * 业务异常 + * + * @author 郭世旭 + * @version 1.0 + * @createTime 2021-03-22 09:58:30 + */ +public class BizException extends RuntimeException { + + private static final long serialVersionUID = 1L; + /** + * 错误码 + */ + private Integer code; + /** + * 错误消息 + */ + private String message; + + /** + * 错误带返回值 + */ + private Object data; + + public BizException(String message) { + super(message); + this.code = 500; + this.message = message; + } + + public BizException(Integer code, String message) { + super(message); + this.code = code; + this.message = message; + } + + public BizException(ResultConstants resultConstants) { + this.code = resultConstants.status; + this.message = resultConstants.message; + } + + public BizException(ResultConstants resultConstants,String message) { + this.code = resultConstants.status; + this.message = resultConstants.message+":"+message; + } + + public BizException(ResultConstants resultConstants,Object obj) { + this.code = resultConstants.status; + this.message = resultConstants.message; + this.data = obj; + } + + public static long getSerialVersionUID() { + return serialVersionUID; + } + + public Integer getCode() { + return code; + } + + public void setCode(Integer code) { + this.code = code; + } + + @Override + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public Object getData() { + return data; + } + + public void setData(Object data) { + this.data = data; + } +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/exception/CommissionException.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/exception/CommissionException.java new file mode 100644 index 0000000..c6b935d --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/exception/CommissionException.java @@ -0,0 +1,86 @@ +package com.ssdmn.biz.login.exception; + + +import com.ssdmn.common.result.ResultConstants; + +/** + * 分佣异常 + * + * @author 郭世旭 + * @version 1.0 + * @createTime 2021-03-22 09:58:30 + */ +public class CommissionException extends RuntimeException { + + private static final long serialVersionUID = 1L; + /** + * 错误码 + */ + private Integer code; + /** + * 错误消息 + */ + private String message; + + /** + * 错误带返回值 + */ + private Object data; + + public CommissionException(String message) { + super(message); + this.code = 500; + this.message = message; + } + + public CommissionException(Integer code, String message) { + super(message); + this.code = code; + this.message = message; + } + + public CommissionException(ResultConstants resultConstants) { + this.code = resultConstants.status; + this.message = resultConstants.message; + } + + public CommissionException(ResultConstants resultConstants, String message) { + this.code = resultConstants.status; + this.message = resultConstants.message+":"+message; + } + + public CommissionException(ResultConstants resultConstants, Object obj) { + this.code = resultConstants.status; + this.message = resultConstants.message; + this.data = obj; + } + + public static long getSerialVersionUID() { + return serialVersionUID; + } + + public Integer getCode() { + return code; + } + + public void setCode(Integer code) { + this.code = code; + } + + @Override + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public Object getData() { + return data; + } + + public void setData(Object data) { + this.data = data; + } +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/exception/FileUploadException.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/exception/FileUploadException.java new file mode 100644 index 0000000..3b8449a --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/exception/FileUploadException.java @@ -0,0 +1,85 @@ +package com.ssdmn.biz.login.exception; + +import com.ssdmn.common.result.ResultConstants; + +/** + * 业务异常 + * + * @author 郭世旭 + * @version 1.0 + * @createTime 2021-05-11 21:58:30 + */ +public class FileUploadException extends RuntimeException { + + private static final long serialVersionUID = 1L; + /** + * 错误码 + */ + private Integer code; + /** + * 错误消息 + */ + private String message; + + /** + * 错误带返回值 + */ + private Object data; + + public FileUploadException(String message) { + super(message); + this.code = 500; + this.message = message; + } + + public FileUploadException(Integer code, String message) { + super(message); + this.code = code; + this.message = message; + } + + public FileUploadException(ResultConstants resultConstants) { + this.code = resultConstants.status; + this.message = resultConstants.message; + } + + public FileUploadException(ResultConstants resultConstants, String message) { + this.code = resultConstants.status; + this.message = resultConstants.message+":"+message; + } + + public FileUploadException(ResultConstants resultConstants, Object obj) { + this.code = resultConstants.status; + this.message = resultConstants.message; + this.data = obj; + } + + public static long getSerialVersionUID() { + return serialVersionUID; + } + + public Integer getCode() { + return code; + } + + public void setCode(Integer code) { + this.code = code; + } + + @Override + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public Object getData() { + return data; + } + + public void setData(Object data) { + this.data = data; + } +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/exception/PartnerIntegralException.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/exception/PartnerIntegralException.java new file mode 100644 index 0000000..4552440 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/exception/PartnerIntegralException.java @@ -0,0 +1,86 @@ +package com.ssdmn.biz.login.exception; + + +import com.ssdmn.common.result.ResultConstants; + +/** + * 积分异常 + * + * @author 范成毅 + * @version 1.0 + * @Data: 2021/7/22 14:55 + */ +public class PartnerIntegralException extends RuntimeException { + + private static final long serialVersionUID = 1L; + /** + * 错误码 + */ + private Integer code; + /** + * 错误消息 + */ + private String message; + + /** + * 错误带返回值 + */ + private Object data; + + public PartnerIntegralException(String message) { + super(message); + this.code = 500; + this.message = message; + } + + public PartnerIntegralException(Integer code, String message) { + super(message); + this.code = code; + this.message = message; + } + + public PartnerIntegralException(ResultConstants resultConstants) { + this.code = resultConstants.status; + this.message = resultConstants.message; + } + + public PartnerIntegralException(ResultConstants resultConstants, String message) { + this.code = resultConstants.status; + this.message = resultConstants.message+":"+message; + } + + public PartnerIntegralException(ResultConstants resultConstants, Object obj) { + this.code = resultConstants.status; + this.message = resultConstants.message; + this.data = obj; + } + + public static long getSerialVersionUID() { + return serialVersionUID; + } + + public Integer getCode() { + return code; + } + + public void setCode(Integer code) { + this.code = code; + } + + @Override + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public Object getData() { + return data; + } + + public void setData(Object data) { + this.data = data; + } +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/exception/TaskException.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/exception/TaskException.java new file mode 100644 index 0000000..39bc5a8 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/exception/TaskException.java @@ -0,0 +1,34 @@ +package com.ssdmn.biz.login.exception; + +/** + * 计划策略异常 + * + * @author 郭世旭 + */ +public class TaskException extends Exception +{ + private static final long serialVersionUID = 1L; + + private Code code; + + public TaskException(String msg, Code code) + { + this(msg, code, null); + } + + public TaskException(String msg, Code code, Exception nestedEx) + { + super(msg, nestedEx); + this.code = code; + } + + public Code getCode() + { + return code; + } + + public enum Code + { + TASK_EXISTS, NO_TASK_EXISTS, TASK_ALREADY_STARTED, UNKNOWN, CONFIG_ERROR, TASK_NODE_NOT_AVAILABLE + } +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/exception/user/UserBindLoginException.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/exception/user/UserBindLoginException.java new file mode 100644 index 0000000..2cc02ba --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/exception/user/UserBindLoginException.java @@ -0,0 +1,86 @@ +package com.ssdmn.biz.login.exception.user; + +import com.ssdmn.common.result.ResultConstants; + +/** + * 业务异常 + * + * @author 郭世旭 + * @version 1.0 + * @createTime 2021-03-22 09:58:30 + */ +public class UserBindLoginException extends RuntimeException { + + private static final long serialVersionUID = 1L; + /** + * 错误码 + */ + private Integer code; + /** + * 错误消息 + */ + private String message; + + /** + * 数据 + */ + private Object data; + + public UserBindLoginException(String message) { + super(message); + this.code = 500; + this.message = message; + } + + public UserBindLoginException(Integer code, String message) { + super(message); + this.code = code; + this.message = message; + } + + public UserBindLoginException(Integer code, String message, Object data) { + this.code = code; + this.message = message; + this.data = data; + } + + public UserBindLoginException(ResultConstants resultConstants) { + this.code = resultConstants.status; + this.message = resultConstants.message; + } + + public UserBindLoginException(ResultConstants resultConstants, Object data) { + this.code = resultConstants.status; + this.message = resultConstants.message; + this.data = data; + } + + public static long getSerialVersionUID() { + return serialVersionUID; + } + + public Integer getCode() { + return code; + } + + public void setCode(Integer code) { + this.code = code; + } + + @Override + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public Object getData() { + return data; + } + + public void setData(Object data) { + this.data = data; + } +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/exception/user/UserLoginException.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/exception/user/UserLoginException.java new file mode 100644 index 0000000..0d99bb6 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/exception/user/UserLoginException.java @@ -0,0 +1,81 @@ +package com.ssdmn.biz.login.exception.user; + + +import com.ssdmn.common.result.ResultConstants; + +/** + * 业务异常 + * + * @author 郭世旭 + * @version 1.0 + * @createTime 2021-03-22 09:58:30 + */ +public class UserLoginException extends RuntimeException { + + private static final long serialVersionUID = 1L; + /** + * 错误码 + */ + private Integer code; + /** + * 错误消息 + */ + private String message; + + /** + * 数据 + */ + private Object data; + + public UserLoginException(String message) { + super(message); + this.code = 500; + this.message = message; + } + + public UserLoginException(Integer code, String message) { + super(message); + this.code = code; + this.message = message; + } + + public UserLoginException(ResultConstants resultConstants) { + this.code = resultConstants.status; + this.message = resultConstants.message; + } + + public UserLoginException(ResultConstants resultConstants,Object data) { + this.code = resultConstants.status; + this.message = resultConstants.message; + this.data = data; + } + + public static long getSerialVersionUID() { + return serialVersionUID; + } + + public Integer getCode() { + return code; + } + + public void setCode(Integer code) { + this.code = code; + } + + @Override + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public Object getData() { + return data; + } + + public void setData(Object data) { + this.data = data; + } +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/mapper/SUserMapper.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/mapper/SUserMapper.java new file mode 100644 index 0000000..e12108f --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/mapper/SUserMapper.java @@ -0,0 +1,10 @@ +package com.ssdmn.biz.login.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.ssdmn.biz.login.pojo.entity.User; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface SUserMapper extends BaseMapper { + +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/mapper/UserLoginMapper.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/mapper/UserLoginMapper.java new file mode 100644 index 0000000..751947b --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/mapper/UserLoginMapper.java @@ -0,0 +1,10 @@ +package com.ssdmn.biz.login.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.ssdmn.biz.login.pojo.entity.UserLogin; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface UserLoginMapper extends BaseMapper { + +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/mapper/UserLoginRecordMapper.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/mapper/UserLoginRecordMapper.java new file mode 100644 index 0000000..fb73e87 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/mapper/UserLoginRecordMapper.java @@ -0,0 +1,15 @@ +package com.ssdmn.biz.login.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.ssdmn.biz.login.pojo.entity.UserLoginRecord; +import org.apache.ibatis.annotations.Mapper; + +/** + * @Description: 用户登录记录 + * @Author: fan + * @Date: 2021/8/4 9:13 + * @Version: 1.0v + */ +@Mapper +public interface UserLoginRecordMapper extends BaseMapper { +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/mapper/UserLoginWayMapper.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/mapper/UserLoginWayMapper.java new file mode 100644 index 0000000..af84e8d --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/mapper/UserLoginWayMapper.java @@ -0,0 +1,13 @@ +package com.ssdmn.biz.login.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.ssdmn.biz.login.pojo.entity.UserLoginWay; +import org.apache.ibatis.annotations.Mapper; + +/** + * @author 郭世旭 + */ +@Mapper +public interface UserLoginWayMapper extends BaseMapper { + +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/pojo/dto/BasePageRequest.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/pojo/dto/BasePageRequest.java new file mode 100644 index 0000000..b08110d --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/pojo/dto/BasePageRequest.java @@ -0,0 +1,26 @@ +package com.ssdmn.biz.login.pojo.dto; + +import cn.hutool.core.date.LocalDateTimeUtil; +import cn.hutool.core.util.ObjectUtil; +import com.ssdmn.common.page.PageModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.time.LocalDateTime; + +@Data +public class BasePageRequest { + + @ApiModelProperty("月") + private String localDate; + + @ApiModelProperty("分页参数") + private PageModel pageModel; + + public LocalDateTime getLocalDate() { + if (ObjectUtil.isNotEmpty(localDate)) { + return LocalDateTimeUtil.parse(localDate + "-01", "yyyy-MM-dd"); + } + return LocalDateTime.now(); + } +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/pojo/dto/ChatRecordDetailRequest.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/pojo/dto/ChatRecordDetailRequest.java new file mode 100644 index 0000000..36623f7 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/pojo/dto/ChatRecordDetailRequest.java @@ -0,0 +1,26 @@ +package com.ssdmn.biz.login.pojo.dto; + +import com.ssdmn.common.page.Comparison; +import com.ssdmn.common.page.QueryField; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.validation.constraints.NotNull; + +@Data +public class ChatRecordDetailRequest { + + @ApiModelProperty(hidden = true) + @QueryField(comparison = Comparison.EQ) + Long userId; + + @ApiModelProperty("批次号") + @NotNull + @QueryField(comparison = Comparison.EQ) + private String batchNum; + + @ApiModelProperty("会话id") + @NotNull + @QueryField(comparison = Comparison.EQ) + private String conversationId; +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/pojo/dto/DatePageRequest.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/pojo/dto/DatePageRequest.java new file mode 100644 index 0000000..9727a5b --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/pojo/dto/DatePageRequest.java @@ -0,0 +1,27 @@ +package com.ssdmn.biz.login.pojo.dto; + +import com.ssdmn.common.page.Comparison; +import com.ssdmn.common.page.PageModel; +import com.ssdmn.common.page.QueryField; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +@Data +public class DatePageRequest extends BasePageRequest { + + + @ApiModelProperty("批次号") + @QueryField(comparison = Comparison.EQ) + private String batchNum; + + + @ApiModelProperty("用户id") + @QueryField(comparison = Comparison.EQ) + private Long userId; + + @ApiModelProperty("会话id") + @QueryField(comparison = Comparison.EQ) + private String conversationId; + + private PageModel pageModel; +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/pojo/dto/FillUserInfoRequest.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/pojo/dto/FillUserInfoRequest.java new file mode 100644 index 0000000..0f76e48 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/pojo/dto/FillUserInfoRequest.java @@ -0,0 +1,59 @@ +package com.ssdmn.biz.login.pojo.dto; + +import cn.hutool.core.util.ObjectUtil; +import lombok.Data; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import java.io.Serializable; +import java.time.LocalDate; + +/** + * 填写用户信息 + */ +@Data +public class FillUserInfoRequest implements Serializable { + + /** + * 昵称 + */ + @NotBlank + private String nickname; + + /** + * 学校 + */ + private String school; + + /** + * 学历 + */ + @NotBlank + private String educationBackground; + + /** + * 职业 + */ + private String occupation; + + /** + * 职业 + */ + private String job; + + /** + * 生日 + */ + @NotNull + private LocalDate birthday; + + /** + * 姓名 + */ + private String realName; + + public String getOccupation() { + + return ObjectUtil.isNotEmpty(occupation)?occupation:job; + } +} \ No newline at end of file diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/pojo/dto/LoginBaseDTO.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/pojo/dto/LoginBaseDTO.java new file mode 100644 index 0000000..c4ea85f --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/pojo/dto/LoginBaseDTO.java @@ -0,0 +1,26 @@ +package com.ssdmn.biz.login.pojo.dto; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.validation.constraints.NotNull; + +/** + * @description: 登录顶级父类 + * @author: 郭世旭 + * @create: 2021-05-19 20:47 + **/ +@Data +public class LoginBaseDTO { + @ApiModelProperty(value = "上级id") + private Long superiorId; + + @ApiModelProperty(value = "创建方式 1-用户自己登录 2-用户二维码分享 3-普通商品分享 4-超值套餐分享 5-拼团分享 6-拼桌分享 7-后台创建 8-火锅达人身份升级套餐分享创建") + @NotNull + /** + * 创建方式 + * @see Constants.Partner.CreateWay + */ + private Integer createWay; + +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/pojo/dto/LoginRequest.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/pojo/dto/LoginRequest.java new file mode 100644 index 0000000..aff3912 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/pojo/dto/LoginRequest.java @@ -0,0 +1,21 @@ +package com.ssdmn.biz.login.pojo.dto; + +import com.ssdmn.common.annotation.valid.Phone; +import com.ssdmn.common.annotation.valid.SmsCode; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +@Data +public class LoginRequest { + + @ApiModelProperty("手机号") + @Phone + private String phone; + + @ApiModelProperty("验证码") + @SmsCode + private String smsCode; + + @ApiModelProperty("邀请参数") + private String invitationParam; +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/pojo/dto/MiniAppFirstPhoneDTO.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/pojo/dto/MiniAppFirstPhoneDTO.java new file mode 100644 index 0000000..088fd20 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/pojo/dto/MiniAppFirstPhoneDTO.java @@ -0,0 +1,43 @@ +package com.ssdmn.biz.login.pojo.dto; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.validation.constraints.NotNull; + +/** + * @Description: 小程序第一次绑定手机 + * @Author: fan + * @Data: 2021/5/19 14:05 + * @Version: 1.0v + */ +@Data +public class MiniAppFirstPhoneDTO { + + /** + * 临时code + */ + @ApiModelProperty("临时code") + @NotNull(message = "临时code不能为空") + private String temporaryCode; + + /** + * 包括敏感数据在内的完整用户信息的加密数据 + */ + @ApiModelProperty("包括敏感数据在内的完整用户信息的加密数据") + @NotNull(message = "加密数据不能为空") + private String encryptedData; + /** + * 加密算法的初始向量 + */ + @ApiModelProperty("加密算法的初始向量") + @NotNull(message = "初始向量不能为空") + private String iv; + /** + * 微信code + */ + @ApiModelProperty("微信code") + @NotNull(message = "code不能为空") + private String code; + +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/pojo/dto/MiniAppSecondPhoneDTO.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/pojo/dto/MiniAppSecondPhoneDTO.java new file mode 100644 index 0000000..f6dcde3 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/pojo/dto/MiniAppSecondPhoneDTO.java @@ -0,0 +1,31 @@ +package com.ssdmn.biz.login.pojo.dto; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; + +/** + * @Description: 小程序第二次绑定手机 + * @Author: fan + * @Data: 2021/5/20 14:32 + * @Version: 1.0v + */ +@Data +public class MiniAppSecondPhoneDTO { + + @ApiModelProperty("临时code") + @NotNull(message = "临时code不能为空") + private String temporaryCode; + + @ApiModelProperty("电话号码") + @NotNull(message = "电话号码不能为空") + private String phoneNumber; + + + @ApiModelProperty(value="验证码") + @NotBlank(message = "验证码不能为空") + private String smsCode; + +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/pojo/dto/MiniAppUserUserInfoDTO.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/pojo/dto/MiniAppUserUserInfoDTO.java new file mode 100644 index 0000000..a8bf26b --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/pojo/dto/MiniAppUserUserInfoDTO.java @@ -0,0 +1,39 @@ +package com.ssdmn.biz.login.pojo.dto; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.validation.constraints.NotNull; + +/** + * @Description: 小程序登录得到用户基本信息 + * @Author: fan + * @Data: 2021/5/19 20:16 + * @Version: 1.0v + */ +@Data +public class MiniAppUserUserInfoDTO extends LoginBaseDTO { + + /** + * 包括敏感数据在内的完整用户信息的加密数据 + */ + @ApiModelProperty("包括敏感数据在内的完整用户信息的加密数据") + @NotNull(message = "加密数据不能为空") + private String encryptedData; + + /** + * 加密算法的初始向量 + */ + @ApiModelProperty("加密算法的初始向量") + @NotNull(message = "初始向量不能为空") + private String iv; + + /** + * 微信code + */ + @ApiModelProperty("微信code") + @NotNull(message = "code不能为空") + private String code; + + +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/pojo/dto/PageChatRecordRequest.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/pojo/dto/PageChatRecordRequest.java new file mode 100644 index 0000000..3964bf4 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/pojo/dto/PageChatRecordRequest.java @@ -0,0 +1,13 @@ +package com.ssdmn.biz.login.pojo.dto; + +import com.ssdmn.common.page.PageModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +@Data +public class PageChatRecordRequest extends BasePageRequest { + + @ApiModelProperty(hidden = true) + Long userId; + private PageModel pageModel; +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/pojo/dto/SendSmsRequest.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/pojo/dto/SendSmsRequest.java new file mode 100644 index 0000000..0564b98 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/pojo/dto/SendSmsRequest.java @@ -0,0 +1,15 @@ +package com.ssdmn.biz.login.pojo.dto; + +import com.ssdmn.common.annotation.valid.Phone; +import com.ssdmn.common.annotation.valid.SmsCode; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +@Data +public class SendSmsRequest { + + @ApiModelProperty("手机号") + @Phone + private String phone; + +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/pojo/dto/SmsCodeDTO.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/pojo/dto/SmsCodeDTO.java new file mode 100644 index 0000000..86759bc --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/pojo/dto/SmsCodeDTO.java @@ -0,0 +1,26 @@ +package com.ssdmn.biz.login.pojo.dto; + +import com.ssdmn.biz.contants.Constants; +import com.ssdmn.common.annotation.valid.EnumType; +import com.ssdmn.common.annotation.valid.Phone; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.io.Serializable; + +/** + * 用户登录 + * @author 郭世旭 + * @date 2021/01/15 + */ +@Data +public class SmsCodeDTO implements Serializable { + + @ApiModelProperty(value="手机号", example = "13500000000") + @Phone + private String phone; + + @ApiModelProperty(value="验证码类型 1-登录 2-微信小程序绑定手机",example = "1") + @EnumType(enumType = Constants.SmsCodeType.class) + private Integer type; +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/pojo/dto/UserLoginDTO.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/pojo/dto/UserLoginDTO.java new file mode 100644 index 0000000..63cf649 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/pojo/dto/UserLoginDTO.java @@ -0,0 +1,53 @@ +package com.ssdmn.biz.login.pojo.dto; + +import com.ssdmn.common.page.QueryField; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.validation.constraints.NotNull; +import java.io.Serializable; + +/** + * @author 郭世旭 + */ +@Data +public class UserLoginDTO implements Serializable { + + @ApiModelProperty(value="登录方式 1-手机号 2-微信,暂时没用 3-账号密码 4-app微信登录 5-小程序微信登录 6-公众号微信登录",example = "1") + @NotNull(message = "登录方法不能为空!") + @QueryField + private Integer userLoginWayId; + + @ApiModelProperty(value="手机号 type为1,3的时候必传",example = "13500000000") + @QueryField + private String phone; + + @ApiModelProperty(value="真实姓名",example = "张三") + private String realName; + + @ApiModelProperty(value="昵称",example = "13500000000") + private String nickName; + + @ApiModelProperty(value="头像",example = "https://smatch-oss.oss-cn-hangzhou.aliyuncs.com/f86da278-415e-460d-936f-09840719b2da.jpg") + private String avatar; + + @ApiModelProperty(value = "苹果id", example = "100000") + @QueryField + private String iosId; + + @ApiModelProperty(value="区域code",example = "100000") + private Integer areaId; + + @ApiModelProperty(value="用户的open_id",example = "13500000000") + @QueryField + private String openId; + + @ApiModelProperty(value="用户的union_id",example = "13500000000") + @QueryField + private String unionId; + + @ApiModelProperty(value="密码",example = "13500000000") + @QueryField + private String password; + +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/pojo/dto/UserSmsCodeLoginDTO.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/pojo/dto/UserSmsCodeLoginDTO.java new file mode 100644 index 0000000..2200f57 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/pojo/dto/UserSmsCodeLoginDTO.java @@ -0,0 +1,23 @@ +package com.ssdmn.biz.login.pojo.dto; + +import com.ssdmn.common.annotation.valid.Phone; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.validation.constraints.NotBlank; +import java.io.Serializable; + +/** + * @author 郭世旭 + */ +@Data +public class UserSmsCodeLoginDTO extends LoginBaseDTO implements Serializable { + +// @ApiModelProperty(value="验证码") +// @NotBlank(message = "验证码不能为空") +// private String smsCode; + + @ApiModelProperty(value="手机号",example = "13500000000") + @Phone + private String phone; +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/pojo/entity/User.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/pojo/entity/User.java new file mode 100644 index 0000000..b168ece --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/pojo/entity/User.java @@ -0,0 +1,72 @@ +package com.ssdmn.biz.login.pojo.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; + +import java.io.Serializable; +import java.util.Date; + +/** + * CarType + * + * @author gsx + * @date 2020/11/7 + */ +@Data +@TableName("t_user") +@ApiModel("用户表") +public class User implements Serializable { + + @TableId(type = IdType.AUTO) + @ApiModelProperty(value="主键") + private Long id; + + @ApiModelProperty(value="手机号",example = "13500000000") + private String phone; + + @ApiModelProperty(value="最后修改时间") + private Date lastModifiedDate; + + @ApiModelProperty(value="真实姓名",example = "张三") + private String realName; + + @ApiModelProperty(value="昵称",example = "13500000000") + private String nickName; + + @ApiModelProperty(value="头像",example = "https://smatch-oss.oss-cn-hangzhou.aliyuncs.com/f86da278-415e-460d-936f-09840719b2da.jpg") + private String avatar; + + @ApiModelProperty(value="账号状态:1-正常 2-注销") + private Integer status; + + @ApiModelProperty(value="创建时间") + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createDate; + + @ApiModelProperty(value="区域code",example = "100000") + private Integer areaId; + + @ApiModelProperty(value="是否锁定",example = "0") + private boolean locked; + + @ApiModelProperty(value="锁定时间",example = "yyyy-MM-dd HH:mm:ss") + private Date lockDate; + + @ApiModelProperty(value="最后登录时间",example = "yyyy-MM-dd HH:mm:ss") + private Date lastLoginDate; + + @ApiModelProperty(value="最后登录ip",example = "127.0.0.1") + private String lastLoginIp; + + @ApiModelProperty(value="版本",example = "") + private String version; + + @ApiModelProperty(value="人脸照片",example = "https://smatch-oss.oss-cn-hangzhou.aliyuncs.com/f86da278-415e-460d-936f-09840719b2da.jpg") + private String facePhoto; + +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/pojo/entity/UserLogin.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/pojo/entity/UserLogin.java new file mode 100644 index 0000000..1cb3873 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/pojo/entity/UserLogin.java @@ -0,0 +1,61 @@ +package com.ssdmn.biz.login.pojo.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.io.Serializable; +import java.util.Date; + +/** + * 用户渠道信息 + * + * @author gsx + * @date 2020/11/7 + */ +@Data +@TableName("t_user_login") +@ApiModel("渠道表") +public class UserLogin implements Serializable { + + @TableId(type = IdType.AUTO) + @ApiModelProperty("主键") + private Long id; + + @ApiModelProperty("用户id") + private Long userId; + + @ApiModelProperty("密码") + private String password; + + @ApiModelProperty("用户渠道id") + private Integer userLoginWayId; + + @ApiModelProperty("用户open_id") + private String openId; + + @ApiModelProperty("用户union_id") + private String unionId; + + @ApiModelProperty("用户手机号") + private String phone; + + @ApiModelProperty("用户iosId") + private String iosId; + + @ApiModelProperty(value="最后登录时间") + private Date lastDate; + + @ApiModelProperty(value="昵称",example = "13500000000") + private String nickName; + + @ApiModelProperty(value="头像",example = "https://smatch-oss.oss-cn-hangzhou.aliyuncs.com/f86da278-415e-460d-936f-09840719b2da.jpg") + private String avatar; + + @ApiModelProperty(value="创建时间") + private Date createDate; + +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/pojo/entity/UserLoginRecord.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/pojo/entity/UserLoginRecord.java new file mode 100644 index 0000000..52676b5 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/pojo/entity/UserLoginRecord.java @@ -0,0 +1,41 @@ +package com.ssdmn.biz.login.pojo.entity; + +import com.baomidou.mybatisplus.annotation.*; +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.time.LocalDateTime; +import java.util.Date; + +/** + * @Description: 用户登录记录 + * @Author: fan + * @Date: 2021/8/4 9:09 + * @Version: 1.0v + */ +@Data +@TableName("t_user_login_record") +public class UserLoginRecord { + + @TableId(type = IdType.AUTO) + @ApiModelProperty("主键") + private Long id; + + @ApiModelProperty("userId") + private Long userId; + + @ApiModelProperty("登录渠道") + private Integer loginWay; + + @ApiModelProperty("登录时间") + @JsonFormat(pattern = "yyyy-MM-dd") + private Date loginDate; + + @ApiModelProperty("登录时间") + @TableField(fill = FieldFill.INSERT) + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private LocalDateTime createTime; + + +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/pojo/entity/UserLoginWay.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/pojo/entity/UserLoginWay.java new file mode 100644 index 0000000..4c2c64f --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/pojo/entity/UserLoginWay.java @@ -0,0 +1,38 @@ +package com.ssdmn.biz.login.pojo.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Builder; +import lombok.Data; + +import java.io.Serializable; + +/** + * CarType + * + * @author gsx + * @date 2020/11/7 + */ +@Data +@Builder +@TableName("t_user_login_way") +@ApiModel("渠道表") +public class UserLoginWay implements Serializable { + + @TableId(type = IdType.AUTO) + @ApiModelProperty("主键") + private Long id; + + @ApiModelProperty(value = "渠道类型",example = "1") + private String type; + + @ApiModelProperty(value = "渠道类型说明") + private String expain; + + @ApiModelProperty("是否有用") + private Integer isUsed; + +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/pojo/vo/BaseUserVO.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/pojo/vo/BaseUserVO.java new file mode 100644 index 0000000..43cfd75 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/pojo/vo/BaseUserVO.java @@ -0,0 +1,47 @@ +package com.ssdmn.biz.login.pojo.vo; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.io.Serializable; +import java.util.Date; + +/** + * 用户信息Vo + * + * @author gsx + * @date 2020/11/7 + */ +@Data +public class BaseUserVO implements Serializable { + + private static final long serialVersionUID = 1L; + + @ApiModelProperty(value="主键") + private Long id; + + @ApiModelProperty(value="手机号",example = "13500000000") + private String phone; + + @ApiModelProperty(value="真实姓名",example = "张三") + private String realName; + + @ApiModelProperty(value="昵称",example = "13500000000") + private String nickName; + + @ApiModelProperty(value="头像",example = "https://smatch-oss.oss-cn-hangzhou.aliyuncs.com/f86da278-415e-460d-936f-09840719b2da.jpg") + private String avatar; + + @ApiModelProperty(value="账号状态:1-正常 2-注销") + private Integer status; + + @ApiModelProperty(value="创建时间") + private Date createDate; + + @ApiModelProperty(value="区域code",example = "100000") + private Integer areaId; + + @ApiModelProperty(value="人脸照片",example = "https://smatch-oss.oss-cn-hangzhou.aliyuncs.com/f86da278-415e-460d-936f-09840719b2da.jpg") + private String facePhoto; + +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/pojo/vo/HomeInfoVO.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/pojo/vo/HomeInfoVO.java new file mode 100644 index 0000000..ca9d1ad --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/pojo/vo/HomeInfoVO.java @@ -0,0 +1,20 @@ +package com.ssdmn.biz.login.pojo.vo; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +@Data +public class HomeInfoVO { + + @ApiModelProperty("用户信息") + private UserVO userVO; + + @ApiModelProperty("是否签到") + private Integer isSignIn; + + @ApiModelProperty("是否充值") + private Integer isRecharge; + + @ApiModelProperty("是否完善资料") + private Integer isCompleteInfo; +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/pojo/vo/LoginVO.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/pojo/vo/LoginVO.java new file mode 100644 index 0000000..b1cdb43 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/pojo/vo/LoginVO.java @@ -0,0 +1,14 @@ +package com.ssdmn.biz.login.pojo.vo; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +@Data +public class LoginVO { + + @ApiModelProperty("用户信息") + private UserVO userVO; + + @ApiModelProperty("token") + private String token; +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/pojo/vo/PageUserAndPartnerVO.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/pojo/vo/PageUserAndPartnerVO.java new file mode 100644 index 0000000..ed2841a --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/pojo/vo/PageUserAndPartnerVO.java @@ -0,0 +1,81 @@ +package com.ssdmn.biz.login.pojo.vo; + +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.util.Date; + +/** + * @Description: 会员列表 + * @Author: fan + * @Data: 2021/5/25 15:22 + * @Version: 1.0v + */ +@Data +public class PageUserAndPartnerVO { + + private Long id; + + @ApiModelProperty("头像") + private String avatar; + + @ApiModelProperty("昵称") + private String nickName; + + @ApiModelProperty("昵称") + private String realName; + + @ApiModelProperty("电话") + private String phone; + + @ApiModelProperty("余额") + private String balance; + + @ApiModelProperty("已提现金额") + private String totalWithdraw; + + @ApiModelProperty("积分") + private String vipIntegral; + + @ApiModelProperty("推荐人") + private String superiorName; + + @ApiModelProperty("会员ID") + private String vipId; + + @ApiModelProperty("会员身份ID") + private String vipIdentityId; + + @ApiModelProperty("会员等级") + private String identityName; + + @ApiModelProperty("推荐人Id") + private Long superiorId; + + @ApiModelProperty("推荐人电话") + private String superiorPhone; + + @ApiModelProperty("股份") + private String share; + + @ApiModelProperty("注册时间") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createDate; + + @ApiModelProperty("是否锁定") + private Boolean locked; + + @ApiModelProperty("创建方式") + private String createWayName; + + /** + * 测评积分 + */ + @ApiModelProperty("测评积分") + private Integer assessScore; + + @ApiModelProperty("分享二维码") + private String qrCode; + +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/pojo/vo/UserCouponVO.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/pojo/vo/UserCouponVO.java new file mode 100644 index 0000000..47207ac --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/pojo/vo/UserCouponVO.java @@ -0,0 +1,32 @@ +package com.ssdmn.biz.login.pojo.vo; + + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * @author zhang + */ +@Data +public class UserCouponVO { + + + @ApiModelProperty(value = "主键") + private Long id; + + @ApiModelProperty(value = "手机号") + private String phone; + + @ApiModelProperty(value = "昵称") + private String nickName; + + @ApiModelProperty(value = "头像") + private String avatar; + + @ApiModelProperty(value = "优惠券数量") + private Integer couponCount; + + @ApiModelProperty(value = "优惠券名称") + private String couponName; + +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/pojo/vo/UserIntegralVO.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/pojo/vo/UserIntegralVO.java new file mode 100644 index 0000000..fb36ba3 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/pojo/vo/UserIntegralVO.java @@ -0,0 +1,26 @@ +package com.ssdmn.biz.login.pojo.vo; + + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * @author zhang + */ +@Data +public class UserIntegralVO { + + + @ApiModelProperty(value = "主键") + private Long id; + + @ApiModelProperty(value = "手机号") + private String phone; + + @ApiModelProperty(value = "昵称") + private String nickName; + + @ApiModelProperty(value = "头像") + private String avatar; + +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/pojo/vo/UserInviteInfo.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/pojo/vo/UserInviteInfo.java new file mode 100644 index 0000000..193f1a0 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/pojo/vo/UserInviteInfo.java @@ -0,0 +1,11 @@ +package com.ssdmn.biz.login.pojo.vo; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +@Data +public class UserInviteInfo { + + @ApiModelProperty("用户推广二维码") + private String qrCode; +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/pojo/vo/UserLoginVO.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/pojo/vo/UserLoginVO.java new file mode 100644 index 0000000..6c8a3d9 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/pojo/vo/UserLoginVO.java @@ -0,0 +1,50 @@ +package com.ssdmn.biz.login.pojo.vo; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.io.Serializable; +import java.util.Date; + +/** + * 用户渠道信息Vo + * + * @author gsx + * @date 2020/11/7 + */ +@Data +public class UserLoginVO implements Serializable { + + private static final long serialVersionUID = 1L; + + @ApiModelProperty("主键") + private Long id; + + @ApiModelProperty("用户id") + private Long userId; + + @ApiModelProperty("用户渠道 1-手机号渠道 2-微信小程序渠道 3-账号密码渠道") + private Long userLoginWayId; + + @ApiModelProperty("用户open_id") + private String openId; + + @ApiModelProperty("用户union_id") + private String unionId; + + @ApiModelProperty("用户手机号") + private String phone; + + @ApiModelProperty(value="最后登录时间") + private Date lastDate; + + @ApiModelProperty(value="昵称",example = "13500000000") + private String nickName; + + @ApiModelProperty(value="头像",example = "https://smatch-oss.oss-cn-hangzhou.aliyuncs.com/f86da278-415e-460d-936f-09840719b2da.jpg") + private String avatar; + + @ApiModelProperty(value="创建时间") + private Date createDate; + +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/pojo/vo/UserPartVO.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/pojo/vo/UserPartVO.java new file mode 100644 index 0000000..d4f7a04 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/pojo/vo/UserPartVO.java @@ -0,0 +1,28 @@ +package com.ssdmn.biz.login.pojo.vo; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.io.Serializable; + +/** + * 用户信息Vo + * + * @author gsx + * @date 2021/01/4 + */ +@Data +public class UserPartVO implements Serializable { + + @ApiModelProperty(value="主键") + private Long id; + + @ApiModelProperty(value="手机号",example = "13500000000") + private String phone; + + @ApiModelProperty(value="昵称",example = "13500000000") + private String nickName; + + @ApiModelProperty(value="头像",example = "https://smatch-oss.oss-cn-hangzhou.aliyuncs.com/f86da278-415e-460d-936f-09840719b2da.jpg") + private String avatar; +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/pojo/vo/UserRedisVO.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/pojo/vo/UserRedisVO.java new file mode 100644 index 0000000..3fe5c82 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/pojo/vo/UserRedisVO.java @@ -0,0 +1,18 @@ +package com.ssdmn.biz.login.pojo.vo; + + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.io.Serializable; + +/** + * @author 郭世旭 + */ +@Data +public class UserRedisVO extends UserVO implements Serializable { + + @ApiModelProperty(value="token") + private String token; + +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/pojo/vo/UserVO.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/pojo/vo/UserVO.java new file mode 100644 index 0000000..e203659 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/pojo/vo/UserVO.java @@ -0,0 +1,21 @@ +package com.ssdmn.biz.login.pojo.vo; + + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.util.List; + +/** + * @author 郭世旭 + */ +@Data +public class UserVO { + + @ApiModelProperty(value="用户信息Dbo") + private BaseUserVO baseUserVo; + + @ApiModelProperty(value="渠道Dbo集合") + private List userLoginVoList; + +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/pojo/vo/UserWxInfoVO.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/pojo/vo/UserWxInfoVO.java new file mode 100644 index 0000000..a654895 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/pojo/vo/UserWxInfoVO.java @@ -0,0 +1,32 @@ +package com.ssdmn.biz.login.pojo.vo; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * @author 郭世旭 + */ +@Data +public class UserWxInfoVO { + @ApiModelProperty(value="手机号",example = "13500000000") + private String phone; + + @ApiModelProperty(value="真实姓名",example = "张三") + private String realName; + + @ApiModelProperty(value="昵称",example = "13500000000") + private String nickName; + + @ApiModelProperty(value="头像",example = "https://smatch-oss.oss-cn-hangzhou.aliyuncs.com/f86da278-415e-460d-936f-09840719b2da.jpg") + private String avatar; + + @ApiModelProperty(value="区域code",example = "100000") + private Integer areaId; + + @ApiModelProperty("用户open_id") + private String openId; + + @ApiModelProperty("用户union_id") + private String unionId; + +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/pojo/vo/WxOAuth2UserInfoVO.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/pojo/vo/WxOAuth2UserInfoVO.java new file mode 100644 index 0000000..8adae95 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/pojo/vo/WxOAuth2UserInfoVO.java @@ -0,0 +1,51 @@ +package com.ssdmn.biz.login.pojo.vo; + +import lombok.Data; + +/** + * 微信登录信息 + * + * @author 郭世旭 + */ +@Data +public class WxOAuth2UserInfoVO { + + private String sessionKey; + /** + * openid 普通用户的标识,对当前开发者帐号唯一 + */ + private String openid; + /** + * unionid 用户统一标识。针对一个微信开放平台帐号下的应用,同一用户的unionid是唯一的。 + */ + private String unionId; + /** + * nickname 普通用户昵称 + */ + private String nickname; + /** + * sex 普通用户性别,1为男性,2为女性 + */ + private Integer sex; + /** + * city 普通用户个人资料填写的城市 + */ + private String city; + /** + * province 普通用户个人资料填写的省份 + */ + private String province; + /** + * country 国家,如中国为CN + */ + private String country; + /** + * headimgurl 用户头像,最后一个数值代表正方形头像大小(有0、46、64、96、132数值可选,0代表640*640正方形头像), + * 用户没有头像时该项为空 + */ + private String headImgUrl; + /** + * privilege 用户特权信息,json数组,如微信沃卡用户为(chinaunicom) + */ + private String[] privileges; +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/service/LoginService.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/service/LoginService.java new file mode 100644 index 0000000..0417ddc --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/service/LoginService.java @@ -0,0 +1,52 @@ +package com.ssdmn.biz.login.service; + + +import com.ssdmn.biz.login.pojo.dto.MiniAppFirstPhoneDTO; +import com.ssdmn.biz.login.pojo.dto.MiniAppSecondPhoneDTO; +import com.ssdmn.biz.login.pojo.dto.MiniAppUserUserInfoDTO; +import com.ssdmn.biz.login.pojo.dto.UserSmsCodeLoginDTO; +import me.chanjar.weixin.common.error.WxErrorException; + +/** + * @Description: 用户端接口 对应controller + * @Author: 郭世旭 + * @Date: 2021-04-22 10:13 + **/ +public interface LoginService { + + /** + * 验证码登录 + * + * @param userSmsCodeLoginDTO + * @return token + */ + String smsCodeLogin(UserSmsCodeLoginDTO userSmsCodeLoginDTO); + + /** + * 微信小程序授权基本信息登录 + * + * @param miniAppUserUserInfoDTO 微信小程序授权基本信息登录 + * @return token + * @throws Exception 连接微信网络异常 + */ + String wxMiniLogin(MiniAppUserUserInfoDTO miniAppUserUserInfoDTO) throws WxErrorException; + + /** + * 微信小程序授权手机号登录 + * + * @param miniAppFirstPhoneDTO 微信小程序授权手机号登录 + * @return token + * @throws Exception 连接微信网络异常 + */ + String wxMiniFirstBindPhone(MiniAppFirstPhoneDTO miniAppFirstPhoneDTO) throws Exception; + + /** + * 微信小程序第二次绑定手机号 + * + * @param miniAppSecondPhoneDTO 微信小程序授权手机号登录 + * @return 成功返回token + */ + String wxMiniSecondBindPhone(MiniAppSecondPhoneDTO miniAppSecondPhoneDTO); + + +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/service/SUserService.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/service/SUserService.java new file mode 100644 index 0000000..ea0b9a1 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/service/SUserService.java @@ -0,0 +1,40 @@ +package com.ssdmn.biz.login.service; + + + +import com.ssdmn.biz.login.pojo.dto.UserLoginDTO; +import com.ssdmn.biz.login.pojo.vo.UserVO; + +/** + * 用户登录接口 + * + * @author 郭世旭 + */ +public interface SUserService { + + /** + * 用户注册或登录 + * + * @param userLoginDTO + * @return + */ + UserVO userLogin(UserLoginDTO userLoginDTO); + + /** + * 绑定手机号 + * + * @param id + * @param phone + * @return + */ + UserVO bindingPhone(Long id, String phone); + + + + + + + + + +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/service/impl/LoginServiceImpl.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/service/impl/LoginServiceImpl.java new file mode 100644 index 0000000..c37ed44 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/service/impl/LoginServiceImpl.java @@ -0,0 +1,372 @@ +package com.ssdmn.biz.login.service.impl; + +import cn.binarywang.wx.miniapp.api.WxMaService; +import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult; +import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo; +import cn.binarywang.wx.miniapp.bean.WxMaUserInfo; +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.http.HttpUtil; +import cn.hutool.json.JSONUtil; +import com.ssdmn.biz.login.exception.user.UserBindLoginException; +import com.ssdmn.biz.login.exception.user.UserLoginException; +import com.ssdmn.biz.login.pojo.dto.MiniAppFirstPhoneDTO; +import com.ssdmn.biz.login.pojo.dto.MiniAppSecondPhoneDTO; +import com.ssdmn.biz.login.pojo.dto.MiniAppUserUserInfoDTO; +import com.ssdmn.biz.login.pojo.dto.UserSmsCodeLoginDTO; +import com.ssdmn.biz.login.pojo.vo.UserVO; +import com.ssdmn.biz.login.pojo.vo.WxOAuth2UserInfoVO; +import com.ssdmn.biz.login.service.LoginService; +import com.ssdmn.biz.login.service.SUserService; +import com.ssdmn.common.exception.BizException; +import com.ssdmn.common.jwt.JWTUtil; +import com.ssdmn.common.redis.service.RedisService; +import com.ssdmn.common.utils.BigNum; +import com.ssdmn.common.utils.UserRedis; +import com.ssdmn.web.pojo.dto.login.LoginDTO; +import com.ssdmn.web.pojo.dto.login.WxLoginRedisDTO; +import lombok.extern.slf4j.Slf4j; +import me.chanjar.weixin.common.error.WxErrorException; +import org.springframework.jdbc.datasource.DataSourceTransactionManager; +import org.springframework.stereotype.Service; +import org.springframework.transaction.TransactionDefinition; +import org.springframework.transaction.TransactionStatus; + +import javax.annotation.Resource; + +import static com.ssdmn.biz.contants.Constants.RedisPrefix.WX_MINI_LOG; +import static com.ssdmn.biz.contants.Constants.UserLoginWay.*; +import static com.ssdmn.biz.contants.Constants.Wx.Mini.EXPIRE_DATE; +import static com.ssdmn.biz.login.constants.Constants.REDIS_PREFIX_SMS_LOGIN; +import static com.ssdmn.common.result.ResultConstants.*; +import static com.ssdmn.common.utils.BigNum.Obj2Long; + + +/** + * @author 郭世旭 + */ +@Service +@Slf4j +public class LoginServiceImpl implements LoginService { + + + @Resource + private SUserService sUserService; + + @Resource + private RedisService redisService; + + @Resource(name = "wxLoginMaService") + private WxMaService wxLoginMaService; + + + + @Resource + private DataSourceTransactionManager dataSourceTransactionManager; + + @Resource + private TransactionDefinition transactionDefinition; + + @Override + public String smsCodeLogin(UserSmsCodeLoginDTO userSmsCodeLoginDTO) { +// String smsCode = redisService.getStr(StrUtil.format(REDIS_PREFIX_SMS_LOGIN, userSmsCodeLoginDTO.getPhone())); +// +// if (ObjectUtil.isEmpty(smsCode)) { +// throw new UserLoginException(SMS_CODE_NOT_EXIST); +// } +// +// if (!ObjectUtil.equals(smsCode, userSmsCodeLoginDTO.getSmsCode())) { +// throw new UserLoginException(SMS_CODE_NOT_EXIST); +// } + + LoginDTO loginDTO = BeanUtil.toBean(userSmsCodeLoginDTO, LoginDTO.class); + String token = userLoginWithPhone(loginDTO); + //清除redis中的短信缓存 +// redisService.del(StrUtil.format(REDIS_PREFIX_SMS_LOGIN, userSmsCodeLoginDTO.getPhone())); + + return token; + } + + @Override + public String wxMiniLogin(MiniAppUserUserInfoDTO miniAppUserUserInfoDTO) throws WxErrorException { + synchronized (miniAppUserUserInfoDTO.getCode().intern()) { + WxMaJscode2SessionResult session = wxLoginMaService.getUserService().getSessionInfo(miniAppUserUserInfoDTO.getCode()); + log.info("微信小程序登录session :{}", session); + WxMaUserInfo userInfo; + try { + userInfo = wxLoginMaService.getUserService().getUserInfo(session.getSessionKey(), miniAppUserUserInfoDTO.getEncryptedData(), miniAppUserUserInfoDTO.getIv()); + } catch (Exception e) { + e.printStackTrace(); + log.error(e.getMessage()); + throw new BizException("网络繁忙,请重试"); + } + log.info("微信小程序登录用户信息:{}", userInfo); + + if (ObjectUtil.isEmpty(userInfo.getOpenId())) { + userInfo.setOpenId(session.getOpenid()); + } + + if (ObjectUtil.isEmpty(userInfo.getUnionId())) { + userInfo.setUnionId(session.getUnionid()); + } + + // 设置用户信息 + LoginDTO loginDTO = BeanUtil.toBean(userInfo, LoginDTO.class); + loginDTO.setCreateWay(miniAppUserUserInfoDTO.getCreateWay()); + loginDTO.setSuperiorId(miniAppUserUserInfoDTO.getSuperiorId()); + + try { + return userLoginWithMiniWx(loginDTO); + } catch (UserBindLoginException e) { + WxLoginRedisDTO wxLoginRedisDTO = BeanUtil.toBean(loginDTO, WxLoginRedisDTO.class); + wxLoginRedisDTO.setUserLoginId(Obj2Long(e.getData())); + String templateCode = setUserWxInfo2Redis(wxLoginRedisDTO); + e.setData(templateCode); + throw e; + } + } + } + + @Override + public String wxMiniFirstBindPhone(MiniAppFirstPhoneDTO miniAppFirstPhoneDTO) throws WxErrorException { + WxMaJscode2SessionResult session = wxLoginMaService.getUserService().getSessionInfo(miniAppFirstPhoneDTO.getCode()); + log.info("微信小程序登录session :{}", session); + WxMaPhoneNumberInfo phoneNumberInfo = wxLoginMaService.getUserService().getPhoneNoInfo(session.getSessionKey(), miniAppFirstPhoneDTO.getEncryptedData(), miniAppFirstPhoneDTO.getIv()); + log.info("微信小程序登录用户手机信息:{}", phoneNumberInfo); + String temporaryCode = miniAppFirstPhoneDTO.getTemporaryCode(); + + String token = wxBindPhoneByTemplateCode(temporaryCode, phoneNumberInfo.getPhoneNumber()); + //清楚redis用户微信信息缓存 + redisService.del(StrUtil.format(WX_MINI_LOG, temporaryCode)); + + return token; + } + + /** + * --++- + * 微信小程序第二次绑定手机号 + * + * @param miniAppSecondPhoneDTO 临时code+手机号 + * @return 成功返回token + */ + @Override + public String wxMiniSecondBindPhone(MiniAppSecondPhoneDTO miniAppSecondPhoneDTO) { + String smsCode = redisService.getStr(StrUtil.format(WX_MINI_LOG, miniAppSecondPhoneDTO.getPhoneNumber())); + + // 在缓存中没有找到对应的key + if (ObjectUtil.isEmpty(smsCode)) { + throw new UserLoginException(SMS_CODE_NOT_EXIST); + } + + // 验证码不对 + if (!ObjectUtil.equals(smsCode, miniAppSecondPhoneDTO.getSmsCode())) { + throw new UserLoginException(SMS_CODE_NOT_EXIST); + } + String token = wxBindPhoneByTemplateCode(miniAppSecondPhoneDTO.getTemporaryCode(), miniAppSecondPhoneDTO.getPhoneNumber()); + //清除redis中的短信缓存 + redisService.del(StrUtil.format(WX_MINI_LOG, miniAppSecondPhoneDTO.getPhoneNumber())); + redisService.del(StrUtil.format(WX_MINI_LOG, miniAppSecondPhoneDTO.getTemporaryCode())); + return token; + } + + + /** + * 获取静默授权的微信信息 + * + * @param code 公众号临时授权code + * @param appId 公众号appId + * @param secret 公众号密钥 + * @return + */ + private WxOAuth2UserInfoVO getWxOAuth2UserInfoVOByBase(String code, String appId, String secret) { + String baseUrl = "https://api.weixin.qq.com/sns/oauth2/access_token"; + String url = StrUtil.format("{}?appid={}&secret={}&code={}&grant_type=authorization_code", + baseUrl, appId, secret, code); + String response = HttpUtil.get(url); + return JSONUtil.toBean(response, WxOAuth2UserInfoVO.class); + } + + /** + * 缓存用户微信信息到redis,key为临时code + * + * @param wxLoginRedisDTO + * @return + */ + private String setUserWxInfo2Redis(WxLoginRedisDTO wxLoginRedisDTO) { + //创建临时code + String templateCode = String.valueOf(BigNum.createOrderNo()); + //缓存redis 5分钟过期 + redisService.set(StrUtil.format(WX_MINI_LOG, templateCode), wxLoginRedisDTO, EXPIRE_DATE); + + return templateCode; + } + + /** + * 通过临时code取用户微信信息 + * + * @param templateCode + * @param phone + * @return + */ + private String wxBindPhoneByTemplateCode(String templateCode, String phone) { + WxLoginRedisDTO wxLoginRedisDTO; + synchronized (phone.intern()) { + //取redis缓存的用户微信信息 + wxLoginRedisDTO = getRedisWxLoginInfo(templateCode); + + try { + //微信绑定手机号 这里的632异常要处理 + wxBindPhone(wxLoginRedisDTO.getUserLoginId(), phone); + } catch (UserBindLoginException e) { + templateCode = setUserWxInfo2Redis(wxLoginRedisDTO); + + throw new UserBindLoginException(e.getCode(), e.getMessage(), templateCode); + } + } + + //登录* + return userLoginWithMiniWx(wxLoginRedisDTO); + } + + /** + * 小程序绑定手机号 + * + * @param userLoginId 渠道登录id + * @param phone 手机号 + * @return 返回登录成功的token + */ + private void wxBindPhone(Long userLoginId, String phone) { + // 得到渠道表主键 + sUserService.bindingPhone(userLoginId, phone); + } + + /** + * 获取redis中微信登录的缓存信息 + * + * @param tempCode 临时code + * @return redis中微信登录的缓存信息 + */ + private WxLoginRedisDTO getRedisWxLoginInfo(String tempCode) { + // 取redis中的数据 + String key = StrUtil.format(WX_MINI_LOG, tempCode); + WxLoginRedisDTO wxLoginRedisDTO = redisService.getObj(key, WxLoginRedisDTO.class); + if (ObjectUtil.isEmpty(wxLoginRedisDTO)) { + throw new BizException(WX_TEMPLATE_CODE_EXPIRED); + } + + return wxLoginRedisDTO; + } + + /** + * 小程序登录 + * + * @param loginDTO + * @return + */ + private String userLoginWithMiniWx(LoginDTO loginDTO) { + loginDTO.setUserLoginWayId(MINI_WX); + + return userLogin(loginDTO); + } + + + + /** + * 手机登录 + * + * @param loginDTO + * @return + */ + private String userLoginWithPhone(LoginDTO loginDTO) { + loginDTO.setUserLoginWayId(PHONE); + + return userLogin(loginDTO); + } + + /** + * 用户登录统一入口 + * + * @param loginDTO + * @return + */ + public String userLogin(LoginDTO loginDTO) { + String token; + String lock = getLock(loginDTO); + TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition); + synchronized (lock.intern()) { + try { + //用户数据+渠道信息 + UserVO userVO = sUserService.userLogin(loginDTO); + + Long userId = userVO.getBaseUserVo().getId(); + + if (UserRedis.userIsExistRedis(userId)) { + dataSourceTransactionManager.commit(transactionStatus); + return UserRedis.getTokenByUserId(userId); + } + + //创建token + token = createToken(userVO.getBaseUserVo().getId()); + + //缓存redis + UserRedis.setUserInfo2Redis(userVO, token); + + dataSourceTransactionManager.commit(transactionStatus); + } catch (UserBindLoginException e) { + dataSourceTransactionManager.commit(transactionStatus); + throw e; + } catch (Exception e) { + e.printStackTrace(); + dataSourceTransactionManager.rollback(transactionStatus); + log.error("用户登录异常:e->{}", e.getMessage()); + throw e; + } + } + + return token; + } + + + /** + * 获取用户登录的锁对象 + * + * @param loginDTO + * @return + */ + private String getLock(LoginDTO loginDTO) { + String lock = ""; + + if (ObjectUtil.isNotEmpty(loginDTO.getPhone())) { + lock = loginDTO.getPhone(); + } else if (ObjectUtil.isNotEmpty(loginDTO.getOpenId())) { + lock = loginDTO.getOpenId(); + } else if (ObjectUtil.isNotEmpty(loginDTO.getIosId())) { + lock = loginDTO.getIosId(); + } + + return lock; + } + + /** + * 用userId创建token + * + * @param userId + * @return + */ + private String createToken(Long userId) { + + return JWTUtil.createSsdmnToken(userId); + +// //创建token +// JWTObject.JWTPayload payload = new JWTObject.JWTPayload(1); +// payload.put("userId", userId + ""); +// String token = JWTUtil.getToken(payload); +// log.info("token->{}", token); +// +// return token; + } + + + +} \ No newline at end of file diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/service/impl/SUserServiceImpl.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/service/impl/SUserServiceImpl.java new file mode 100644 index 0000000..a5179af --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/service/impl/SUserServiceImpl.java @@ -0,0 +1,369 @@ +package com.ssdmn.biz.login.service.impl; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.ssdmn.biz.login.exception.user.UserBindLoginException; +import com.ssdmn.biz.login.exception.user.UserLoginException; +import com.ssdmn.biz.login.mapper.SUserMapper; +import com.ssdmn.biz.login.mapper.UserLoginMapper; +import com.ssdmn.biz.login.pojo.dto.UserLoginDTO; +import com.ssdmn.biz.login.pojo.entity.User; +import com.ssdmn.biz.login.pojo.entity.UserLogin; +import com.ssdmn.biz.login.pojo.vo.BaseUserVO; +import com.ssdmn.biz.login.pojo.vo.UserLoginVO; +import com.ssdmn.biz.login.pojo.vo.UserVO; +import com.ssdmn.biz.login.service.SUserService; +import com.ssdmn.common.page.Querys; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.stream.Collectors; + +import static com.ssdmn.biz.contants.Constants.DataState.VALID; +import static com.ssdmn.biz.contants.Constants.UserLoginWay.*; +import static com.ssdmn.common.result.ResultConstants.*; + + +/** + * 用户模块 + * + * @author gsx + * @since 2020/12/22 10:45 + */ +@Service +@Slf4j +public class SUserServiceImpl implements SUserService { + + @Resource + private UserLoginMapper userLoginMapper; + + @Resource + private SUserMapper sUserMapper; + + + @Resource + UserLoginRecordService userLoginRecordService; + + @Override + public UserVO userLogin(UserLoginDTO userLoginDto) { + long begin = System.currentTimeMillis(); + log.info("userLoginDto->{}", userLoginDto); + if (ObjectUtil.isEmpty(userLoginDto.getUserLoginWayId())) { + throw new UserLoginException(LOGIN_TYPE_NULL_ERROR); + } + long end = System.currentTimeMillis(); + + UserVO userVO = userLoginAll(userLoginDto); + log.info("time->{},message->{}", (end - begin), userVO); + + //用户登录记录 + userLoginRecordService.insertUserLoginRecord(userVO.getBaseUserVo().getId(), userLoginDto.getUserLoginWayId()); + + return userVO; + } + + /** + * 处理所有用户登录 + * + * @param userLoginDTO + * @return + */ + private UserVO userLoginAll(UserLoginDTO userLoginDTO) { + LambdaQueryWrapper wrapper = Querys.wrapper(userLoginDTO, UserLogin.class); + UserLogin userLogin = userLoginMapper.selectOne(wrapper); + //没有用户信息 + if (ObjectUtil.isEmpty(userLogin)) { + userLogin = BeanUtil.toBean(userLoginDTO, UserLogin.class); + //新增渠道记录 + //设置默认信息,创建时间,头像,昵称等。 + setUserDefaultValue(userLogin); + //插入渠道表 密码渠道查不到表示登录失败,不插入渠道表 + if (!ObjectUtil.equals(userLoginDTO.getUserLoginWayId(), PASSWORD)) { + userLoginMapper.insert(userLogin); + } + /* + 插入渠道表后: + 1.手机号登录-> 新增数据到用户表,赋值到渠道表,返回用户信息 + 2.账号密码登录-> 返回登录失败,账号密码不匹配 + 3.微信登录-> 查询unionId下所有微信渠道:绑定了手机->返回用户数据以及渠道集合 + 未绑定手机->返回未绑定手机 + 4.ios登录-> 返回请绑定手机号 + */ + switch (userLoginDTO.getUserLoginWayId()) { + case PHONE: + return userLoginWithPhone(BeanUtil.toBean(userLoginDTO, User.class), userLogin); + case PASSWORD: + throw new UserLoginException(USERNAME_PASSWORD_ERROR); + case PUBLIC_NUMBER_WX: + case APP_WX: + case MINI_WX: + return userLoginWithWX(userLogin); + case IOS: + return userLoginWithIOS(userLogin); + default: + //都不满足,返回登录方式不能为空错误 + throw new UserLoginException(LOGIN_TYPE_NULL_ERROR); + } + } else { + //密码登录且密码为空,返回错误(多重防御) + if (ObjectUtil.equals(userLogin.getUserLoginWayId(), PASSWORD) && ObjectUtil.isEmpty(userLogin.getPassword())) { + throw new UserLoginException(USERNAME_PASSWORD_ERROR); + } + if (ObjectUtil.isNotEmpty(userLogin.getUserId())) { + User user = sUserMapper.selectById(userLogin.getUserId()); + + return doUserVo(user); + } + switch (userLogin.getUserLoginWayId()) { + case PUBLIC_NUMBER_WX: + case APP_WX: + case MINI_WX: + case IOS: + throw new UserBindLoginException(PHONE_NO_BINDING, userLogin.getId()); + default: + throw new UserLoginException(NOT_KNOW_ERROR); + } + + } + + } + + /** + * ios设备登录 + * + * @param userLogin + * @return + */ + private UserVO userLoginWithIOS(UserLogin userLogin) { + + if (ObjectUtil.isEmpty(userLogin.getIosId())) { + throw new UserLoginException(IOS_ID_NOT_EXIST); + } + + UserLogin userLoginDbo1 = userLoginMapper.selectOne(Wrappers.lambdaQuery(UserLogin.class) + .eq(UserLogin::getIosId, userLogin.getIosId())); + + if (ObjectUtil.isEmpty(userLoginDbo1)) { + userLogin.setLastDate(new Date()); + userLoginMapper.insert(userLogin); + + throw new UserLoginException(IOS_ID_NOT_EXIST, doUserVoWithoutUser(userLogin)); + } + + if (ObjectUtil.isEmpty(userLoginDbo1.getUserId())) { + throw new UserLoginException(PHONE_NO_BINDING, doUserVoWithoutUser(userLoginDbo1)); + } + + //userId不为空,查询用户信息,返回 + User user = sUserMapper.selectOne(new LambdaQueryWrapper().eq(User::getId, userLoginDbo1.getUserId())); + + return doUserVo(user); + } + + /** + * 微信登录 + * + * @param userLogin + * @return + */ + private UserVO userLoginWithWX(UserLogin userLogin) { + + return returnUserVoWithUnionId(userLogin); + } + + /** + * 通过unionId返回用户信息 + * + * @param userLogin 渠道信息bo + * @return + */ + private UserVO returnUserVoWithUnionId(UserLogin userLogin) { + /* + 通过unionId查到所有微信登录渠道 + */ + List userLoginList = userLoginMapper.selectList(Wrappers.lambdaQuery(UserLogin.class) + .eq(UserLogin::getUnionId, userLogin.getUnionId())); + + /* + 去除没有userId的数据 + */ + userLoginList = userLoginList.stream() + .filter(bo -> ObjectUtil.isNotNull(bo.getUserId())) + .distinct() + .collect(Collectors.toList()); + + /* + 查得到userId,就将userId的值设置给所有该unionId的userId字段。返回成功。 + 查不到,直接返回失败。 + */ + if (ObjectUtil.isEmpty(userLoginList)) { + throw new UserBindLoginException(PHONE_NO_BINDING, userLogin.getId()); + } + + Long userId = userLoginList.get(0).getUserId(); + //赋值给所有该unionId的userId字段 + UserLogin build = new UserLogin(); + build.setUserId(userId); + userLoginMapper.update(build, Wrappers.lambdaUpdate(UserLogin.class) + .eq(UserLogin::getUnionId, userLogin.getUnionId())); + //通过userId去查询用户信息并返回 + User user = sUserMapper.selectOne(Wrappers.lambdaQuery(User.class).eq(User::getId, userId)); + + return doUserVo(user); + } + + private UserVO doUserVoWithoutUser(UserLogin userLogin) { + //封装返回的UserVo,基本数据为空,渠道数据为传入的渠道数据,并返回错误信息,手机未绑定。 + UserVO userVO = new UserVO(); + ArrayList list = new ArrayList<>(); + list.add(BeanUtil.toBean(userLogin, UserLoginVO.class)); + userVO.setUserLoginVoList(list); + + return userVO; + } + + /** + * 手机号登录 + * + * @param user + * @return + */ + private UserVO userLoginWithPhone(User user, UserLogin userLogin) { + //插入用户表 + setUserDefaultValue(user); + sUserMapper.insert(user); + //将userId设置到渠道表 + userLogin.setUserId(user.getId()); + userLoginMapper.updateById(userLogin); + //新增一个密码渠道,密码值为空 + userLogin = new UserLogin(); + userLogin.setUserLoginWayId(PASSWORD); + setUserDefaultValue(userLogin); + userLoginMapper.insert(userLogin); + //整合返回数据 + return doUserVo(user); + } + + /** + * 新增用户时设置必要默认值 + * + * @param user + */ + private void setUserDefaultValue(User user) { + user.setCreateDate(new Date()); + user.setStatus(VALID); + + + if (ObjectUtil.isNull(user.getNickName())) { + user.setNickName(user.getPhone()); + } + } + + /** + * 新增渠道时设置必要默认值 + * + * @param userLogin + */ + private void setUserDefaultValue(UserLogin userLogin) { + Date date = new Date(); + userLogin.setCreateDate(date); + userLogin.setLastDate(date); + if (ObjectUtil.isNull(userLogin.getNickName())) { + userLogin.setNickName(userLogin.getPhone()); + } + } + + /** + * 封装用户基本信息和渠道信息返回 + * + * @param user + * @return + */ + private UserVO doUserVo(User user) { + //修改用户最后登录时间 + updateLastLoginTime(user); + + return getUserVO(user); + } + + private UserVO getUserVO(User user) { + //查该用户及他的所有渠道信息+用户信息返回 + List userLogins = userLoginMapper.selectList(new LambdaQueryWrapper().eq(UserLogin::getUserId, user.getId())); + List collect = userLogins.stream() + .map(userLogin -> BeanUtil.toBean(userLogin, UserLoginVO.class)) + .collect(Collectors.toList()); + UserVO userVo = new UserVO(); + userVo.setBaseUserVo(BeanUtil.toBean(user, BaseUserVO.class)); + userVo.setUserLoginVoList(collect); + + return userVo; + } + + /** + * 修改用户最后登录时间 + * + * @param user + */ + private void updateLastLoginTime(User user) { + user.setLastLoginDate(new Date()); + sUserMapper.updateById(user); + } + + + /** + * 绑定手机号 + * + * @param id 渠道表主键 + * @param phone 手机号 + * @return + */ + @Override + public UserVO bindingPhone(Long id, String phone) { + UserLogin userLogin = userLoginMapper.selectById(id); + + if (ObjectUtil.isNull(userLogin)) { + throw new UserLoginException(WAY_NOT_EXIST); + } + + if (ObjectUtil.isNotEmpty(userLogin.getUserId())) { + throw new UserLoginException(WX_BIND_OTHER_USER); + } + + //查询该手机号是否存在用户信息,不存在便新增。 + User user = sUserMapper.selectOne(Wrappers.lambdaQuery(User.class).eq(User::getPhone, phone)); + if (ObjectUtil.isNotEmpty(user)) { + //判断该手机号是否已经绑定了其他微信 + UserLogin userLogin1 = userLoginMapper.selectOne(Wrappers.lambdaQuery(UserLogin.class) + .eq(UserLogin::getUserId, user.getId()) + .isNotNull(UserLogin::getOpenId) + .last("limit 1")); + if (ObjectUtil.isNotEmpty(userLogin1)) { + if (!ObjectUtil.equals(userLogin1.getUnionId(), userLogin.getUnionId())) { + throw new UserBindLoginException(USER_BIND_OTHER_WX, id); + } + } + } else { + UserLoginDTO userLoginDTO = BeanUtil.toBean(userLogin, UserLoginDTO.class); + userLoginDTO.setPhone(phone); + userLoginDTO.setUserLoginWayId(PHONE); + //登录 + BaseUserVO baseUserVo = userLoginAll(userLoginDTO).getBaseUserVo(); + user = BeanUtil.toBean(baseUserVo, User.class); + } + + //将用户id绑定都渠道数据上 + userLogin.setUserId(user.getId()); + + userLoginMapper.updateById(userLogin); + + return doUserVo(user); + } + + +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/service/impl/UserLoginRecordService.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/service/impl/UserLoginRecordService.java new file mode 100644 index 0000000..3ec1184 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/login/service/impl/UserLoginRecordService.java @@ -0,0 +1,37 @@ +package com.ssdmn.biz.login.service.impl; + +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.ssdmn.biz.login.mapper.UserLoginRecordMapper; +import com.ssdmn.biz.login.pojo.entity.UserLoginRecord; +import org.springframework.stereotype.Service; + +import java.util.Date; + +/** + * @Description: 用户登录记录表 + * @Author: fan + * @Date: 2021/8/4 9:14 + * @Version: 1.0v + */ +@Service +public class UserLoginRecordService extends ServiceImpl { + + /** + * 添加数据 + * @param userId 用户id + * @param loginWay 登录方式 + * @return 成功返回true + */ + Boolean insertUserLoginRecord(Long userId,Integer loginWay){ + UserLoginRecord userLoginRecord = new UserLoginRecord(); + userLoginRecord.setUserId(userId); + userLoginRecord.setLoginWay(loginWay); + userLoginRecord.setLoginDate(new Date()); + + return save(userLoginRecord); + } + + +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/tool/controller/ToolController.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/tool/controller/ToolController.java new file mode 100644 index 0000000..a923a92 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/tool/controller/ToolController.java @@ -0,0 +1,34 @@ +package com.ssdmn.biz.tool.controller; + + +import com.ssdmn.biz.tool.pojo.domain.GptTool; +import com.ssdmn.biz.tool.service.GptToolService; +import com.ssdmn.common.annotation.combination.PathRestController; +import com.ssdmn.common.result.Result; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.PostMapping; + +import javax.annotation.Resource; + +import java.util.List; + +import static com.ssdmn.common.result.Results.ok; + +@Api(tags = "工具模块") +@PathRestController("/tool") +@Slf4j +public class ToolController { + + @Resource + private GptToolService gptToolService; + + @PostMapping("/listTool") + @ApiOperation("工具列表") + public Result> listTool() { + + return ok(gptToolService.list()); + } + +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/tool/mapper/GptToolMapper.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/tool/mapper/GptToolMapper.java new file mode 100644 index 0000000..6f97446 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/tool/mapper/GptToolMapper.java @@ -0,0 +1,18 @@ +package com.ssdmn.biz.tool.mapper; + +import com.ssdmn.biz.tool.pojo.domain.GptTool; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +/** +* @author 黑色字符 +* @description 针对表【gpt_tool(gpt工具)】的数据库操作Mapper +* @createDate 2023-03-29 14:49:59 +* @Entity com.ssdmn.biz.tool.pojo.domain.GptTool +*/ +public interface GptToolMapper extends BaseMapper { + +} + + + + diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/tool/mapper/GptToolMapper.xml b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/tool/mapper/GptToolMapper.xml new file mode 100644 index 0000000..b703c9e --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/tool/mapper/GptToolMapper.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + id,icon,title, + description,example,course, + is_num_edit,create_time,update_time + + diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/tool/pojo/domain/GptTool.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/tool/pojo/domain/GptTool.java new file mode 100644 index 0000000..fb1e7c7 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/tool/pojo/domain/GptTool.java @@ -0,0 +1,74 @@ +package com.ssdmn.biz.tool.pojo.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 lombok.Data; + +import java.io.Serializable; +import java.time.LocalDateTime; +import java.util.Date; + +/** + * gpt工具 + * + * @TableName gpt_tool + */ +@TableName(value = "gpt_tool") +@Data +public class GptTool implements Serializable { + /** + * id + */ + @TableId(type = IdType.AUTO) + private Long id; + + /** + * 图标 + */ + private String icon; + + /** + * 标题 + */ + private String title; + + /** + * 描述 + */ + private String description; + + /** + * 示例 + */ + private String example; + + /** + * 教程 + */ + private String course; + + /** + * 上下文 + */ + private String contextText; + + /** + * 是否有数字输入框 + */ + private Integer isNumEdit; + + /** + * 创建时间 + */ + private LocalDateTime createTime; + + /** + * 修改时间 + */ + private LocalDateTime updateTime; + + @TableField(exist = false) + private static final long serialVersionUID = 1L; +} \ No newline at end of file diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/tool/service/GptToolService.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/tool/service/GptToolService.java new file mode 100644 index 0000000..947e40f --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/tool/service/GptToolService.java @@ -0,0 +1,17 @@ +package com.ssdmn.biz.tool.service; + +import com.ssdmn.biz.tool.pojo.domain.GptTool; +import com.baomidou.mybatisplus.extension.service.IService; + +/** + * @author 黑色字符 + * @description 针对表【gpt_tool(gpt工具)】的数据库操作Service + * @createDate 2023-03-29 14:49:59 + */ +public interface GptToolService extends IService { + + GptTool getGptToolById(Long id); + + GptTool getGptToolByIdNonNull(Long id); + +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/tool/service/impl/GptToolServiceImpl.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/tool/service/impl/GptToolServiceImpl.java new file mode 100644 index 0000000..5df37d1 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/tool/service/impl/GptToolServiceImpl.java @@ -0,0 +1,34 @@ +package com.ssdmn.biz.tool.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.ssdmn.biz.tool.pojo.domain.GptTool; +import com.ssdmn.biz.tool.service.GptToolService; +import com.ssdmn.biz.tool.mapper.GptToolMapper; +import com.ssdmn.common.lang.Assert; +import org.springframework.stereotype.Service; + +/** + * @author 黑色字符 + * @description 针对表【gpt_tool(gpt工具)】的数据库操作Service实现 + * @createDate 2023-03-29 14:49:59 + */ +@Service +public class GptToolServiceImpl extends ServiceImpl + implements GptToolService { + + @Override + public GptTool getGptToolById(Long id) { + return getById(id); + } + + @Override + public GptTool getGptToolByIdNonNull(Long id) { + GptTool gptTool = getGptToolById(id); + Assert.notNull(gptTool, "所选工具不存在"); + return gptTool; + } +} + + + + diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/user/mapper/UserOperLogMapper.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/user/mapper/UserOperLogMapper.java new file mode 100644 index 0000000..b9f195b --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/user/mapper/UserOperLogMapper.java @@ -0,0 +1,18 @@ +package com.ssdmn.biz.user.mapper; + +import com.ssdmn.biz.user.pojo.domain.UserOperLog; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +/** +* @author 黑色字符 +* @description 针对表【user_oper_log(操作日志记录)】的数据库操作Mapper +* @createDate 2023-03-30 10:51:36 +* @Entity com.ssdmn.biz.user.pojo.domain.UserOperLog +*/ +public interface UserOperLogMapper extends BaseMapper { + +} + + + + diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/user/mapper/UserOperLogMapper.xml b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/user/mapper/UserOperLogMapper.xml new file mode 100644 index 0000000..1850564 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/user/mapper/UserOperLogMapper.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + id,request_method,phone, + user_id,method_name,url, + ip,oper_param,json_result, + status,error_msg,os, + brower,token,long_time, + channel_type,channel_name,create_time, + update_time,ssdmn_token + + diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/user/pojo/domain/UserOperLog.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/user/pojo/domain/UserOperLog.java new file mode 100644 index 0000000..6f831dc --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/user/pojo/domain/UserOperLog.java @@ -0,0 +1,123 @@ +package com.ssdmn.biz.user.pojo.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 java.io.Serializable; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.Date; +import lombok.Data; + +/** + * 操作日志记录 + * @TableName user_oper_log + */ +@TableName(value ="user_oper_log") +@Data +public class UserOperLog implements Serializable { + /** + * 日志主键 + */ + @TableId(type = IdType.AUTO) + private Long id; + + /** + * 请求方式 + */ + private String requestMethod; + + /** + * 请求者的电话号码 + */ + private String phone; + + /** + * 请求者的user_id + */ + private Long userId; + + /** + * 请求的方法名 + */ + private String methodName; + + /** + * 请求URL + */ + private String url; + + /** + * 请求的ip地址 + */ + private String ip; + + /** + * 请求参数 + */ + private String operParam; + + /** + * 返回参数 + */ + private String jsonResult; + + /** + * 操作状态(0正常 1异常) + */ + private Integer status; + + /** + * 错误消息 + */ + private String errorMsg; + + /** + * 操作系统 + */ + private String os; + + /** + * 浏览器 + */ + private String brower; + + /** + * 用户在福域的token + */ + private String token; + + /** + * 耗时 + */ + private Long longTime; + + /** + * 渠道类型 + */ + private String channelType; + + /** + * 渠道名称 + */ + private String channelName; + + /** + * + */ + private LocalDateTime createTime; + + /** + * + */ + private LocalDateTime updateTime; + + /** + * + */ + private String ssdmnToken; + + @TableField(exist = false) + private static final long serialVersionUID = 1L; +} \ No newline at end of file diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/user/service/UserOperLogService.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/user/service/UserOperLogService.java new file mode 100644 index 0000000..95092d3 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/user/service/UserOperLogService.java @@ -0,0 +1,13 @@ +package com.ssdmn.biz.user.service; + +import com.ssdmn.biz.user.pojo.domain.UserOperLog; +import com.baomidou.mybatisplus.extension.service.IService; + +/** +* @author 黑色字符 +* @description 针对表【user_oper_log(操作日志记录)】的数据库操作Service +* @createDate 2023-03-30 10:51:36 +*/ +public interface UserOperLogService extends IService { + +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/user/service/impl/UserOperLogServiceImpl.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/user/service/impl/UserOperLogServiceImpl.java new file mode 100644 index 0000000..89e509f --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/biz/user/service/impl/UserOperLogServiceImpl.java @@ -0,0 +1,22 @@ +package com.ssdmn.biz.user.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.ssdmn.biz.user.pojo.domain.UserOperLog; +import com.ssdmn.biz.user.service.UserOperLogService; +import com.ssdmn.biz.user.mapper.UserOperLogMapper; +import org.springframework.stereotype.Service; + +/** +* @author 黑色字符 +* @description 针对表【user_oper_log(操作日志记录)】的数据库操作Service实现 +* @createDate 2023-03-30 10:51:36 +*/ +@Service +public class UserOperLogServiceImpl extends ServiceImpl + implements UserOperLogService{ + +} + + + + diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/annotation/boot/SpringBootApplication.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/annotation/boot/SpringBootApplication.java new file mode 100644 index 0000000..f76e114 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/annotation/boot/SpringBootApplication.java @@ -0,0 +1,18 @@ +package com.ssdmn.common.annotation.boot; + +import org.mybatis.spring.annotation.MapperScan; + +import java.lang.annotation.*; + +/** + * RestController+RequestMapping组合注解 + * @author 郭世旭 + */ +@Target({ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@org.springframework.boot.autoconfigure.SpringBootApplication +@MapperScan("com.ssdmn.biz.**.mapper") +public @interface SpringBootApplication { + +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/annotation/combination/PathRestController.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/annotation/combination/PathRestController.java new file mode 100644 index 0000000..eb3c750 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/annotation/combination/PathRestController.java @@ -0,0 +1,27 @@ +package com.ssdmn.common.annotation.combination; + +import org.springframework.core.annotation.AliasFor; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.validation.Valid; +import java.lang.annotation.*; + +/** + * RestController+RequestMapping组合注解 + * @author 郭世旭 + */ +@Target({ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Valid +@RestController +@RequestMapping +public @interface PathRestController { + + @AliasFor("path") + String[] value() default {}; + + @AliasFor("value") + String[] path() default {}; +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/annotation/swagger/SwaggerFiledExplain.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/annotation/swagger/SwaggerFiledExplain.java new file mode 100644 index 0000000..cf9a266 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/annotation/swagger/SwaggerFiledExplain.java @@ -0,0 +1,15 @@ +package com.ssdmn.common.annotation.swagger; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * @author 郭世旭 + */ +@Target(ElementType.FIELD) +@Retention(RetentionPolicy.RUNTIME) +public @interface SwaggerFiledExplain { + String value() default ""; +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/annotation/valid/EnumType.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/annotation/valid/EnumType.java new file mode 100644 index 0000000..be1f74e --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/annotation/valid/EnumType.java @@ -0,0 +1,56 @@ +package com.ssdmn.common.annotation.valid; + +import cn.hutool.core.util.ObjectUtil; +import lombok.SneakyThrows; + +import javax.validation.Constraint; +import javax.validation.ConstraintValidator; +import javax.validation.ConstraintValidatorContext; +import javax.validation.Payload; +import java.lang.annotation.*; +import java.lang.reflect.Field; + +/** + * @author 郭世旭 + */ +@Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Constraint(validatedBy = {EnumType.EnumTypeValidator.class}) +public @interface EnumType { + String message() default "该类型不存在!"; + + Class enumType(); + + Class[] groups() default {}; + + Class[] payload() default {}; + + class EnumTypeValidator implements ConstraintValidator { + + Class enumType; + + @Override + public void initialize(EnumType constraintAnnotation) { + enumType = constraintAnnotation.enumType(); + } + + @SneakyThrows + @Override + public boolean isValid(Object value, ConstraintValidatorContext constraintValidatorContext) { + if (ObjectUtil.isEmpty(value)) { + return true; + } + Field[] fields = enumType.getFields(); + for (Field field : fields) { + String toString = field.get(null).toString(); + if (ObjectUtil.equal(toString, value.toString())) { + return true; + } + } + + return false; + } + } + +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/annotation/valid/Phone.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/annotation/valid/Phone.java new file mode 100644 index 0000000..535f098 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/annotation/valid/Phone.java @@ -0,0 +1,53 @@ +package com.ssdmn.common.annotation.valid; + +import cn.hutool.core.util.ObjectUtil; + +import javax.validation.Constraint; +import javax.validation.ConstraintValidator; +import javax.validation.ConstraintValidatorContext; +import javax.validation.Payload; +import java.lang.annotation.*; +import java.util.regex.Pattern; + +/** + * @author 郭世旭 + */ +@Target({ElementType.FIELD, ElementType.PARAMETER}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Constraint(validatedBy = {Phone.PhoneValidator.class}) +public @interface Phone { + String message() default "手机号为空或格式错误"; + + boolean required() default true; + + Class[] groups() default {}; + + Class[] payload() default {}; + + class PhoneValidator implements ConstraintValidator { + + private static final Pattern PATTERN = Pattern.compile("^1[3456789]\\d{9}$"); + + private boolean required; + + @Override + public void initialize(Phone constraintAnnotation) { + required = constraintAnnotation.required(); + } + + @Override + public boolean isValid(String value, ConstraintValidatorContext constraintValidatorContext) { + if (ObjectUtil.isEmpty(value)) { + if (!required) { + return true; + } + + return false; + } + + return PATTERN.matcher(value).matches(); + } + } + +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/annotation/valid/SmsCode.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/annotation/valid/SmsCode.java new file mode 100644 index 0000000..9afab0d --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/annotation/valid/SmsCode.java @@ -0,0 +1,61 @@ +package com.ssdmn.common.annotation.valid; + +import cn.hutool.core.util.NumberUtil; +import cn.hutool.core.util.ObjectUtil; + +import javax.validation.Constraint; +import javax.validation.ConstraintValidator; +import javax.validation.ConstraintValidatorContext; +import javax.validation.Payload; +import java.lang.annotation.*; + +/** + * @author 郭世旭 + */ +@Target({ElementType.FIELD, ElementType.PARAMETER}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Constraint(validatedBy = {SmsCode.SmsCodeValidator.class}) +public @interface SmsCode { + String message() default "验证码格式错误"; + + boolean required() default true; + + boolean isNumber() default true; + + int lenth() default 6; + + Class[] groups() default {}; + + Class[] payload() default {}; + + class SmsCodeValidator implements ConstraintValidator { + + private boolean required; + + private boolean isNumber; + + private int length; + + @Override + public void initialize(SmsCode constraintAnnotation) { + required = constraintAnnotation.required(); + this.length = constraintAnnotation.lenth(); + this.isNumber = constraintAnnotation.isNumber(); + } + + @Override + public boolean isValid(String value, ConstraintValidatorContext constraintValidatorContext) { + if (ObjectUtil.isEmpty(value) && required) { + return false; + } + int length = value.length(); + if (isNumber && !NumberUtil.isInteger(value)) { + return false; + } + + return this.length == length; + } + } + +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/annotation/valid/Url.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/annotation/valid/Url.java new file mode 100644 index 0000000..7cbcacb --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/annotation/valid/Url.java @@ -0,0 +1,44 @@ +package com.ssdmn.common.annotation.valid; + +import cn.hutool.core.util.ObjectUtil; + +import javax.validation.Constraint; +import javax.validation.ConstraintValidator; +import javax.validation.ConstraintValidatorContext; +import javax.validation.Payload; +import java.lang.annotation.*; +import java.util.regex.Pattern; + +/** + * @author 郭世旭 + */ +@Target({ElementType.FIELD,ElementType.PARAMETER}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Constraint(validatedBy = {Url.PhoneValidator.class}) +public @interface Url { + String message() default "路径为空或格式错误"; + + Class[] groups() default {}; + + Class[] payload() default {}; + + class PhoneValidator implements ConstraintValidator { + + private static final Pattern PATTERN= Pattern.compile("^([hH][tT]{2}[pP]://|[hH][tT]{2}[pP][sS]://)(([A-Za-z0-9-~]+).)+([A-Za-z0-9-~\\/])+$"); + + @Override + public void initialize(Url constraintAnnotation) { + } + + @Override + public boolean isValid(String value, ConstraintValidatorContext constraintValidatorContext) { + if (ObjectUtil.isEmpty(value)) { + return false; + } else { + return PATTERN.matcher(value).matches(); + } + } + } + +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/contants/Constants.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/contants/Constants.java new file mode 100644 index 0000000..828e136 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/contants/Constants.java @@ -0,0 +1,96 @@ +package com.ssdmn.common.contants; + + +/** + * 常量 + * + * @author 郭世旭 + * @version 1.0 + * @createTime 2021-03-22 09:58:30 + */ +public interface Constants { + + public static final int NO = 0; + + public static final int YES = 1; + + public static final int DEDUCT = 0; + + public static final int INCREASE = 1; + + public static final String USER_CONTEXT = "USER:CONTEXT:"; + + String ALIYUN_UPLOAD = "aliyun/upload"; + + Integer ALIYUN_UPLOAD_TYPE = 1; + + /** + * 微信支付相关常量 + */ + interface WxPay { + interface WxType { + /** + * 支付 + */ + Integer PAY = 1; + /** + * 退款 + */ + Integer REFUND = 2; + + } + + interface WxChannel { + //微信支付类型常量值 + /** + * 公众号 + */ + int PUBLISH_NUMBER = 101; + + /** + * 小程序 + */ + int MINI = 102; + + /** + * app + */ + int APP = 103; + + /** + * h5 + */ + int H5 = 104; + } + + /** + * 微信退款成功标识 + */ + String OK = "OK"; + } + + interface OrderStatus { + /** + * 支付失败 + */ + Integer PAY_FAIL = 0; + + /** + * 支付成功 + */ + Integer PAY_SUCCESS = 1; + } + + + interface PayType { + /** + * 普通下单 + */ + int ORDINARY_ORDER = 1; + + /** + * 充值 + */ + int RECHARGE = 2; + } +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/contants/HttpStatus.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/contants/HttpStatus.java new file mode 100644 index 0000000..5d0350e --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/contants/HttpStatus.java @@ -0,0 +1,89 @@ +package com.ssdmn.common.contants; + +/** + * 返回状态码 + * + * @author 郭世旭 + */ +public class HttpStatus +{ + /** + * 操作成功 + */ + public static final int SUCCESS = 200; + + /** + * 对象创建成功 + */ + public static final int CREATED = 201; + + /** + * 请求已经被接受 + */ + public static final int ACCEPTED = 202; + + /** + * 操作已经执行成功,但是没有返回数据 + */ + public static final int NO_CONTENT = 204; + + /** + * 资源已被移除 + */ + public static final int MOVED_PERM = 301; + + /** + * 重定向 + */ + public static final int SEE_OTHER = 303; + + /** + * 资源没有被修改 + */ + public static final int NOT_MODIFIED = 304; + + /** + * 参数列表错误(缺少,格式不匹配) + */ + public static final int BAD_REQUEST = 400; + + /** + * 未授权 + */ + public static final int UNAUTHORIZED = 401; + + /** + * 访问受限,授权过期 + */ + public static final int FORBIDDEN = 403; + + /** + * 资源,服务未找到 + */ + public static final int NOT_FOUND = 404; + + /** + * 不允许的http方法 + */ + public static final int BAD_METHOD = 405; + + /** + * 资源冲突,或者资源被锁 + */ + public static final int CONFLICT = 409; + + /** + * 不支持的数据,媒体类型 + */ + public static final int UNSUPPORTED_TYPE = 415; + + /** + * 系统内部错误 + */ + public static final int ERROR = 500; + + /** + * 接口未实现 + */ + public static final int NOT_IMPLEMENTED = 501; +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/contants/ScheduleConstants.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/contants/ScheduleConstants.java new file mode 100644 index 0000000..1bd6575 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/contants/ScheduleConstants.java @@ -0,0 +1,56 @@ +package com.ssdmn.common.contants; + +/** + * 常量 + * + * @author 郭世旭 + * @version 1.0 + * @createTime 2021-03-22 09:58:30 + */ +public interface ScheduleConstants { + String TASK_CLASS_NAME = "TASK_CLASS_NAME"; + + /** 执行目标key */ + String TASK_PROPERTIES = "TASK_PROPERTIES"; + + /** 默认 */ + String MISFIRE_DEFAULT = "0"; + + /** 立即触发执行 */ + String MISFIRE_IGNORE_MISFIRES = "1"; + + /** 触发一次执行 */ + String MISFIRE_FIRE_AND_PROCEED = "2"; + + /** 不触发立即执行 */ + String MISFIRE_DO_NOTHING = "3"; + + /** 允许并发 */ + String CONCURRENT_YES = "1"; + + /** 不允许并发执行 */ + String CONCURRENT_NO = "0"; + + enum Status{ + /** + * 正常 + */ + NORMAL("0"), + /** + * 暂停 + */ + PAUSE("1"); + + private String value; + + private Status(String value) + { + this.value = value; + } + + public String getValue() + { + return value; + } + } +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/contants/SpringConstants.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/contants/SpringConstants.java new file mode 100644 index 0000000..467d333 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/contants/SpringConstants.java @@ -0,0 +1,24 @@ +package com.ssdmn.common.contants; + +/** + * spring相关常量 + * @author 郭世旭 + */ +public class SpringConstants { + + /** + * 开发环境 + */ + public static String DEV = "dev"; + + /** + * 测试环境 + */ + public static String TEST = "test"; + + /** + * 正式环境 + */ + public static String PROD = "prod"; + +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/date/DateConverter.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/date/DateConverter.java new file mode 100644 index 0000000..971c293 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/date/DateConverter.java @@ -0,0 +1,27 @@ +package com.ssdmn.common.date; + +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.StrUtil; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.convert.converter.Converter; + +import java.util.Date; + +/** + * 时间转换器 + * + * @author 余洲 + * @version 1.0 + * @createTime 2021-05-26 18:07:08 + */ +@Configuration +public class DateConverter implements Converter { + + @Override + public Date convert(String source) { + if (StrUtil.isNotBlank(source)) { + return DateUtil.parse(source); + } + return null; + } +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/date/DateConverterConfig.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/date/DateConverterConfig.java new file mode 100644 index 0000000..fc707fc --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/date/DateConverterConfig.java @@ -0,0 +1,74 @@ +package com.ssdmn.common.date; + + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean; +import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; + +/** + * 类型转换器 + * @author 郭世旭 + */ +@Configuration +public class DateConverterConfig { + + @Bean + public LocalDateTimeDeserializationConverter localDateTimeJacksonConverter() { + return new LocalDateTimeDeserializationConverter(); + } + + @Bean + public LocalDateDeserializationConverter localDateJacksonConverter() { + return new LocalDateDeserializationConverter(); + } + + @Bean + public LocalTimeDeserializationConverter localTimeJacksonConverter() { + return new LocalTimeDeserializationConverter(); + } + + @Bean + public LocalDateTimeSerializationConverter localDateTimeSerializationConverter() { + return new LocalDateTimeSerializationConverter(); + } + + @Bean + public LocalDateSerializationConverter localDateSerializationConverter() { + return new LocalDateSerializationConverter(); + } + + @Bean + public LocalTimeSerializationConverter localTimeSerializationConverter() { + return new LocalTimeSerializationConverter(); + } + + @Bean + public Jackson2ObjectMapperFactoryBean jackson2ObjectMapperFactoryBean( + @Autowired + LocalDateTimeDeserializationConverter localDateTimeDeserializationConverter, + @Autowired LocalTimeDeserializationConverter localTimeDeserializationConverter, + @Autowired LocalDateDeserializationConverter localDateDeserializationConverter, + @Autowired LocalDateTimeSerializationConverter localDateTimeSerializationConverter, + @Autowired LocalDateSerializationConverter localDateSerializationConverter, + @Autowired LocalTimeSerializationConverter localTimeSerializationConverter + ) { + Jackson2ObjectMapperFactoryBean jackson2ObjectMapperFactoryBean = new Jackson2ObjectMapperFactoryBean(); + + jackson2ObjectMapperFactoryBean.setDeserializers(localDateTimeDeserializationConverter, localTimeDeserializationConverter, localDateDeserializationConverter); + jackson2ObjectMapperFactoryBean.setSerializers(localDateTimeSerializationConverter, localDateSerializationConverter, localTimeSerializationConverter); + return jackson2ObjectMapperFactoryBean; + } + + @Bean + public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter( + @Autowired + ObjectMapper objectMapper) { + MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter = + new MappingJackson2HttpMessageConverter(); + mappingJackson2HttpMessageConverter.setObjectMapper(objectMapper); + return mappingJackson2HttpMessageConverter; + } +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/date/DateFactory.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/date/DateFactory.java new file mode 100644 index 0000000..64bf9bb --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/date/DateFactory.java @@ -0,0 +1,98 @@ +package com.ssdmn.common.date; + +import cn.hutool.core.date.DateTime; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.format.DateTimeFormatter; +import java.util.regex.Pattern; + +/** + * + * @author 郭世旭 + */ +public class DateFactory { + + private static final String DEFAULT_DATE_SEPARATOR_CHAR = "-"; + + private static final Pattern PATTERN = Pattern.compile("[^0-9]"); + + private static final DateTimeFormatter LOCAL_DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyyMMdd"); + private static final DateTimeFormatter LOCAL_DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyyMMddHHmmss"); + + public static String reservedNumbers(String src){ + + return PATTERN.matcher(src).replaceAll("").trim(); + } + + public static DateTime getDateTime(String src){ + if(ObjectUtil.isEmpty(src)){ + return null; + } + + return DateUtil.parse(reservedNumbers(src), LOCAL_DATE_TIME_FORMATTER); + } + + public static String getDateTimeStr(DateTime dateTime){ + if(ObjectUtil.isEmpty(dateTime)){ + return null; + } + + return DateUtil.formatDateTime(dateTime); + } + + public static LocalDate getLocalDate(String src){ + if(ObjectUtil.isEmpty(src)){ + return null; + } + return LocalDate.parse(reservedNumbers(src), LOCAL_DATE_FORMATTER); + } + + public static String getLocalDateStr(LocalDate localDate){ + + return getLocalDateStr(localDate, DEFAULT_DATE_SEPARATOR_CHAR); + } + + public static String getLocalDateStr(LocalDate localDate, String separatorChar){ + if(ObjectUtil.isEmpty(localDate)){ + return ""; + } + DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(StrUtil.format("yyyy{}MM{}dd", separatorChar, separatorChar)); + + return localDate.format(dateTimeFormatter); + } + + public static LocalTime getLocalTime(String src){ + if(ObjectUtil.isEmpty(src)){ + return null; + } + return LocalTime.parse(src); + } + + public static String getLocalTimeStr(LocalTime localTime){ + return String.valueOf(localTime); + } + + public static LocalDateTime getLocalDateTime(String src){ + if(ObjectUtil.isEmpty(src)){ + return null; + } + return LocalDateTime.parse(reservedNumbers(src), LOCAL_DATE_TIME_FORMATTER); + } + + public static String getLocalDateTimeStr(LocalDateTime localDateTime){ + return getLocalDateTimeStr(localDateTime, DEFAULT_DATE_SEPARATOR_CHAR); + } + + public static String getLocalDateTimeStr(LocalDateTime localDateTime, String separatorChar){ + if(ObjectUtil.isEmpty(localDateTime)){ + return ""; + } + DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(StrUtil.format("yyyy{}MM{}dd HH:mm:ss", separatorChar, separatorChar)); + + return localDateTime.format(dateTimeFormatter); + } +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/date/DateUtil.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/date/DateUtil.java new file mode 100644 index 0000000..eb9837a --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/date/DateUtil.java @@ -0,0 +1,106 @@ +package com.ssdmn.common.date; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.date.DateField; +import cn.hutool.core.date.DateRange; +import cn.hutool.core.date.DateTime; +import cn.hutool.core.date.LocalDateTimeUtil; + +import java.time.LocalDateTime; +import java.time.ZoneOffset; +import java.time.temporal.TemporalAdjusters; +import java.util.Date; +import java.util.List; + +import static java.time.temporal.ChronoUnit.MINUTES; + +/** + * 日期类工具 + * + * @author 郭世旭 + */ +public class DateUtil extends cn.hutool.core.date.DateUtil { + + public static long toEpochSecond(LocalDateTime localDateTime) { + + return localDateTime.toEpochSecond(ZoneOffset.of("+8")); + } + + /** + * 时间偏移 + * + * @param dateTime 偏移时间字符串 + * @param min 正数向未来偏移,负数向历史偏移 + * @return + */ + public static String offsetMinute(String dateTime, int min) { + + return DateFactory.getDateTimeStr(DateUtil.offsetMinute(DateFactory.getDateTime(dateTime), min)); + } + + /** + * 时间偏移 + * + * @param dateTime 偏移时间字符串 + * @param min 正数向未来偏移,负数向历史偏移 + * @return + */ + public static LocalDateTime offsetMinute(LocalDateTime dateTime, int min) { + + return LocalDateTimeUtil.offset(dateTime, min, MINUTES); + } + + /** + * 时间偏移 + * + * @param dateTime 时间字符串 + * @return + */ + public static LocalDateTime beginOfMonth(LocalDateTime dateTime) { + return dateTime.with(TemporalAdjusters.firstDayOfMonth()).withHour(0) + .withMinute(0) + .withSecond(0) + .withNano(0); + } + + /** + * 时间偏移 + * + * @param dateTime 时间字符串 + * @return + */ + public static LocalDateTime lastOfMonth(LocalDateTime dateTime) { + return dateTime.with(TemporalAdjusters.lastDayOfMonth()).withHour(23) + .withMinute(59) + .withSecond(59) + .withNano(0); + } + + /** + * 创建日期范围生成器 + * + * @param start 起始日期时间(包括) + * @param end 结束日期时间 + * @param unit 步进单位 + * @param isIncludeStartTime 是否包含开始时间 + * @param isIncludeEndTime 是否包含结束时间 + * @return {@link DateRange} + */ + public static DateRange range(Date start, Date end, final DateField unit, boolean isIncludeStartTime, boolean isIncludeEndTime) { + return new DateRange(start, end, unit, 1, isIncludeStartTime, isIncludeEndTime); + } + + /** + * 创建日期范围生成器 + * + * @param start 起始日期时间 + * @param end 结束日期时间 + * @param unit 步进单位 + * @param isIncludeStartTime 是否包含开始时间 + * @param isIncludeEndTime 是否包含结束时间 + * @return {@link DateRange} + */ + public static List rangeToList(Date start, Date end, final DateField unit, boolean isIncludeStartTime, boolean isIncludeEndTime) { + return CollUtil.newArrayList((Iterable) range(start, end, unit, isIncludeStartTime, isIncludeEndTime)); + } +} \ No newline at end of file diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/date/LocalDateConverter.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/date/LocalDateConverter.java new file mode 100644 index 0000000..2ef2781 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/date/LocalDateConverter.java @@ -0,0 +1,24 @@ +package com.ssdmn.common.date; + +import org.springframework.context.annotation.Configuration; +import org.springframework.core.convert.converter.Converter; + +import java.time.LocalDate; + +/** + * 时间转换器 + * + * @author 郭世旭 + * @version 1.0 + * @createTime 2021-05-24 09:58:30 + */ +@Configuration +public class LocalDateConverter implements Converter { + + @Override + public LocalDate convert(String source) { + + return DateFactory.getLocalDate(source); + } + +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/date/LocalDateDeserializationConverter.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/date/LocalDateDeserializationConverter.java new file mode 100644 index 0000000..35c43d4 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/date/LocalDateDeserializationConverter.java @@ -0,0 +1,22 @@ +package com.ssdmn.common.date; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; + +import java.io.IOException; +import java.time.LocalDate; + +public class LocalDateDeserializationConverter extends JsonDeserializer { + + @Override + public LocalDate deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException { + + return DateFactory.getLocalDate(jsonParser.getText()); + } + + @Override + public Class handledType() { + return LocalDate.class; + } +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/date/LocalDateSerializationConverter.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/date/LocalDateSerializationConverter.java new file mode 100644 index 0000000..71ffc48 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/date/LocalDateSerializationConverter.java @@ -0,0 +1,22 @@ +package com.ssdmn.common.date; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; + +import java.io.IOException; +import java.time.LocalDate; + +public class LocalDateSerializationConverter extends JsonSerializer { + + @Override + public void serialize(LocalDate localDate, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException { + + jsonGenerator.writeString(DateFactory.getLocalDateStr(localDate)); + } + + @Override + public Class handledType() { + return LocalDate.class; + } +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/date/LocalDateTimeConverter.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/date/LocalDateTimeConverter.java new file mode 100644 index 0000000..e2d8241 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/date/LocalDateTimeConverter.java @@ -0,0 +1,23 @@ +package com.ssdmn.common.date; + +import org.springframework.context.annotation.Configuration; +import org.springframework.core.convert.converter.Converter; + +import java.time.LocalDateTime; + +/** + * 时间转换器 + * + * @author 郭世旭 + * @version 1.0 + * @createTime 2021-05-24 09:58:30 + */ +@Configuration +public class LocalDateTimeConverter implements Converter { + + @Override + public LocalDateTime convert(String source) { + + return DateFactory.getLocalDateTime(source); + } +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/date/LocalDateTimeDeserializationConverter.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/date/LocalDateTimeDeserializationConverter.java new file mode 100644 index 0000000..dc61db5 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/date/LocalDateTimeDeserializationConverter.java @@ -0,0 +1,26 @@ +package com.ssdmn.common.date; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; + +import java.io.IOException; +import java.time.LocalDateTime; + +/** + * LocalDateTime在流请求中的自动解析 + * @author 郭世旭 + */ +public class LocalDateTimeDeserializationConverter extends JsonDeserializer { + + @Override + public LocalDateTime deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException { + + return DateFactory.getLocalDateTime(jsonParser.getText()); + } + + @Override + public Class handledType() { + return LocalDateTime.class; + } +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/date/LocalDateTimeSerializationConverter.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/date/LocalDateTimeSerializationConverter.java new file mode 100644 index 0000000..09c4980 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/date/LocalDateTimeSerializationConverter.java @@ -0,0 +1,22 @@ +package com.ssdmn.common.date; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; + +import java.io.IOException; +import java.time.LocalDateTime; + +public class LocalDateTimeSerializationConverter extends JsonSerializer { + + @Override + public void serialize(LocalDateTime localDateTime, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException { + + jsonGenerator.writeString(DateFactory.getLocalDateTimeStr(localDateTime)); + } + + @Override + public Class handledType() { + return LocalDateTime.class; + } +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/date/LocalTimeConverter.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/date/LocalTimeConverter.java new file mode 100644 index 0000000..105b8d3 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/date/LocalTimeConverter.java @@ -0,0 +1,23 @@ +package com.ssdmn.common.date; + +import org.springframework.context.annotation.Configuration; +import org.springframework.core.convert.converter.Converter; + +import java.time.LocalTime; + +/** + * 时间转换器 + * + * @author 郭世旭 + * @version 1.0 + * @createTime 2021-05-24 09:58:30 + */ +@Configuration +public class LocalTimeConverter implements Converter { + + @Override + public LocalTime convert(String source) { + + return DateFactory.getLocalTime(source); + } +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/date/LocalTimeDeserializationConverter.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/date/LocalTimeDeserializationConverter.java new file mode 100644 index 0000000..de8ba16 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/date/LocalTimeDeserializationConverter.java @@ -0,0 +1,22 @@ +package com.ssdmn.common.date; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; + +import java.io.IOException; +import java.time.LocalTime; + +public class LocalTimeDeserializationConverter extends JsonDeserializer { + + @Override + public LocalTime deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException { + + return DateFactory.getLocalTime(jsonParser.getText()); + } + + @Override + public Class handledType() { + return LocalTime.class; + } +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/date/LocalTimeSerializationConverter.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/date/LocalTimeSerializationConverter.java new file mode 100644 index 0000000..02279c1 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/date/LocalTimeSerializationConverter.java @@ -0,0 +1,22 @@ +package com.ssdmn.common.date; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; + +import java.io.IOException; +import java.time.LocalTime; + +public class LocalTimeSerializationConverter extends JsonSerializer { + + @Override + public void serialize(LocalTime localTime, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException { + + jsonGenerator.writeString(DateFactory.getLocalTimeStr(localTime)); + } + + @Override + public Class handledType() { + return LocalTime.class; + } +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/exception/BizException.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/exception/BizException.java new file mode 100644 index 0000000..7608271 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/exception/BizException.java @@ -0,0 +1,86 @@ +package com.ssdmn.common.exception; + + +import com.ssdmn.common.result.ResultConstants; + +/** + * 业务异常 + * + * @author 郭世旭 + * @version 1.0 + * @createTime 2021-03-22 09:58:30 + */ +public class BizException extends RuntimeException { + + private static final long serialVersionUID = 1L; + /** + * 错误码 + */ + private Integer code; + /** + * 错误消息 + */ + private String message; + + /** + * 错误带返回值 + */ + private Object data; + + public BizException(String message) { + super(message); + this.code = 600; + this.message = message; + } + + public BizException(Integer code, String message) { + super(message); + this.code = code; + this.message = message; + } + + public BizException(ResultConstants resultConstants) { + this.code = resultConstants.status; + this.message = resultConstants.message; + } + + public BizException(ResultConstants resultConstants, String message) { + this.code = resultConstants.status; + this.message = resultConstants.message+":"+message; + } + + public BizException(ResultConstants resultConstants, Object obj) { + this.code = resultConstants.status; + this.message = resultConstants.message; + this.data = obj; + } + + public static long getSerialVersionUID() { + return serialVersionUID; + } + + public Integer getCode() { + return code; + } + + public void setCode(Integer code) { + this.code = code; + } + + @Override + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public Object getData() { + return data; + } + + public void setData(Object data) { + this.data = data; + } +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/html/EscapeUtil.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/html/EscapeUtil.java new file mode 100644 index 0000000..a541dd1 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/html/EscapeUtil.java @@ -0,0 +1,156 @@ +package com.ssdmn.common.html; + + +import cn.hutool.core.util.ObjectUtil; + +/** + * 转义和反转义工具类 + * + * @author 郭世旭 + */ +public class EscapeUtil +{ + public static final String RE_HTML_MARK = "(<[^<]*?>)|(<[\\s]*?/[^<]*?>)|(<[^<]*?/[\\s]*?>)"; + + private static final char[][] TEXT = new char[64][]; + + static + { + for (int i = 0; i < 64; i++) + { + TEXT[i] = new char[] { (char) i }; + } + + // special HTML characters + TEXT['\''] = "'".toCharArray(); // 单引号 + TEXT['"'] = """.toCharArray(); // 双引号 + TEXT['&'] = "&".toCharArray(); // &符 + TEXT['<'] = "<".toCharArray(); // 小于号 + TEXT['>'] = ">".toCharArray(); // 大于号 + } + + /** + * 转义文本中的HTML字符为安全的字符 + * + * @param text 被转义的文本 + * @return 转义后的文本 + */ + public static String escape(String text) + { + return encode(text); + } + + /** + * 还原被转义的HTML特殊字符 + * + * @param content 包含转义符的HTML内容 + * @return 转换后的字符串 + */ + public static String unescape(String content) + { + return decode(content); + } + + /** + * 清除所有HTML标签,但是不删除标签内的内容 + * + * @param content 文本 + * @return 清除标签后的文本 + */ + public static String clean(String content) + { + return new HTMLFilter().filter(content); + } + + /** + * Escape编码 + * + * @param text 被编码的文本 + * @return 编码后的字符 + */ + private static String encode(String text) + { + if (ObjectUtil.isEmpty(text)) + { + return null; + } + + final StringBuilder tmp = new StringBuilder(text.length() * 6); + char c; + for (int i = 0; i < text.length(); i++) + { + c = text.charAt(i); + if (c < 256) + { + tmp.append("%"); + if (c < 16) + { + tmp.append("0"); + } + tmp.append(Integer.toString(c, 16)); + } + else + { + tmp.append("%u"); + if (c <= 0xfff) + { + // issue#I49JU8@Gitee + tmp.append("0"); + } + tmp.append(Integer.toString(c, 16)); + } + } + return tmp.toString(); + } + + /** + * Escape解码 + * + * @param content 被转义的内容 + * @return 解码后的字符串 + */ + public static String decode(String content) + { + if (ObjectUtil.isEmpty(content)) + { + return content; + } + + StringBuilder tmp = new StringBuilder(content.length()); + int lastPos = 0, pos = 0; + char ch; + while (lastPos < content.length()) + { + pos = content.indexOf("%", lastPos); + if (pos == lastPos) + { + if (content.charAt(pos + 1) == 'u') + { + ch = (char) Integer.parseInt(content.substring(pos + 2, pos + 6), 16); + tmp.append(ch); + lastPos = pos + 6; + } + else + { + ch = (char) Integer.parseInt(content.substring(pos + 1, pos + 3), 16); + tmp.append(ch); + lastPos = pos + 3; + } + } + else + { + if (pos == -1) + { + tmp.append(content.substring(lastPos)); + lastPos = content.length(); + } + else + { + tmp.append(content.substring(lastPos, pos)); + lastPos = pos; + } + } + } + return tmp.toString(); + } +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/html/HTMLFilter.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/html/HTMLFilter.java new file mode 100644 index 0000000..68c3f1c --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/html/HTMLFilter.java @@ -0,0 +1,566 @@ +package com.ssdmn.common.html; + +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * HTML过滤器,用于去除XSS漏洞隐患。 + * + * @author 郭世旭 + */ +public final class HTMLFilter +{ + /** + * regex flag union representing /si modifiers in php + **/ + private static final int REGEX_FLAGS_SI = Pattern.CASE_INSENSITIVE | Pattern.DOTALL; + private static final Pattern P_COMMENTS = Pattern.compile("", Pattern.DOTALL); + private static final Pattern P_COMMENT = Pattern.compile("^!--(.*)--$", REGEX_FLAGS_SI); + private static final Pattern P_TAGS = Pattern.compile("<(.*?)>", Pattern.DOTALL); + private static final Pattern P_END_TAG = Pattern.compile("^/([a-z0-9]+)", REGEX_FLAGS_SI); + private static final Pattern P_START_TAG = Pattern.compile("^([a-z0-9]+)(.*?)(/?)$", REGEX_FLAGS_SI); + private static final Pattern P_QUOTED_ATTRIBUTES = Pattern.compile("([a-z0-9]+)=([\"'])(.*?)\\2", REGEX_FLAGS_SI); + private static final Pattern P_UNQUOTED_ATTRIBUTES = Pattern.compile("([a-z0-9]+)(=)([^\"\\s']+)", REGEX_FLAGS_SI); + private static final Pattern P_PROTOCOL = Pattern.compile("^([^:]+):", REGEX_FLAGS_SI); + private static final Pattern P_ENTITY = Pattern.compile("&#(\\d+);?"); + private static final Pattern P_ENTITY_UNICODE = Pattern.compile("&#x([0-9a-f]+);?"); + private static final Pattern P_ENCODE = Pattern.compile("%([0-9a-f]{2});?"); + private static final Pattern P_VALID_ENTITIES = Pattern.compile("&([^&;]*)(?=(;|&|$))"); + private static final Pattern P_VALID_QUOTES = Pattern.compile("(>|^)([^<]+?)(<|$)", Pattern.DOTALL); + private static final Pattern P_END_ARROW = Pattern.compile("^>"); + private static final Pattern P_BODY_TO_END = Pattern.compile("<([^>]*?)(?=<|$)"); + private static final Pattern P_XML_CONTENT = Pattern.compile("(^|>)([^<]*?)(?=>)"); + private static final Pattern P_STRAY_LEFT_ARROW = Pattern.compile("<([^>]*?)(?=<|$)"); + private static final Pattern P_STRAY_RIGHT_ARROW = Pattern.compile("(^|>)([^<]*?)(?=>)"); + private static final Pattern P_AMP = Pattern.compile("&"); + private static final Pattern P_QUOTE = Pattern.compile("\""); + private static final Pattern P_LEFT_ARROW = Pattern.compile("<"); + private static final Pattern P_RIGHT_ARROW = Pattern.compile(">"); + private static final Pattern P_BOTH_ARROWS = Pattern.compile("<>"); + + // @xxx could grow large... maybe use sesat's ReferenceMap + private static final ConcurrentMap P_REMOVE_PAIR_BLANKS = new ConcurrentHashMap<>(); + private static final ConcurrentMap P_REMOVE_SELF_BLANKS = new ConcurrentHashMap<>(); + + /** + * set of allowed html elements, along with allowed attributes for each element + **/ + private final Map> vAllowed; + /** + * counts of open tags for each (allowable) html element + **/ + private final Map vTagCounts = new HashMap<>(); + + /** + * html elements which must always be self-closing (e.g. "") + **/ + private final String[] vSelfClosingTags; + /** + * html elements which must always have separate opening and closing tags (e.g. "") + **/ + private final String[] vNeedClosingTags; + /** + * set of disallowed html elements + **/ + private final String[] vDisallowed; + /** + * attributes which should be checked for valid protocols + **/ + private final String[] vProtocolAtts; + /** + * allowed protocols + **/ + private final String[] vAllowedProtocols; + /** + * tags which should be removed if they contain no content (e.g. "" or "") + **/ + private final String[] vRemoveBlanks; + /** + * entities allowed within html markup + **/ + private final String[] vAllowedEntities; + /** + * flag determining whether comments are allowed in input String. + */ + private final boolean stripComment; + private final boolean encodeQuotes; + /** + * flag determining whether to try to make tags when presented with "unbalanced" angle brackets (e.g. "" + * becomes " text "). If set to false, unbalanced angle brackets will be html escaped. + */ + private final boolean alwaysMakeTags; + + /** + * Default constructor. + */ + public HTMLFilter() + { + vAllowed = new HashMap<>(); + + final ArrayList a_atts = new ArrayList<>(); + a_atts.add("href"); + a_atts.add("target"); + vAllowed.put("a", a_atts); + + final ArrayList img_atts = new ArrayList<>(); + img_atts.add("src"); + img_atts.add("width"); + img_atts.add("height"); + img_atts.add("alt"); + vAllowed.put("img", img_atts); + + final ArrayList no_atts = new ArrayList<>(); + vAllowed.put("b", no_atts); + vAllowed.put("strong", no_atts); + vAllowed.put("i", no_atts); + vAllowed.put("em", no_atts); + + vSelfClosingTags = new String[] { "img" }; + vNeedClosingTags = new String[] { "a", "b", "strong", "i", "em" }; + vDisallowed = new String[] {}; + vAllowedProtocols = new String[] { "http", "mailto", "https" }; // no ftp. + vProtocolAtts = new String[] { "src", "href" }; + vRemoveBlanks = new String[] { "a", "b", "strong", "i", "em" }; + vAllowedEntities = new String[] { "amp", "gt", "lt", "quot" }; + stripComment = true; + encodeQuotes = true; + alwaysMakeTags = false; + } + + /** + * Map-parameter configurable constructor. + * + * @param conf map containing configuration. keys match field names. + */ + @SuppressWarnings("unchecked") + public HTMLFilter(final Map conf) + { + + assert conf.containsKey("vAllowed") : "configuration requires vAllowed"; + assert conf.containsKey("vSelfClosingTags") : "configuration requires vSelfClosingTags"; + assert conf.containsKey("vNeedClosingTags") : "configuration requires vNeedClosingTags"; + assert conf.containsKey("vDisallowed") : "configuration requires vDisallowed"; + assert conf.containsKey("vAllowedProtocols") : "configuration requires vAllowedProtocols"; + assert conf.containsKey("vProtocolAtts") : "configuration requires vProtocolAtts"; + assert conf.containsKey("vRemoveBlanks") : "configuration requires vRemoveBlanks"; + assert conf.containsKey("vAllowedEntities") : "configuration requires vAllowedEntities"; + + vAllowed = Collections.unmodifiableMap((HashMap>) conf.get("vAllowed")); + vSelfClosingTags = (String[]) conf.get("vSelfClosingTags"); + vNeedClosingTags = (String[]) conf.get("vNeedClosingTags"); + vDisallowed = (String[]) conf.get("vDisallowed"); + vAllowedProtocols = (String[]) conf.get("vAllowedProtocols"); + vProtocolAtts = (String[]) conf.get("vProtocolAtts"); + vRemoveBlanks = (String[]) conf.get("vRemoveBlanks"); + vAllowedEntities = (String[]) conf.get("vAllowedEntities"); + stripComment = conf.containsKey("stripComment") ? (Boolean) conf.get("stripComment") : true; + encodeQuotes = conf.containsKey("encodeQuotes") ? (Boolean) conf.get("encodeQuotes") : true; + alwaysMakeTags = conf.containsKey("alwaysMakeTags") ? (Boolean) conf.get("alwaysMakeTags") : true; + } + + private void reset() + { + vTagCounts.clear(); + } + + // --------------------------------------------------------------- + // my versions of some PHP library functions + public static String chr(final int decimal) + { + return String.valueOf((char) decimal); + } + + public static String htmlSpecialChars(final String s) + { + String result = s; + result = regexReplace(P_AMP, "&", result); + result = regexReplace(P_QUOTE, """, result); + result = regexReplace(P_LEFT_ARROW, "<", result); + result = regexReplace(P_RIGHT_ARROW, ">", result); + return result; + } + + // --------------------------------------------------------------- + + /** + * given a user submitted input String, filter out any invalid or restricted html. + * + * @param input text (i.e. submitted by a user) than may contain html + * @return "clean" version of input, with only valid, whitelisted html elements allowed + */ + public String filter(final String input) + { + reset(); + String s = input; + + s = escapeComments(s); + + s = balanceHTML(s); + + s = checkTags(s); + + s = processRemoveBlanks(s); + + // s = validateEntities(s); + + return s; + } + + public boolean isAlwaysMakeTags() + { + return alwaysMakeTags; + } + + public boolean isStripComments() + { + return stripComment; + } + + private String escapeComments(final String s) + { + final Matcher m = P_COMMENTS.matcher(s); + final StringBuffer buf = new StringBuffer(); + if (m.find()) + { + final String match = m.group(1); // (.*?) + m.appendReplacement(buf, Matcher.quoteReplacement("")); + } + m.appendTail(buf); + + return buf.toString(); + } + + private String balanceHTML(String s) + { + if (alwaysMakeTags) + { + // + // try and form html + // + s = regexReplace(P_END_ARROW, "", s); + // 不追加结束标签 + s = regexReplace(P_BODY_TO_END, "<$1>", s); + s = regexReplace(P_XML_CONTENT, "$1<$2", s); + + } + else + { + // + // escape stray brackets + // + s = regexReplace(P_STRAY_LEFT_ARROW, "<$1", s); + s = regexReplace(P_STRAY_RIGHT_ARROW, "$1$2><", s); + + // + // the last regexp causes '<>' entities to appear + // (we need to do a lookahead assertion so that the last bracket can + // be used in the next pass of the regexp) + // + s = regexReplace(P_BOTH_ARROWS, "", s); + } + + return s; + } + + private String checkTags(String s) + { + Matcher m = P_TAGS.matcher(s); + + final StringBuffer buf = new StringBuffer(); + while (m.find()) + { + String replaceStr = m.group(1); + replaceStr = processTag(replaceStr); + m.appendReplacement(buf, Matcher.quoteReplacement(replaceStr)); + } + m.appendTail(buf); + + // these get tallied in processTag + // (remember to reset before subsequent calls to filter method) + final StringBuilder sBuilder = new StringBuilder(buf.toString()); + for (String key : vTagCounts.keySet()) + { + for (int ii = 0; ii < vTagCounts.get(key); ii++) + { + sBuilder.append(""); + } + } + s = sBuilder.toString(); + + return s; + } + + private String processRemoveBlanks(final String s) + { + String result = s; + for (String tag : vRemoveBlanks) + { + if (!P_REMOVE_PAIR_BLANKS.containsKey(tag)) + { + P_REMOVE_PAIR_BLANKS.putIfAbsent(tag, Pattern.compile("<" + tag + "(\\s[^>]*)?>")); + } + result = regexReplace(P_REMOVE_PAIR_BLANKS.get(tag), "", result); + if (!P_REMOVE_SELF_BLANKS.containsKey(tag)) + { + P_REMOVE_SELF_BLANKS.putIfAbsent(tag, Pattern.compile("<" + tag + "(\\s[^>]*)?/>")); + } + result = regexReplace(P_REMOVE_SELF_BLANKS.get(tag), "", result); + } + + return result; + } + + private static String regexReplace(final Pattern regex_pattern, final String replacement, final String s) + { + Matcher m = regex_pattern.matcher(s); + return m.replaceAll(replacement); + } + + private String processTag(final String s) + { + // ending tags + Matcher m = P_END_TAG.matcher(s); + if (m.find()) + { + final String name = m.group(1).toLowerCase(); + if (allowed(name)) + { + if (!inArray(name, vSelfClosingTags)) + { + if (vTagCounts.containsKey(name)) + { + vTagCounts.put(name, vTagCounts.get(name) - 1); + return ""; + } + } + } + } + + // starting tags + m = P_START_TAG.matcher(s); + if (m.find()) + { + final String name = m.group(1).toLowerCase(); + final String body = m.group(2); + String ending = m.group(3); + + // debug( "in a starting tag, name='" + name + "'; body='" + body + "'; ending='" + ending + "'" ); + if (allowed(name)) + { + final StringBuilder params = new StringBuilder(); + + final Matcher m2 = P_QUOTED_ATTRIBUTES.matcher(body); + final Matcher m3 = P_UNQUOTED_ATTRIBUTES.matcher(body); + final List paramNames = new ArrayList<>(); + final List paramValues = new ArrayList<>(); + while (m2.find()) + { + paramNames.add(m2.group(1)); // ([a-z0-9]+) + paramValues.add(m2.group(3)); // (.*?) + } + while (m3.find()) + { + paramNames.add(m3.group(1)); // ([a-z0-9]+) + paramValues.add(m3.group(3)); // ([^\"\\s']+) + } + + String paramName, paramValue; + for (int ii = 0; ii < paramNames.size(); ii++) + { + paramName = paramNames.get(ii).toLowerCase(); + paramValue = paramValues.get(ii); + + // debug( "paramName='" + paramName + "'" ); + // debug( "paramValue='" + paramValue + "'" ); + // debug( "allowed? " + vAllowed.get( name ).contains( paramName ) ); + + if (allowedAttribute(name, paramName)) + { + if (inArray(paramName, vProtocolAtts)) + { + paramValue = processParamProtocol(paramValue); + } + params.append(' ').append(paramName).append("=\\\"").append(paramValue).append("\""); + } + } + + if (inArray(name, vSelfClosingTags)) + { + ending = " /"; + } + + if (inArray(name, vNeedClosingTags)) + { + ending = ""; + } + + if (ending == null || ending.length() < 1) + { + if (vTagCounts.containsKey(name)) + { + vTagCounts.put(name, vTagCounts.get(name) + 1); + } + else + { + vTagCounts.put(name, 1); + } + } + else + { + ending = " /"; + } + return "<" + name + params + ending + ">"; + } + else + { + return ""; + } + } + + // comments + m = P_COMMENT.matcher(s); + if (!stripComment && m.find()) + { + return "<" + m.group() + ">"; + } + + return ""; + } + + private String processParamProtocol(String s) + { + s = decodeEntities(s); + final Matcher m = P_PROTOCOL.matcher(s); + if (m.find()) + { + final String protocol = m.group(1); + if (!inArray(protocol, vAllowedProtocols)) + { + // bad protocol, turn into local anchor link instead + s = "#" + s.substring(protocol.length() + 1); + if (s.startsWith("#//")) + { + s = "#" + s.substring(3); + } + } + } + + return s; + } + + private String decodeEntities(String s) + { + StringBuffer buf = new StringBuffer(); + + Matcher m = P_ENTITY.matcher(s); + while (m.find()) + { + final String match = m.group(1); + final int decimal = Integer.decode(match).intValue(); + m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal))); + } + m.appendTail(buf); + s = buf.toString(); + + buf = new StringBuffer(); + m = P_ENTITY_UNICODE.matcher(s); + while (m.find()) + { + final String match = m.group(1); + final int decimal = Integer.valueOf(match, 16).intValue(); + m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal))); + } + m.appendTail(buf); + s = buf.toString(); + + buf = new StringBuffer(); + m = P_ENCODE.matcher(s); + while (m.find()) + { + final String match = m.group(1); + final int decimal = Integer.valueOf(match, 16).intValue(); + m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal))); + } + m.appendTail(buf); + s = buf.toString(); + + s = validateEntities(s); + return s; + } + + private String validateEntities(final String s) + { + StringBuffer buf = new StringBuffer(); + + // validate entities throughout the string + Matcher m = P_VALID_ENTITIES.matcher(s); + while (m.find()) + { + final String one = m.group(1); // ([^&;]*) + final String two = m.group(2); // (?=(;|&|$)) + m.appendReplacement(buf, Matcher.quoteReplacement(checkEntity(one, two))); + } + m.appendTail(buf); + + return encodeQuotes(buf.toString()); + } + + private String encodeQuotes(final String s) + { + if (encodeQuotes) + { + StringBuffer buf = new StringBuffer(); + Matcher m = P_VALID_QUOTES.matcher(s); + while (m.find()) + { + final String one = m.group(1); // (>|^) + final String two = m.group(2); // ([^<]+?) + final String three = m.group(3); // (<|$) + // 不替换双引号为",防止json格式无效 regexReplace(P_QUOTE, """, two) + m.appendReplacement(buf, Matcher.quoteReplacement(one + two + three)); + } + m.appendTail(buf); + return buf.toString(); + } + else + { + return s; + } + } + + private String checkEntity(final String preamble, final String term) + { + + return ";".equals(term) && isValidEntity(preamble) ? '&' + preamble : "&" + preamble; + } + + private boolean isValidEntity(final String entity) + { + return inArray(entity, vAllowedEntities); + } + + private static boolean inArray(final String s, final String[] array) + { + for (String item : array) + { + if (item != null && item.equals(s)) + { + return true; + } + } + return false; + } + + private boolean allowed(final String name) + { + return (vAllowed.isEmpty() || vAllowed.containsKey(name)) && !inArray(name, vDisallowed); + } + + private boolean allowedAttribute(final String name, final String paramName) + { + return allowed(name) && (vAllowed.isEmpty() || vAllowed.get(name).contains(paramName)); + } +} \ No newline at end of file diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/http/HttpHelper.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/http/HttpHelper.java new file mode 100644 index 0000000..8271877 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/http/HttpHelper.java @@ -0,0 +1,56 @@ +package com.ssdmn.common.http; + +import org.apache.commons.lang3.exception.ExceptionUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.servlet.ServletRequest; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; + +/** + * 通用http工具封装 + * + * @author 郭世旭 + */ +public class HttpHelper +{ + private static final Logger LOGGER = LoggerFactory.getLogger(HttpHelper.class); + + public static String getBodyString(ServletRequest request) + { + StringBuilder sb = new StringBuilder(); + BufferedReader reader = null; + try (InputStream inputStream = request.getInputStream()) + { + reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8)); + String line = ""; + while ((line = reader.readLine()) != null) + { + sb.append(line); + } + } + catch (IOException e) + { + LOGGER.warn("getBodyString出现问题!"); + } + finally + { + if (reader != null) + { + try + { + reader.close(); + } + catch (IOException e) + { + LOGGER.error(ExceptionUtils.getMessage(e)); + } + } + } + return sb.toString(); + } +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/http/HttpUtils.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/http/HttpUtils.java new file mode 100644 index 0000000..8c1cb75 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/http/HttpUtils.java @@ -0,0 +1,265 @@ +package com.ssdmn.common.http; + +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.net.ssl.*; +import java.io.*; +import java.net.ConnectException; +import java.net.SocketTimeoutException; +import java.net.URL; +import java.net.URLConnection; +import java.nio.charset.StandardCharsets; +import java.security.cert.X509Certificate; + +/** + * 通用http发送方法 + * + * @author 郭世旭 + */ +public class HttpUtils +{ + private static final Logger log = LoggerFactory.getLogger(HttpUtils.class); + + /** + * 向指定 URL 发送GET方法的请求 + * + * @param url 发送请求的 URL + * @return 所代表远程资源的响应结果 + */ + public static String sendGet(String url) + { + return sendGet(url, StringUtils.EMPTY); + } + + /** + * 向指定 URL 发送GET方法的请求 + * + * @param url 发送请求的 URL + * @param param 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。 + * @return 所代表远程资源的响应结果 + */ + public static String sendGet(String url, String param) + { + return sendGet(url, param, "UTF-8"); + } + + /** + * 向指定 URL 发送GET方法的请求 + * + * @param url 发送请求的 URL + * @param param 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。 + * @param contentType 编码类型 + * @return 所代表远程资源的响应结果 + */ + public static String sendGet(String url, String param, String contentType) + { + StringBuilder result = new StringBuilder(); + BufferedReader in = null; + try + { + String urlNameString = StringUtils.isNotBlank(param) ? url + "?" + param : url; + log.info("sendGet - {}", urlNameString); + URL realUrl = new URL(urlNameString); + URLConnection connection = realUrl.openConnection(); + connection.setRequestProperty("accept", "*/*"); + connection.setRequestProperty("connection", "Keep-Alive"); + connection.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)"); + connection.connect(); + in = new BufferedReader(new InputStreamReader(connection.getInputStream(), contentType)); + String line; + while ((line = in.readLine()) != null) + { + result.append(line); + } + log.info("recv - {}", result); + } + catch (ConnectException e) + { + log.error("调用HttpUtils.sendGet ConnectException, url=" + url + ",param=" + param, e); + } + catch (SocketTimeoutException e) + { + log.error("调用HttpUtils.sendGet SocketTimeoutException, url=" + url + ",param=" + param, e); + } + catch (IOException e) + { + log.error("调用HttpUtils.sendGet IOException, url=" + url + ",param=" + param, e); + } + catch (Exception e) + { + log.error("调用HttpsUtil.sendGet Exception, url=" + url + ",param=" + param, e); + } + finally + { + try + { + if (in != null) + { + in.close(); + } + } + catch (Exception ex) + { + log.error("调用in.close Exception, url=" + url + ",param=" + param, ex); + } + } + return result.toString(); + } + + /** + * 向指定 URL 发送POST方法的请求 + * + * @param url 发送请求的 URL + * @param param 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。 + * @return 所代表远程资源的响应结果 + */ + public static String sendPost(String url, String param) + { + PrintWriter out = null; + BufferedReader in = null; + StringBuilder result = new StringBuilder(); + try + { + log.info("sendPost - {}", url); + URL realUrl = new URL(url); + URLConnection conn = realUrl.openConnection(); + conn.setRequestProperty("accept", "*/*"); + conn.setRequestProperty("connection", "Keep-Alive"); + conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)"); + conn.setRequestProperty("Accept-Charset", "utf-8"); + conn.setRequestProperty("contentType", "utf-8"); + conn.setDoOutput(true); + conn.setDoInput(true); + out = new PrintWriter(conn.getOutputStream()); + out.print(param); + out.flush(); + in = new BufferedReader(new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8)); + String line; + while ((line = in.readLine()) != null) + { + result.append(line); + } + log.info("recv - {}", result); + } + catch (ConnectException e) + { + log.error("调用HttpUtils.sendPost ConnectException, url=" + url + ",param=" + param, e); + } + catch (SocketTimeoutException e) + { + log.error("调用HttpUtils.sendPost SocketTimeoutException, url=" + url + ",param=" + param, e); + } + catch (IOException e) + { + log.error("调用HttpUtils.sendPost IOException, url=" + url + ",param=" + param, e); + } + catch (Exception e) + { + log.error("调用HttpsUtil.sendPost Exception, url=" + url + ",param=" + param, e); + } + finally + { + try + { + if (out != null) + { + out.close(); + } + if (in != null) + { + in.close(); + } + } + catch (IOException ex) + { + log.error("调用in.close Exception, url=" + url + ",param=" + param, ex); + } + } + return result.toString(); + } + + public static String sendSSLPost(String url, String param) + { + StringBuilder result = new StringBuilder(); + String urlNameString = url + "?" + param; + try + { + log.info("sendSSLPost - {}", urlNameString); + SSLContext sc = SSLContext.getInstance("SSL"); + sc.init(null, new TrustManager[] { new TrustAnyTrustManager() }, new java.security.SecureRandom()); + URL console = new URL(urlNameString); + HttpsURLConnection conn = (HttpsURLConnection) console.openConnection(); + conn.setRequestProperty("accept", "*/*"); + conn.setRequestProperty("connection", "Keep-Alive"); + conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)"); + conn.setRequestProperty("Accept-Charset", "utf-8"); + conn.setRequestProperty("contentType", "utf-8"); + conn.setDoOutput(true); + conn.setDoInput(true); + + conn.setSSLSocketFactory(sc.getSocketFactory()); + conn.setHostnameVerifier(new TrustAnyHostnameVerifier()); + conn.connect(); + InputStream is = conn.getInputStream(); + BufferedReader br = new BufferedReader(new InputStreamReader(is)); + String ret = ""; + while ((ret = br.readLine()) != null) + { + if (ret != null && !"".equals(ret.trim())) + { + result.append(new String(ret.getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8)); + } + } + log.info("recv - {}", result); + conn.disconnect(); + br.close(); + } + catch (ConnectException e) + { + log.error("调用HttpUtils.sendSSLPost ConnectException, url=" + url + ",param=" + param, e); + } + catch (SocketTimeoutException e) + { + log.error("调用HttpUtils.sendSSLPost SocketTimeoutException, url=" + url + ",param=" + param, e); + } + catch (IOException e) + { + log.error("调用HttpUtils.sendSSLPost IOException, url=" + url + ",param=" + param, e); + } + catch (Exception e) + { + log.error("调用HttpsUtil.sendSSLPost Exception, url=" + url + ",param=" + param, e); + } + return result.toString(); + } + + private static class TrustAnyTrustManager implements X509TrustManager + { + @Override + public void checkClientTrusted(X509Certificate[] chain, String authType) + { + } + + @Override + public void checkServerTrusted(X509Certificate[] chain, String authType) + { + } + + @Override + public X509Certificate[] getAcceptedIssuers() + { + return new X509Certificate[] {}; + } + } + + private static class TrustAnyHostnameVerifier implements HostnameVerifier + { + @Override + public boolean verify(String hostname, SSLSession session) + { + return true; + } + } +} \ No newline at end of file diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/interceptor/AuthUserHandlerResolver.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/interceptor/AuthUserHandlerResolver.java new file mode 100644 index 0000000..d9229cf --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/interceptor/AuthUserHandlerResolver.java @@ -0,0 +1,74 @@ +package com.ssdmn.common.interceptor; + +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import com.ssdmn.common.exception.BizException; +import com.ssdmn.common.interceptor.annotation.AuthUser; +import com.ssdmn.common.jwt.JWTUtil; +import com.ssdmn.common.redis.service.RedisService; +import com.ssdmn.common.result.SysConstants; +import lombok.extern.slf4j.Slf4j; +import org.springframework.core.MethodParameter; +import org.springframework.stereotype.Component; +import org.springframework.web.bind.support.WebDataBinderFactory; +import org.springframework.web.context.request.NativeWebRequest; +import org.springframework.web.method.support.HandlerMethodArgumentResolver; +import org.springframework.web.method.support.ModelAndViewContainer; + +import javax.annotation.Resource; + +import static com.ssdmn.common.result.ResultConstants.TOKEN_ILLEGAL; +import static com.ssdmn.common.result.ResultConstants.USER_NOT_LOGIN; + +/** + * 解析token中当前用户信息 + * + * @author 郭世旭 + * @version 1.0 + * @createTime 2021-04-17 09:58:30 + */ +@Component +@Slf4j +public class AuthUserHandlerResolver implements HandlerMethodArgumentResolver { + + @Resource + private RedisService redisService; + + @Override + public boolean supportsParameter(MethodParameter parameter) { + //如果有注解 AuthThirdParty 才会封装请求参数到业务方法 + return parameter.hasParameterAnnotation(AuthUser.class); + } + + @Override + public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception { + UserCache userCache = new UserCache(); + + AuthUser authUser = parameter.getParameterAnnotation(AuthUser.class); + assert authUser != null; + + String token = webRequest.getHeader(SysConstants.SSDMN_TOKEN_KEY); + if (StrUtil.isEmpty(token) && !authUser.required()) { + return userCache; + } + + if (StrUtil.isEmpty(token)) { + throw new BizException(USER_NOT_LOGIN); + } + + userCache = JWTUtil.parseSsdmnToken(token); + //验证token是否有效 + if (userCache == null || !JWTUtil.verifyToken(token)) { + //sendCode(response, 403); + throw new BizException(TOKEN_ILLEGAL); + } + + if (ObjectUtil.isEmpty(userCache)) { + log.warn("token 不合法:{}", token); + throw new BizException(USER_NOT_LOGIN); + } + + return userCache; + } + +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/interceptor/RepeatSubmitInterceptor.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/interceptor/RepeatSubmitInterceptor.java new file mode 100644 index 0000000..6ab51bb --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/interceptor/RepeatSubmitInterceptor.java @@ -0,0 +1,43 @@ +package com.ssdmn.common.interceptor; + +import com.ssdmn.common.exception.BizException; +import com.ssdmn.common.interceptor.annotation.RepeatSubmit; +import com.ssdmn.framework.handler.interceptor.DefaultHandlerInterceptor; +import org.springframework.web.method.HandlerMethod; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.lang.reflect.Method; + +/** + * 防止重复提交拦截器 + * + * @author 郭世旭 + */ +public abstract class RepeatSubmitInterceptor extends DefaultHandlerInterceptor { + @Override + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { + if (handler instanceof HandlerMethod) { + HandlerMethod handlerMethod = (HandlerMethod) handler; + Method method = handlerMethod.getMethod(); + RepeatSubmit annotation = method.getAnnotation(RepeatSubmit.class); + if (annotation != null) { + if (this.isRepeatSubmit(request, annotation)) { + throw new BizException(annotation.message()); + } + } + return true; + } else { + return true; + } + } + + /** + * 验证是否重复提交由子类实现具体的防重复提交的规则 + * + * @param request + * @return + * @throws Exception + */ + public abstract boolean isRepeatSubmit(HttpServletRequest request, RepeatSubmit annotation); +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/interceptor/UserCache.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/interceptor/UserCache.java new file mode 100644 index 0000000..0ce92d9 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/interceptor/UserCache.java @@ -0,0 +1,15 @@ +package com.ssdmn.common.interceptor; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +@Data +public class UserCache { + + @ApiModelProperty(hidden = true) + private Long userId; + +// @ApiModelProperty(hidden = true) +// private String phone; + +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/interceptor/annotation/AuthUser.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/interceptor/annotation/AuthUser.java new file mode 100644 index 0000000..c5efd3b --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/interceptor/annotation/AuthUser.java @@ -0,0 +1,17 @@ +package com.ssdmn.common.interceptor.annotation; + +import java.lang.annotation.*; + +/** + * 解析token中当前用户信息注解 + * + * @author 郭世旭 + * @version 1.0 + * @createTime 2021-03-22 09:58:30 + */ +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Target({ElementType.PARAMETER,ElementType.METHOD,ElementType.TYPE}) +public @interface AuthUser {//eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJwaG9uZSI6IjE4ODgzNTU1NzI0IiwidXNlcklkIjowfQ.5FSrnXizr0lnFO8_xkivLeYVpDqiTADVbB4gmf-U3L0 + boolean required() default true; +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/interceptor/annotation/RepeatSubmit.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/interceptor/annotation/RepeatSubmit.java new file mode 100644 index 0000000..48243f6 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/interceptor/annotation/RepeatSubmit.java @@ -0,0 +1,26 @@ +package com.ssdmn.common.interceptor.annotation; + +import java.lang.annotation.*; + +/** + * 自定义注解防止表单重复提交 + * + * @author 郭世旭 + * + */ +@Inherited +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface RepeatSubmit +{ + /** + * 间隔时间(ms),小于此时间视为重复提交 + */ + long interval() default 1000; + + /** + * 提示消息 + */ + String message() default "请勿频繁点击"; +} \ No newline at end of file diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/interceptor/impl/SameUrlDataInterceptor.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/interceptor/impl/SameUrlDataInterceptor.java new file mode 100644 index 0000000..df50080 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/interceptor/impl/SameUrlDataInterceptor.java @@ -0,0 +1,111 @@ +package com.ssdmn.common.interceptor.impl; + +import com.alibaba.fastjson.JSONObject; +import com.ssdmn.common.http.HttpHelper; +import com.ssdmn.common.interceptor.RepeatSubmitInterceptor; +import com.ssdmn.common.interceptor.annotation.RepeatSubmit; +import com.ssdmn.common.redis.service.RedisService; +import com.ssdmn.config.filter.RepeatedlyRequestWrapper; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; +import javax.servlet.http.HttpServletRequest; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +import static com.ssdmn.common.result.SysConstants.SSDMN_TOKEN_KEY; + +/** + * 判断请求url和数据是否和上一次相同, + * 如果和上次相同,则是重复提交表单。 + * + * @author 郭世旭 + */ +@Component +public class SameUrlDataInterceptor extends RepeatSubmitInterceptor +{ + public final String REPEAT_PARAMS = "repeatParams"; + + public final String REPEAT_TIME = "repeatTime"; + + @Resource + private RedisService redisCache; + + @SuppressWarnings("unchecked") + @Override + public boolean isRepeatSubmit(HttpServletRequest request, RepeatSubmit annotation) + { + String nowParams = ""; + if (request instanceof RepeatedlyRequestWrapper) + { + RepeatedlyRequestWrapper repeatedlyRequest = (RepeatedlyRequestWrapper) request; + nowParams = HttpHelper.getBodyString(repeatedlyRequest); + } + + // body参数为空,获取Parameter的数据 + if (StringUtils.isEmpty(nowParams)) + { + nowParams = JSONObject.toJSONString(request.getParameterMap()); + } + Map nowDataMap = new HashMap(); + nowDataMap.put(REPEAT_PARAMS, nowParams); + nowDataMap.put(REPEAT_TIME, System.currentTimeMillis()); + + // 请求地址(作为存放cache的key值) + String url = request.getRequestURI(); + + // 唯一值(没有消息头则使用请求地址) + String submitKey = request.getHeader(SSDMN_TOKEN_KEY); + if (StringUtils.isEmpty(submitKey)) + { + submitKey = url; + } + + // 唯一标识(指定key + url + 消息头) + String cacheRepeatKey = "repeat_submit:" + submitKey; + + Object sessionObj = redisCache.get(cacheRepeatKey); + if (sessionObj != null) + { + Map sessionMap = (Map) sessionObj; + if (sessionMap.containsKey(url)) + { + Map preDataMap = (Map) sessionMap.get(url); + if (compareParams(nowDataMap, preDataMap) && compareTime(nowDataMap, preDataMap, annotation.interval())) + { + return true; + } + } + } + Map cacheMap = new HashMap(); + cacheMap.put(url, nowDataMap); + redisCache.set(cacheRepeatKey, cacheMap, annotation.interval(), TimeUnit.MILLISECONDS); + return false; + } + + /** + * 判断参数是否相同 + */ + private boolean compareParams(Map nowMap, Map preMap) + { + String nowParams = (String) nowMap.get(REPEAT_PARAMS); + String preParams = (String) preMap.get(REPEAT_PARAMS); + return nowParams.equals(preParams); + } + + /** + * 判断两次间隔时间 + */ + private boolean compareTime(Map nowMap, Map preMap, long interval) + { + long time1 = (Long) nowMap.get(REPEAT_TIME); + long time2 = (Long) preMap.get(REPEAT_TIME); + if ((time1 - time2) < interval) + { + return true; + } + return false; + } +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/ip/AddressUtils.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/ip/AddressUtils.java new file mode 100644 index 0000000..d6e71f5 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/ip/AddressUtils.java @@ -0,0 +1,43 @@ +package com.ssdmn.common.ip; + +import cn.hutool.core.util.ObjectUtil; +import com.alibaba.fastjson.JSONObject; +import com.ssdmn.common.http.HttpUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * 获取地址类 + * + * @author ruoyi + */ +public class AddressUtils { + private static final Logger log = LoggerFactory.getLogger(AddressUtils.class); + + // IP地址查询 + public static final String IP_URL = "http://whois.pconline.com.cn/ipJson.jsp"; + + // 未知地址 + public static final String UNKNOWN = "XX XX"; + + public static String getRealAddressByIP(String ip) { + // 内网不查询 + if (IpUtils.internalIp(ip)) { + return "内网IP"; + } + try { + String rspStr = HttpUtils.sendGet(IP_URL, "ip=" + ip + "&json=true", "GBK"); + if (ObjectUtil.isEmpty(rspStr)) { + log.error("获取地理位置异常 {}", ip); + return UNKNOWN; + } + JSONObject obj = JSONObject.parseObject(rspStr); + String region = obj.getString("pro"); + String city = obj.getString("city"); + return String.format("%s %s", region, city); + } catch (Exception e) { + log.error("获取地理位置异常 {}", ip); + } + return UNKNOWN; + } +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/ip/IpUtils.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/ip/IpUtils.java new file mode 100644 index 0000000..8d26b18 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/ip/IpUtils.java @@ -0,0 +1,173 @@ +package com.ssdmn.common.ip; + +import cn.hutool.core.util.ObjectUtil; +import com.ssdmn.common.html.EscapeUtil; +import com.ssdmn.common.utils.ServletUtil; + +import javax.servlet.http.HttpServletRequest; +import java.net.InetAddress; +import java.net.UnknownHostException; + +/** + * 获取IP方法 + * + * @author 郭世旭 + */ +public class IpUtils{ + + public static String getIpAddr(){ + + return getIpAddr(ServletUtil.getRequest()); + } + + public static String getIpAddr(HttpServletRequest request) { + if (request == null) { + return "unknown"; + } + String ip = request.getHeader("x-forwarded-for"); + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { + ip = request.getHeader("Proxy-Client-IP"); + } + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { + ip = request.getHeader("X-Forwarded-For"); + } + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { + ip = request.getHeader("WL-Proxy-Client-IP"); + } + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { + ip = request.getHeader("X-Real-IP"); + } + + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { + ip = request.getRemoteAddr(); + } + return "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : EscapeUtil.clean(ip); + } + + public static boolean internalIp(String ip) { + byte[] addr = textToNumericFormatV4(ip); + return internalIp(addr) || "127.0.0.1".equals(ip); + } + + private static boolean internalIp(byte[] addr) { + if (ObjectUtil.isNull(addr) || addr.length < 2) { + return true; + } + final byte b0 = addr[0]; + final byte b1 = addr[1]; + // 10.x.x.x/8 + final byte SECTION_1 = 0x0A; + // 172.16.x.x/12 + final byte SECTION_2 = (byte) 0xAC; + final byte SECTION_3 = (byte) 0x10; + final byte SECTION_4 = (byte) 0x1F; + // 192.168.x.x/16 + final byte SECTION_5 = (byte) 0xC0; + final byte SECTION_6 = (byte) 0xA8; + switch (b0) { + case SECTION_1: + return true; + case SECTION_2: + if (b1 >= SECTION_3 && b1 <= SECTION_4) { + return true; + } + case SECTION_5: + switch (b1) { + case SECTION_6: + return true; + } + default: + return false; + } + } + + /** + * 将IPv4地址转换成字节 + * + * @param text IPv4地址 + * @return byte 字节 + */ + public static byte[] textToNumericFormatV4(String text) { + if (text.length() == 0) { + return null; + } + + byte[] bytes = new byte[4]; + String[] elements = text.split("\\.", -1); + try { + long l; + int i; + switch (elements.length) { + case 1: + l = Long.parseLong(elements[0]); + if ((l < 0L) || (l > 4294967295L)) { + return null; + } + bytes[0] = (byte) (int) (l >> 24 & 0xFF); + bytes[1] = (byte) (int) ((l & 0xFFFFFF) >> 16 & 0xFF); + bytes[2] = (byte) (int) ((l & 0xFFFF) >> 8 & 0xFF); + bytes[3] = (byte) (int) (l & 0xFF); + break; + case 2: + l = Integer.parseInt(elements[0]); + if ((l < 0L) || (l > 255L)) { + return null; + } + bytes[0] = (byte) (int) (l & 0xFF); + l = Integer.parseInt(elements[1]); + if ((l < 0L) || (l > 16777215L)) { + return null; + } + bytes[1] = (byte) (int) (l >> 16 & 0xFF); + bytes[2] = (byte) (int) ((l & 0xFFFF) >> 8 & 0xFF); + bytes[3] = (byte) (int) (l & 0xFF); + break; + case 3: + for (i = 0; i < 2; ++i) { + l = Integer.parseInt(elements[i]); + if ((l < 0L) || (l > 255L)) { + return null; + } + bytes[i] = (byte) (int) (l & 0xFF); + } + l = Integer.parseInt(elements[2]); + if ((l < 0L) || (l > 65535L)) { + return null; + } + bytes[2] = (byte) (int) (l >> 8 & 0xFF); + bytes[3] = (byte) (int) (l & 0xFF); + break; + case 4: + for (i = 0; i < 4; ++i) { + l = Integer.parseInt(elements[i]); + if ((l < 0L) || (l > 255L)) { + return null; + } + bytes[i] = (byte) (int) (l & 0xFF); + } + break; + default: + return null; + } + } catch (NumberFormatException e) { + return null; + } + return bytes; + } + + public static String getHostIp() { + try { + return InetAddress.getLocalHost().getHostAddress(); + } catch (UnknownHostException e) { + } + return "127.0.0.1"; + } + + public static String getHostName() { + try { + return InetAddress.getLocalHost().getHostName(); + } catch (UnknownHostException e) { + } + return "未知"; + } +} \ No newline at end of file diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/jwt/JWTUtil.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/jwt/JWTUtil.java new file mode 100644 index 0000000..a38736a --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/jwt/JWTUtil.java @@ -0,0 +1,49 @@ +package com.ssdmn.common.jwt; + +import cn.hutool.json.JSONObject; +import cn.hutool.jwt.JWT; +import com.ssdmn.common.interceptor.UserCache; +import com.ssdmn.common.utils.SpringUtil; +import lombok.extern.slf4j.Slf4j; + +import java.util.HashMap; +import java.util.Map; + +/** + * JWT工具类 + * + * @author z + * @version 1.0 + */ +@Slf4j +public class JWTUtil extends cn.hutool.jwt.JWTUtil { + + private static final String JWT_TOKEN_KEY = "-jwt-token"; + + public static String createSsdmnToken(Long userId) { + Map payload = new HashMap<>(); + payload.put("userId", userId); + return createToken(payload, (SpringUtil.getApplicationName() + JWT_TOKEN_KEY).getBytes()); + } + + public static boolean verifyToken(String token) { + + return verify(token, (SpringUtil.getApplicationName() + JWT_TOKEN_KEY).getBytes()); + } + + public static UserCache parseSsdmnToken(String token) { + UserCache userCache = null; + try { + JWT jwt = parseToken(token); + JSONObject payloads = jwt.getPayloads(); + userCache = new UserCache(); + userCache.setUserId(payloads.getLong("userId")); + } catch (Exception e) { + e.printStackTrace(); + log.error("token解析失败"); + } + + return userCache; + } + +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/lang/Assert.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/lang/Assert.java new file mode 100644 index 0000000..616a8bc --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/lang/Assert.java @@ -0,0 +1,66 @@ +package com.ssdmn.common.lang; + + +import cn.hutool.core.util.ObjectUtil; +import com.ssdmn.common.exception.BizException; +import com.ssdmn.common.result.ResultConstants; + +import java.util.function.Supplier; + +/** + * 断言 + * + * @author 郭世旭 + */ +public class Assert extends cn.hutool.core.lang.Assert { + + public static void isTrue(boolean bl, String message) { + + isTrue(bl, () -> new BizException(message)); + } + + public static void isTrue(boolean bl, ResultConstants resultConstants) { + + isTrue(bl, () -> new BizException(resultConstants.status, resultConstants.message)); + } + + public static void isFalse(boolean bl, String message) { + + isFalse(bl, () -> new BizException(message)); + } + + public static T notBlank(T text, String message) { + + return notBlank(text, () -> new BizException(message)); + } + + public static T notNull(T text, String message) { + + return notNull(text, () -> new BizException(message)); + } + + public static T notBlank(T obj, Supplier supplier) throws X { + + if (ObjectUtil.isEmpty(obj)) { + throw supplier.get(); + } + + return obj; + } + + public static > T notLt(T number, T compareNumber, String message) { + + return notLt(number, compareNumber, () -> new BizException(message)); + } + + public static , X extends Throwable> T notLt(T number, T compareNumber, Supplier supplier) throws X { + + if (ObjectUtil.compare(number, compareNumber) < 0) { + throw supplier.get(); + } + + return number; + } + + +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/page/Comparison.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/page/Comparison.java new file mode 100644 index 0000000..fcb95aa --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/page/Comparison.java @@ -0,0 +1,43 @@ +package com.ssdmn.common.page; + +/** + * 自动构建查询条件类型 + * + * @author 郭世旭 + * @version 1.0 + * @createTime 2021-03-22 09:58:30 + */ +public enum Comparison { + /** + * 相等,默认 + */ + EQ, + /** + * 包含,等同LIKE + */ + LIKE, + /** + * 不等于 + */ + NE, + /** + * 大于 + */ + GT, + /** + * 大于等于 + */ + GE, + /** + * 小于 + */ + LT, + /** + * 小于等于 + */ + LE, + /** + * 其中之一 等同于in + */ + IN +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/page/PageList.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/page/PageList.java new file mode 100644 index 0000000..e9c0e3a --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/page/PageList.java @@ -0,0 +1,39 @@ +package com.ssdmn.common.page; + +import lombok.Data; + +import java.io.Serializable; +import java.util.List; + +/** + * 分页数据 + * + * @author 郭世旭 + * @version 1.0 + * @createTime 2021-03-22 09:58:30 + */ +@Data +public class PageList implements Serializable { + /** + * 当前页数 + */ + private int pageNo; + /** + * 每页记录数 + */ + private int pageSize; + /** + * 总页数 + */ + private int totalPage; + /** + * 总记录数 + */ + private int totalCount; + + /** + * 列表数据 + */ + private List list; + +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/page/PageModel.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/page/PageModel.java new file mode 100644 index 0000000..4072598 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/page/PageModel.java @@ -0,0 +1,58 @@ +package com.ssdmn.common.page; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.io.Serializable; + + +/** + * 分页信息 + * + * @author 郭世旭 + * @version 1.0 + * @createTime 2021-03-22 09:58:30 + */ +@Data +public class PageModel { + private volatile static PageModel pageModel; + /** + * 当前页 + */ + @ApiModelProperty("当前页 默认第一页") + private Integer pageNo = 1; + /** + * 显示条数 + */ + @ApiModelProperty("显示条数 默认20条") + private Integer pageSize = 10; + /** + * 排序字段 + */ + @ApiModelProperty(value = "排序字段 如有多个字段排序逗号隔开") + private String sortField; + /** + * 排序方式 + */ + @ApiModelProperty(value = "排序方式 -> 默认倒叙,asc正序,desc倒叙)") + private String sortWay; + + /** + * + * @return + */ + public static PageModel getInstance(){ + PageModel pageModel = PageModel.pageModel; + if(pageModel==null){ + synchronized (PageModel.class){ + pageModel = PageModel.pageModel; + if(pageModel==null){ + pageModel = new PageModel(); + PageModel.pageModel = pageModel; + } + } + } + + return pageModel; + } +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/page/PageUtils.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/page/PageUtils.java new file mode 100644 index 0000000..3ac6e5c --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/page/PageUtils.java @@ -0,0 +1,68 @@ +package com.ssdmn.common.page; + +import com.alibaba.fastjson.JSON; +import com.baomidou.mybatisplus.core.metadata.IPage; + +import java.util.List; + +/** + * 分页数据工具类 + * + * @author 郭世旭 + * @version 1.0 + * @createTime 2021-03-22 09:58:30 + */ +public class PageUtils { + /** + * MP分页数据 + * + * @param page MP分页数据 + */ + public static PageList pageList(IPage page) { + PageList pageList = new PageList<>(); + pageList.setPageNo((int) page.getCurrent()); + pageList.setPageSize((int) page.getSize()); + pageList.setTotalPage((int) page.getPages()); + pageList.setTotalCount((int) page.getTotal()); + pageList.setList(page.getRecords()); + return pageList; + } + + /** + * 自定义分页数据 + * + * @param list 列表数据 + * @param totalCount 总记录数 + * @param pageSize 每页记录数 + * @param pageNo 当前页数 + */ + public static PageList pageList(int pageNo, int pageSize, int totalCount, List list) { + PageList pageList = new PageList<>(); + pageList.setPageNo(pageNo); + pageList.setPageSize(pageSize); + pageList.setTotalCount(totalCount); + pageList.setTotalPage((int) Math.ceil((double) totalCount / pageSize)); + pageList.setList(list); + return pageList; + } + + /** + * 分页数据转换 + * + * @param page MP分页数据 + * @param clazz 转换类型 + */ + public static PageList pageConvert(IPage page, Class clazz) { + PageList pageList = new PageList<>(); + + List list = JSON.parseArray(JSON.toJSONString(page.getRecords()), clazz); + pageList.setPageNo((int) page.getCurrent()); + pageList.setPageSize((int) page.getSize()); + pageList.setTotalPage((int) page.getPages()); + pageList.setTotalCount((int) page.getTotal()); + pageList.setList(list); + + return pageList; + } + +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/page/QueryField.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/page/QueryField.java new file mode 100644 index 0000000..0ad1443 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/page/QueryField.java @@ -0,0 +1,18 @@ +package com.ssdmn.common.page; + +import java.lang.annotation.*; + +/** + * 查询参数注解 + * + * @author 郭世旭 + * @version 1.0 + * @createTime 2021-03-22 09:58:30 + */ +@Target(ElementType.FIELD) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface QueryField{ + Comparison comparison() default Comparison.EQ; + String fieldName() default ""; +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/page/Querys.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/page/Querys.java new file mode 100644 index 0000000..578fe2b --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/page/Querys.java @@ -0,0 +1,300 @@ +package com.ssdmn.common.page; + +import cn.hutool.core.text.CharSequenceUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.OrderItem; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.ssdmn.common.exception.BizException; + +import java.lang.reflect.Field; +import java.util.Arrays; +import java.util.Collection; + +/** + * 查询参数构造器 + * + * @author 郭世旭 + * @version 1.0 + * @createTime 2021-03-22 09:58:30 + */ +public class Querys { + + /** + * 默认排序 + */ + public static final String DEFAULT_SORT = "desc"; + + /** + * 正序 + */ + public static final String ASC = "asc"; + + /** + * 获取分页对象 + * + * @return 分页对象 + */ + public static Page page() { + return page(null); + } + + /** + * 获取分页对象 + * + * @param pageModel 分页信息 + * @return 分页对象 + */ + public static Page page(PageModel pageModel) { + return page(pageModel, false); + } + + /** + * 获取分页对象 + * + * @param pageModel 分页信息 + * @param isSupportsConnectedTables 是否支持连表 + * @return 分页对象 + */ + public static Page page(PageModel pageModel, boolean isSupportsConnectedTables) { + if (ObjectUtil.isEmpty(pageModel)) { + pageModel = PageModel.getInstance(); + } + //当前页 + Integer pageNo = pageModel.getPageNo(); + //如为空 默认1 + pageNo = ObjectUtil.isEmpty(pageNo) ? 1 : pageNo; + //页面大小 + Integer pageSize = pageModel.getPageSize(); + //如为空 默认20 + pageSize = ObjectUtil.isEmpty(pageSize) ? 20 : pageSize; + //排序字段 防止SQL注入(因为sidx、order是通过拼接SQL实现排序的,会有SQL注入风险) + String sortField = sqlInject(pageModel.getSortField()); + //排序方式 + String sortWay = pageModel.getSortWay(); + //分页对象 + Page page = new Page<>(pageNo, pageSize); + //前端字段排序 + if (CharSequenceUtil.isNotEmpty(sortField) && CharSequenceUtil.isNotEmpty(sortWay)) { + //排序字段数组 + String[] sortFieldArr = sortField.split(","); + //排序方式数组 + String[] sortWayArr = sortWay.split(","); + if (sortFieldArr.length > sortWayArr.length) { + for (int i = 0; i < sortFieldArr.length - sortWayArr.length; i++) { + sortWayArr = Arrays.copyOf(sortWayArr, sortWayArr.length + 1); + sortWayArr[sortWayArr.length - 1] = DEFAULT_SORT; + } + } + + for (int i = 0; i < sortFieldArr.length; i++) { + String sortFieldFinal = sortFieldArr[i]; + if (!isSupportsConnectedTables) { + sortFieldFinal = CharSequenceUtil.toSymbolCase(sortFieldArr[i], '_'); + } + if (ASC.equalsIgnoreCase(sortWayArr[i])) { + page.addOrder(OrderItem.asc(sortFieldFinal)); + } else { + page.addOrder(OrderItem.desc(sortFieldFinal)); + } + } + //默认ID排序 + } else { + if (!isSupportsConnectedTables) { + page.addOrder(OrderItem.desc("id")); + } + } + + return page; + } + + /** + * 获取条件构造器 + * 如需自定义需要删除字段上的注解然后手动构建条件 + * 获取参数对象中字段值不为空的自动封装条件 默认为EQ(=) + * + * @param param 查询参数 + * @return 查询条件 + */ + public static LambdaQueryWrapper wrapper(Object param, Class tClass) { + //条件构造器 + QueryWrapper wrapper = Wrappers.query(); + + //获取对象的类对象 + Class clazz = param.getClass(); + for (; clazz != Object.class; clazz = clazz.getSuperclass()) { + //获取字段 + Field[] fields = clazz.getDeclaredFields(); + for (Field field : fields) { + field.setAccessible(true); + Object val = null; + try { + val = field.get(param); + if (ObjectUtil.isEmpty(val)) { + continue; + } + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + //获取字段上的注解 + QueryField annotation = field.getAnnotation(QueryField.class); + //获取条件 + if (annotation != null) { + //比较器 + Comparison comparison = annotation.comparison(); + //指定的查询字段 + String column = annotation.fieldName(); + //表列名 默认为驼峰转下划线 + if(ObjectUtil.isEmpty(column)){ + column = StrUtil.toSymbolCase(field.getName(), '_'); + } + switch (comparison) { + case EQ: + wrapper.eq(column, val); + break; + case GT: + wrapper.gt(column, val); + break; + case GE: + wrapper.ge(column, val); + break; + case LT: + wrapper.lt(column, val); + break; + case LE: + wrapper.le(column, val); + break; + case NE: + wrapper.ne(column, val); + break; + case LIKE: + wrapper.like(column, val); + break; + case IN: + if (val instanceof Object[]) { + wrapper.in(column, (Object[]) val); + } else if (val instanceof Collection) { + wrapper.in(column, (Collection) val); + } else { + throw new BizException("Comparison.IN只适用于数组或者集合"); + } + break; + default: + } + } + } + } + + return wrapper.lambda(); + } + + /** + * 获取条件构造器 + * 如需自定义需要删除字段上的注解然后手动构建条件 + * 获取参数对象中字段值不为空的自动封装条件 默认为EQ(=) + * + * @param param 查询参数 + * @return 查询条件 + */ + @Deprecated + public static LambdaQueryWrapper wrapper(Object param) { + //条件构造器 + QueryWrapper wrapper = Wrappers.query(); + + //获取对象的类对象 + Class clazz = param.getClass(); + for (; clazz != Object.class; clazz = clazz.getSuperclass()) { + //获取字段 + Field[] fields = clazz.getDeclaredFields(); + for (Field field : fields) { + field.setAccessible(true); + Object val = null; + try { + val = field.get(param); + if (ObjectUtil.isEmpty(val)) { + continue; + } + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + //获取字段上的注解 + QueryField annotation = field.getAnnotation(QueryField.class); + //获取条件 + if (annotation != null) { + Comparison comparison = annotation.comparison(); + //表列名 默认为驼峰转下划线 + String column = StrUtil.toSymbolCase(field.getName(), '_'); + switch (comparison) { + case EQ: + wrapper.eq(column, val); + break; + case GT: + wrapper.gt(column, val); + break; + case GE: + wrapper.ge(column, val); + break; + case LT: + wrapper.lt(column, val); + break; + case LE: + wrapper.le(column, val); + break; + case NE: + wrapper.ne(column, val); + break; + case LIKE: + wrapper.like(column, val); + break; + case IN: + if (val instanceof Object[]) { + wrapper.in(column, (Object[]) val); + } else if (val instanceof Collection) { + wrapper.in(column, (Collection) val); + } else { + throw new BizException("Comparison.IN只适用于数组或者集合"); + } + break; + default: + } + } + } + } + + return wrapper.lambda(); + } + + /** + * SQL注入过滤 + * + * @param str 待验证的字符串 + */ + private static String sqlInject(String str) { + if (StrUtil.isBlank(str)) { + return null; + } + //去掉'|"|;|\字符 + str = StrUtil.replace(str, "'", ""); + str = StrUtil.replace(str, "\"", ""); + str = StrUtil.replace(str, ";", ""); + str = StrUtil.replace(str, "\\", ""); + + //转换成小写 + String str1 = str.toLowerCase(); + + //非法字符 + String[] keywords = {"master", "truncate", "insert", "select", "delete", "update", "declare", "alter", "drop"}; + + //判断是否包含非法字符 + for (String keyword : keywords) { + if (str1.contains(keyword)) { + throw new RuntimeException("包含非法字符"); + } + } + + return str; + } +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/qrCode/service/QRCodeService.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/qrCode/service/QRCodeService.java new file mode 100644 index 0000000..29e56de --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/qrCode/service/QRCodeService.java @@ -0,0 +1,48 @@ +package com.ssdmn.common.qrCode.service; + +/** + * @Description: + * @Author: fan + * @Data: 2021/5/9 9:16 + * @Version: 1.0v + */ +public interface QRCodeService { + + /** + * 生成普通二维码 + * @param content 正文 + * @param width 宽 + * @param height 高 + * @return 访问路径 + */ + String generateQRCode(String content,Integer width,Integer height); + String generateBase64QRCode(String content,Integer width,Integer height); + + /** + * 生成普通二维码(重载接口 默认宽高 300X300) + * @param content 正文 + * @return 访问路径 + */ + String generateQRCode(String content); + + String generateBase64QRCode(String content); + + +// /** +// * 生成带logo的二维码 +// * @param content 正文 +// * @param logoUrl logo网络路径 +// * @param width 宽 +// * @param height 高 +// * @return 访问路径 +// */ +// String generateQrCodeAndImage(String content, String logoUrl, Integer width, Integer height); +// +// /** +// * 生成带logo的二维码 +// * @param content 正文 +// * @param logoUrl logo网络路径 +// * @return 访问路径 +// */ +// String generateQrCodeAndImage(String content, String logoUrl); +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/qrCode/service/impl/QRCodeServiceImpl.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/qrCode/service/impl/QRCodeServiceImpl.java new file mode 100644 index 0000000..3bc5453 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/qrCode/service/impl/QRCodeServiceImpl.java @@ -0,0 +1,74 @@ +package com.ssdmn.common.qrCode.service.impl; + +import cn.hutool.extra.qrcode.QrCodeUtil; +import com.ssdmn.biz.file.FileProperties; +import com.ssdmn.biz.file.service.FileManageService; +import com.ssdmn.common.qrCode.service.QRCodeService; +import com.ssdmn.common.utils.FileUtil; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; +import java.awt.image.BufferedImage; +import java.io.File; + +/** + * @Description: + * @Author: fan + * @Data: 2021/5/9 9:16 + * @Version: 1.0v + */ +@Component +@ConditionalOnBean(FileProperties.class) +public class QRCodeServiceImpl implements QRCodeService { + + @Resource + private FileManageService fileManageService; + + @Resource + private FileProperties fileProperties; + + @Override + public String generateQRCode(String content, Integer width, Integer height) { + File multipartFile = QrCodeUtil.generate(content, width, height, new File(fileProperties.getQr().getFileName())); + + return fileManageService.fileUpload(multipartFile); + } + @Override + public String generateBase64QRCode(String content, Integer width, Integer height) { + BufferedImage bufferedImage = QrCodeUtil.generate(content, width, height); + + return FileUtil.imageToBase64(bufferedImage); + } + + /** + * 生成普通二维码 + * + * @param content 正文 + * @return 访问路径 + */ + @Override + public String generateQRCode(String content) { + return generateQRCode(content, 300, 300); + } + @Override + public String generateBase64QRCode(String content) { + return generateBase64QRCode(content, 300, 300); + } + + +// @Override +// public String generateQrCodeAndImage(String content, String logoUrl, Integer width, Integer height) { +// Assert.notBlank(logoUrl, "logo不能为空"); +// MultipartFile multipartFile = QrCodeUtil.generate(content, logoUrl, width, height); +// return fileManageService.fileUpload(multipartFile); +// } +// +// @Override +// public String generateQrCodeAndImage(String content, String logoUrl) { +// Assert.notBlank(logoUrl, "logo不能为空"); +// MultipartFile multipartFile = QrCodeUtil.generate(content, logoUrl, null, null); +// return fileManageService.fileUpload(multipartFile); +// } + +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/redis/annotation/RedisLock.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/redis/annotation/RedisLock.java new file mode 100644 index 0000000..322cd64 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/redis/annotation/RedisLock.java @@ -0,0 +1,26 @@ +package com.ssdmn.common.redis.annotation; + +/** + * redis锁注解 + * @author 郭世旭 + */ +public @interface RedisLock { + + // 锁前缀 --建议使用模块化名字,并且写在常量类中 + String lockPrefix() default ""; + + // 锁对象(与锁前缀拼接成锁名),尽量不要用对象,map等,对象会toString后与锁前缀拼接 + String lockObj() default ""; + + // 尝试加锁,最多等待时间(毫秒) + long lockWait() default 50L; + + // 自动解锁时间 (毫秒) + long autoUnlockTime() default 2000L; + + // 重试次数 + int retryNum() default 3; + + // 重试等待时间 (毫秒) + long retryWait() default 50L; +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/redis/aop/RedisLockAspect.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/redis/aop/RedisLockAspect.java new file mode 100644 index 0000000..ec899fe --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/redis/aop/RedisLockAspect.java @@ -0,0 +1,150 @@ +//package com.ssdmn.common.redis.aop; +// +//import cn.hutool.core.lang.Assert; +//import cn.hutool.core.util.ObjectUtil; +//import com.ssdmn.common.exception.BizException; +//import com.ssdmn.common.redis.annotation.RedisLock; +//import lombok.extern.slf4j.Slf4j; +//import org.aspectj.lang.ProceedingJoinPoint; +//import org.aspectj.lang.annotation.Around; +//import org.aspectj.lang.annotation.Aspect; +//import org.aspectj.lang.annotation.Pointcut; +//import org.aspectj.lang.reflect.MethodSignature; +//import org.redisson.api.RLock; +//import org.redisson.api.RedissonClient; +// +//import java.lang.reflect.Method; +//import java.util.HashMap; +//import java.util.Map; +//import java.util.concurrent.TimeUnit; +// +///** +// * Description: 分布式锁--切面实现 +// *

+// * 先获取锁, 获取不到则继续等待(指定时间), 失败次数(指定)次后跳出, 消费降级(抛出,系统繁忙稍后再试) +// * 如果没有重试次数,方法返回null 记得捕获NP 当重试次数有, 但是重试间隔时间没写, 默认200ms 间隔 +// *

+// * +// * @author 郭世旭 +// */ +//@Aspect +//@Slf4j +//public class RedisLockAspect { +// private static final String LOCK_NAME = "lockName"; +// private static final String lOCK_WAIT = "lockWait"; +// private static final String AUTO_UNLOCK_TIME = "autoUnlockTime"; +// private static final String RETRY_NUM = "retryNum"; +// private static final String RETRY_WAIT = "retryWait"; +// +// /** +// * redis工具类 +// */ +// private RedissonClient redissonClient; +// +// public RedisLockAspect(RedissonClient redissonClient){ +// this.redissonClient = redissonClient; +// } +// +// @Pointcut("@annotation(com.ssdmn.common.redis.annotation.RedisLock)") +// public void lockAspect() { +// } +// +// @Around("lockAspect()") +// public Object lockAroundAction(ProceedingJoinPoint proceeding) throws Throwable { +// Map annotationArgs = this.getAnnotationArgs(proceeding); +// String lockName = (String) annotationArgs.get(LOCK_NAME); +// Assert.notNull(lockName, "分布式,锁名不能为空"); +// int retryNum = (int) annotationArgs.get(RETRY_NUM); +// long retryWait = (long) annotationArgs.get(RETRY_WAIT); +// long lockWait = (long) annotationArgs.get(lOCK_WAIT); +// long autoUnlockTime = (long) annotationArgs.get(AUTO_UNLOCK_TIME); +// +// // 获取锁 +// RLock lock = redissonClient.getLock(lockName); +// try { +// boolean res = lock.tryLock(lockWait, autoUnlockTime, TimeUnit.SECONDS); +// if (res) { +// // 执行主逻辑 +// return proceeding.proceed(); +// +// } else { +// // 如果重试次数为零, 则不重试 +// if (retryNum <= 0) { +// log.info(String.format("{%s}已经被锁, 不重试", lockName)); +// throw new BizException(String.format("{%s}已经被锁, 不重试", lockName)); +// } +// +// if (retryWait == 0) { +// retryWait = 200L; +// } +// // 设置失败次数计数器, 当到达指定次数时, 返回失败 +// int failCount = 1; +// while (failCount <= retryNum) { +// // 等待指定时间ms +// Thread.sleep(retryWait); +// if (lock.tryLock(lockWait, autoUnlockTime, TimeUnit.SECONDS)) { +// // 执行主逻辑 +// return proceeding.proceed(); +// } else { +// log.info(String.format("{%s}已经被锁, 正在重试[ %s/%s ],重试间隔{%s}毫秒", lockName, failCount, retryNum, retryWait)); +// failCount++; +// } +// } +// throw new BizException("系统繁忙, 请稍等再试"); +// } +// } catch (Throwable throwable) { +// log.error(String.format("执行分布式锁发生异常锁名:{%s},异常名称:{%s}", lockName, throwable.getMessage())); +// throw throwable; +// } finally { +// lock.unlock(); +// } +// +// +// } +// +// /** +// * 获取锁参数 +// * +// * @param proceeding +// * @return +// */ +// private Map getAnnotationArgs(ProceedingJoinPoint proceeding) { +//// if (!(objs[i] instanceof ExtendedServletRequestDataBinder) +//// && !(objs[i] instanceof HttpServletResponseWrapper)) { +// +// proceeding.getArgs(); +// Object[] objs = proceeding.getArgs(); +// // 参数名 +// String[] argNames = ((MethodSignature) proceeding.getSignature()).getParameterNames(); +// +// Class target = proceeding.getTarget().getClass(); +// Method[] methods = target.getMethods(); +// String methodName = proceeding.getSignature().getName(); +// for (Method method : methods) { +// if (method.getName().equals(methodName)) { +// Map result = new HashMap<>(5); +// RedisLock redisLock = method.getAnnotation(RedisLock.class); +// +// if (ObjectUtil.isNotEmpty(redisLock.lockObj())) { +// for (int i = 0; i < objs.length; i++) { +// if (redisLock.lockObj().equals(argNames[i])) { +// result.put(LOCK_NAME, redisLock.lockPrefix() + objs[i]); +// break; +// } +// +// } +// } else { +// result.put(LOCK_NAME, redisLock.lockPrefix()); +// } +// result.put(lOCK_WAIT, redisLock.lockWait()); +// result.put(AUTO_UNLOCK_TIME, redisLock.autoUnlockTime()); +// result.put(RETRY_NUM, redisLock.retryNum()); +// result.put(RETRY_WAIT, redisLock.retryWait()); +// +// return result; +// } +// } +// throw new BizException("异常"); +// +// } +//} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/redis/service/RedisConfig.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/redis/service/RedisConfig.java new file mode 100644 index 0000000..6669b72 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/redis/service/RedisConfig.java @@ -0,0 +1,130 @@ +package com.ssdmn.common.redis.service; + +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.PropertyAccessor; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +//import com.ssdmn.common.redis.aop.RedisLockAspect; +import lombok.Data; +//import org.redisson.Redisson; +//import org.redisson.api.RedissonClient; +//import org.redisson.client.codec.StringCodec; +//import org.redisson.config.Config; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; +import org.springframework.data.redis.serializer.StringRedisSerializer; + +/** + *

+ * + *

+ * + * @author 郭世旭 + */ +@Data +@Configuration +@ConditionalOnClass(RedisConnectionFactory.class) +public class RedisConfig { + + @Value("${spring.redis.host}") + private String host; + + @Value("${spring.redis.port}") + private String port; + + @Value("${spring.redis.password}") + private String password; + + @Value("${spring.redis.timeout}") + private int timeout; + + /** + * 配置redisTemplate + * + * @param redisConnectionFactory 连接工厂 + * @return redisTemplate + */ + @Bean("redisTemplate") + public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) { + + RedisTemplate template = new RedisTemplate<>(); + template.setConnectionFactory(redisConnectionFactory); + // 使用Jackson2JsonRedisSerialize 替换默认序列化 + Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = initJacksonSerializer(); + // 设置value的序列化规则和 key的序列化规则 + StringRedisSerializer stringSerial = new StringRedisSerializer(); + // redis key 序列化方式使用stringSerial + template.setKeySerializer(stringSerial); + // redis value 序列化方式使用jackson + template.setValueSerializer(jackson2JsonRedisSerializer); + // redis hash key 序列化方式使用stringSerial + template.setHashKeySerializer(stringSerial); + // redis hash value 序列化方式使用jackson + template.setHashValueSerializer(jackson2JsonRedisSerializer); + template.afterPropertiesSet(); + + return template; + } +// +// @Bean +// @ConditionalOnClass(RedissonClient.class) +// public RedissonClient redissonClient(){ +// Config config = new Config(); +// config.useSingleServer() +// .setAddress("redis://"+host+":"+port) +// .setPassword(password) +// .setConnectionMinimumIdleSize(1) +// .setConnectionPoolSize(100) +// .setIdleConnectionTimeout(600000) +// .setSubscriptionConnectionMinimumIdleSize(1) +// .setSubscriptionConnectionPoolSize(100) +// .setTimeout(timeout); +// +// config.setCodec(new StringCodec()); +// config.setThreads(5); +// config.setNettyThreads(5); +// +// return Redisson.create(config); +// } + + @Bean + @ConditionalOnClass(RedisConnectionFactory.class) + public RedisService redisCache(RedisTemplate redisTemplate) { + + return new RedisService(redisTemplate); + } + +// @Bean +// @ConditionalOnClass(RedissonClient.class) +// public RedisLockAspect redisLock(RedissonClient redissonClient) { +// +// return new RedisLockAspect(redissonClient); +// } + + + /** + * 处理redis序列化问题 + * + * @return Jackson2JsonRedisSerializer + */ + private Jackson2JsonRedisSerializer initJacksonSerializer() { + Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class); + ObjectMapper om = new ObjectMapper(); + om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); + //以下替代旧版本 enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); + om.activateDefaultTyping(om.getPolymorphicTypeValidator(), ObjectMapper.DefaultTyping.NON_FINAL); + //bugFix Jackson2反序列化数据处理LocalDateTime类型时出错 + om.disable(SerializationFeature.WRITE_DATE_KEYS_AS_TIMESTAMPS); + // java8 时间支持 + om.registerModule(new JavaTimeModule()); + jackson2JsonRedisSerializer.setObjectMapper(om); + + return jackson2JsonRedisSerializer; + } +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/redis/service/RedisService.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/redis/service/RedisService.java new file mode 100644 index 0000000..0bc888f --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/redis/service/RedisService.java @@ -0,0 +1,693 @@ +package com.ssdmn.common.redis.service; + +import cn.hutool.json.JSONUtil; +import com.alibaba.fastjson.JSONObject; +import lombok.extern.slf4j.Slf4j; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.core.ZSetOperations; +import org.springframework.data.redis.core.script.DefaultRedisScript; +import org.springframework.data.redis.core.script.RedisScript; + +import java.util.*; +import java.util.concurrent.TimeUnit; + +/** + * RedisTemplate工具类 + * + * @author 郭世旭 + * @version 1.0 + * @createTime 2021-03-22 09:58:30 + */ +@Slf4j +public class RedisService { + + private RedisTemplate redisTemplate; + + + public RedisService(RedisTemplate redisTemplate){ + this.redisTemplate = redisTemplate; + } + + /** + * 获取锁 + * + * @param lockKey 锁名称 + * @param value 值 + * @param expire 过期时间(s) -1为不设置过期时间 + * @return + */ + public boolean lock(String lockKey, String value, Long expire) { + + String script = "if redis.call('setNx',KEYS[1],ARGV[1]) == 1 then if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('expire',KEYS[1],ARGV[2]) else return 0 end else return 0 end"; + + RedisScript redisScript = new DefaultRedisScript<>(script, Long.class); + List keys = Collections.singletonList(lockKey); + Long execute = redisTemplate.execute(redisScript, keys, value, expire); + return execute != null && execute == 1L; + } + + /** + * 释放锁 + * + * @param lockKey 锁 + * @param value 值 + * @return + */ + public boolean unlock(String lockKey, String value) { + + String script = "if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end"; + + RedisScript redisScript = new DefaultRedisScript<>(script, Long.class); + List keys = Collections.singletonList(lockKey); + Long execute = redisTemplate.execute(redisScript, keys, value); + return execute != null && 1L == execute; + } + + /** + * 指定缓存失效时间 + * + * @param key 键 + * @param time 时间(秒) + * @return Boolean + */ + public Boolean expire(String key, Long time) { + try { + if (time > 0) { + redisTemplate.expire(key, time, TimeUnit.SECONDS); + } + return true; + } catch (Exception e) { + log.error("redis操作失败==> ", e); + return false; + } + } + + /** + * 根据key获取过期时间 + * + * @param key 键 不能为 null + * @return 时间(秒) 返回 0代表为永久有效 + */ + public Long getExpire(String key) { + return redisTemplate.getExpire(key, TimeUnit.SECONDS); + } + + /** + * 判断 key是否存在 + * + * @param key 键 + * @return true 存在 false不存在 + */ + public Boolean hasKey(String key) { + try { + return redisTemplate.hasKey(key); + } catch (Exception e) { + log.error("redis操作失败==> ", e); + return false; + } + } + + /** + * 删除缓存 + * + * @param key 可以传一个值 或多个 + */ + public void del(String... key) { + if (key != null && key.length > 0) { + redisTemplate.delete(Arrays.asList(key)); + } + } + + /** + * 普通缓存获取 + * + * @param key 键 + * @return 值 + */ + public Object get(String key) { + return key == null ? null : redisTemplate.opsForValue().get(key); + } + + /** + * 普通缓存获取 + * + * @param key 键 + * @return 值 + */ + public String getStr(String key) { + return key == null ? null : JSONUtil.toJsonStr(redisTemplate.opsForValue().get(key)); + } + + /** + * 普通缓存获取 + * + * @param key 键 + * @return 值 + */ + public T getObj(String key, Class clazz) { + return JSONObject.parseObject(getStr(key), clazz); + } + + /** + * 普通缓存获取 + * + * @param key 键 + * @return 值 + */ + public List list(String key, Class clazz) { + + return JSONObject.parseArray(getStr(key), clazz); + } + + /** + * 普通缓存放入 + * + * @param key 键 + * @param value 值 + * @return true成功 false失败 + */ + public Boolean set(String key, Object value) { + try { + redisTemplate.opsForValue().set(key, value); + return true; + } catch (Exception e) { + log.error("redis操作失败==> ", e); + return false; + } + } + + /** + * 普通缓存放入并设置时间 + * + * @param key 键 + * @param value 值 + * @param time 时间(秒) time要大于0 如果time小于等于0 将设置无限期 + * @return true成功 false 失败 + */ + public Boolean set(final String key,final Object value,final Long time) { + try { + if (time > 0) { + redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS); + } else { + set(key, value); + } + return true; + } catch (Exception e) { + log.error("redis操作失败==> ", e); + return false; + } + } + + /** + * 普通缓存放入并设置时间 + * + * @param key 键 + * @param value 值 + * @param time 时间(秒) time要大于0 如果time小于等于0 将设置无限期 + * @return true成功 false 失败 + */ + public Boolean set(final String key,final Object value,final Long time,final TimeUnit timeUnit) { + try { + if (time > 0) { + redisTemplate.opsForValue().set(key, value, time, timeUnit); + } else { + set(key, value); + } + return true; + } catch (Exception e) { + log.error("redis操作失败==> ", e); + return false; + } + } + + /** + * 递增 + * + * @param key 键 + * @param delta 要增加几(大于0) + * @return Long + */ + public Long incr(String key, Long delta) { + if (delta < 0) { + throw new RuntimeException("递增因子必须大于0"); + } + return redisTemplate.opsForValue().increment(key, delta); + } + + /** + * 递减 + * + * @param key 键 + * @param delta 要减少几(小于0) + * @return Long + */ + public Long decr(String key, Long delta) { + if (delta < 0) { + throw new RuntimeException("递减因子必须大于0"); + } + return redisTemplate.opsForValue().increment(key, -delta); + } + + /** + * HashGet + * + * @param key 键 不能为 null + * @param item 项 不能为 null + * @return 值 + */ + public Object hget(String key, String item) { + return redisTemplate.opsForHash().get(key, item); + } + + /** + * 获取 hashKey对应的所有键值 + * + * @param key 键 + * @return 对应的多个键值 + */ + public Map hmget(String key) { + return redisTemplate.opsForHash().entries(key); + } + + /** + * HashSet + * + * @param key 键 + * @param map 对应多个键值 + * @return true 成功 false 失败 + */ + public Boolean hmset(String key, Map map) { + try { + redisTemplate.opsForHash().putAll(key, map); + return true; + } catch (Exception e) { + log.error("redis操作失败==> ", e); + return false; + } + } + + /** + * HashSet 并设置时间 + * + * @param key 键 + * @param map 对应多个键值 + * @param time 时间(秒) + * @return true成功 false失败 + */ + public Boolean hmset(String key, Map map, Long time) { + try { + redisTemplate.opsForHash().putAll(key, map); + if (time > 0) { + expire(key, time); + } + return true; + } catch (Exception e) { + log.error("redis操作失败==> ", e); + return false; + } + } + + /** + * 向一张hash表中放入数据,如果不存在将创建 + * + * @param key 键 + * @param item 项 + * @param value 值 + * @return true 成功 false失败 + */ + public Boolean hset(String key, String item, Object value) { + try { + redisTemplate.opsForHash().put(key, item, value); + return true; + } catch (Exception e) { + log.error("redis操作失败==> ", e); + return false; + } + } + + /** + * 向一张hash表中放入数据,如果不存在将创建 + * + * @param key 键 + * @param item 项 + * @param value 值 + * @param time 时间(秒) 注意:如果已存在的hash表有时间,这里将会替换原有的时间 + * @return true 成功 false失败 + */ + public Boolean hset(String key, String item, Object value, Long time) { + try { + redisTemplate.opsForHash().put(key, item, value); + if (time > 0) { + expire(key, time); + } + return true; + } catch (Exception e) { + log.error("redis操作失败==> ", e); + return false; + } + } + + /** + * 删除hash表中的值 + * + * @param key 键 不能为 null + * @param item 项 可以使多个不能为 null + */ + public void hdel(String key, Object... item) { + redisTemplate.opsForHash().delete(key, item); + } + + /** + * 判断hash表中是否有该项的值 + * + * @param key 键 不能为 null + * @param item 项 不能为 null + * @return true 存在 false不存在 + */ + public Boolean hHasKey(String key, String item) { + return redisTemplate.opsForHash().hasKey(key, item); + } + + /** + * hash递增 如果不存在,就会创建一个 并把新增后的值返回 + * + * @param key 键 + * @param item 项 + * @param by 要增加几(大于0) + * @return Double + */ + public Double hincr(String key, String item, Double by) { + return redisTemplate.opsForHash().increment(key, item, by); + } + + /** + * hash递减 + * + * @param key 键 + * @param item 项 + * @param by 要减少记(小于0) + * @return Double + */ + public Double hdecr(String key, String item, Double by) { + return redisTemplate.opsForHash().increment(key, item, -by); + } + + /** + * 根据 key获取 Set中的所有值 + * + * @param key 键 + * @return Set + */ + public Set sGet(String key) { + try { + return redisTemplate.opsForSet().members(key); + } catch (Exception e) { + log.error("redis操作失败==> ", e); + return null; + } + } + + /** + * 根据value从一个set中查询,是否存在 + * + * @param key 键 + * @param value 值 + * @return true 存在 false不存在 + */ + public Boolean sHasKey(String key, Object value) { + try { + return redisTemplate.opsForSet().isMember(key, value); + } catch (Exception e) { + log.error("redis操作失败==> ", e); + return false; + } + } + + /** + * 将数据放入set缓存 + * + * @param key 键 + * @param values 值 可以是多个 + * @return 成功个数 + */ + public Long sSet(String key, Object... values) { + try { + return redisTemplate.opsForSet().add(key, values); + } catch (Exception e) { + log.error("redis操作失败==> ", e); + return 0L; + } + } + + /** + * 将set数据放入缓存 + * + * @param key 键 + * @param time 时间(秒) + * @param values 值 可以是多个 + * @return 成功个数 + */ + public Long sSetAndTime(String key, Long time, Object... values) { + try { + Long count = redisTemplate.opsForSet().add(key, values); + if (time > 0) { + expire(key, time); + } + return count; + } catch (Exception e) { + log.error("redis操作失败==> ", e); + return 0L; + } + } + + /** + * 获取set缓存的长度 + * + * @param key 键 + * @return Long + */ + public Long sGetSetSize(String key) { + try { + return redisTemplate.opsForSet().size(key); + } catch (Exception e) { + log.error("redis操作失败==> ", e); + return 0L; + } + } + + /** + * 移除值为value的 + * + * @param key 键 + * @param values 值 可以是多个 + * @return 移除的个数 + */ + public Long setRemove(String key, Object... values) { + try { + return redisTemplate.opsForSet().remove(key, values); + } catch (Exception e) { + log.error("redis操作失败==> ", e); + return 0L; + } + } + + /** + * 获取list缓存的内容 + * + * @param key 键 + * @param start 开始 + * @param end 结束 0 到 -1代表所有值 + * @return List + */ + public List lGet(String key, Long start, Long end) { + try { + return redisTemplate.opsForList().range(key, start, end); + } catch (Exception e) { + log.error("redis操作失败==> ", e); + return null; + } + } + + /** + * 获取list缓存的长度 + * + * @param key 键 + * @return Long + */ + public Long lGetListSize(String key) { + try { + return redisTemplate.opsForList().size(key); + } catch (Exception e) { + log.error("redis操作失败==> ", e); + return 0L; + } + } + + /** + * 通过索引 获取list中的值 + * + * @param key 键 + * @param index 索引 index>=0时, 0 表头,1 第二个元素,依次类推; + * index<0时,-1,表尾,-2倒数第二个元素,依次类推 + * @return Object + */ + public Object lGetIndex(String key, Long index) { + try { + return redisTemplate.opsForList().index(key, index); + } catch (Exception e) { + log.error("redis操作失败==> ", e); + return null; + } + } + + /** + * 将list放入缓存 + * + * @param key 键 + * @param value 值 + * @return Boolean + */ + public Boolean lSet(String key, Object value) { + try { + redisTemplate.opsForList().rightPush(key, value); + return true; + } catch (Exception e) { + log.error("redis操作失败==> ", e); + return false; + } + } + + /** + * 将list放入缓存 + * + * @param key 键 + * @param value 值 + * @param time 时间(秒) + * @return Boolean + */ + public Boolean lSet(String key, Object value, Long time) { + try { + redisTemplate.opsForList().rightPush(key, value); + if (time > 0) { + expire(key, time); + } + return true; + } catch (Exception e) { + log.error("redis操作失败==> ", e); + return false; + } + } + + /** + * 将list放入缓存 + * + * @param key 键 + * @param value 值 + * @return Boolean + */ + public Boolean lSet(String key, List value) { + try { + redisTemplate.opsForList().rightPushAll(key, value); + return true; + } catch (Exception e) { + log.error("redis操作失败==> ", e); + return false; + } + } + + /** + * 将list放入缓存 + * + * @param key 键 + * @param value 值 + * @param time 时间(秒) + * @return Boolean + */ + public Boolean lSet(String key, List value, Long time) { + try { + redisTemplate.opsForList().rightPushAll(key, value); + if (time > 0) { + expire(key, time); + } + return true; + } catch (Exception e) { + log.error("redis操作失败==> ", e); + return false; + } + } + + /** + * 根据索引修改list中的某条数据 + * + * @param key 键 + * @param index 索引 + * @param value 值 + * @return Boolean + */ + public Boolean lUpdateIndex(String key, Long index, Object value) { + try { + redisTemplate.opsForList().set(key, index, value); + return true; + } catch (Exception e) { + log.error("redis操作失败==> ", e); + return false; + } + } + + /** + * 移除N个值为value + * + * @param key 键 + * @param count 移除多少个 + * @param value 值 + * @return 移除的个数 + */ + public Long lRemove(String key, Long count, Object value) { + try { + return redisTemplate.opsForList().remove(key, count, value); + } catch (Exception e) { + log.error("redis操作失败==> ", e); + return 0L; + } + } + + /** + * 将数据放入set缓存 + * + * @param key 键 + * @param values 值 可以是多个 + * @return 成功个数 + */ + public Long sZSet(String key, Set> values) { + try { + return redisTemplate.opsForZSet().add(key, values); + } catch (Exception e) { + log.error("redis操作失败==> ", e); + return 0L; + } + } + + /** + * 将set数据放入缓存 + * + * @param key 键 + * @param time 时间(秒) + * @param values 值 可以是多个 + * @return 成功个数 + */ + public Long sSetAndTime(String key, Set> values, Long time) { + try { + Long count = redisTemplate.opsForZSet().add(key, values); + if (time > 0) { + expire(key, time); + } + return count; + } catch (Exception e) { + log.error("redis操作失败==> ", e); + return 0L; + } + } + + + +} \ No newline at end of file diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/restrict/enums/BlacklistOrWhiteListEnum.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/restrict/enums/BlacklistOrWhiteListEnum.java new file mode 100644 index 0000000..8b52a9d --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/restrict/enums/BlacklistOrWhiteListEnum.java @@ -0,0 +1,6 @@ +package com.ssdmn.common.restrict.enums; + +public enum BlacklistOrWhiteListEnum { + BLACK_LIST, + WHITE_LIST +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/restrict/enums/RestrictTypeEnum.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/restrict/enums/RestrictTypeEnum.java new file mode 100644 index 0000000..facd301 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/restrict/enums/RestrictTypeEnum.java @@ -0,0 +1,6 @@ +package com.ssdmn.common.restrict.enums; + +public enum RestrictTypeEnum { + IP, + PHONE +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/restrict/interceptor/RestrictInterceptor.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/restrict/interceptor/RestrictInterceptor.java new file mode 100644 index 0000000..2a00c60 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/restrict/interceptor/RestrictInterceptor.java @@ -0,0 +1,57 @@ +package com.ssdmn.common.restrict.interceptor; + +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.extra.spring.SpringUtil; +import com.ssdmn.common.restrict.interceptor.annotation.Restrict; +import com.ssdmn.common.restrict.strategy.RestrictStrategy; +import com.ssdmn.common.exception.BizException; +import com.ssdmn.framework.handler.interceptor.DefaultHandlerInterceptor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; +import org.springframework.web.method.HandlerMethod; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +/** + * 请求参数拦截器 + * + * @author zhang + * @version 1.0 + * @createTime 2022-10-18 + */ +@Slf4j +@Component +public class RestrictInterceptor extends DefaultHandlerInterceptor { + + @Override + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { + if (handler instanceof HandlerMethod) { + HandlerMethod handlerMethod = (HandlerMethod) handler; + Restrict classAnnotation = handlerMethod.getMethod().getDeclaringClass().getAnnotation(Restrict.class); + Restrict methodAnnotation = handlerMethod.getMethod().getAnnotation(Restrict.class); + if (classAnnotation != null) { + if (ObjectUtil.isNotNull(methodAnnotation) && !methodAnnotation.required()) { + return true; + } + return verifyRestrictStrategy(request, methodAnnotation); + } else { + if (ObjectUtil.isNotNull(methodAnnotation) && methodAnnotation.required()) { + return verifyRestrictStrategy(request, methodAnnotation); + } + } + } + return true; + } + + private boolean verifyRestrictStrategy(HttpServletRequest request, Restrict methodAnnotation) { + for (Class restrictStrategy : methodAnnotation.restrictStrategy()) { + RestrictStrategy restrictInterceptor = SpringUtil.getBean(restrictStrategy); + boolean b = restrictInterceptor.isValid(request); + if (!b) { + throw new BizException(restrictInterceptor.getMessage()); + } + } + return true; + } +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/restrict/interceptor/annotation/Restrict.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/restrict/interceptor/annotation/Restrict.java new file mode 100644 index 0000000..38ff854 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/restrict/interceptor/annotation/Restrict.java @@ -0,0 +1,23 @@ +package com.ssdmn.common.restrict.interceptor.annotation; + +import com.ssdmn.common.restrict.strategy.RestrictStrategy; + +import java.lang.annotation.*; + +@Inherited +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Target({ElementType.METHOD}) +public @interface Restrict { + + /** + * 限制策略 + */ + Class[] restrictStrategy(); + + /** + * 是否需要验证 + */ + boolean required() default true; + +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/restrict/strategy/RestrictStrategy.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/restrict/strategy/RestrictStrategy.java new file mode 100644 index 0000000..2530011 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/restrict/strategy/RestrictStrategy.java @@ -0,0 +1,10 @@ +package com.ssdmn.common.restrict.strategy; + +import com.ssdmn.framework.handler.interfaces.Valid; + +import javax.servlet.http.HttpServletRequest; + +public interface RestrictStrategy extends Valid { + + String getMessage(); +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/restrict/strategy/impl/RepeatSubmitRestrictStrategy.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/restrict/strategy/impl/RepeatSubmitRestrictStrategy.java new file mode 100644 index 0000000..ca39806 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/restrict/strategy/impl/RepeatSubmitRestrictStrategy.java @@ -0,0 +1,97 @@ +package com.ssdmn.common.restrict.strategy.impl; + +import com.alibaba.fastjson.JSONObject; +import com.ssdmn.common.http.HttpHelper; +import com.ssdmn.common.redis.service.RedisService; +import com.ssdmn.common.restrict.strategy.RestrictStrategy; +import com.ssdmn.config.filter.RepeatedlyRequestWrapper; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; +import javax.servlet.http.HttpServletRequest; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +import static com.ssdmn.common.result.SysConstants.SSDMN_TOKEN_KEY; + +@Component +public class RepeatSubmitRestrictStrategy implements RestrictStrategy { + + public final String REPEAT_PARAMS = "repeatParams"; + public final String REPEAT_TIME = "repeatTime"; + + @Resource + private RedisService redisCache; + + @Override + public boolean isValid(HttpServletRequest request) { + String nowParams = ""; + if (request instanceof RepeatedlyRequestWrapper) { + RepeatedlyRequestWrapper repeatedlyRequest = (RepeatedlyRequestWrapper) request; + nowParams = HttpHelper.getBodyString(repeatedlyRequest); + } + + // body参数为空,获取Parameter的数据 + if (StringUtils.isEmpty(nowParams)) { + nowParams = JSONObject.toJSONString(request.getParameterMap()); + } + Map nowDataMap = new HashMap(); + nowDataMap.put(REPEAT_PARAMS, nowParams); + nowDataMap.put(REPEAT_TIME, System.currentTimeMillis()); + + // 请求地址(作为存放cache的key值) + String url = request.getRequestURI(); + + // 唯一值(没有消息头则使用请求地址) + String submitKey = request.getHeader(SSDMN_TOKEN_KEY); + if (StringUtils.isEmpty(submitKey)) { + submitKey = url; + } + + // 唯一标识(指定key + url + 消息头) + String cacheRepeatKey = "repeat_submit:" + submitKey; + + Object sessionObj = redisCache.get(cacheRepeatKey); + if (sessionObj != null) { + Map sessionMap = (Map) sessionObj; + if (sessionMap.containsKey(url)) { + Map preDataMap = (Map) sessionMap.get(url); + if (compareParams(nowDataMap, preDataMap) && compareTime(nowDataMap, preDataMap, 1000)) { + return true; + } + } + } + Map cacheMap = new HashMap(); + cacheMap.put(url, nowDataMap); + redisCache.set(cacheRepeatKey, cacheMap, 1000L, TimeUnit.MILLISECONDS); + return false; + } + + /** + * 判断参数是否相同 + */ + private boolean compareParams(Map nowMap, Map preMap) { + String nowParams = (String) nowMap.get(REPEAT_PARAMS); + String preParams = (String) preMap.get(REPEAT_PARAMS); + return nowParams.equals(preParams); + } + + /** + * 判断两次间隔时间 + */ + private boolean compareTime(Map nowMap, Map preMap, long interval) { + long time1 = (Long) nowMap.get(REPEAT_TIME); + long time2 = (Long) preMap.get(REPEAT_TIME); + if ((time1 - time2) < interval) { + return true; + } + return false; + } + + @Override + public String getMessage() { + return "请勿重复点击"; + } +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/result/ResponseResult.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/result/ResponseResult.java new file mode 100644 index 0000000..6955f59 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/result/ResponseResult.java @@ -0,0 +1,56 @@ +package com.ssdmn.common.result; + +import lombok.Data; + +/** + * 响应数据 + * + * @author 郭世旭 + * @version 1.0 + * @createTime 2021-03-22 09:58:30 + */ +@Data +public class ResponseResult { + /** + * 响应码 + */ + private Integer code; + /** + * 响应消息 + */ + private String msg; + + /** + * 响应数据 + */ + private T data; + + public ResponseResult(Integer code, String msg) { + this.code = code; + this.msg = msg; + } + + public static ResponseResult error(String msg) { + return new ResponseResult<>(SysConstants.ERROR_CODE, msg); + } + + public static ResponseResult error(Integer code, String msg) { + return new ResponseResult<>(code, msg); + } + + public static ResponseResult error(String msg, T data) { + ResponseResult result = new ResponseResult<>(SysConstants.ERROR_CODE, msg); + result.setData(data); + return result; + } + + public static ResponseResult success(T data) { + ResponseResult result = new ResponseResult<>(SysConstants.SUCCESS_CODE, SysConstants.SUCCESS_MESSAGE); + result.setData(data); + return result; + } + + public static ResponseResult success() { + return new ResponseResult<>(SysConstants.SUCCESS_CODE, SysConstants.SUCCESS_MESSAGE); + } +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/result/Result.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/result/Result.java new file mode 100644 index 0000000..706b897 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/result/Result.java @@ -0,0 +1,90 @@ +package com.ssdmn.common.result; + +import lombok.Data; + +@Data +public class Result { + /** + * 请求成功或失败 + */ + private boolean success; + + /** + * 返回状态 + */ + private int code; + + /** + * 返回提示信息 + */ + private String msg; + /** + * 返回列表数据时的总条数 + */ + private Long count; + /** + * 返回数据 + */ + private T data; + /** + * 返回数据2 + */ + private Object attach; + + /** + * 以下为构建方法 无参数 + */ + public Result() { + } + + /** + * 以下为构建方法 状态 status 中文提示消息 message + */ + public Result(int code, String msg, boolean bl) { + this.code = code; + this.msg = msg; + this.success = bl; + } + + /** + * 以下为构建方法 状态 status 中文提示消息 message 数据 data + */ + public Result(int code, String msg, T data, boolean bl) { + this.code = code; + this.msg = msg; + this.data = data; + this.success = bl; + } + + /** + * 以下为构建方法 状态 status 中文提示消息 message 数据 data 返回列表数据时的总条数 + */ + public Result(int code, String msg, T data,Long count, boolean bl) { + this.code = code; + this.msg = msg; + this.data = data; + this.count = count; + this.success = bl; + } + + /** + * 以下为构建方法 状态 status 中文提示消息 message 数据 data 数据2 data + */ + public Result(int code, String msg, T data, Object attach, boolean bl) { + this.code = code; + this.msg = msg; + this.data = data; + this.attach = attach; + this.success = bl; + } + + @Override + public String toString() { + return "Result{" + + "code=" + code + + ", msg='" + msg + '\'' + + ", data=" + data + + ",count=" + count + + '}'; + } +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/result/ResultConstants.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/result/ResultConstants.java new file mode 100644 index 0000000..a3ef208 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/result/ResultConstants.java @@ -0,0 +1,377 @@ +package com.ssdmn.common.result; + + +import lombok.AllArgsConstructor; +import lombok.NoArgsConstructor; + +import java.util.LinkedHashMap; + +/** + * @Description: 响应返回封装类 + * @Author: 2621 + * @Date: 2020/10/28 + */ +@NoArgsConstructor +@AllArgsConstructor +public enum ResultConstants { + + /*-----Basic-----*/ + SUCCESS(200, "ok"), + FAIL(500, "fail"), + SYSTEM_PARAM_FAIL(400, "参数错误"), + SYSTEM_TOKEN_FAIL(401, "会话无效,请重新登录"), + AUTHENTICATION_FAILED(401, "登录失效,请重新登录"), + TOKEN_ILLEGAL(401, "登录信息不合法"), + TOKEN_EXPIRED(401, "登录信息已过期,请重新登录"), + USER_NOT_LOGIN(401, "请重新登录"), + SYSTEM_SIGNATURE_FAIL(402, "签名错误"), + REQUEST_OVER_TIME(408, "当前网络不佳,请稍后再试"), + SYSTEM_DATA_FAIL(500, "系统数据异常"), + SYSTEM_SERVER_BUSY(501, "服务器繁忙"), + SYSTEM_ERROR(502, "系统异常,请联系客服人员"), + CONFIG_QUERY_ERROR(801, "配置表查询数据异常!"), + USER_ID_NOT_NULL(802, "用户ID不能为空"), + DATA_NOT_COMPLETE(31020, "数据缺失!"), + DELETE_ERROR(804, "删除失败!"), + PERMISSIONS_INSUFFICIENT(805,"权限不足!"), + + /*-------------------登录-------------------*/ + JPUSH_LOGIN_ERROR(601, "极光登录失败,请重新登录"), + ALIYUN_LOGIN_ERROR(602, "阿里云登录失败,请重新登录"), + GENDER_TYPE_ERROR(603, "性别类型错误"), + DEVICE_NOT_EXIST(604, "设备不存在"), + DEVICE_REGISTER_TOO_MUCH(605, "一个设备只能注册3个账号"), + LOGIN_BANNED(606, "账号ID:*已被锁定,请联系客服。\n客服QQ:80056867"), + WITHDRAW_BANNED(607, "提现受限,请联系客服"), + LOG_OFF(608, "您账号已经注销"), + DEVICE_BANNED(609, "帐号异常:*已被锁定,请联系客服。\n客服QQ:80056867"), + APPLE_EXPIRED(610, "苹果token过期"), + APPLE_ILLEGAL(611, "苹果token非法"), + APPLE_FAIL(612, "苹果token验证失败"), + INVITATION_CODE_ERROR(613, "邀请码不正确"), + PHONE_NULL_ERROR(614, "手机号不能为空"), + PHONE_STYLE_ERROR(615, "手机号格式错误"), + LOGIN_TYPE_NULL_ERROR(616, "登录类型为空或不存在!"), + PASSWORD_NULL_ERROR(617, "密码不能为空"), + USERNAME_PASSWORD_ERROR(618, "账号或密码错误"), + INSERT_SUCCESS(619, "用户新增成功!"), + PHONE_NOT_EXIST(620, "手机号不存在!"), + PHONE_EXIST(621, "手机号存在!"), + UNION_ID_NO_EQUAL(622, "同一个openId对应的unionId不相等!"), + WAY_NOT_EXIST(623, "渠道不存在!"), + USER_NOT_EXIST(624, "用户不存在!"), + PHONE_USED(625, "该手机号已被其他用户使用!"), + PHONE_SAME(626, "手机号与之前相同,不需要修改!"), + USER_NO_BIND_WX(627, "该用户未绑定微信!"), + USER_IS_BIND_WX(628, "用户已经绑定微信,请先解绑!"), + PHONE_NO_BINDING(629, "未绑定手机号!"), + OPENID_NULL_ERROR(630, "openid不能为空!"), + UNION_ID_NULL_ERROR(631, "unionId不能为空!"), + USER_BIND_OTHER_WX(632, "该手机号已经绑定其他微信,请先解绑!"), + WX_BIND_OTHER_USER(633, "该微信已经绑定其他用户,请先解绑!"), + IOS_ID_NOT_EXIST(634, "ios登录时设备id不能为空!"), + SMS_CODE_NOT_EXIST(636, "验证码不存在或已失效!"), + WX_CODE_ERROR(666, "code错误或已失效!"), + WX_TEMPLATE_CODE_EXPIRED(667, "绑定过期,请重新绑定!"), + USER_ID_NOT_EXIST(668, "用户不存在!"), + NOT_KNOW_ERROR(699, "登录未知错误,请联系管理员!"), + NO_PERMISSIONS(700, "您没有查看权限,请联系管理员添加!"), + + /*---------------------------短信------------------------------*/ + SMS_SEND_FAIL(800, "短信发送失败!"), + + /*--------------------支付-----------------*/ + PAY_TYPE_NOT_EXIST(900, "支付类型不存在"), + ORDER_STATUS_SUCCESS(901, "订单已支付,无需重复支付"), + NO_BIND_WX(902, "用户未绑定微信"), + NO_PAY_WAY(903, "支付方式不存在"), + ORDER_HAS_REFUNDED(904, "该订单已退款成功"), + PAY_INFO_NOT_EXIST(905, "该订单没查询支付信息"), + REFUND_MONEY_TOO_LARGE(906, "总退款金额大于支付金额"), + PAY_TYPE_NULL(907, "支付类型为空"), + ORDER_NO_NOT_EXIST(908, "该订单号不存在"), + ORDER_STATE_CAN_NOT_PAY(909, "订单处于不可支付状态"), + + /*--------------------------商品----------------------------*/ + GOODS_IS_EMPTY(30011, "您查看的商品不存在"), + GOODS_IS_NOT_ON_SALE(30012, "您查看的商品已下架!"), + GOODS_IS_DELETED(30013, "商品已被删除"), + GOODS_STOCK_NOT_ENOUGH(30014, "库存不足"), + GOODS_SERVICE_BUSY(30015, "当前下单人数较多,服务器繁忙,请稍后再试"), + GOODS_DATA_ERROR(31001, "商品数据异常,请联系客服处理"), + GOODS_NOT_COMPLETE(31020, "商品数据不齐全!请联系客服处理"), + HAVE_NO_SKU_ON_SALE(31021, "该商品没有任何sku上架,请至少上架一个sku才能上架商品"), + GOODS_LIST_CONTAIN_NOT_ON_SALE(32011,"存在未上架的商品!"), + + /*------------------------露天坝----------------------------------*/ + OPEN_DAM_DOING(4001, "您有拼桌正在进行,不能在分享拼桌"), + OPEN_DAM_EMPTY(4002, "露天坝数据不存在"), + OPEN_DAM_FULL(4003, "拼桌人数已满,不能在参与"), + VISITING_TYPE_NOT_EXIST(4010,"访问类型不存在!"), + ADD_ARTICLE_FAIL(4011,"数据库繁忙,文章添加失败!"), + UPDATE_ARTICLE_FAIL(4012,"数据库繁忙,文章修改失败!"), + ARTICLE_NOT_EXIST(4013,"文章不存在"), + ARTICLE_INFO_TOO_LONG(4014,"文章描述内容字数过长!"), + + + /*--------------------------收货地址-------------------------------*/ + ORDER_ADDRESS_EMPTY(5001, "收货地址不存在"), + ORDER_ADDRESS_NULL(5002, "您还没有添加地址"), + + /*---------------------------订单--------------------------------*/ + ORDER_NUM_NOT_EXIST(6100, "订单号不存在!"), + ORDER_CANNOT_GET_GOODS_JSON(6101, "未能获取到商品详情信息"), + ORDER_CANNOT_GET_SKU_DATA(6102, "未能获取到商品SKU信息"), + ORDER_CANNOT_GET_USER_JSON(6201, "网络连接异常,请重新登录!"), + ORDER_PARAM_ERROR(6400, "订单参数错误!参数对象NULL"), + ORDER_DATA_ERROR(6401, "订单数据异常!"), + ORDER_RECEIVER_PARAM_ERROR(6402, "收货地址ID参数错误!"), + ORDER_RECEIVER_DATA_ERROR(6403, "收货地址查询数据为空!"), + ORDER_ACTIVITIES_TYPE_NOT_EXIST(6405, "活动类型不存在"), + ORDER_ACTIVITIES_REPETITION(6406, "订单编号重复"), + ORDER_CANT_CANCEL(6407, "订单不能取消"), + ORDER_ORDER_DELIVERED(6408, "订单已发货"), + + /*---------------------------区域--------------------------------*/ + AREA_NOT_EXIST(6001, "区域编号错误"), + AREA_NOT_EXIST_PROVINCE(6002, "省 区域编号不存在"), + AREA_NOT_EXIST_CITY(6003, "市 区域编号不存在"), + AREA_NOT_EXIST_DISTRICT(6004, "区 区域编号不存在"), + + /*---------------------------火锅店套餐--------------------------------*/ + FOODS_NOT_EXIST(7001, "套餐产品不存在"), + FOODS_NOT_GOODS_EXIST(7001, "套餐中没有该商品了"), + FOODS_DETAILS_NOT_EXIST(7002, "套餐详情产品为空"), + + + /*---------------------------商品套餐--------------------------------*/ + GOODS_NOT_EXIST(8001, "商品套餐不存在"), + GOODS_NOT_GOODS_EXIST(8001, "商品套餐中没有该商品了"), + GOODS_DETAILS_NOT_EXIST(8002, "商品套餐详情产品为空"), + + /*-------------门店--------------------*/ + STORE_NULL(9001, "门店不存在"), + STORE_ORDER_NOT_EXIST(9002, "该订单不存在"), + STORE_ORDER_STATE(9003, "订单状态不合理"), + STORE_ORDER_TIME_EX(9004, "订单时间不合理"), + STORE_ORDER_TIME_EATE(9004, "用餐时间不合里"), + STORE_ORDER_REPETITION(9005, "该桌子已经被预订,请选择其他桌子"), + STORE_ORDER_NOT_EDIT(9006, "订单不能修改"), + STORE_IMAGE(9007, "门店图片超过最大数量"), + STORE_EMPTY_TABLE(9008, "门店桌子为空,不能开业"), + STORE_NOT_HEAD_IMAGE(9008, "门店主图为空,不能开业"), + STORE_NOT_IMAGES(9008, "门店轮播图为空,不能开业"), + STORE_NOT_TYPE(9008, "门店类型为空,不能开业"), + NO_NAMING_TABLE(9009, "该名店没有可冠名的桌位"), + STORE_TYPE_NOT_EXIT(9010, "门店类型不存在"), + + /*-------------桌子--------------------*/ + TABLE_NUMBER_REPETITION(10001, "该门店中桌子编号重复"), + TABLE_NOT_EXIST(10002, "当前桌号不存在!"), + TABLE_BING_USER(10003, "桌子已绑定其他用户,请重新选择"), + TABLE_NOT_USERID_TABLEID(1004, "没用用户Id或桌子Id"), + TABLE_NOT_MATCHING_VIRTUAL(1005, "卡卷和桌子类型不匹配,请重新选择"), + TABLE_SERIAL_NOT_EXIST(10006, "没有找到桌子座位配置"), + TABLE_SERIAL_ERROR(10007, "生成座位编号参数不全"), + TABLE_TYPE(10008, "桌子位置为空或类型不正确"), + TABLE_NOT_TYPE(10008, "桌子没有分配类型,不能关联冠名人,请联系管理员分配"), + + /*-------------卡卷--------------------*/ + VIRTUAL_CARD_NO_REPETITION(11001, "卡卷编号重复"), + VIRTUAL_PAST_TIME_BREAK(11002, "卡卷过期时间必须大于等于今天"), + VIRTUAL_VALUE_BREAK(11003, "卡卷价值不能小于等于0"), + VIRTUAL_NOT_EXIST(11004, "卡卷不存在或已使用"), + VIRTUAL_VALID_DAY(11005, "有效天数不能不于0"), + VIRTUAL_TYPE_NOT_EXIST(11006, "卡卷类型不存在或已被删除"), + VIRTUAL_TYPE_NOT_USER(11007, "您没有冠名权卡卷"), + VIRTUAL_NOT_TIME(11008, "卡卷已过期,不能再使用"), + VIRTUAL_NOT_MATCHING_USER(11009, "卡券不属于当前用户"), + + /*-------------活动--------------------*/ + BATCH_IS_NOT_EXIST(12001, "批次号不存在或已失效"), + SKU_IS_NOT_JOIN_LEG_UP_GROUP_MARKET(12002, "该商品未参与拼团活动"), + USER_IS_NOT_ORGANIZER(12003, "您不是团长,无法执行此操作,如有疑问请联系客服人员"), + FREE_RECEIVE_CONDITION_NOT_ENOUGH(12004, "未满足免费领取条件"), + DISCOUNT_BUY_CONDITION_NOT_ENOUGH(12005, "未满足折扣购买条件"), + USER_ALREADY_EXIST(12006, "您已加入该团,请勿重复操作"), + GROUP_FINISHED(12007, "该拼团已结束"), + TABLE_FINISHED(12008, "拼桌已结束"), + USER_DATA_ERROR(12009, "用户数据异常!"), + STORE_DATA_ERROR(12010, "门店数据异常!"), + ORDER_ID_CAN_NOT_FIND_USER(12011, "未查询到该订单号对应的用户"), + NOW_BIG_MORE_RESERVE_TIME(12012, "预约时间必须大于当前时间!"), + TABLE_TOGETHER_DATA_ERROR(12013, "拼桌数据异常!"), + CREATE_GROUP_EXCESS_LIMIT(10504, "该商品超过了最大开团数量限制!"), + SHARER_IS_NOT_ORGANIZER(10505, "只有团长才能分享活动信息"), + ACTIVITIES_STATE_CAN_NOT_QUIT(10506, "当前活动状态不允许取消参与活动"), + GPS_NOT_ON(10508, "请开启设备位置信息(GPS)!"), + ORGANIZER_CAN_NOT_EXIT(10509, "团长不允许退出活动!"), + ACTIVITIES_GOODS_NOT_EXITS(10510, "活动商品不存在!"), + USER_IS_NOT_EXIST(12011, "用户信息不存在"), + /*-----------------系统消息--------------------------*/ + + SYS_INFO_TYPE_NOT_EXIST(12001, "消息类型不存在"), + SYS_INFO_NOT_EXIST(12002, "系统消息不存在"), + + /*-------------------后台管理-----------------------------*/ + + MANAGER_NOT_ADMIN(13001, "你还不是超级管理员"), + ACCOUNT_NOT_EXIST(13002, "用户不存在"), + ROLE_NOT_EXIST(13003, "没有对应的角色"), + ROLE_EXIST(13004, "已经有相同的角色"), + ACCOUNT_EXIST(13005, "用户已存在"), + ACCOUNT_NOT_USED(13007, "您已被禁用"), + ACCOUNT_PWD_ERROR(13008, "密码长度不能小于6位数"), + DATE_ERROR(13009, "开始时间不能大于结束时间"), + UNKNOWN_TYPE(13010, "未知统计类型"), + SUPER_NOT_EXIST(13011, "上级用户不存在"), + + /*------------------员工--------------------*/ + EMPLOYEE_NOT_SHOT_MANAGER(13001, "你不是店长"), + EMPLOYEE_NOT_EXIST(13002, "用户不存在"), + + /*-------------物流--------------------*/ + UP_LOGISTICS_FAIL(14001, "更新物流信息失败"), + TRACES_EXIST(14002, "该订单已发货"), + TRACES_NOT_EXIST(14003, "该订单未发货"), + + /*-------------------文件-------------------*/ + FILE_URL_ERROR(15001, "该路径无法解析成文件"), + UPLOAD_TYPE_ERROR(15002, "上传文件类型有误"), + FILE_NOT_EXIST(15003, "文件不存在"), + FILE_LOAD_ERROR(15004, "文件上传失败"), + QR_CREATE_ERROR(15005, "二维码生成失败"), + + /*------------------核销-------------------------*/ + VERIFICATION_TYPE_NULL(160000, "核销类型不能为空"), + VERIFICATION_TYPE_NOT_EXIST(160001, "核销类型不存在"), + VERIFICATION_NOT_EXIST(160002, "核销码不存在"), + VERIFICATION_USED(160003, "核销码已使用"), + VERIFICATION_STORE_ERROR(160004, "此拼桌核销码不是本店,请前往"), + VERIFICATION_NOT_SUPPORT(160004, "暂时不支持该核销类型"), + + + /*------------------富文本-------------------------*/ + RICH_TEXT_TYPE_REPEAT(170000, "富文本类型重复"), + RICH_TEXT_NOT_EXIST(160000, "修改的富文本不存在"), + + /* ------------------客如云--------------------*/ + PARAMETER_ERROR(180000, "参数错误"), + DISH_NEW_ERROR(180001, "参数错误"), + PAY_INFO_IS_EMPTY(180002,"支付信息不存在!"), + + + /*--------------------合伙人-------------------------*/ + PARTNER_ACCOUNT_NOT_EXIST(180000, "用户账户不存在"), + PARTNER_INTEGRAL_NOT_ENOUGH(180001, "用户积分不足"), + INSUFFICIENT_BALANCE(180002, "账户余额不足"), + FAILED_TO_CHANGE_SUBORDINATE_TO_SUPERIOR(180003, "不能将自己的下级用户改为自己的上级"), + CAN_T_BE_ONE_S_SUPERIOR(180004, "不能成为自己的上级"), + CAN_ACCESSORY(180005, "已经购买过合伙人了,不能再购买了"), + ACCESSORY_ERROR(180006, "合伙人配置数据不正确"), + IDENTITY_EXIST(180007, "身份已存在"), + IDENTITY_NOT_EXIST(180008, "身份不存在"), + + + /*------------------------员工--------------------------*/ + EMPLOYEE_USER_NAME_REPEAT(190000, "用户名重复"), + EMPLOYEE_PHONE_REPEAT(190001, "手机号和其他用户重复"), + EMPLOYEE_JOB_NUMBER_REPEAT(190002, "工号重复"), + EMPLOYEE_PERMISSION_REPEAT(190003, "权限名称重复"), + EMPLOYEE_NULL(190004, "员工不存在"), + EMPLOYEE_POINT(190005, "分佣比例应该在[0,100)"), + EMPLOYEE_JOB_NAME_REPEAT(190006, "已经有相同的职务名称"), + EMPLOYEE_JOB_TYPE_REPEAT(190007, "已经有相同的职务类型"), + /*-------------------------分佣-------------------------*/ + SUPERIOR_USER_NOT_EXIST(200001, "用户未绑定上级"), + COMMISSION_REQUIREMENT_INVALID(200002, "用户没有分佣资格"), + COMMISSION_HAS_FINISHED(200003, "当前分佣已经完成,无需重复分佣"), + COMMISSION_MODEL_NOT_EXIST(200004, "分佣模式不存在"), + ACCESSORY_TYPE_NOT_EXIST(200005, "合伙人类型不存在"), + + /*-------------------------门店支出-------------------------*/ + STORE_EXPENDITURE_TYPE_REPORT(210001, "类型重复"), + STORE_EXPENDITURE_NAME_REPORT(210002, "名称重复"), + STORE_EXPENDITURE_NOT_EXIST(210003, "支出类型不存在,请添加"), + STORE_EXPENDITURE_NULL(210004, "没有收支数据"), + STORE_EXPENDITURE_TYPE_MATCH(210005, "不是人工录入数据,不能删除"), + + /*----------------------------拼付----------------------------*/ + PAY_TOGETHER_FINISHED(210007, "当前拼付已结束"), + TABLE_HAVE_NO_ORDER(21008, "当前桌位没有订单可进行拼付"), + PAY_TOGETHER_ORDER_EXIST(21009, "当前订单的拼付已存在,请不要重复操作!"), + DISH_LIST_IS_EMPTY(21010, "未查询到订单菜品数据!"), + + /*----------------------------露天坝---------------------------------*/ + DYNAMIC_IS_NOT_EXITS(22001, "该动态内容不存在!"), + COMMENT_IS_NOT_EXITS(22002, "该评论不存在!"), + VIDEO_IS_NOT_EXITS(22003,"您查看的视频不存在!"), + CAN_NOT_REMOVE_OTHER_DYNAMIC(22003, "不能删除他人的动态!"), + + /*----------------------------共享合伙人---------------------------------*/ + SHARED_PARTNER_TYPE_DOES_NOT_EXIST(23001, "共享合伙人类型不存在!"), + + /*----------------------------用户积分----------------------------------------*/ + + PARTNER_INTEGRAL_CONFIG_RATIO(24002, "积分换算比例不能小于0"), + PARTNER_INTEGRAL_CONFIG_NAME(24000, "已经有相同积分名称"), + PARTNER_INTEGRAL_CONFIG_NOT_EXIST(24001, "没有对应的积分配置"), + + PARTNER_INTEGRAL_NOT_EXIST(24003, "积分类型不存在"), + PARTNER_INTEGRAL_EXIST(24004, "用户积分类型已存在"), + PARTNER_INTEGRAL_INVALID(24005, "您当前类型的积分无效"), + PARTNER_INTEGRAL_RULE_NAME(24006, "积分规则名不能为空"), + PARTNER_INTEGRAL_RULE_NAME_EXIST(24007, "积分规则名已存在"), + PARTNER_INTEGRAL_RULE_VALUE(24008, "积分规则对应的使用值不合理"), + PARTNER_INTEGRAL_RULE_NOT_EXIST(24009, "积分规则不存在"), + PARTNER_INTEGRAL_SCOPE_CONFIG_NAME_EXIST(24010, "用户积分范围配置-名称重复"), + PARTNER_INTEGRAL_SCOPE_CONFIG_NAME(24011, "用户积分范围配置-名称不能为空"), + + PARTNER_INTEGRAL_SCOPE_CONFIG_RELATION_NOT_EXIST(24012, "当前积分不能在此商城中使用"), + + /*-----------------------------双品牌门店---------------------------------------------------*/ + STORE_DOUBLE_APPLICANT_NOT_EXIST(25000, "申请用户Id不能为空"), + STORE_DOUBLE_RESPONSIBLE_NOT_EXIST(25001, "责任人或责任人电话不能为空"), + STORE_DOUBLE_ADDRESS_NOT_EXIST(25002, "地址不能为空"), + STORE_DOUBLE_LON_OR_LAT_NOT_EXIST(25003, "经纬度不能为空"), + STORE_DOUBLE_NAME(25004, "门店名称必填"), + STORE_DOUBLE_AREA(25005, "面积不能空且小于0"), + STORE_DOUBLE_BUSINESS(25006, "营业执照正面必传"), + STORE_DOUBLE_HEAD_IMAGE(25007, "主图必传"), + STORE_DOUBLE_IMAGES(25008, "详情图必传"), + STORE_DOUBLE_IMAGE_MAX(25009, "图片超过最大数量"), + STORE_DOUBLE_NAME_DUPLICATE(25010, "已有相同申请"), + STORE_DOUBLE_PHONE_DUPLICATE(25011, "该手机号已申请过了"), + + /*------------------------------签到--------------------------------------------------*/ + USER_SIGNED(26000,"今日已签到"), + + + + + + ; + + + public Integer status; + + public String message; + + ResultConstants(int status, String message) { + this.status = status; + this.message = message; + } + + /** + * 获取所有回复码 + * + * @return + */ + public static LinkedHashMap getArrayMessage() { + LinkedHashMap responseMessages = new LinkedHashMap<>(); + for (ResultConstants statusEnum : ResultConstants.values()) { + responseMessages.put(statusEnum.status, statusEnum.message); + } + return responseMessages; + } +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/result/Results.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/result/Results.java new file mode 100644 index 0000000..8530a29 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/result/Results.java @@ -0,0 +1,159 @@ +package com.ssdmn.common.result; + + +/** + * 返回值构建工厂类 + * @author 郭世旭 + */ +public class Results { + + private Results() { + + } + + /** + * 创建Result 参数:返回状态 status 提示信息 message 返回数据 data + * + * @return 返回值对象 result + */ + public static Result build(int status, String message, T data, boolean bl) { + return new Result<>(status, message, data, bl); + } + + /** + * 创建Result 参数:返回状态 status 提示信息 message 返回数据 data 返回数据2 attach + * + * @return 返回值对象 + */ + public static Result build(int status, String message, T data, Object attach, boolean bl) { + return new Result<>(status, message, data, attach, bl); + } + + /** + * 创建Result 参数:枚举 resultConstants 返回数据 data + * + * @return 返回值对象 + */ + public static Result build(ResultConstants resultConstants, T data, boolean bl) { + return build(resultConstants.status, resultConstants.message, data, bl); + } + + /** + * 创建Result 参数:枚举 resultConstants 返回数据 data 返回数据2 attach + * + * @return 返回值对象 + */ + public static Result build(ResultConstants resultConstants, T data, T attach, boolean bl) { + return build(resultConstants.status, resultConstants.message, data, attach, bl); + } + + + /** + * 返回默认成功 不带任何数据 + * + * @return 返回值对象 + */ + public static Result ok() { + return build(ResultConstants.SUCCESS.status, ResultConstants.SUCCESS.message, null, true); + } + + /** + * 返回默认成功 带返回数据 + * + * @return 返回值对象 + */ + public static Result ok(T data) { + return build(ResultConstants.SUCCESS.status, ResultConstants.SUCCESS.message, data, true); + } + + + /** + * 返回默认成功 带返回数据两个 + * + * @return 返回值对象 + */ + public static Result ok(T data, Object attach) { + return build(ResultConstants.SUCCESS.status, ResultConstants.SUCCESS.message, data, attach, true); + } + + /** + * 返回默认成功 带返回数据和成功提示消息 + * + * @return 返回值对象 + */ + public static Result ok(String message, T data) { + return build(ResultConstants.SUCCESS.status, message, data, true); + } + + /** + * 返回默认成功 带返回数据和成功提示枚举 + * + * @return 返回值对象 + */ + public static Result ok(ResultConstants resultConstants, T data) { + return build(resultConstants.status, resultConstants.message, data, true); + } + + /** + * 返回默认失败,不带数据 + * + * @return 返回值对象 + */ + public static Result fail() { + return build(ResultConstants.FAIL.status, ResultConstants.FAIL.message, null, false); + } + + /** + * 返回,根据枚举来返回,不带数据 + * + * @return 返回值对象 + */ + public static Result fail(ResultConstants resultConstants) { + return build(resultConstants.status, resultConstants.message, null, false); + } + + /** + * 返回,根据枚举来返回,不带数据 + * + * @return 返回值对象 + */ + public static Result fail(ResultConstants resultConstants, T obj) { + return build(resultConstants.status, resultConstants.message, obj, false); + } + + /** + * 返回失败,自定义错误消息 + * + * @return 返回值对象 + */ + public static Result fail(String message) { + return build(ResultConstants.FAIL.status, message, null, false); + } + + /** + * 返回,失败code和自定义错误消息 + * + * @return 返回值对象 + */ + public static Result fail(Integer status, String message) { + return build(status, message, null, false); + } + + /** + * 返回,失败code和自定义错误消息 + * + * @return 返回值对象 + */ + public static Result fail(Integer status, String message, T data) { + return build(status, message, data, false); + } + + /** + * 返回失败,自定义错误消息和返回数据 + * + * @return 返回值对象 + */ + public static Result fail(String message, T data) { + return build(ResultConstants.FAIL.status, message, data, false); + } +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/result/SysConstants.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/result/SysConstants.java new file mode 100644 index 0000000..0c93418 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/result/SysConstants.java @@ -0,0 +1,100 @@ +package com.ssdmn.common.result; + +import java.math.BigDecimal; + +/** + * 常量 + * + * @author 郭世旭 + * @version 1.0 + * @createTime 2021-03-22 09:58:30 + */ +public interface SysConstants { + /** + * 成功 200 + */ + int SUCCESS_CODE = 200; + /** + * 成功 0 + */ + String SUCCESS_MESSAGE = "success"; + /** + * 请求成功消息 + */ + String RESPONSE_SUCCESS_MESSAGE = "true"; + /** + * 失败 -1 + */ + int ERROR_CODE = -1; + /** + * 失败 -1 + */ + String ERROR_MESSAGE = "error"; + /** + * 登录相关错误代码 + */ + int LOGIN_CODE = 401; + /** + * 请求头token键 + */ + String SSDMN_TOKEN_KEY = "ssdmn-token"; + /** + * 有效 + */ + @Deprecated + Integer ONE = 1; + /** + * 无效 + */ + @Deprecated + Integer ZERO = 0; + /** + * 有效 + */ + Integer VALID = 1; + /** + * 无效 + */ + Integer INVALID = 0; + /** + * 已删除 + */ + Integer DELETED = 1; + /** + * 未删除 + */ + Integer NOT_DELETED =0; + /** + * 上架 + */ + Integer ON_SALE = 1; + /** + * 未上架 + */ + Integer NOT_ON_SALE =0; + + /** + * session有效时间 + */ + Integer SESSION_KEEP_TIME_SECONDS = 10; + + /** + * 一百 + */ + BigDecimal ONE_HUNDRED = new BigDecimal("100"); + + /** + * 员工默认密码 + */ + String DEFAULT_PWD = "123456"; + + /** + * 定义临时文件存放的位置 + */ + String FILE_PATH_UPLOAD = "/opt/hot-pot/houduan/temp/temporary-hotPot-fileUpload/"; + + /** + * 门店图片 + */ + int STORE_IMAGE_NUMBER = 5; +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/sql/MyMetaObjectHandler.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/sql/MyMetaObjectHandler.java new file mode 100644 index 0000000..7c7e17d --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/sql/MyMetaObjectHandler.java @@ -0,0 +1,44 @@ +package com.ssdmn.common.sql; + +import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler; +import lombok.extern.slf4j.Slf4j; +import org.apache.ibatis.reflection.MetaObject; +import org.slf4j.MDC; +import org.springframework.context.annotation.Configuration; +import org.springframework.stereotype.Component; + +import java.util.Date; + + +/** + * @description: 自动注入时间拦截器 + * @author: 郭世旭 + * @create: 2021-04-17 18:51 + **/ +@Component +@Configuration +@Slf4j +public class MyMetaObjectHandler implements MetaObjectHandler { + private static final String TRACE_ID = "traceId"; + + //使用mp实现添加操作,这个方法会执行,metaObject元数据(表中的名字,表中的字段) + @Override + public void insertFill(MetaObject metaObject) { + //根据名称设置属性值 + try { + String traceId = String.valueOf(MDC.get(TRACE_ID)); + this.setFieldValByName("createTime", new Date(), metaObject); + this.setFieldValByName("updateTime", new Date(), metaObject); + this.setFieldValByName("traceId", traceId, metaObject); + } catch (Exception e) { + log.warn("时间自动注入异常:e->{}", e.getMessage()); + } + } + + //使用mp实现修改操作,这个方法会执行 + @Override + public void updateFill(MetaObject metaObject) { + this.setFieldValByName("updateTime", new Date(), metaObject); + } + +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/sql/SqlInterceptor.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/sql/SqlInterceptor.java new file mode 100644 index 0000000..a675c02 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/sql/SqlInterceptor.java @@ -0,0 +1,96 @@ +package com.ssdmn.common.sql; + +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.mybatisplus.extension.handlers.AbstractSqlParserHandler; +import org.apache.commons.lang3.ArrayUtils; +import org.apache.ibatis.executor.Executor; +import org.apache.ibatis.mapping.MappedStatement; +import org.apache.ibatis.mapping.SqlCommandType; +import org.apache.ibatis.plugin.*; + +import java.lang.reflect.Field; +import java.time.LocalDateTime; +import java.util.Map; +import java.util.Objects; +import java.util.Properties; + +/** + * @author 郭世旭 + */ +@Intercepts({@Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class})}) +public class SqlInterceptor extends AbstractSqlParserHandler implements Interceptor { + /** + * 创建时间 + */ + private static final String CREATE_TIME = "createTime"; + /** + * 更新时间 + */ + private static final String UPDATE_TIME = "updateTime"; + + @Override + public Object intercept(Invocation invocation) throws Throwable { + LocalDateTime now = LocalDateTime.now(); + MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0]; + // SQL操作命令 + SqlCommandType sqlCommandType = mappedStatement.getSqlCommandType(); + // 获取新增或修改的对象参数 + Object parameter = invocation.getArgs()[1]; + if(ObjectUtil.isEmpty(parameter)){ + return invocation.proceed(); + } + // 获取对象中所有的私有成员变量(对应表字段) + Field[] declaredFields = parameter.getClass().getDeclaredFields(); + if (parameter.getClass().getSuperclass() != null) { + Field[] superField = parameter.getClass().getSuperclass().getDeclaredFields(); + declaredFields = ArrayUtils.addAll(declaredFields, superField); + } + // mybatis plus判断 + boolean plus= parameter.getClass().getDeclaredFields().length == 1 && parameter.getClass().getDeclaredFields()[0].getName().equals("serialVersionUID"); + //兼容mybatis plus + if (plus) { + Map updateParam = (Map) parameter; + Class updateParamType = updateParam.get("param1").getClass(); + declaredFields = updateParamType.getDeclaredFields(); + if (updateParamType.getSuperclass() != null) { + Field[] superField = updateParamType.getSuperclass().getDeclaredFields(); + declaredFields = ArrayUtils.addAll(declaredFields, superField); + } + } + String fieldName; + for (Field field : declaredFields) { + fieldName = field.getName(); + if (Objects.equals(CREATE_TIME, fieldName)) { + if (SqlCommandType.INSERT.equals(sqlCommandType)) { + field.setAccessible(true); + field.set(parameter, now); + } + } + if (Objects.equals(UPDATE_TIME, fieldName)) { + if (SqlCommandType.INSERT.equals(sqlCommandType) || SqlCommandType.UPDATE.equals(sqlCommandType)) { + field.setAccessible(true); + //兼容mybatis plus的update + if (plus) { + Map updateParam = (Map) parameter; + field.set(updateParam.get("param1"), now); + } else { + field.set(parameter, now); + } + } + } + } + return invocation.proceed(); + } + + @Override + public Object plugin(Object target) { + if (target instanceof Executor) { + return Plugin.wrap(target, this); + } + return target; + } + + @Override + public void setProperties(Properties properties) { + } +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/str/Convert.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/str/Convert.java new file mode 100644 index 0000000..f13a309 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/str/Convert.java @@ -0,0 +1,1008 @@ +package com.ssdmn.common.str; + +import org.apache.commons.lang3.ArrayUtils; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.nio.ByteBuffer; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.text.NumberFormat; +import java.util.Set; + +/** + * 类型转换器 + * + * @author 郭世旭 + */ +public class Convert +{ + /** + * 转换为字符串
+ * 如果给定的值为null,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static String toStr(Object value, String defaultValue) + { + if (null == value) + { + return defaultValue; + } + if (value instanceof String) + { + return (String) value; + } + return value.toString(); + } + + /** + * 转换为字符串
+ * 如果给定的值为null,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static String toStr(Object value) + { + return toStr(value, null); + } + + /** + * 转换为字符
+ * 如果给定的值为null,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Character toChar(Object value, Character defaultValue) + { + if (null == value) + { + return defaultValue; + } + if (value instanceof Character) + { + return (Character) value; + } + + final String valueStr = toStr(value, null); + return StringUtils.isEmpty(valueStr) ? defaultValue : valueStr.charAt(0); + } + + /** + * 转换为字符
+ * 如果给定的值为null,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Character toChar(Object value) + { + return toChar(value, null); + } + + /** + * 转换为byte
+ * 如果给定的值为null,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Byte toByte(Object value, Byte defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof Byte) + { + return (Byte) value; + } + if (value instanceof Number) + { + return ((Number) value).byteValue(); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + return Byte.parseByte(valueStr); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为byte
+ * 如果给定的值为null,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Byte toByte(Object value) + { + return toByte(value, null); + } + + /** + * 转换为Short
+ * 如果给定的值为null,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Short toShort(Object value, Short defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof Short) + { + return (Short) value; + } + if (value instanceof Number) + { + return ((Number) value).shortValue(); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + return Short.parseShort(valueStr.trim()); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为Short
+ * 如果给定的值为null,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Short toShort(Object value) + { + return toShort(value, null); + } + + /** + * 转换为Number
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Number toNumber(Object value, Number defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof Number) + { + return (Number) value; + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + return NumberFormat.getInstance().parse(valueStr); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为Number
+ * 如果给定的值为空,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Number toNumber(Object value) + { + return toNumber(value, null); + } + + /** + * 转换为int
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Integer toInt(Object value, Integer defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof Integer) + { + return (Integer) value; + } + if (value instanceof Number) + { + return ((Number) value).intValue(); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + return Integer.parseInt(valueStr.trim()); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为int
+ * 如果给定的值为null,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Integer toInt(Object value) + { + return toInt(value, null); + } + + /** + * 转换为Integer数组
+ * + * @param str 被转换的值 + * @return 结果 + */ + public static Integer[] toIntArray(String str) + { + return toIntArray(",", str); + } + + /** + * 转换为Long数组
+ * + * @param str 被转换的值 + * @return 结果 + */ + public static Long[] toLongArray(String str) + { + return toLongArray(",", str); + } + + /** + * 转换为Integer数组
+ * + * @param split 分隔符 + * @param split 被转换的值 + * @return 结果 + */ + public static Integer[] toIntArray(String split, String str) + { + if (StringUtils.isEmpty(str)) + { + return new Integer[] {}; + } + String[] arr = str.split(split); + final Integer[] ints = new Integer[arr.length]; + for (int i = 0; i < arr.length; i++) + { + final Integer v = toInt(arr[i], 0); + ints[i] = v; + } + return ints; + } + + /** + * 转换为Long数组
+ * + * @param split 分隔符 + * @param str 被转换的值 + * @return 结果 + */ + public static Long[] toLongArray(String split, String str) + { + if (StringUtils.isEmpty(str)) + { + return new Long[] {}; + } + String[] arr = str.split(split); + final Long[] longs = new Long[arr.length]; + for (int i = 0; i < arr.length; i++) + { + final Long v = toLong(arr[i], null); + longs[i] = v; + } + return longs; + } + + /** + * 转换为String数组
+ * + * @param str 被转换的值 + * @return 结果 + */ + public static String[] toStrArray(String str) + { + return toStrArray(",", str); + } + + /** + * 转换为String数组
+ * + * @param split 分隔符 + * @param split 被转换的值 + * @return 结果 + */ + public static String[] toStrArray(String split, String str) + { + return str.split(split); + } + + /** + * 转换为long
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Long toLong(Object value, Long defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof Long) + { + return (Long) value; + } + if (value instanceof Number) + { + return ((Number) value).longValue(); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + // 支持科学计数法 + return new BigDecimal(valueStr.trim()).longValue(); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为long
+ * 如果给定的值为null,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Long toLong(Object value) + { + return toLong(value, null); + } + + /** + * 转换为double
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Double toDouble(Object value, Double defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof Double) + { + return (Double) value; + } + if (value instanceof Number) + { + return ((Number) value).doubleValue(); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + // 支持科学计数法 + return new BigDecimal(valueStr.trim()).doubleValue(); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为double
+ * 如果给定的值为空,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Double toDouble(Object value) + { + return toDouble(value, null); + } + + /** + * 转换为Float
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Float toFloat(Object value, Float defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof Float) + { + return (Float) value; + } + if (value instanceof Number) + { + return ((Number) value).floatValue(); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + return Float.parseFloat(valueStr.trim()); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为Float
+ * 如果给定的值为空,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Float toFloat(Object value) + { + return toFloat(value, null); + } + + /** + * 转换为boolean
+ * String支持的值为:true、false、yes、ok、no,1,0 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Boolean toBool(Object value, Boolean defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof Boolean) + { + return (Boolean) value; + } + String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + valueStr = valueStr.trim().toLowerCase(); + switch (valueStr) + { + case "true": + return true; + case "false": + return false; + case "yes": + return true; + case "ok": + return true; + case "fail": + return false; + case "no": + return false; + case "1": + return true; + case "0": + return false; + default: + return defaultValue; + } + } + + /** + * 转换为boolean
+ * 如果给定的值为空,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Boolean toBool(Object value) + { + return toBool(value, null); + } + + /** + * 转换为Enum对象
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * + * @param clazz Enum的Class + * @param value 值 + * @param defaultValue 默认值 + * @return Enum + */ + public static > E toEnum(Class clazz, Object value, E defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (clazz.isAssignableFrom(value.getClass())) + { + @SuppressWarnings("unchecked") + E myE = (E) value; + return myE; + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + return Enum.valueOf(clazz, valueStr); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为Enum对象
+ * 如果给定的值为空,或者转换失败,返回默认值null
+ * + * @param clazz Enum的Class + * @param value 值 + * @return Enum + */ + public static > E toEnum(Class clazz, Object value) + { + return toEnum(clazz, value, null); + } + + /** + * 转换为BigInteger
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static BigInteger toBigInteger(Object value, BigInteger defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof BigInteger) + { + return (BigInteger) value; + } + if (value instanceof Long) + { + return BigInteger.valueOf((Long) value); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + return new BigInteger(valueStr); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为BigInteger
+ * 如果给定的值为空,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static BigInteger toBigInteger(Object value) + { + return toBigInteger(value, null); + } + + /** + * 转换为BigDecimal
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static BigDecimal toBigDecimal(Object value, BigDecimal defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof BigDecimal) + { + return (BigDecimal) value; + } + if (value instanceof Long) + { + return new BigDecimal((Long) value); + } + if (value instanceof Double) + { + return new BigDecimal((Double) value); + } + if (value instanceof Integer) + { + return new BigDecimal((Integer) value); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + return new BigDecimal(valueStr); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为BigDecimal
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static BigDecimal toBigDecimal(Object value) + { + return toBigDecimal(value, null); + } + + /** + * 将对象转为字符串
+ * 1、Byte数组和ByteBuffer会被转换为对应字符串的数组 2、对象数组会调用Arrays.toString方法 + * + * @param obj 对象 + * @return 字符串 + */ + public static String utf8Str(Object obj) + { + return str(obj, StandardCharsets.UTF_8); + } + + /** + * 将对象转为字符串
+ * 1、Byte数组和ByteBuffer会被转换为对应字符串的数组 2、对象数组会调用Arrays.toString方法 + * + * @param obj 对象 + * @param charsetName 字符集 + * @return 字符串 + */ + public static String str(Object obj, String charsetName) + { + return str(obj, Charset.forName(charsetName)); + } + + /** + * 将对象转为字符串
+ * 1、Byte数组和ByteBuffer会被转换为对应字符串的数组 2、对象数组会调用Arrays.toString方法 + * + * @param obj 对象 + * @param charset 字符集 + * @return 字符串 + */ + public static String str(Object obj, Charset charset) + { + if (null == obj) + { + return null; + } + + if (obj instanceof String) + { + return (String) obj; + } + else if (obj instanceof byte[]) + { + return str((byte[]) obj, charset); + } + else if (obj instanceof Byte[]) + { + byte[] bytes = ArrayUtils.toPrimitive((Byte[]) obj); + return str(bytes, charset); + } + else if (obj instanceof ByteBuffer) + { + return str((ByteBuffer) obj, charset); + } + return obj.toString(); + } + + /** + * 将byte数组转为字符串 + * + * @param bytes byte数组 + * @param charset 字符集 + * @return 字符串 + */ + public static String str(byte[] bytes, String charset) + { + return str(bytes, StringUtils.isEmpty(charset) ? Charset.defaultCharset() : Charset.forName(charset)); + } + + /** + * 解码字节码 + * + * @param data 字符串 + * @param charset 字符集,如果此字段为空,则解码的结果取决于平台 + * @return 解码后的字符串 + */ + public static String str(byte[] data, Charset charset) + { + if (data == null) + { + return null; + } + + if (null == charset) + { + return new String(data); + } + return new String(data, charset); + } + + /** + * 将编码的byteBuffer数据转换为字符串 + * + * @param data 数据 + * @param charset 字符集,如果为空使用当前系统字符集 + * @return 字符串 + */ + public static String str(ByteBuffer data, String charset) + { + if (data == null) + { + return null; + } + + return str(data, Charset.forName(charset)); + } + + /** + * 将编码的byteBuffer数据转换为字符串 + * + * @param data 数据 + * @param charset 字符集,如果为空使用当前系统字符集 + * @return 字符串 + */ + public static String str(ByteBuffer data, Charset charset) + { + if (null == charset) + { + charset = Charset.defaultCharset(); + } + return charset.decode(data).toString(); + } + + // ----------------------------------------------------------------------- 全角半角转换 + /** + * 半角转全角 + * + * @param input String. + * @return 全角字符串. + */ + public static String toSBC(String input) + { + return toSBC(input, null); + } + + /** + * 半角转全角 + * + * @param input String + * @param notConvertSet 不替换的字符集合 + * @return 全角字符串. + */ + public static String toSBC(String input, Set notConvertSet) + { + char c[] = input.toCharArray(); + for (int i = 0; i < c.length; i++) + { + if (null != notConvertSet && notConvertSet.contains(c[i])) + { + // 跳过不替换的字符 + continue; + } + + if (c[i] == ' ') + { + c[i] = '\u3000'; + } + else if (c[i] < '\177') + { + c[i] = (char) (c[i] + 65248); + + } + } + return new String(c); + } + + /** + * 全角转半角 + * + * @param input String. + * @return 半角字符串 + */ + public static String toDBC(String input) + { + return toDBC(input, null); + } + + /** + * 替换全角为半角 + * + * @param text 文本 + * @param notConvertSet 不替换的字符集合 + * @return 替换后的字符 + */ + public static String toDBC(String text, Set notConvertSet) + { + char c[] = text.toCharArray(); + for (int i = 0; i < c.length; i++) + { + if (null != notConvertSet && notConvertSet.contains(c[i])) + { + // 跳过不替换的字符 + continue; + } + + if (c[i] == '\u3000') + { + c[i] = ' '; + } + else if (c[i] > '\uFF00' && c[i] < '\uFF5F') + { + c[i] = (char) (c[i] - 65248); + } + } + String returnString = new String(c); + + return returnString; + } + + /** + * 数字金额大写转换 先写个完整的然后将如零拾替换成零 + * + * @param n 数字 + * @return 中文大写数字 + */ + public static String digitUppercase(double n) + { + String[] fraction = { "角", "分" }; + String[] digit = { "零", "壹", "贰", "叁", "肆", "伍", "陆", "柒", "捌", "玖" }; + String[][] unit = { { "元", "万", "亿" }, { "", "拾", "佰", "仟" } }; + + String head = n < 0 ? "负" : ""; + n = Math.abs(n); + + String s = ""; + for (int i = 0; i < fraction.length; i++) + { + s += (digit[(int) (Math.floor(n * 10 * Math.pow(10, i)) % 10)] + fraction[i]).replaceAll("(零.)+", ""); + } + if (s.length() < 1) + { + s = "整"; + } + int integerPart = (int) Math.floor(n); + + for (int i = 0; i < unit[0].length && integerPart > 0; i++) + { + String p = ""; + for (int j = 0; j < unit[1].length && n > 0; j++) + { + p = digit[integerPart % 10] + unit[1][j] + p; + integerPart = integerPart / 10; + } + s = p.replaceAll("(零.)*零$", "").replaceAll("^$", "零") + unit[0][i] + s; + } + return head + s.replaceAll("(零.)*零元", "元").replaceFirst("(零.)+", "").replaceAll("(零.)+", "零").replaceAll("^整$", "零元整"); + } +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/str/StrFormatter.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/str/StrFormatter.java new file mode 100644 index 0000000..1cb1984 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/str/StrFormatter.java @@ -0,0 +1,90 @@ +package com.ssdmn.common.str; + +/** + * 字符串格式化 + * + * @author 郭世旭 + */ +public class StrFormatter +{ + public static final String EMPTY_JSON = "{}"; + public static final char C_BACKSLASH = '\\'; + public static final char C_DELIM_START = '{'; + public static final char C_DELIM_END = '}'; + + /** + * 格式化字符串
+ * 此方法只是简单将占位符 {} 按照顺序替换为参数
+ * 如果想输出 {} 使用 \\转义 { 即可,如果想输出 {} 之前的 \ 使用双转义符 \\\\ 即可
+ * 例:
+ * 通常使用:format("this is {} for {}", "a", "b") -> this is a for b
+ * 转义{}: format("this is \\{} for {}", "a", "b") -> this is \{} for a
+ * 转义\: format("this is \\\\{} for {}", "a", "b") -> this is \a for b
+ * + * @param strPattern 字符串模板 + * @param argArray 参数列表 + * @return 结果 + */ + public static String format(final String strPattern, final Object... argArray) + { + if (StringUtils.isEmpty(strPattern) || StringUtils.isEmpty(argArray)) + { + return strPattern; + } + final int strPatternLength = strPattern.length(); + + // 初始化定义好的长度以获得更好的性能 + StringBuilder sbuf = new StringBuilder(strPatternLength + 50); + + int handledPosition = 0; + int delimIndex;// 占位符所在位置 + for (int argIndex = 0; argIndex < argArray.length; argIndex++) + { + delimIndex = strPattern.indexOf(EMPTY_JSON, handledPosition); + if (delimIndex == -1) + { + if (handledPosition == 0) + { + return strPattern; + } + else + { // 字符串模板剩余部分不再包含占位符,加入剩余部分后返回结果 + sbuf.append(strPattern, handledPosition, strPatternLength); + return sbuf.toString(); + } + } + else + { + if (delimIndex > 0 && strPattern.charAt(delimIndex - 1) == C_BACKSLASH) + { + if (delimIndex > 1 && strPattern.charAt(delimIndex - 2) == C_BACKSLASH) + { + // 转义符之前还有一个转义符,占位符依旧有效 + sbuf.append(strPattern, handledPosition, delimIndex - 1); + sbuf.append(Convert.utf8Str(argArray[argIndex])); + handledPosition = delimIndex + 2; + } + else + { + // 占位符被转义 + argIndex--; + sbuf.append(strPattern, handledPosition, delimIndex - 1); + sbuf.append(C_DELIM_START); + handledPosition = delimIndex + 1; + } + } + else + { + // 正常占位符 + sbuf.append(strPattern, handledPosition, delimIndex); + sbuf.append(Convert.utf8Str(argArray[argIndex])); + handledPosition = delimIndex + 2; + } + } + } + // 加入最后一个占位符后所有的字符 + sbuf.append(strPattern, handledPosition, strPattern.length()); + + return sbuf.toString(); + } +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/str/StringUtils.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/str/StringUtils.java new file mode 100644 index 0000000..078174a --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/str/StringUtils.java @@ -0,0 +1,577 @@ +package com.ssdmn.common.str; + +import org.springframework.util.AntPathMatcher; + +import java.util.*; + +/** + * 字符串工具类 + * + * @author 郭世旭 + */ +public class StringUtils extends org.apache.commons.lang3.StringUtils +{ + /** 空字符串 */ + private static final String NULLSTR = ""; + + /** 下划线 */ + private static final char SEPARATOR = '_'; + + /** + * 获取参数不为空值 + * + * @param value defaultValue 要判断的value + * @return value 返回值 + */ + public static T nvl(T value, T defaultValue) + { + return value != null ? value : defaultValue; + } + + /** + * * 判断一个Collection是否为空, 包含List,Set,Queue + * + * @param coll 要判断的Collection + * @return true:为空 false:非空 + */ + public static boolean isEmpty(Collection coll) + { + return isNull(coll) || coll.isEmpty(); + } + + /** + * * 判断一个Collection是否非空,包含List,Set,Queue + * + * @param coll 要判断的Collection + * @return true:非空 false:空 + */ + public static boolean isNotEmpty(Collection coll) + { + return !isEmpty(coll); + } + + /** + * * 判断一个对象数组是否为空 + * + * @param objects 要判断的对象数组 + ** @return true:为空 false:非空 + */ + public static boolean isEmpty(Object[] objects) + { + return isNull(objects) || (objects.length == 0); + } + + /** + * * 判断一个对象数组是否非空 + * + * @param objects 要判断的对象数组 + * @return true:非空 false:空 + */ + public static boolean isNotEmpty(Object[] objects) + { + return !isEmpty(objects); + } + + /** + * * 判断一个Map是否为空 + * + * @param map 要判断的Map + * @return true:为空 false:非空 + */ + public static boolean isEmpty(Map map) + { + return isNull(map) || map.isEmpty(); + } + + /** + * * 判断一个Map是否为空 + * + * @param map 要判断的Map + * @return true:非空 false:空 + */ + public static boolean isNotEmpty(Map map) + { + return !isEmpty(map); + } + + /** + * * 判断一个字符串是否为空串 + * + * @param str String + * @return true:为空 false:非空 + */ + public static boolean isEmpty(String str) + { + return isNull(str) || NULLSTR.equals(str.trim()); + } + + /** + * * 判断一个字符串是否为非空串 + * + * @param str String + * @return true:非空串 false:空串 + */ + public static boolean isNotEmpty(String str) + { + return !isEmpty(str); + } + + /** + * * 判断一个对象是否为空 + * + * @param object Object + * @return true:为空 false:非空 + */ + public static boolean isNull(Object object) + { + return object == null; + } + + /** + * * 判断一个对象是否非空 + * + * @param object Object + * @return true:非空 false:空 + */ + public static boolean isNotNull(Object object) + { + return !isNull(object); + } + + /** + * * 判断一个对象是否是数组类型(Java基本型别的数组) + * + * @param object 对象 + * @return true:是数组 false:不是数组 + */ + public static boolean isArray(Object object) + { + return isNotNull(object) && object.getClass().isArray(); + } + + /** + * 去空格 + */ + public static String trim(String str) + { + return (str == null ? "" : str.trim()); + } + + /** + * 截取字符串 + * + * @param str 字符串 + * @param start 开始 + * @return 结果 + */ + public static String substring(final String str, int start) + { + if (str == null) + { + return NULLSTR; + } + + if (start < 0) + { + start = str.length() + start; + } + + if (start < 0) + { + start = 0; + } + if (start > str.length()) + { + return NULLSTR; + } + + return str.substring(start); + } + + /** + * 截取字符串 + * + * @param str 字符串 + * @param start 开始 + * @param end 结束 + * @return 结果 + */ + public static String substring(final String str, int start, int end) + { + if (str == null) + { + return NULLSTR; + } + + if (end < 0) + { + end = str.length() + end; + } + if (start < 0) + { + start = str.length() + start; + } + + if (end > str.length()) + { + end = str.length(); + } + + if (start > end) + { + return NULLSTR; + } + + if (start < 0) + { + start = 0; + } + if (end < 0) + { + end = 0; + } + + return str.substring(start, end); + } + + /** + * 格式化文本, {} 表示占位符
+ * 此方法只是简单将占位符 {} 按照顺序替换为参数
+ * 如果想输出 {} 使用 \\转义 { 即可,如果想输出 {} 之前的 \ 使用双转义符 \\\\ 即可
+ * 例:
+ * 通常使用:format("this is {} for {}", "a", "b") -> this is a for b
+ * 转义{}: format("this is \\{} for {}", "a", "b") -> this is \{} for a
+ * 转义\: format("this is \\\\{} for {}", "a", "b") -> this is \a for b
+ * + * @param template 文本模板,被替换的部分用 {} 表示 + * @param params 参数值 + * @return 格式化后的文本 + */ + public static String format(String template, Object... params) + { + if (isEmpty(params) || isEmpty(template)) + { + return template; + } + return StrFormatter.format(template, params); + } + + /** + * 是否为http(s)://开头 + * + * @param link 链接 + * @return 结果 + */ + public static boolean ishttp(String link) + { + return StringUtils.startsWithAny(link, "http", "https"); + } + + /** + * 字符串转set + * + * @param str 字符串 + * @param sep 分隔符 + * @return set集合 + */ + public static final Set str2Set(String str, String sep) + { + return new HashSet(str2List(str, sep, true, false)); + } + + /** + * 字符串转list + * + * @param str 字符串 + * @param sep 分隔符 + * @param filterBlank 过滤纯空白 + * @param trim 去掉首尾空白 + * @return list集合 + */ + public static final List str2List(String str, String sep, boolean filterBlank, boolean trim) + { + List list = new ArrayList(); + if (StringUtils.isEmpty(str)) + { + return list; + } + + // 过滤空白字符串 + if (filterBlank && StringUtils.isBlank(str)) + { + return list; + } + String[] split = str.split(sep); + for (String string : split) + { + if (filterBlank && StringUtils.isBlank(string)) + { + continue; + } + if (trim) + { + string = string.trim(); + } + list.add(string); + } + + return list; + } + + /** + * 查找指定字符串是否包含指定字符串列表中的任意一个字符串同时串忽略大小写 + * + * @param cs 指定字符串 + * @param searchCharSequences 需要检查的字符串数组 + * @return 是否包含任意一个字符串 + */ + public static boolean containsAnyIgnoreCase(CharSequence cs, CharSequence... searchCharSequences) + { + if (isEmpty(cs) || isEmpty(searchCharSequences)) + { + return false; + } + for (CharSequence testStr : searchCharSequences) + { + if (containsIgnoreCase(cs, testStr)) + { + return true; + } + } + return false; + } + + /** + * 驼峰转下划线命名 + */ + public static String toUnderScoreCase(String str) + { + if (str == null) + { + return null; + } + StringBuilder sb = new StringBuilder(); + // 前置字符是否大写 + boolean preCharIsUpperCase = true; + // 当前字符是否大写 + boolean curreCharIsUpperCase = true; + // 下一字符是否大写 + boolean nexteCharIsUpperCase = true; + for (int i = 0; i < str.length(); i++) + { + char c = str.charAt(i); + if (i > 0) + { + preCharIsUpperCase = Character.isUpperCase(str.charAt(i - 1)); + } + else + { + preCharIsUpperCase = false; + } + + curreCharIsUpperCase = Character.isUpperCase(c); + + if (i < (str.length() - 1)) + { + nexteCharIsUpperCase = Character.isUpperCase(str.charAt(i + 1)); + } + + if (preCharIsUpperCase && curreCharIsUpperCase && !nexteCharIsUpperCase) + { + sb.append(SEPARATOR); + } + else if ((i != 0 && !preCharIsUpperCase) && curreCharIsUpperCase) + { + sb.append(SEPARATOR); + } + sb.append(Character.toLowerCase(c)); + } + + return sb.toString(); + } + + /** + * 是否包含字符串 + * + * @param str 验证字符串 + * @param strs 字符串组 + * @return 包含返回true + */ + public static boolean inStringIgnoreCase(String str, String... strs) + { + if (str != null && strs != null) + { + for (String s : strs) + { + if (str.equalsIgnoreCase(trim(s))) + { + return true; + } + } + } + return false; + } + + /** + * 将下划线大写方式命名的字符串转换为驼峰式。如果转换前的下划线大写方式命名的字符串为空,则返回空字符串。 例如:HELLO_WORLD->HelloWorld + * + * @param name 转换前的下划线大写方式命名的字符串 + * @return 转换后的驼峰式命名的字符串 + */ + public static String convertToCamelCase(String name) + { + StringBuilder result = new StringBuilder(); + // 快速检查 + if (name == null || name.isEmpty()) + { + // 没必要转换 + return ""; + } + else if (!name.contains("_")) + { + // 不含下划线,仅将首字母大写 + return name.substring(0, 1).toUpperCase() + name.substring(1); + } + // 用下划线将原始字符串分割 + String[] camels = name.split("_"); + for (String camel : camels) + { + // 跳过原始字符串中开头、结尾的下换线或双重下划线 + if (camel.isEmpty()) + { + continue; + } + // 首字母大写 + result.append(camel.substring(0, 1).toUpperCase()); + result.append(camel.substring(1).toLowerCase()); + } + return result.toString(); + } + + /** + * 驼峰式命名法 例如:user_name->userName + */ + public static String toCamelCase(String s) + { + if (s == null) + { + return null; + } + s = s.toLowerCase(); + StringBuilder sb = new StringBuilder(s.length()); + boolean upperCase = false; + for (int i = 0; i < s.length(); i++) + { + char c = s.charAt(i); + + if (c == SEPARATOR) + { + upperCase = true; + } + else if (upperCase) + { + sb.append(Character.toUpperCase(c)); + upperCase = false; + } + else + { + sb.append(c); + } + } + return sb.toString(); + } + + /** + * 查找指定字符串是否匹配指定字符串列表中的任意一个字符串 + * + * @param str 指定字符串 + * @param strs 需要检查的字符串数组 + * @return 是否匹配 + */ + public static boolean matches(String str, List strs) + { + if (isEmpty(str) || isEmpty(strs)) + { + return false; + } + for (String pattern : strs) + { + if (isMatch(pattern, str)) + { + return true; + } + } + return false; + } + + /** + * 判断url是否与规则配置: + * ? 表示单个字符; + * * 表示一层路径内的任意字符串,不可跨层级; + * ** 表示任意层路径; + * + * @param pattern 匹配规则 + * @param url 需要匹配的url + * @return + */ + public static boolean isMatch(String pattern, String url) + { + AntPathMatcher matcher = new AntPathMatcher(); + return matcher.match(pattern, url); + } + + @SuppressWarnings("unchecked") + public static T cast(Object obj) + { + return (T) obj; + } + + /** + * 数字左边补齐0,使之达到指定长度。注意,如果数字转换为字符串后,长度大于size,则只保留 最后size个字符。 + * + * @param num 数字对象 + * @param size 字符串指定长度 + * @return 返回数字的字符串格式,该字符串为指定长度。 + */ + public static final String padl(final Number num, final int size) + { + return padl(num.toString(), size, '0'); + } + + /** + * 字符串左补齐。如果原始字符串s长度大于size,则只保留最后size个字符。 + * + * @param s 原始字符串 + * @param size 字符串指定长度 + * @param c 用于补齐的字符 + * @return 返回指定长度的字符串,由原字符串左补齐或截取得到。 + */ + public static final String padl(final String s, final int size, final char c) + { + final StringBuilder sb = new StringBuilder(size); + if (s != null) + { + final int len = s.length(); + if (s.length() <= size) + { + for (int i = size - len; i > 0; i--) + { + sb.append(c); + } + sb.append(s); + } + else + { + return s.substring(len - size, len); + } + } + else + { + for (int i = size; i > 0; i--) + { + sb.append(c); + } + } + return sb.toString(); + } +} \ No newline at end of file diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/utils/Base64Utils.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/utils/Base64Utils.java new file mode 100644 index 0000000..d5cfa8a --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/utils/Base64Utils.java @@ -0,0 +1,59 @@ +package com.ssdmn.common.utils; + +import java.io.IOException; +import java.net.URLDecoder; +import java.util.Base64; + +/** + * BASE64工具类 + * + * @author 郭世旭 + * @version 1.0 + * @createTime 2021-03-22 09:58:30 + */ +public class Base64Utils { + + + /** + * Base64编码(JDK1.8以后才能使用) + * + * @param data 要加密的字符数组 + * @return String 加密后的16进制字符串 + */ + public static String encode_JDK18(byte[] data) { + return Base64.getEncoder().encodeToString(data); + } + + /** + * Base64解码(JDK1.8以后才能使用) + * + * @param data 要解密的字符串 + * @return String 解密后的字符串 + * @throws IOException + */ + public static String decode_JDK18(String data) throws IOException { + if (StringUtil.isEmpty(data)) { + return null; + } + String encodeToString = new String(Base64.getDecoder().decode(data)); + return URLDecoder.decode(encodeToString, "UTF-8"); + } + + public static byte[] decode(String data) throws IOException { + if (StringUtil.isEmpty(data)) { + return null; + } + return Base64.getDecoder().decode(data); + } + +// public static void main(String[] args) throws IOException { +// String s = "L2ltYWdlL2dldEltYWdlLzIwMTkxMjA0L0czNTBRTGhrbHBPSE90aXI2R2FqQUtrdUUzUnQzQmIwLnBuZw=="; +// String decode = Base64Utils.decode_JDK18(s); +// System.out.println(decode); +// String image = "/dlaqyjfkservice/ep_file_service/image/getImage/20191130/nHK8M0ozsPDn9fpmyM8YUY1KmxX8sPqz.png"; +// String s1 = Base64Utils.encode_JDK18(image.getBytes()); +// System.out.println(s1); +// +// } + +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/utils/BigNum.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/utils/BigNum.java new file mode 100644 index 0000000..f1376f8 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/utils/BigNum.java @@ -0,0 +1,587 @@ +package com.ssdmn.common.utils; + +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.NumberUtil; +import cn.hutool.core.util.ObjectUtil; + +import javax.script.ScriptEngine; +import javax.script.ScriptEngineManager; +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.text.DecimalFormat; +import java.util.UUID; +import java.util.stream.Stream; + +/** + * 数字工具类 + * + * @author 郭世旭 + * @version 1.0 + * @createTime 2021-03-22 09:58:30 + */ +public class BigNum { + + public static final int ZERO = 0; + public static final int ONE = 1; + public static final int TWO = 2; + public static final String BIG_NUM_FMT_COMMA = "#,###,###,###,###,###,##0.00";//千位分隔符 方便查看金额具体大小 + public static final String BIG_NUM_FMT = "##################0.00";//不带千位分隔符/// + public static final String BIG_NUM_HUNDRED = "100";//100常量 + public static final int BIG_NUM_SCALE = 2;//保留两位小数 + + + private BigNum() { + + } + + /** + * 判断data_value是否在interval区间范围内 + * + * @param data_value 数值类型的 + * @param interval 正常的数学区间,包括无穷大等,如:(1,3)、>5%、(-∞,6]、(125%,135%)U(70%,80%) + * @return true:表示data_value在区间interval范围内,false:表示data_value不在区间interval范围内 + */ + public static boolean isInTheInterval(String data_value, String interval) { + //将区间和data_value转化为可计算的表达式 + String formula = getFormulaByAllInterval(data_value, interval, "||"); + ScriptEngine jse = new ScriptEngineManager().getEngineByName("JavaScript"); + try { + //计算表达式 + return (Boolean) jse.eval(formula); + } catch (Exception t) { + return false; + } + } + + /** + * 将所有阀值区间转化为公式:如 + * [75,80) =》 date_value < 80 && date_value >= 75 + * (125%,135%)U(70%,80%) =》 (date_value < 1.35 && date_value > 1.25) || (date_value < 0.8 && date_value > 0.7) + * + * @param date_value + * @param interval 形式如:(125%,135%)U(70%,80%) + * @param connector 连接符 如:") || (" + */ + private static String getFormulaByAllInterval(String date_value, String interval, String connector) { + StringBuffer buff = new StringBuffer(); + String[] us = interval.split("U"); + for (String limit : interval.split("U")) {//如:(125%,135%)U (70%,80%) + buff.append("(").append(getFormulaByInterval(date_value, limit, " && ")).append(")").append(connector); + } + String allLimitInvel = buff.toString(); + int index = allLimitInvel.lastIndexOf(connector); + allLimitInvel = allLimitInvel.substring(0, index); + return allLimitInvel; + } + + /** + * 将整个阀值区间转化为公式:如 + * 145) =》 date_value < 145 + * [75,80) =》 date_value < 80 && date_value >= 75 + * + * @param date_value + * @param interval 形式如:145)、[75,80) + * @param connector 连接符 如:&& + */ + private static String getFormulaByInterval(String date_value, String interval, String connector) { + StringBuffer buff = new StringBuffer(); + for (String halfInterval : interval.split(",")) {//如:[75,80)、≥80 + buff.append(getFormulaByHalfInterval(halfInterval, date_value)).append(connector); + } + String limitInvel = buff.toString(); + int index = limitInvel.lastIndexOf(connector); + limitInvel = limitInvel.substring(0, index); + return limitInvel; + } + + /** + * 将半个阀值区间转化为公式:如 + * 145) =》 date_value < 145 + * ≥80% =》 date_value >= 0.8 + * [130 =》 date_value >= 130 + * <80% =》 date_value < 0.8 + * + * @param halfInterval 形式如:145)、≥80%、[130、<80% + * @param date_value + * @return date_value < 145 + */ + private static String getFormulaByHalfInterval(String halfInterval, String date_value) { + halfInterval = halfInterval.trim(); + if (halfInterval.contains("∞")) {//包含无穷大则不需要公式 + return "1 == 1"; + } + StringBuffer formula = new StringBuffer(); + String data = ""; + String opera = ""; + if (halfInterval.matches("^([<>≤≥\\[\\(]{1}(-?\\d+.?\\d*\\%?))$")) {//表示判断方向(如>)在前面 如:≥80% + opera = halfInterval.substring(0, 1); + data = halfInterval.substring(1); + } else if (halfInterval.matches("^([<>≤≥\\[\\(]((([0-9]{3}[1-9]|[0-9]{2}[1-9][0-9]{1}|[0-9]{1}[1-9][0-9]{2}|[1-9][0-9]{3})-(((0[13578]|1[02])-(0[1-9]|[12][0-9]|3[01]))|((0[469]|11)-(0[1-9]|[12][0-9]|30))|(02-(0[1-9]|[1][0-9]|2[0-8]))))|((([0-9]{2})(0[48]|[2468][048]|[13579][26])|((0[48]|[2468][048]|[3579][26])00))-02-29))\\s+([0-1]?[0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9]))$")) { + opera = halfInterval.substring(0, 1); + data = halfInterval.substring(1); + data = String.valueOf(DateUtil.parse(data, "yyyy-MM-dd HH:mm:ss").getTime()); + } else if (halfInterval.matches("^(((([0-9]{3}[1-9]|[0-9]{2}[1-9][0-9]{1}|[0-9]{1}[1-9][0-9]{2}|[1-9][0-9]{3})-(((0[13578]|1[02])-(0[1-9]|[12][0-9]|3[01]))|((0[469]|11)-(0[1-9]|[12][0-9]|30))|(02-(0[1-9]|[1][0-9]|2[0-8]))))|((([0-9]{2})(0[48]|[2468][048]|[13579][26])|((0[48]|[2468][048]|[3579][26])00))-02-29))\\s+([0-1]?[0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9])[)\\]])$")) { + opera = halfInterval.substring(halfInterval.length() - 1); + data = halfInterval.substring(0, halfInterval.length() - 1); + data = String.valueOf(DateUtil.parse(data, "yyyy-MM-dd HH:mm:ss").getTime()); + } else {//[130、145) + opera = halfInterval.substring(halfInterval.length() - 1); + data = halfInterval.substring(0, halfInterval.length() - 1); + } + double value = dealPercent(data); + formula.append(date_value).append(" ").append(opera).append(" ").append(value); + String a = formula.toString(); + //转化特定字符 + return a.replace("[", ">=").replace("(", ">").replace("]", "<=").replace(")", "<").replace("≤", "<=").replace("≥", ">="); + } + + /** + * 去除百分号,转为小数 + * + * @param str 可能含百分号的数字 + * @return + */ + private static double dealPercent(String str) { + double d = 0.0; + if (str.contains("%")) { + str = str.substring(0, str.length() - 1); + d = Double.parseDouble(str) / 100; + } else { + d = Double.parseDouble(str); + } + return d; + } + + public static Long Obj2Long(Object obj) { + return Long.valueOf(String.valueOf(obj)); + } + + /** + * @Description: 获取随机数 + * @Return: int + * @Author: wt + * @Date: 2020/11/6 + */ + public static int createOrderNo() { + int hashCodeV = UUID.randomUUID().toString().hashCode(); + if (hashCodeV < 0) { + hashCodeV = -hashCodeV; + } + // 0 代表前面补充0 + // 4 代表长度为4 + // d 代表参数为正数型 + return hashCodeV; + } + + /** + * 高精度百分比 + * + * @param v1 原值 + * @param v2 百分比数值 + * @return 相乘结果 + */ + public static BigDecimal percentage(String v1, String v2) { + BigDecimal b1 = new BigDecimal(v1); + BigDecimal b2 = new BigDecimal(v2); + + return percentage(b1, b2); + } + + /** + * 高精度百分比 + * + * @param v1 + * @param v2 百分比 + * @return + */ + public static BigDecimal percentages(String v1, String v2) { + BigDecimal b1 = new BigDecimal(v1); +// BigDecimal b2 = new BigDecimal(v2); + BigDecimal b2 = mul(v2, "10"); + + return percentage(b1, b2); + } + + /** + * 高精度百分比 + * + * @param b1 + * @param v2 百分比 + * @return + */ + public static BigDecimal percentages(BigDecimal b1, String v2) { +// BigDecimal b1 = new BigDecimal(v1); +// BigDecimal b2 = new BigDecimal(v2); + BigDecimal b2 = mul(v2, "10"); + + return percentage(b1, b2); + } + + /** + * 高精度百分比 + * + * @param b1 + * @param b2 百分比 + * @return + */ + public static BigDecimal percentage(BigDecimal b1, BigDecimal b2) { + BigDecimal b3 = new BigDecimal("100"); + + return b1.multiply(b2).divide(b3, 2, BigDecimal.ROUND_DOWN); + } + + /** + * 高精度百分比 + * + * @param b1 + * @param v2 百分比 + * @return + */ + public static BigDecimal percentage(BigDecimal b1, String v2) { + BigDecimal b2 = new BigDecimal(v2); + + return percentage(b1, b2); + } + + /** + * 高精度加法 + * + * @param v1 + * @param v2 + * @return + */ + public static BigDecimal add(String v1, String v2) { + BigDecimal b1 = new BigDecimal(v1); + BigDecimal b2 = new BigDecimal(v2); + return b1.add(b2); + } + + /** + * 高精度加法 + * + * @param v1 + * @param v2 + * @return + */ + public static double add2Double(String v1, String v2) { + BigDecimal b1 = new BigDecimal(v1); + BigDecimal b2 = new BigDecimal(v2); + + return Double.parseDouble(b1.add(b2).toPlainString()); + } + + /** + * 高精度减法 + * + * @param v1 + * @param v2 + * @return + */ + public static BigDecimal sub(String v1, String v2) { + BigDecimal b1 = new BigDecimal(v1); + BigDecimal b2 = new BigDecimal(v2); + return b1.subtract(b2); + } + + /** + * 高精度减法 + * + * @param v1 + * @param v2 + * @return + */ + public static double sub2Double(String v1, String v2) { + BigDecimal b1 = new BigDecimal(v1); + BigDecimal b2 = new BigDecimal(v2); + + return Double.parseDouble(b1.subtract(b2).toPlainString()); + } + + /** + * 高精度乘法 + * + * @param v1 + * @param v2 + * @return + */ + public static BigDecimal mul(String v1, String v2) { + BigDecimal b1 = new BigDecimal(v1); + BigDecimal b2 = new BigDecimal(v2); + return b1.multiply(b2); + } + + /** + * 高精度乘法 + * + * @param v1 + * @param v2 + * @return + */ + public static double mul2Double(String v1, String v2) { + BigDecimal b1 = new BigDecimal(v1); + BigDecimal b2 = new BigDecimal(v2); + + return Double.parseDouble(b1.multiply(b2).toPlainString()); + } + + + /** + * 高精度除法 + * R + * + * @param v1 + * @param v2 + * @param scale + * @return + */ + public static BigDecimal div(String v1, String v2, int scale) { + if (scale < 0) { + throw new IllegalArgumentException( + "The scale must be a positive integer or zero"); + } + BigDecimal b1 = new BigDecimal(v1); + BigDecimal b2 = new BigDecimal(v2); + return b1.divide(b2, scale, BigDecimal.ROUND_HALF_UP); + } + + /** + * 高精度除法 + * R + * + * @param v1 + * @param v2 + * @param scale + * @return + */ + public static double div2Double(String v1, String v2, int scale) { + if (scale < 0) { + throw new IllegalArgumentException( + "The scale must be a positive integer or zero"); + } + BigDecimal b1 = new BigDecimal(v1); + BigDecimal b2 = new BigDecimal(v2); + + return Double.parseDouble(b1.divide(b2, scale, BigDecimal.ROUND_HALF_UP).toPlainString()); + } + + /** + * 保留小数位 + * + * @param v + * @param scale + * @return + */ + public static BigDecimal round(String v, int scale) { + if (scale < 0) { + throw new IllegalArgumentException( + "The scale must be a positive integer or zero"); + } + BigDecimal b = new BigDecimal(v); + BigDecimal one = new BigDecimal("1"); + return b.divide(one, scale, BigDecimal.ROUND_HALF_UP); + } + + /** + * 分转换成元 + * + * @param v + * @return + */ + public static BigDecimal penny2dollar(String v) { + BigDecimal s = div(v, "100", 2);//保留两位小数 + return s; + } + + /** + * 元转换成分 + * + * @param v + * @return + */ + public static BigDecimal dollar2penny(String v) { + + return mul(v, "100"); + } + + /** + * 格式化金额 + * 千位分隔符 方便查看金额具体大小 BIG_NUM_FMT = "#,###,###,###,###,###,##0.00" + * 精确两位小数 .99 -> 0.99 + * 1111111.985 -> 1,111,111.99 + * + * @param v + * @return + */ + public static String formatNumber(String v) { + return formatNumber(v, BIG_NUM_FMT_COMMA); + } + + /** + * 格式化金额 + * + * @param v + * @param pattern BigNum类中的常量 BIG_NUM_FMT_COMMA,BIG_NUM_FMT + * @return + */ + public static String formatNumber(String v, String pattern) { + return new DecimalFormat(pattern).format(new BigDecimal(v)); + } + + /** + * 格式化两位小数金额 + * + * @param bigDecimal 金额 + * @return 两位小数金额 + */ + public static BigDecimal formatBigDecimal(BigDecimal bigDecimal) { + if (ObjectUtil.isEmpty(bigDecimal)) { + bigDecimal = new BigDecimal("0.00"); + } + + return bigDecimal.setScale(2, RoundingMode.DOWN); + } + + /** + * 指定一个长度,如果不够,前面填充0 + * + * @param src + * @param length + * @return + */ + public static String fillZero(int src, int length) { + Long srcLong = (long) src; + + return fillZero(srcLong, length); + } + + /** + * 指定一个长度,如果不够,前面填充0 + * + * @param src + * @param length + * @return + */ + public static String fillZero(long src, int length) { + String srcStr = src + ""; + int srcLength = srcStr.length(); + if (srcLength > length) { + return srcStr.substring(0, length); + } else if (srcLength < length) { + + String s = Stream.iterate("0", n -> n) + .limit(length - srcLength) + .reduce((m1, m2) -> m1 + m2) + .orElse(""); + + return s + src; + } + + return srcStr; + } + + public static String formatterMMss(String date) { + try { + String[] dates = date.split("\\:"); + + return fillZero(Integer.parseInt(dates[0]), 2) + ":" + dates[1]; + } catch (Exception e) { + return ""; + } + + } + + /** + * 比较大小 + * + * @param bNum1 + * @param num2 + * @return + */ + public static int compare(BigDecimal bNum1, String num2) { + BigDecimal bNum2 = new BigDecimal(num2); + + return ObjectUtil.compare(bNum1, bNum2); + } + + /** + * 比较大小 + * + * @param bNum2 + * @param num1 + * @return + */ + public static int compare(String num1, BigDecimal bNum2) { + BigDecimal bNum1 = new BigDecimal(num1); + + return ObjectUtil.compare(bNum1, bNum2); + } + + + /** + * 比较大小 + * + * @param num1 + * @param num2 + * @return + */ + public static int compare(String num1, String num2) { + BigDecimal bNum1 = new BigDecimal(num1); + BigDecimal bNum2 = new BigDecimal(num2); + + return ObjectUtil.compare(bNum1, bNum2); + } + + /** + * 比较大小 + * + * @param num1 + * @param num2 + * @return + */ + public static BigDecimal max(String num1, String num2) { + BigDecimal bNum1 = new BigDecimal(num1); + BigDecimal bNum2 = new BigDecimal(num2); + + return ObjectUtil.compare(bNum1, bNum2) == 1 ? bNum1 : bNum2; + } + + /** + * 比较大小 + * + * @param num1 + * @param num2 + * @return + */ + public static BigDecimal min(String num1, String num2) { + BigDecimal bNum1 = new BigDecimal(num1); + BigDecimal bNum2 = new BigDecimal(num2); + + return ObjectUtil.compare(bNum1, bNum2) == -1 ? bNum1 : bNum2; + } + + /** + * 是否不小于等于0 + * @param number + * @return + */ + public static boolean notLe0(int number){ + if(ObjectUtil.compare(number, 0) <= 0){ + return false; + } + + return true; + } + + /** + * 是否不小于等于0 + * @param number + * @return + */ + public static boolean notLe0(double number){ + if(ObjectUtil.compare(number, 0.0) <= 0){ + return false; + } + + return true; + } +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/utils/CronPatternUtil.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/utils/CronPatternUtil.java new file mode 100644 index 0000000..f6f1b04 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/utils/CronPatternUtil.java @@ -0,0 +1,38 @@ +package com.ssdmn.common.utils; + +import cn.hutool.core.date.DateUtil; + +import java.time.LocalDateTime; +import java.util.Date; + +/** + * cron表达式工具类 + * @author 郭世旭 + */ +public class CronPatternUtil extends cn.hutool.cron.pattern.CronPatternUtil { + + /** + * 生成 指定时间的任务表达式 + * @param date + * @return + */ + public static String expressionHour(Date date){ + return DateUtil.second(date) + " " + DateUtil.minute(date) + + " " + DateUtil.hour(date, true) + " " + + DateUtil.dayOfMonth(date) + " " + (DateUtil.month(date) + 1) + + " ? " + DateUtil.year(date); + } + + /** + * 生成 指定时间的任务表达式 + * @param localDateTime + * @return + */ + public static String expressionHour(LocalDateTime localDateTime){ + + return localDateTime.getSecond() + " " + localDateTime.getMinute() + + " " + localDateTime.getHour() + " " + + localDateTime.getDayOfMonth() + " " + localDateTime.getMonthValue() + + " ? " + localDateTime.getYear(); + } +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/utils/CronUtil.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/utils/CronUtil.java new file mode 100644 index 0000000..088fe67 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/utils/CronUtil.java @@ -0,0 +1,10 @@ +package com.ssdmn.common.utils; + +/** + * cron执行工具类 + * @author 郭世旭 + */ +public class CronUtil extends cn.hutool.cron.CronUtil { + + +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/utils/FileTypeUtil.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/utils/FileTypeUtil.java new file mode 100644 index 0000000..a17d741 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/utils/FileTypeUtil.java @@ -0,0 +1,262 @@ +package com.ssdmn.common.utils; + +import cn.hutool.core.util.ObjectUtil; +import org.apache.commons.lang3.StringUtils; + +import java.io.InputStream; +import java.net.URLConnection; + +public enum FileTypeUtil { + AAC("acc","AAC音频","audio/aac"), + + ABW("abw","AbiWord文件","application/x-abiword"), + + ARC("arc","存档文件","application/x-freearc"), + + AVI("avi","音频视频交错格式","video/x-msvideo"), + + AZW("azw","亚马逊Kindle电子书格式","application/vnd.amazon.ebook"), + + BIN("bin","任何类型的二进制数据","application/octet-stream"), + + BMP("bmp","Windows OS / 2位图图形","image/bmp"), + + BZ("bz","BZip存档","application/x-bzip"), + + BZ2("bz2","BZip2存档","application/x-bzip2"), + + CSH("csh","C-Shell脚本","application/x-csh"), + + CSS("css","级联样式表(CSS)","text/css"), + + CSV("csv","逗号分隔值(CSV)","text/csv"), + + DOC("doc","微软Word文件","application/msword"), + + DOCX("docx","Microsoft Word(OpenXML)","application/vnd.openxmlformats-officedocument.wordprocessingml.document"), + + EOT("eot","MS Embedded OpenType字体","application/vnd.ms-fontobject"), + + EPUB("epub","电子出版物(EPUB)","application/epub+zip"), + + GZ("gz","GZip压缩档案","application/gzip"), + + GIF("gif","图形交换格式(GIF)","image/gif"), + + HTML("html","超文本标记语言(HTML)","text/html"), + + ICO("ico","图标格式","image/vnd.microsoft.icon"), + + ICS("ics","iCalendar格式","text/calendar"), + + JAR("jar","Java存档","application/java-archive"), + + JPEG("jpeg","JPEG图像","image/jpeg"), + + JPG("jpg","JPEG图像","image/jpeg"), + + JS("js","JavaScript","text/javascript"), + + JSON("json","JSON格式","application/json"), + + JSONLD("jsonld","JSON-LD格式","application/ld+json"), + + MID("mid","乐器数字接口(MIDI)","audio/midi"), + + MIDI("midi","乐器数字接口(MIDI)","audio/midi"), + + MJS("mjs","JavaScript模块","text/javascript"), + + MP3("mp3","MP3音频","audio/mpeg"), + + MPEG("mpeg","MPEG视频","video/mpeg"), + + MPKG("mpkg","苹果安装程序包","application/vnd.apple.installer+xml"), + + ODP("odp","OpenDocument演示文稿文档","application/vnd.oasis.opendocument.presentation"), + + ODS("ods","OpenDocument电子表格文档","application/vnd.oasis.opendocument.spreadsheet"), + + ODT("odt","OpenDocument文字文件","application/vnd.oasis.opendocument.text"), + + OGA("oga","OGG音讯","audio/ogg"), + + OGV("ogv","OGG视频","video/ogg"), + + OGX("ogx","OGG","application/ogg"), + + OPUS("opus","OPUS音频","audio/opus"), + + OTF("otf","otf字体","font/otf"), + + PNG("png","便携式网络图形","image/png"), + + PDF("pdf","Adobe 可移植文档格式(PDF)","application/pdf"), + + PHP("php","php","application/x-httpd-php"), + + PPT("ppt","Microsoft PowerPoint","application/vnd.ms-powerpoint"), + + PPTX("pptx","Microsoft PowerPoint(OpenXML)","application/vnd.openxmlformats-officedocument.presentationml.presentation"), + + RAR("rar","RAR档案","application/vnd.rar"), + + RTF("rtf","富文本格式","application/rtf"), + + SH("sh","Bourne Shell脚本","application/x-sh"), + + SVG("svg","可缩放矢量图形(SVG)","image/svg+xml"), + + SWF("swf","小型Web格式(SWF)或Adobe Flash文档","application/x-shockwave-flash"), + + TAR("tar","磁带存档(TAR)","application/x-tar"), + + TIF("tif","标记图像文件格式(TIFF)","image/tiff"), + + TIFF("tiff","标记图像文件格式(TIFF)","image/tiff"), + + TS("ts","MPEG传输流","video/mp2t"), + + TTF("ttf","ttf字体","font/ttf"), + + TXT("txt","文本(通常为ASCII或ISO 8859- n","text/plain"), + + VSD("vsd","微软Visio","application/vnd.visio"), + + WAV("wav","波形音频格式","audio/wav"), + + WEBA("weba","WEBM音频","audio/webm"), + + WEBM("webm","WEBM视频","video/webm"), + + WEBP("webp","WEBP图像","image/webp"), + + WOFF("woff","Web开放字体格式(WOFF)","font/woff"), + + WOFF2("woff2","Web开放字体格式(WOFF)","font/woff2"), + + XHTML("xhtml","XHTML","application/xhtml+xml"), + + XLS("xls","微软Excel","application/vnd.ms-excel"), + + XLSX("xlsx","微软Excel(OpenXML)","application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"), + + XML("xml","XML","application/xml"), + + XUL("xul","XUL","application/vnd.mozilla.xul+xml"), + + ZIP("zip","ZIP","application/zip"), + + MIME_3GP("3gp", "3GPP audio/video container", "video/3gpp"), + + MIME_3GP_WITHOUT_VIDEO("3gp", "3GPP audio/video container doesn't contain video", "audio/3gpp2"), + + MIME_3G2("3g2", "3GPP2 audio/video container", "video/3gpp2"), + + MIME_3G2_WITHOUT_VIDEO("3g2", "3GPP2 audio/video container doesn't contain video", "audio/3gpp2"), + + MIME_7Z("7z","7-zip存档","application/x-7z-compressed"), + + MP4("mp4","视频格式","video/mpeg4"), + + MPV("mpv","MPV视频格式","video/mpg"), + + MOVIE("movie","视频格式","video/x-sgi-movie"), + + RMVB("rmvb","多媒体格式","application/vnd.rn-realmedia-vbr"), + + WMV("wmv","视频格式","video/x-ms-wmv"), + + WMA("wma","音频格式","audio/x-ms-wma"), + ; + + /** + * 后缀名 + */ + final String mSuffix; + + /** + * 文件说明 + */ + final String explain; + /** + * 文件类型 + */ + final String mMIME; + + FileTypeUtil(String suffix, String explain, String mime) { + this.mSuffix = suffix; + this.explain = explain; + this.mMIME = mime; + } + + public static String getSuffixFromUrl(String url) throws Exception { + url = java.net.URLDecoder.decode(url, "UTF-8"); + String contentType = getMIMETypeFromUrl(url); + return mimeMapingSuffix(contentType); + } + + public static String getMIMETypeFromUrl(String url) throws Exception{ + url = java.net.URLDecoder.decode(url, "UTF-8"); + if (StringUtils.isBlank(url)) { + return ""; + } + return URLConnection.guessContentTypeFromName(url); + } + + public static String getMIMETypeFromInputStream(InputStream inputStream) throws Exception{ + String contentType = URLConnection.guessContentTypeFromStream(inputStream); + + return mimeMapingSuffix(contentType); + } + + /** + * mime类型对应的后缀名 + */ + public static String mimeMapingSuffix(String mime) { + if(ObjectUtil.isNull(mime)){ + return ""; + } + FileTypeUtil[] fileTypes = FileTypeUtil.values(); + for (FileTypeUtil fileType : fileTypes) { + if (fileType.mime().equals(mime)) { + return fileType.suffix(); + } + } + + return ""; + } + + /** + * mime类型对应的后缀名 + */ + public static String getMIMETypeFromFileName(String fileName) { + if(ObjectUtil.isNull(fileName)){ + return ""; + } + String[] split = fileName.split("\\."); + String suffix = split[split.length - 1]; + + FileTypeUtil[] fileTypes = FileTypeUtil.values(); + for (FileTypeUtil fileType : fileTypes) { + if (fileType.suffix().equals(suffix)) { + return fileType.mime(); + } + } + + return ""; + } + + public String mime() { + return mMIME; + } + + /** + * 获取后缀名 + * + * @return 指定类型的后缀名,如'.mp4' + */ + public String suffix() { + return this.mSuffix; + } +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/utils/FileUtil.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/utils/FileUtil.java new file mode 100644 index 0000000..93c88f9 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/utils/FileUtil.java @@ -0,0 +1,282 @@ +package com.ssdmn.common.utils; + +import cn.hutool.core.codec.Base64; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.core.codec.Base64Encoder; +import com.ssdmn.common.exception.BizException; +import org.springframework.mock.web.MockMultipartFile; +import org.springframework.web.multipart.MultipartFile; + +import javax.imageio.ImageIO; +import java.awt.*; +import java.awt.image.BufferedImage; +import java.io.*; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Random; +import java.util.UUID; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + + +/** + * @Description: 文件工具类 + * @Author: fan + * @Data: 2021/5/8 10:20 + * @Version: 1.0v + */ +public class FileUtil extends cn.hutool.core.io.FileUtil { + + /** + * 判断文件是否图片 + * + * @param file 文件 + * @return boolean + */ + public static boolean getImg(File file) { + try { + Image image = ImageIO.read(file); + if (!ObjectUtil.isNull(image)) { + return true; + } + } catch (Exception e) { + return false; + } + return false; + } + + + /** + * 判断文件是否图片 + * + * @param multipartFile 文件 + * @return boolean + */ + public static boolean getImg(MultipartFile multipartFile) { + try { + File file = multipartFileToFile(multipartFile); + Image image = ImageIO.read(file); + if (!ObjectUtil.isNull(image)) { + return true; + } + } catch (Exception e) { + return false; + } + return false; + } + + /** + * MultipartFile 转 File + * + * @param file + * @throws Exception + */ + public static File multipartFileToFile(MultipartFile file) throws Exception { + File toFile; + // 为空 + if (ObjectUtil.isEmpty(file) || file.getSize() <= 0) { + return null; + } + + InputStream ins = file.getInputStream(); + toFile = new File(file.getOriginalFilename()); + inputStreamToFile(ins, toFile); + ins.close(); + + return toFile; + } + + /** + * MultipartFile 转 File + * + * @param file + * @throws Exception + */ + public static File multipartFileToFile(MultipartFile file, String path) throws IOException { + if (ObjectUtil.isEmpty(path)) { + throw new BizException("文件路径不合理"); + } + mkdirs(path); + File toFile; + // 为空 + if (ObjectUtil.isEmpty(file) || file.getSize() <= 0) { + return null; + } + + InputStream ins = file.getInputStream(); + toFile = new File(path + File.separator + file.getOriginalFilename()); + inputStreamToFile(ins, toFile); + ins.close(); + + return toFile; + } + + /** + * 当文件夹不存在时,mkdirs会自动创建多层目录,区别于mkdir. + * (mkdir如果父目录不存在则会抛出异常) + * + * @param destPath 存放目录 + */ + public static void mkdirs(String destPath) { + File file = new File(destPath); + if (!file.exists() && !file.isDirectory()) { + file.mkdirs(); + } + } + + /** + * 获取流文件 + * + * @param ins + * @param file + */ + private static void inputStreamToFile(InputStream ins, File file) { + try { + OutputStream os = new FileOutputStream(file); + int bytesRead = 0; + byte[] buffer = new byte[8192]; + while ((bytesRead = ins.read(buffer, 0, 8192)) != -1) { + os.write(buffer, 0, bytesRead); + } + os.close(); + ins.close(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + public static String checkType(String type) { + + switch (type) { + case "FFD8FF": + return "jpg"; + case "89504E": + return "png"; + case "474946": + return "jif"; + + default: + return null; + } + } + + public static String bytesToHexString(byte[] src) { + StringBuilder stringBuilder = new StringBuilder(); + if (src == null || src.length <= 0) { + return null; + } + for (int i = 0; i < src.length; i++) { + int v = src[i] & 0xFF; + String hv = Integer.toHexString(v); + if (hv.length() < 2) { + stringBuilder.append(0); + } + stringBuilder.append(hv); + } + return stringBuilder.toString(); + } + + /** + * 作者: wt + * 时间 2020年6月4日10:19:16 + * 描述:随机数,数量自定义 + * + * @param o + * @return + */ + public static synchronized String getRandomNumber(Integer o) { + Random random = new Random(); + StringBuffer buffer = new StringBuffer(); + Date date = new Date(); + String str = dateToFileNameString(date); + buffer.append(str + "_"); + for (int i = 0; i < o; i++) { + buffer.append(random.nextInt(9) % (9) + 1); + } + return buffer.toString(); + } + + public static String dateToFileNameString(Date date) { + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd_HH~mm~ss"); + return sdf.format(date); + } + + public static MultipartFile base64ToMultipartFile(String baseImg) { + + //定义一个正则表达式的筛选规则,为了获取图片的类型 + String rgex = "data:image/(.*?);base64"; + if (StrUtil.isBlank(baseImg)) { + return null; + } + String type = getSubUtilSimple(baseImg, rgex); + //去除base64图片的前缀 + baseImg = baseImg.replaceFirst("data:(.+?);base64,", ""); + byte[] imageByte; + String fileName = ""; + //把图片转换成二进制 + imageByte = Base64.decode(baseImg.replaceAll(" ", "+")); + + //随机生成图片的名字,同时根据类型结尾 + fileName = UUID.randomUUID().toString() + "." + type; + + InputStream inputStream = new ByteArrayInputStream(imageByte); + MockMultipartFile mockMultipartFile = null; + try { + mockMultipartFile = new MockMultipartFile(fileName, fileName, "", inputStream); + } catch (IOException e) { + e.printStackTrace(); + } + return mockMultipartFile; + } + + /** + * 文件BufferedImage类型转BASE64 + * + * @param bufferedImage + * @return + */ + public static String imageToBase64(BufferedImage bufferedImage) { + ByteArrayOutputStream baos = new ByteArrayOutputStream();//io流 + try { + ImageIO.write(bufferedImage, "png", baos);//写入流中 + } catch (IOException e) { + e.printStackTrace(); + } + byte[] bytes = baos.toByteArray();//转换成字节 + String png_base64 = Base64Encoder.encode(bytes).trim();//转换成base64串 + png_base64 = png_base64.replaceAll("\n", "").replaceAll("\r", ""); + return "data:image/png;base64," + png_base64; + + } + + /** + * 文件File类型转byte[] + * + * @param file + * @return + */ + private static byte[] fileToByte(File file) { + byte[] fileBytes = null; + FileInputStream fis = null; + try { + fis = new FileInputStream(file); + fileBytes = new byte[(int) file.length()]; + fis.read(fileBytes); + fis.close(); + } catch (Exception e) { + e.printStackTrace(); + } + return fileBytes; + } + + + public static String getSubUtilSimple(String soap, String rgex) { + Pattern pattern = Pattern.compile(rgex); + Matcher m = pattern.matcher(soap); + while (m.find()) { + return m.group(1); + } + return ""; + } +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/utils/MapperUtil.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/utils/MapperUtil.java new file mode 100644 index 0000000..18b2508 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/utils/MapperUtil.java @@ -0,0 +1,24 @@ +package com.ssdmn.common.utils; + +import com.ssdmn.common.exception.BizException; +import com.ssdmn.common.lang.Assert; +import com.ssdmn.common.result.ResultConstants; + +/** + * mapper执行工具 + * + * @author 郭世旭 + */ +public class MapperUtil { + + public static int executeCheck(int i, String message) { + + return Assert.notLt(i, 1, message); + } + + public static int executeCheck(int i, ResultConstants resultConstants) { + + return Assert.notLt(i, 1, () -> new BizException(resultConstants)); + } + +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/utils/MessageUtils.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/utils/MessageUtils.java new file mode 100644 index 0000000..e0cdc74 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/utils/MessageUtils.java @@ -0,0 +1,25 @@ +package com.ssdmn.common.utils; + +import org.springframework.context.MessageSource; +import org.springframework.context.i18n.LocaleContextHolder; + +/** + * 获取i18n资源文件 + * + * @author 郭世旭 + */ +public class MessageUtils +{ + /** + * 根据消息键和参数 获取消息 委托给spring messageSource + * + * @param code 消息键 + * @param args 参数 + * @return 获取国际化翻译值 + */ + public static String message(String code, Object... args) + { + MessageSource messageSource = SpringUtils.getBean(MessageSource.class); + return messageSource.getMessage(code, args, LocaleContextHolder.getLocale()); + } +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/utils/RandomUtils.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/utils/RandomUtils.java new file mode 100644 index 0000000..5983688 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/utils/RandomUtils.java @@ -0,0 +1,157 @@ +package com.ssdmn.common.utils; + +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.Random; + +/** + * 随机工具类 + * + * @author 郭世旭 + * @version 1.0 + * @createTime 2021-03-22 09:58:30 + */ + +public class RandomUtils { + public static final String allChar = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIKLMNOPQRSTUVWXYZ"; + public static final String letterChar = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; + public static final String numberChar = "0123456789"; + + /** + * 返回一个定长的随机数字串(只包含数字) + * + * @param length 随机数字长度 + */ + public static String generateNumberChar(int length) { + StringBuffer sb = new StringBuffer(); + Random random = new Random(); + for (int i = 0; i < length; i++) { + sb.append(numberChar.charAt(random.nextInt(numberChar.length()))); + } + return sb.toString(); + } + + + /** + * 返回一个32位字符串,以时间磋 + 16位随便字符串 + * + * @return 随机字符串 + */ + public static String random32() { + StringBuffer sb = new StringBuffer(System.currentTimeMillis() + ""); + Random random = new Random(); + for (int i = 0; i < 19; i++) { + sb.append(allChar.charAt(random.nextInt(allChar.length()))); + } + return sb.toString(); + } + + /** + * 返回一个定长的随机字符串(只包含大小写字母、数字) + * + * @param length 随机字符串长度 + * @return 随机字符串 + */ + public static String generateString(int length) { + StringBuffer sb = new StringBuffer(); + Random random = new Random(); + for (int i = 0; i < length; i++) { + sb.append(allChar.charAt(random.nextInt(allChar.length()))); + } + return sb.toString(); + } + + /** + * 返回一个定长的随机纯字母字符串(只包含大小写字母) + * + * @param length 随机字符串长度 + * @return 随机字符串 + */ + public static String generateMixString(int length) { + StringBuffer sb = new StringBuffer(); + Random random = new Random(); + for (int i = 0; i < length; i++) { + sb.append(allChar.charAt(random.nextInt(letterChar.length()))); + } + return sb.toString(); + } + + /** + * 返回一个定长的随机纯大写字母字符串(只包含大小写字母) + * + * @param length 随机字符串长度 + * @return 随机字符串 + */ + public static String generateLowerString(int length) { + return generateMixString(length).toLowerCase(); + } + + /** + * 返回一个定长的随机纯小写字母字符串(只包含大小写字母) + * + * @param length 随机字符串长度 + * @return 随机字符串 + */ + public static String generateUpperString(int length) { + return generateMixString(length).toUpperCase(); + } + + /** + * 生成一个定长的纯0字符串 + * + * @param length 字符串长度 + * @return 纯0字符串 + */ + public static String generateZeroString(int length) { + StringBuffer sb = new StringBuffer(); + for (int i = 0; i < length; i++) { + sb.append('0'); + } + return sb.toString(); + } + + /** + * 根据数字生成一个定长的字符串,长度不够前面补0 + * + * @param num 数字 + * @param fixdlenth 字符串长度 + * @return 定长的字符串 + */ + public static String toFixdLengthString(long num, int fixdlenth) { + StringBuffer sb = new StringBuffer(); + String strNum = String.valueOf(num); + if (fixdlenth - strNum.length() >= 0) { + sb.append(generateZeroString(fixdlenth - strNum.length())); + } else { + throw new RuntimeException("将数字" + num + "转化为长度为" + fixdlenth + + "的字符串发生异常!"); + } + sb.append(strNum); + return sb.toString(); + } + + /** + * 返回一个定长的随机数字串(只包含数字) + * + * @param length 随机数字长度 + */ + public static String generateNumberCode(int length) { + String numberChar = "23456789ABCDEFGHKLMNPQRSTUVWXYZ"; + StringBuffer sb = new StringBuffer(); + Random random = new Random(); + for (int i = 0; i < length; i++) { + sb.append(numberChar.charAt(random.nextInt(numberChar.length()))); + } + return sb.toString(); + } + + /** + * @param formatType + * @param len + * @return + */ + public static String generateTimeRandom(String formatType, int len) { + SimpleDateFormat format = new SimpleDateFormat(formatType); + return (format.format(Calendar.getInstance().getTime()) + RandomUtils.generateNumberChar(len)); + } +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/utils/ServletUtil.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/utils/ServletUtil.java new file mode 100644 index 0000000..a76997e --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/utils/ServletUtil.java @@ -0,0 +1,154 @@ +package com.ssdmn.common.utils; + +import com.ssdmn.common.str.Convert; +import com.ssdmn.common.str.StringUtils; +import org.springframework.web.context.request.RequestAttributes; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; +import java.io.IOException; + +/** + * 客户端工具类 + * + * @author 郭世旭 + */ +public class ServletUtil extends cn.hutool.extra.servlet.ServletUtil { + + /** + * 定义移动端请求的所有可能类型 + */ + private final static String[] agent = { "Android", "iPhone", "iPod", "iPad", "Windows Phone", "MQQBrowser" }; + + /** + * 获取String参数 + */ + public static String getParameter(String name) { + return getRequest().getParameter(name); + } + + /** + * 获取String参数 + */ + public static String getParameter(String name, String defaultValue) { + return Convert.toStr(getRequest().getParameter(name), defaultValue); + } + + /** + * 获取Integer参数 + */ + public static Integer getParameterToInt(String name) { + return Convert.toInt(getRequest().getParameter(name)); + } + + /** + * 获取Integer参数 + */ + public static Integer getParameterToInt(String name, Integer defaultValue) { + return Convert.toInt(getRequest().getParameter(name), defaultValue); + } + + /** + * 获取Boolean参数 + */ + public static Boolean getParameterToBool(String name) { + return Convert.toBool(getRequest().getParameter(name)); + } + + /** + * 获取Boolean参数 + */ + public static Boolean getParameterToBool(String name, Boolean defaultValue) { + return Convert.toBool(getRequest().getParameter(name), defaultValue); + } + + /** + * 获取request + */ + public static HttpServletRequest getRequest() { + return getRequestAttributes().getRequest(); + } + + /** + * 获取response + */ + public static HttpServletResponse getResponse() { + return getRequestAttributes().getResponse(); + } + + /** + * 获取session + */ + public static HttpSession getSession() { + return getRequest().getSession(); + } + + public static ServletRequestAttributes getRequestAttributes() { + RequestAttributes attributes = RequestContextHolder.getRequestAttributes(); + return (ServletRequestAttributes) attributes; + } + + /** + * 将字符串渲染到客户端 + * + * @param response 渲染对象 + * @param string 待渲染的字符串 + */ + public static void renderString(HttpServletResponse response, String string) { + try { + response.setStatus(200); + response.setContentType("application/json"); + response.setCharacterEncoding("utf-8"); + response.getWriter().print(string); + } catch (IOException e) { + e.printStackTrace(); + } + } + + /** + * 是否是Ajax异步请求 + * + * @param request + */ + public static boolean isAjaxRequest(HttpServletRequest request) { + String accept = request.getHeader("accept"); + if (accept != null && accept.contains("application/json")) { + return true; + } + + String xRequestedWith = request.getHeader("X-Requested-With"); + if (xRequestedWith != null && xRequestedWith.contains("XMLHttpRequest")) { + return true; + } + + String uri = request.getRequestURI(); + if (StringUtils.inStringIgnoreCase(uri, ".json", ".xml")) { + return true; + } + + String ajax = request.getParameter("__ajax"); + return StringUtils.inStringIgnoreCase(ajax, "json", "xml"); + } + + /** + * 判断User-Agent 是不是来自于手机 + */ + public static boolean checkAgentIsMobile(String ua) { + boolean flag = false; + if (!ua.contains("Windows NT") || (ua.contains("Windows NT") && ua.contains("compatible; MSIE 9.0;"))) { + // 排除 苹果桌面系统 + if (!ua.contains("Windows NT") && !ua.contains("Macintosh")) { + for (String item : agent) { + if (ua.contains(item)) { + flag = true; + break; + } + } + } + } + return flag; + } +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/utils/SpringUtil.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/utils/SpringUtil.java new file mode 100644 index 0000000..54dbbf1 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/utils/SpringUtil.java @@ -0,0 +1,24 @@ +package com.ssdmn.common.utils; + +import cn.hutool.core.util.ObjectUtil; + +import static com.ssdmn.common.contants.SpringConstants.*; + +/** + * 环境相关工具类 + * @author 郭世旭 + */ +public class SpringUtil extends cn.hutool.extra.spring.SpringUtil { + + public static boolean isDev(){ + return ObjectUtil.equals(cn.hutool.extra.spring.SpringUtil.getActiveProfile(), DEV); + } + + public static boolean isTest(){ + return ObjectUtil.equals(cn.hutool.extra.spring.SpringUtil.getActiveProfile(), TEST); + } + + public static boolean isProd(){ + return ObjectUtil.equals(cn.hutool.extra.spring.SpringUtil.getActiveProfile(), PROD); + } +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/utils/SpringUtils.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/utils/SpringUtils.java new file mode 100644 index 0000000..449b722 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/utils/SpringUtils.java @@ -0,0 +1,146 @@ +package com.ssdmn.common.utils; + +import com.ssdmn.common.str.StringUtils; +import org.springframework.aop.framework.AopContext; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.NoSuchBeanDefinitionException; +import org.springframework.beans.factory.config.BeanFactoryPostProcessor; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.stereotype.Component; + +/** + * spring工具类 方便在非spring管理环境中获取bean + * + * @author 郭世旭 + */ +@Component +public final class SpringUtils implements BeanFactoryPostProcessor, ApplicationContextAware +{ + /** Spring应用上下文环境 */ + private static ConfigurableListableBeanFactory beanFactory; + + private static ApplicationContext applicationContext; + + @Override + public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException + { + SpringUtils.beanFactory = beanFactory; + } + + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException + { + SpringUtils.applicationContext = applicationContext; + } + + /** + * 获取对象 + * + * @param name + * @return Object 一个以所给名字注册的bean的实例 + * @throws BeansException + * + */ + @SuppressWarnings("unchecked") + public static T getBean(String name) throws BeansException + { + return (T) beanFactory.getBean(name); + } + + /** + * 获取类型为requiredType的对象 + * + * @param clz + * @return + * @throws BeansException + * + */ + public static T getBean(Class clz) throws BeansException + { + T result = (T) beanFactory.getBean(clz); + return result; + } + + /** + * 如果BeanFactory包含一个与所给名称匹配的bean定义,则返回true + * + * @param name + * @return boolean + */ + public static boolean containsBean(String name) + { + return beanFactory.containsBean(name); + } + + /** + * 判断以给定名字注册的bean定义是一个singleton还是一个prototype。 如果与给定名字相应的bean定义没有被找到,将会抛出一个异常(NoSuchBeanDefinitionException) + * + * @param name + * @return boolean + * @throws NoSuchBeanDefinitionException + * + */ + public static boolean isSingleton(String name) throws NoSuchBeanDefinitionException + { + return beanFactory.isSingleton(name); + } + + /** + * @param name + * @return Class 注册对象的类型 + * @throws NoSuchBeanDefinitionException + * + */ + public static Class getType(String name) throws NoSuchBeanDefinitionException + { + return beanFactory.getType(name); + } + + /** + * 如果给定的bean名字在bean定义中有别名,则返回这些别名 + * + * @param name + * @return + * @throws NoSuchBeanDefinitionException + * + */ + public static String[] getAliases(String name) throws NoSuchBeanDefinitionException + { + return beanFactory.getAliases(name); + } + + /** + * 获取aop代理对象 + * + * @param invoker + * @return + */ + @SuppressWarnings("unchecked") + public static T getAopProxy(T invoker) + { + return (T) AopContext.currentProxy(); + } + + /** + * 获取当前的环境配置,无配置返回null + * + * @return 当前的环境配置 + */ + public static String[] getActiveProfiles() + { + return applicationContext.getEnvironment().getActiveProfiles(); + } + + /** + * 获取当前的环境配置,当有多个环境配置时,只获取第一个 + * + * @return 当前的环境配置 + */ + public static String getActiveProfile() + { + final String[] activeProfiles = getActiveProfiles(); + return StringUtils.isNotEmpty(activeProfiles) ? activeProfiles[0] : null; + } +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/utils/StringUtil.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/utils/StringUtil.java new file mode 100644 index 0000000..81d9c28 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/utils/StringUtil.java @@ -0,0 +1,138 @@ +package com.ssdmn.common.utils; + +import com.alibaba.fastjson.JSONArray; + +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * 字符串工具类 + * + * @author 郭世旭 + * @version 1.0 + * @createTime 2021-03-22 09:58:30 + */ + +public final class StringUtil { + + private StringUtil() { + + } + + + /** + * 判断字符串是否为空 + * + * @param str 待验证的字符串 + * @return true 为空 false 不为空 + */ + public static boolean isEmpty(Object str) { + return (str == null || str.toString().isEmpty() || "null".equalsIgnoreCase(str.toString())); + } + + + /** + * 判断字符串是否以某个指定字符开始 + * + * @param str + * @return + */ + public static boolean isStartWith(String str, String tar) { + return (!str.isEmpty() && str.startsWith(tar)); + } + + + /** + * 忽略大小写比较 + */ + public static boolean equalsIgnoreCase(String str1, String str2) { + + if (str1 == null && str2 == null) { + return true; + } else if (str1 != null) { + return str1.equalsIgnoreCase(str2); + } + return false; + + } + + + /** + * 把逗号隔开字符串转为List + * + * @param str 待验证的字符串 + * @return 空返回defaultStr,非空返回Str + */ + public static List conversionListLong(String str) { + List longList = new ArrayList<>(); + if (StringUtil.isEmpty(str)) { + return longList; + } + + String[] split = str.split(","); + for (String s : split) { + longList.add(Long.parseLong(s)); + } + + return longList; + + } + + + /** + * 把json隔开字符串转为List + * + * @param str 待验证的字符串 + * @return 空返回defaultStr,非空返回Str + */ + public static List conversionJsonListLong(String str) { + List longList = new ArrayList<>(); + if (StringUtil.isEmpty(str)) { + return longList; + } + JSONArray jsonArray = JSONArray.parseArray(str); + for (int i = 0; i < jsonArray.size(); i++) { + longList.add(jsonArray.getLong(i)); + } + + + return longList; + + } + + /** + * 电话号码验证 + * + * @param phone + * @return + */ + public static boolean isPhone(String phone) { + if (isEmpty(phone) || phone.length() != 11) { + return false; + } else { + String regex = "^((13[0-9])|(14[5-9])|(15([0-3]|[5-9]))|(166)|(17[0-8])|(18[0-9])|(19[5|8|9]))\\d{8}$"; + Pattern p = Pattern.compile(regex); + Matcher m = p.matcher(phone); + return m.matches(); + } + + } + + /** + * 判断是否是数字,true 为数字 + * + * @param str + * @return + */ + public static boolean isNumeric(String str) { + for (int i = 0; i < str.length(); i++) { + if (!Character.isDigit(str.charAt(i))) { + return false; + } + } + return true; + } + +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/utils/UserAgentUtil.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/utils/UserAgentUtil.java new file mode 100644 index 0000000..bf32351 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/utils/UserAgentUtil.java @@ -0,0 +1,20 @@ +package com.ssdmn.common.utils; + +import cn.hutool.http.useragent.UserAgent; +import cn.hutool.http.useragent.UserAgentParser; + +/** + * 请求节点信息工具类 + * @author 郭世旭 + */ +public class UserAgentUtil extends cn.hutool.http.useragent.UserAgentUtil { + + /** + * 解析User-Agent + * + * @return {@link UserAgent} + */ + public static UserAgent parse() { + return UserAgentParser.parse("User-Agent"); + } +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/utils/UserRedis.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/utils/UserRedis.java new file mode 100644 index 0000000..77a674f --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/common/utils/UserRedis.java @@ -0,0 +1,99 @@ +package com.ssdmn.common.utils; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import com.alibaba.fastjson.JSONObject; +import com.ssdmn.biz.login.pojo.vo.BaseUserVO; +import com.ssdmn.biz.login.pojo.vo.UserRedisVO; +import com.ssdmn.biz.login.pojo.vo.UserVO; +import com.ssdmn.common.redis.service.RedisService; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; + +import static com.ssdmn.biz.contants.Constants.RedisPrefix.REDIS_PREFIX_USER_LOGIN; + + +/** + * @description: 查询用户缓存信息 + * @author: 郭世旭 + * @create: 2021-04-22 00:51 + **/ +@Component +public class UserRedis { + + static RedisService redisService; + + @Resource + public void setRedisService(RedisService redisService) { + UserRedis.redisService = redisService; + } + + public static UserRedisVO getUserRedisVO(Long userId){ + + return JSONObject + .parseObject(redisService.getStr(StrUtil.format(REDIS_PREFIX_USER_LOGIN, userId)), UserRedisVO.class); + } + + /** + * 获取用户基本信息 + * @param userId + * @return + */ + public static BaseUserVO getBaseUserVO(Long userId) { + UserRedisVO userRedisVO = getUserRedisVO(userId); + + if(ObjectUtil.isEmpty(userRedisVO)){ + return null; + } + assert userRedisVO != null; + return userRedisVO.getBaseUserVo(); + } + + /** + * 用户再redis中是否存在 + * @param userId + * @return + */ + public static boolean userIsExistRedis(Long userId) { + UserRedisVO userRedisVO = getUserRedisVO(userId); + if(ObjectUtil.isEmpty(userRedisVO)){ + return false; + } + + return true; + } + + /** + * 通过userId获取用户token + * @param userId + * @return + */ + public static String getTokenByUserId(Long userId) { + UserRedisVO userRedisVO = getUserRedisVO(userId); + + return userRedisVO.getToken(); + } + + /** + * 存储用户信息到redis + */ + public static void setUserInfo2Redis(UserVO userVO, String token){ + UserRedisVO userRedisVO = BeanUtil.toBean(userVO, UserRedisVO.class); + userRedisVO.setBaseUserVo(userVO.getBaseUserVo()); + userRedisVO.setToken(token); + + redisService.set(StrUtil.format(REDIS_PREFIX_USER_LOGIN,userVO.getBaseUserVo().getId()), userRedisVO); + } + + /** + * 更新用户redis信息 + * @param userVO + */ + public static void updateRedisUserInfo(UserVO userVO) { + UserRedisVO userRedisVO = UserRedis.getUserRedisVO(userVO.getBaseUserVo().getId()); + + UserRedis.setUserInfo2Redis(userVO,userRedisVO.getToken()); + } +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/config/GlobalExceptionHandler.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/config/GlobalExceptionHandler.java new file mode 100644 index 0000000..597b59f --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/config/GlobalExceptionHandler.java @@ -0,0 +1,139 @@ +package com.ssdmn.config; + + +import cn.hutool.core.util.StrUtil; +import com.fasterxml.jackson.databind.exc.InvalidFormatException; +import com.ssdmn.common.exception.BizException; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.converter.HttpMessageNotReadableException; +import org.springframework.validation.BindException; +import org.springframework.validation.BindingResult; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.MissingServletRequestParameterException; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +import static com.ssdmn.common.result.Results.fail; + + +/** + * 全局捕获异常 + * + * @author gsx + */ +@RestControllerAdvice +@Slf4j +class GlobalExceptionHandler { + + /** + * 拦截参数不匹配异常 + * + * @param e + * @return + */ + @ResponseBody + @ExceptionHandler({MethodArgumentNotValidException.class, BindException.class}) + public Object handleException(MethodArgumentNotValidException e) { + BindingResult bindingResult = e.getBindingResult(); + if (bindingResult.hasErrors()) { + String messages = bindingResult.getFieldErrors() + .stream() + .map(fieldError -> StrUtil.format("异常参数字段名->{} 参数值->{} 错误信息->{}", fieldError.getField(), + fieldError.getRejectedValue(), fieldError.getDefaultMessage())) + .reduce((m1, m2) -> StrUtil.format("{};{}", m1, m2)) + .orElse("参数输入有误"); + log.error("参数异常信息->{}", messages); + return fail(600, messages); + } + + log.error("参数异常信息->{}", e.getMessage()); + return fail(e.getMessage()); + } + + /** + * 拦截系统参数不匹配异常 + * + * @param e + * @return + */ + @ResponseBody + @ExceptionHandler(InvalidFormatException.class) + public Object handleException(InvalidFormatException e) { + String value = e.getValue().toString(); + String type = e.getTargetType().getTypeName(); + log.error("参数不匹配异常:code->{},error->{}",400,value+"不是"+type+"类型!"); + return fail(400, value+"不是"+type+"类型!或者请求类型错误!"); + } + + /** + * 拦截参数不匹配异常 + * + * @param e + * @return + */ + @ResponseBody + @ExceptionHandler({MissingServletRequestParameterException.class}) + public Object handleException(MissingServletRequestParameterException e) { + String value = e.getParameterName(); + String type = e.getParameterType(); + log.error("参数不匹配异常:code->{},error->{}",400,value+"不是"+type+"类型!"); + return fail(400, value+"不是"+type+"类型!"); + } + + /** + * 拦截参数不匹配异常 + * + * @param e + * @return + */ + @ResponseBody + @ExceptionHandler({HttpMessageNotReadableException.class}) + public Object handleException(HttpMessageNotReadableException e) { + log.error("类型不匹配:error->{}",e.getMessage()); + e.printStackTrace(); + return fail(e.getMessage()); + } + + /** + * 拦截参数不匹配异常 + * + * @param e + * @return + */ + @ResponseBody + @ExceptionHandler({IllegalArgumentException.class}) + public Object handleException(IllegalArgumentException e) { + log.error("类型不匹配:error->{}",e.getMessage()); + e.printStackTrace(); + return fail("类型不匹配->"+e.getMessage()); + } + + /** + * 拦截自定义异常 + * + * @param e + * @return + */ + @ResponseBody + @ExceptionHandler(BizException.class) + public Object handleException(BizException e) { + e.printStackTrace(); + log.error("业务异常:code->{},error->{}",e.getCode(),e.getMessage()); + return fail(e.getCode(), e.getMessage(),e.getData()); + } + + /** + * 拦截自定义异常 + * + * @param e + * @return + */ + @ResponseBody + @ExceptionHandler(Exception.class) + public Object handleException(Exception e) { + e.printStackTrace(); + log.error("Exception异常:error->{}",e.getMessage()); + return fail("系统繁忙"); + } +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/config/MybatisPlusConfig.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/config/MybatisPlusConfig.java new file mode 100644 index 0000000..24cffd0 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/config/MybatisPlusConfig.java @@ -0,0 +1,64 @@ +package com.ssdmn.config; + +import cn.hutool.extra.spring.SpringUtil; +import com.baomidou.mybatisplus.annotation.DbType; +import com.baomidou.mybatisplus.autoconfigure.ConfigurationCustomizer; +import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; +import com.baomidou.mybatisplus.extension.plugins.handler.TenantLineHandler; +import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor; +import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; +import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor; +import com.ssdmn.common.sql.SqlInterceptor; +import net.sf.jsqlparser.expression.Expression; +import net.sf.jsqlparser.expression.LongValue; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import java.util.Arrays; +import java.util.List; + +/** + * Mybatis-plus 配置 + * + * @author 郭世旭 + * @version 1.0 + * @createTime 2021-03-22 09:58:30 + */ +@Configuration +public class MybatisPlusConfig { + + /** + * Mybatis-plus分页插件 + * 新的分页插件,一缓和二缓遵循mybatis的规则 + * 需要设置 MybatisConfiguration#useDeprecatedExecutor = false 避免缓存出现问题 + */ + @Bean + public MybatisPlusInterceptor mybatisPlusInterceptor() { + MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); + interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); + return interceptor; + } + + @Bean + public SqlInterceptor sqlInterceptor() { + return new SqlInterceptor(); + } + + @Bean + public ConfigurationCustomizer configurationCustomizer() { + return configuration -> configuration.setUseDeprecatedExecutor(false); + } + + /** + * 乐观锁组件 + * + * @return + */ + @Bean + public MybatisPlusInterceptor optimisticLockerInnerInterceptor() { + MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); + interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor()); + return interceptor; + } +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/config/ParamConvertConfig.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/config/ParamConvertConfig.java new file mode 100644 index 0000000..74f4e2a --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/config/ParamConvertConfig.java @@ -0,0 +1,25 @@ +package com.ssdmn.config; + +import cn.hutool.core.util.StrUtil; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.convert.converter.Converter; + +/** + * 参数去空格 + * + * @author 郭世旭 + * @version 1.0 + * @createTime 2021-03-22 09:58:30 + */ +@Configuration +public class ParamConvertConfig implements Converter { + + @Override + public String convert(String source) { + if (StrUtil.isNotBlank(source)) { + return source.trim(); + } + + return source; + } +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/config/ParamsInterceptor.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/config/ParamsInterceptor.java new file mode 100644 index 0000000..677143c --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/config/ParamsInterceptor.java @@ -0,0 +1,79 @@ +package com.ssdmn.config; + +import cn.hutool.core.util.RandomUtil; +import cn.hutool.core.util.StrUtil; +import com.ssdmn.common.http.HttpHelper; +import com.ssdmn.common.ip.IpUtils; +import com.ssdmn.config.filter.RepeatedlyRequestWrapper; +import com.ssdmn.framework.handler.interceptor.DefaultHandlerInterceptor; +import lombok.extern.slf4j.Slf4j; +import org.slf4j.MDC; +import org.springframework.stereotype.Component; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.time.Instant; +import java.util.Enumeration; + +import static com.ssdmn.common.result.SysConstants.SSDMN_TOKEN_KEY; + +/** + * 请求参数拦截器 + * + * @author 郭世旭 + * @version 1.0 + * @createTime 2021-03-22 09:58:30 + */ +@Slf4j +@Component +public class ParamsInterceptor extends DefaultHandlerInterceptor { + private static final String TRACE_ID = "traceId"; + + @Override + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object o) { + //调用开始时间 + MDC.put("reqTime", String.valueOf(Instant.now().toEpochMilli())); + //获取前端请求携带ID + String traceId = request.getHeader(TRACE_ID); + //设置日志ID + MDC.put(TRACE_ID, StrUtil.isNotBlank(traceId) ? traceId : RandomUtil.randomString(13).toUpperCase()); + + StringBuilder sb = new StringBuilder(); + sb.append("接口请求:=======").append(request.getServletPath()).append("==方法:").append(request.getMethod()).append("==开始调用======="); + log.info(sb.toString()); + sb = new StringBuilder(); + sb.append("requestParam请求参数:"); + Enumeration parameterNames = request.getParameterNames(); + while (parameterNames.hasMoreElements()) { + String paramName = parameterNames.nextElement(); + String value = request.getParameter(paramName); + if (StrUtil.isNotBlank(value)) { + sb.append(paramName).append("=").append(value).append(" "); + } + } + log.info(sb.toString()); + sb = new StringBuilder(); + sb.append("requestBody请求参数:"); + if (request instanceof RepeatedlyRequestWrapper) + { + RepeatedlyRequestWrapper repeatedlyRequest = (RepeatedlyRequestWrapper) request; + sb.append(HttpHelper.getBodyString(repeatedlyRequest)); + } + log.info(sb.toString()); + String token = request.getHeader(SSDMN_TOKEN_KEY); + log.info("用户token->{}",token); + return true; + } + + @Override + public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { + long reqTime = Long.parseLong(MDC.get("reqTime")); + log.info("调用完成:=======" + request.getServletPath() + "==方法:" + request.getMethod() + "==耗时:" + + (Instant.now().toEpochMilli() - reqTime) + "==IP:" + IpUtils.getIpAddr(request) + "=======" + "\n"); + } + + @Override + public int getOrder() { + return Integer.MIN_VALUE; + } +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/config/SwaggerConfig.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/config/SwaggerConfig.java new file mode 100644 index 0000000..5a78f05 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/config/SwaggerConfig.java @@ -0,0 +1,85 @@ +package com.ssdmn.config; + + +import com.ssdmn.common.result.SysConstants; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Profile; +import springfox.documentation.builders.ApiInfoBuilder; +import springfox.documentation.builders.PathSelectors; +import springfox.documentation.builders.RequestHandlerSelectors; +import springfox.documentation.service.*; +import springfox.documentation.spi.DocumentationType; +import springfox.documentation.spi.service.contexts.SecurityContext; +import springfox.documentation.spring.web.plugins.Docket; +import springfox.documentation.swagger2.annotations.EnableSwagger2; + +import java.util.ArrayList; +import java.util.List; + +/** + * swagger配置 + * + * @author 郭世旭 + */ +@Configuration +@EnableSwagger2 +@Profile("dev") +public class SwaggerConfig { + + @Bean + public Docket createUserApi() { + return new Docket(DocumentationType.SWAGGER_2) + .apiInfo(apiInfo()) + .select() + .apis(RequestHandlerSelectors.basePackage("com.ssdmn.biz")) + .paths(PathSelectors.any()) + .build() + .groupName("chat") + .securitySchemes(securitySchemes()) + .securityContexts(securityContexts()); + } + /** + * 为swagger 添加token框 + */ + private List securitySchemes() { + List list = new ArrayList<>(); + list.add(new ApiKey(SysConstants.SSDMN_TOKEN_KEY, SysConstants.SSDMN_TOKEN_KEY, "header")); + return list; + } + + private List securityContexts() { + List list = new ArrayList<>(); + list.add(SecurityContext.builder() + .securityReferences(defaultAuth()) + .forPaths(PathSelectors.regex("^(?!auth).*$")) + .build()); + return list; + } + + List defaultAuth() { + AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything"); + AuthorizationScope[] authorizationScopes = new AuthorizationScope[1]; + authorizationScopes[0] = authorizationScope; + +// AuthorizationScope paramsScope = new AuthorizationScope("params", "accessEverything2"); +// AuthorizationScope[] paramsScopes = new AuthorizationScope[1]; +// paramsScopes[0] = paramsScope; + + List list = new ArrayList<>(); + list.add(new SecurityReference(SysConstants.SSDMN_TOKEN_KEY, authorizationScopes)); +// list.add(new SecurityReference(SysConstants.PARAMS, paramsScopes)); + return list; + } + + private ApiInfo apiInfo() { + return new ApiInfoBuilder() + .title("api文档") + .description("") + .termsOfServiceUrl("") + .version("1.0") + .build(); + } + + +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/config/SwaggerShowMonitor.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/config/SwaggerShowMonitor.java new file mode 100644 index 0000000..519d0f9 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/config/SwaggerShowMonitor.java @@ -0,0 +1,111 @@ +package com.ssdmn.config; + + +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.ReflectUtil; +import cn.hutool.core.util.StrUtil; +import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition; +import com.ssdmn.common.annotation.swagger.SwaggerFiledExplain; +import io.swagger.annotations.ApiModelProperty; +import lombok.extern.slf4j.Slf4j; +import org.springframework.core.annotation.Order; +import org.springframework.stereotype.Component; +import springfox.documentation.schema.Annotations; +import springfox.documentation.spi.DocumentationType; +import springfox.documentation.spi.schema.ModelPropertyBuilderPlugin; +import springfox.documentation.spi.schema.contexts.ModelPropertyContext; +import springfox.documentation.swagger.schema.ApiModelProperties; + +import java.lang.reflect.Field; +import java.util.Arrays; +import java.util.Objects; +import java.util.Optional; + +/** + * swagger配置 + * + * @author 郭世旭 + */ +@Component +@Slf4j +@Order +public class SwaggerShowMonitor implements ModelPropertyBuilderPlugin { + + @Override + public void apply(ModelPropertyContext context) { + + //为枚举字段设置注释 + Optional annotation = Optional.empty(); + if (ObjectUtil.isNotEmpty(context.getAnnotatedElement())&&context.getAnnotatedElement().isPresent()) { + annotation = Optional.ofNullable(annotation.orElseGet( + ApiModelProperties.findApiModePropertyAnnotation(context.getAnnotatedElement().get())::get)); + } + if (ObjectUtil.isNotEmpty(context.getBeanPropertyDefinition())&&context.getBeanPropertyDefinition().isPresent()) { + Optional beanPropertyDefinitionOptional = context.getBeanPropertyDefinition(); + if(beanPropertyDefinitionOptional.isPresent()){ + BeanPropertyDefinition beanPropertyDefinition = context.getBeanPropertyDefinition().get(); + Optional propertyAnnotation = Annotations.findPropertyAnnotation(beanPropertyDefinition, ApiModelProperty.class); + if(propertyAnnotation.isPresent()){ + annotation = Optional.ofNullable(annotation.orElseGet(propertyAnnotation::get)); + } + } + } + if(!annotation.isPresent()){ + return; + } + String value = annotation.get().value(); + String url = annotation.get().notes(); + if (ObjectUtil.isEmpty(url)) { + return; + } + + Class swaggerParamsClass = tryGetClassForUrl(url); + + Field[] fields = ReflectUtil.getFields(swaggerParamsClass); + if(ObjectUtil.isEmpty(fields)){ + return; + } + + String notes = Arrays.stream(fields).filter(Objects::nonNull) + .filter(field -> ObjectUtil.isNotEmpty(ReflectUtil.getFieldValue(null,field))) + .map(field -> { + String filedValue = ObjectUtil.toString(ReflectUtil.getFieldValue(null, field)); + String annotationValue = field.getAnnotation(SwaggerFiledExplain.class).value(); + + return StrUtil.format("{}-{}", filedValue, annotationValue); + }) + .reduce((m1, m2) -> StrUtil.format("{},{}",m1,m2)) + .orElse(""); + + context.getSpecificationBuilder().description(StrUtil.concat(true,value,": ",notes)); + + } + + @Override + public boolean supports(DocumentationType documentationType) { + return true; + } + + /** + * 获取notes指代的接口类 + * @param url + * @return + */ + private Class tryGetClassForUrl(String url){ + while (true){ + try { + return Class.forName(url); + } catch (ClassNotFoundException e) { + int index = url.lastIndexOf("."); + if(ObjectUtil.equal(index,StrUtil.INDEX_NOT_FOUND)){ + break; + } + url = StrUtil.replace(url,index,index+1, '$'); + } + } + + return null; + } + + +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/config/WebConfigurer.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/config/WebConfigurer.java new file mode 100644 index 0000000..cc35482 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/config/WebConfigurer.java @@ -0,0 +1,75 @@ +package com.ssdmn.config; + +import cn.hutool.extra.spring.SpringUtil; +import com.ssdmn.common.interceptor.AuthUserHandlerResolver; +import com.ssdmn.framework.handler.interceptor.DefaultHandlerInterceptor; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.method.support.HandlerMethodArgumentResolver; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; +import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +import javax.annotation.Resource; +import java.util.Comparator; +import java.util.List; + +/** + * 项目启动配置 + * + * @author 郭世旭 + * @version 1.0 + * @createTime 2021-03-22 09:58:30 + */ +@Configuration +public class WebConfigurer implements WebMvcConfigurer { + + @Resource + private AuthUserHandlerResolver authUserHandlerResolver; + + /** + * 拦截器 + * + * @param registry + */ + @Override + public void addInterceptors(InterceptorRegistry registry) { + SpringUtil.getBeansOfType(DefaultHandlerInterceptor.class) + .values() + .stream() + .sorted(Comparator.comparing(DefaultHandlerInterceptor::getOrder)) + .forEach((bean) -> { + //拦截所有url + registry.addInterceptor(bean).addPathPatterns("/**") + .excludePathPatterns("/swagger-resources/**", "/webjars/**", "/v2/**", "/doc.html/**"); + }); + } + + /** + * 配置springboot支持 HandlerMethodArgumentResolver(参数解释器) + * + * @param resolvers + */ + @Override + public void addArgumentResolvers(List resolvers) { + resolvers.add(authUserHandlerResolver); + } + + /** + * 映射swgger访问,上线注释掉 + * + * @param registry + */ + @Override + public void addResourceHandlers(ResourceHandlerRegistry registry) { + registry.addResourceHandler("doc.html") + .addResourceLocations("classpath:/META-INF/resources/"); + + registry.addResourceHandler("/webjars/**") + .addResourceLocations("classpath:/META-INF/resources/webjars/"); + + registry.addResourceHandler("/**") + .addResourceLocations("classpath:/static/"); + } + + +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/config/banary/WxMpConfiguration.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/config/banary/WxMpConfiguration.java new file mode 100644 index 0000000..c92f296 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/config/banary/WxMpConfiguration.java @@ -0,0 +1,112 @@ +package com.ssdmn.config.banary; + +import cn.binarywang.wx.miniapp.api.WxMaService; +import cn.binarywang.wx.miniapp.api.impl.WxMaServiceImpl; +import cn.binarywang.wx.miniapp.config.impl.WxMaDefaultConfigImpl; +import lombok.AllArgsConstructor; +import me.chanjar.weixin.common.redis.RedisTemplateWxRedisOps; +import me.chanjar.weixin.mp.api.WxMpService; +import me.chanjar.weixin.mp.api.impl.WxMpServiceImpl; +import me.chanjar.weixin.mp.config.WxMpConfigStorage; +import me.chanjar.weixin.mp.config.impl.WxMpDefaultConfigImpl; +import me.chanjar.weixin.mp.config.impl.WxMpRedisConfigImpl; +import org.springframework.beans.factory.annotation.Autowire; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.core.StringRedisTemplate; + +import javax.annotation.Resource; +import java.util.HashMap; +import java.util.Map; + + +/** + * 加载微信配置 + * + * @author 郭世旭 + */ +@AllArgsConstructor +@Configuration +@EnableConfigurationProperties(WxMpProperties.class) +public class WxMpConfiguration { + + private final WxMpProperties properties; + + @Resource + private StringRedisTemplate stringRedisTemplate; + + /** + * 公众号配置 + * @return + */ + @Bean(autowire = Autowire.BY_NAME,value = "wxLoginMpService") + public WxMpService wxLoginMpService() { + // 代码里 getConfigs()处报错的同学,请注意仔细阅读项目说明,你的IDE需要引入lombok插件!!!! + if (properties == null) { + throw new RuntimeException("大哥,拜托先看下项目首页的说明(readme文件),添加下相关配置,注意别配错了!"); + } + WxMpService service = new WxMpServiceImpl(); + WxMpDefaultConfigImpl configStorage; + if (this.properties.isUseRedis()) { + configStorage = new WxMpRedisConfigImpl(new RedisTemplateWxRedisOps(stringRedisTemplate), properties.getAppId()); + } else { + configStorage = new WxMpDefaultConfigImpl(); + } + configStorage.setAppId(properties.getAppId()); + configStorage.setSecret(properties.getSecret()); + + Map configStorageMap = new HashMap(1); + configStorageMap.put(properties.getAppId(), configStorage); + service.setMultiConfigStorages(configStorageMap); + return service; + } + + /** + * 小程序配置 + * @return + */ + @Bean(autowire = Autowire.BY_NAME,value = "wxLoginMaService") + public WxMaService wxLoginMaService() { + // 代码里 getConfigs()处报错的同学,请注意仔细阅读项目说明,你的IDE需要引入lombok插件!!!! + if (properties == null) { + throw new RuntimeException("大哥,拜托先看下项目首页的说明(readme文件),添加下相关配置,注意别配错了!"); + } + WxMaDefaultConfigImpl config = new WxMaDefaultConfigImpl(); + config.setAppid(properties.getMaAppId()); + config.setSecret(properties.getMaSecret()); + config.setToken(properties.getToken()); + config.setAesKey(properties.getAesKey()); + config.setMsgDataFormat("JSON"); + WxMaService service = new WxMaServiceImpl(); + service.setWxMaConfig(config); + return service; + } + + /** + * 开放平台配置 + * @return + */ + @Bean(autowire = Autowire.BY_NAME,value = "wxLoginOpenService") + public WxMpService wxLoginOpenService() { + // 代码里 getConfigs()处报错的同学,请注意仔细阅读项目说明,你的IDE需要引入lombok插件!!!! + if (properties == null) { + throw new RuntimeException("大哥,拜托先看下项目首页的说明(readme文件),添加下相关配置,注意别配错了!"); + } + WxMpService service = new WxMpServiceImpl(); + WxMpDefaultConfigImpl configStorage; + if (this.properties.isUseRedis()) { + configStorage = new WxMpRedisConfigImpl(new RedisTemplateWxRedisOps(stringRedisTemplate), properties.getOpenAppId()); + } else { + configStorage = new WxMpDefaultConfigImpl(); + } + configStorage.setAppId(properties.getOpenAppId()); + configStorage.setSecret(properties.getOpenSecret()); + configStorage.setToken(properties.getToken()); + configStorage.setAesKey(properties.getAesKey()); + Map configStorageMap = new HashMap<>(1); + configStorageMap.put(properties.getOpenAppId(), configStorage); + service.setMultiConfigStorages(configStorageMap); + return service; + } +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/config/banary/WxMpProperties.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/config/banary/WxMpProperties.java new file mode 100644 index 0000000..995e503 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/config/banary/WxMpProperties.java @@ -0,0 +1,59 @@ +package com.ssdmn.config.banary; + +import com.alibaba.fastjson.JSON; +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * 微信公众平台和开放平台配置 + * + * @author 郭世旭 + */ +@Data +@ConfigurationProperties(prefix = "wx.mp") +public class WxMpProperties { + + /** + * 是否使用redis存储access token + */ + private boolean useRedis; + /** + * 设置微信公众号的appid + */ + private String appId; + /** + * 设置微信公众号的app secret + */ + private String secret; + /** + * 微信开放平台的appid + */ + private String openAppId; + /** + * 微信开放平台的secret + */ + private String openSecret; + /** + * 微信小程序的appid + */ + private String maAppId; + /** + * 微信小程序的secret + */ + private String maSecret; + /** + * 设置微信公众号的token + */ + private String token; + + /** + * 设置微信公众号的EncodingAESKey + */ + private String aesKey; + + + @Override + public String toString() { + return JSON.toJSONString(this); + } +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/config/filter/ActivityInfoFilter.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/config/filter/ActivityInfoFilter.java new file mode 100644 index 0000000..c0e303e --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/config/filter/ActivityInfoFilter.java @@ -0,0 +1,93 @@ +package com.ssdmn.config.filter; + +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.filter.OncePerRequestFilter; + +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + + +/** + * 活动时间 过滤器 + * 只有上线时才会开启 + * + * @author 郭世旭 + */ +@Slf4j +public class ActivityInfoFilter extends OncePerRequestFilter { + + /** + * 请求前缀 + */ + private final String contextPath; + + public ActivityInfoFilter(String contextPath) { + this.contextPath = contextPath; + } + + /** + * 路径白名单 + */ + private static final String[] WHITE_LIST = {"/game/getConfig", + "/game/listMyPrize", + "/game/saveReceiptInfo", + "/game/updateReceiptInfo", + "/game/getReceiptInfo/*"}; + + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { + if (!isWhiteList(request.getRequestURI())) { +// HandlerExceptionResolver resolver = SpringUtil.getBean("handlerExceptionResolver"); +// try { +// checkActivityTime(getActivityTime()); +// } catch (Exception e) { +// resolver.resolveException(request, response, null, e); +// return; +// } + } + filterChain.doFilter(request, response); + } + + /** + * 判断url是否在白名单中,支持通配符‘*’ + * + * @param url 请求路径 + * @return 是否在白名单中 + */ + private boolean isWhiteList(String url) { + + if (ObjectUtil.isEmpty(WHITE_LIST)) { + return false; + } + if (ObjectUtil.isEmpty(url)) { + return false; + } + String[] urlStrArr = url.split("/"); + for (String white : WHITE_LIST) { + white = StrUtil.format("/{}/{}", contextPath, white).replaceAll("/+", "/"); + if (ObjectUtil.isEmpty(white)) { + continue; + } + String[] whiteStrArr = white.split("/"); + if (whiteStrArr.length != urlStrArr.length) { + continue; + } + for (int i = 0; i < urlStrArr.length; i++) { + if (ObjectUtil.notEqual(whiteStrArr[i], urlStrArr[i]) && ObjectUtil.notEqual(whiteStrArr[i], "*")) { + break; + } + if (urlStrArr.length - 1 == i) { + return true; + } + } + } + + return false; + } + +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/config/filter/FilterConfig.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/config/filter/FilterConfig.java new file mode 100644 index 0000000..ebac4c6 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/config/filter/FilterConfig.java @@ -0,0 +1,108 @@ +package com.ssdmn.config.filter; + +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.web.servlet.FilterRegistrationBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Profile; +import org.springframework.web.cors.CorsConfiguration; +import org.springframework.web.cors.UrlBasedCorsConfigurationSource; +import org.springframework.web.filter.CorsFilter; + +import javax.servlet.DispatcherType; +import java.util.HashMap; +import java.util.Map; + +/** + * Filter配置 + * + * @author ruoyi + */ +@Configuration +public class FilterConfig +{ + @Value("${xss.excludes}") + private String excludes; + + @Value("${xss.urlPatterns}") + private String urlPatterns; + + @Value("#{'${server.servlet.context-path}'}") + private String contextPath; + + @SuppressWarnings({ "rawtypes", "unchecked" }) + @Bean + @ConditionalOnProperty(value = "xss.enabled", havingValue = "true") + public FilterRegistrationBean xssFilterRegistration() + { + FilterRegistrationBean registration = new FilterRegistrationBean(); + registration.setDispatcherTypes(DispatcherType.REQUEST); + registration.setFilter(new XssFilter()); + registration.addUrlPatterns(StringUtils.split(urlPatterns, ",")); + registration.setName("xssFilter"); + registration.setOrder(FilterRegistrationBean.HIGHEST_PRECEDENCE); + Map initParameters = new HashMap(); + initParameters.put("excludes", excludes); + registration.setInitParameters(initParameters); + return registration; + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + @Bean + public FilterRegistrationBean someFilterRegistration() + { + FilterRegistrationBean registration = new FilterRegistrationBean(); + registration.setFilter(new RepeatableFilter()); + registration.addUrlPatterns("/*"); + registration.setName("repeatableFilter"); + registration.setOrder(FilterRegistrationBean.LOWEST_PRECEDENCE); + return registration; + } + + + @SuppressWarnings({ "rawtypes", "unchecked" }) + @Bean + public FilterRegistrationBean corsFilter() + { + FilterRegistrationBean registration = new FilterRegistrationBean(); + //添加cors配置信息 + CorsConfiguration config = new CorsConfiguration(); +// config.addAllowedOrigin("*"); + config.addAllowedOriginPattern("*"); + //设置是否发送cookie信息 + config.setAllowCredentials(true); + //设置允许的请求方式 + config.addAllowedMethod("*"); + //设置允许的header + config.addAllowedHeader("*"); + + //为url添加映射路径 + UrlBasedCorsConfigurationSource corsSource = new UrlBasedCorsConfigurationSource(); + corsSource.registerCorsConfiguration("/**", config); + //返回重新定义好的source + registration.setFilter(new CorsFilter(corsSource)); + registration.addUrlPatterns("/*"); + registration.setName("corsFilter"); + registration.setOrder(FilterRegistrationBean.LOWEST_PRECEDENCE); + + return registration; + } + +// @SuppressWarnings({ "rawtypes", "unchecked" }) +// @Bean +// @Profile("prod") +// public FilterRegistrationBean activityInfoFilter() +// { +// FilterRegistrationBean registration = new FilterRegistrationBean(); +// ActivityInfoFilter activityInfoFilter = new ActivityInfoFilter(contextPath); +// registration.setFilter(activityInfoFilter); +// registration.addUrlPatterns("/*"); +// registration.setName("activityInfoFilter"); +// registration.setOrder(FilterRegistrationBean.LOWEST_PRECEDENCE); +// +// return registration; +// } + +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/config/filter/RepeatableFilter.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/config/filter/RepeatableFilter.java new file mode 100644 index 0000000..ca44e0e --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/config/filter/RepeatableFilter.java @@ -0,0 +1,49 @@ +package com.ssdmn.config.filter; + +import org.apache.commons.lang3.StringUtils; +import org.springframework.http.MediaType; + +import javax.servlet.FilterConfig; +import javax.servlet.*; +import javax.servlet.http.HttpServletRequest; +import java.io.IOException; + +/** + * Repeatable 过滤器 + * + * @author 郭世旭 + */ +public class RepeatableFilter implements Filter +{ + @Override + public void init(FilterConfig filterConfig) throws ServletException + { + + } + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) + throws IOException, ServletException + { + ServletRequest requestWrapper = null; + if (request instanceof HttpServletRequest + && StringUtils.startsWithIgnoreCase(request.getContentType(), MediaType.APPLICATION_JSON_VALUE)) + { + requestWrapper = new RepeatedlyRequestWrapper((HttpServletRequest) request, response); + } + if (null == requestWrapper) + { + chain.doFilter(request, response); + } + else + { + chain.doFilter(requestWrapper, response); + } + } + + @Override + public void destroy() + { + + } +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/config/filter/RepeatedlyRequestWrapper.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/config/filter/RepeatedlyRequestWrapper.java new file mode 100644 index 0000000..fc021f5 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/config/filter/RepeatedlyRequestWrapper.java @@ -0,0 +1,77 @@ +package com.ssdmn.config.filter; + + +import com.ssdmn.common.http.HttpHelper; + +import javax.servlet.ReadListener; +import javax.servlet.ServletInputStream; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletRequestWrapper; +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStreamReader; + +/** + * 构建可重复读取inputStream的request + * + * @author 郭世旭 + */ +public class RepeatedlyRequestWrapper extends HttpServletRequestWrapper +{ + private final byte[] body; + + public RepeatedlyRequestWrapper(HttpServletRequest request, ServletResponse response) throws IOException + { + super(request); + request.setCharacterEncoding("UTF-8"); + response.setCharacterEncoding("UTF-8"); + + body = HttpHelper.getBodyString(request).getBytes("UTF-8"); + } + + @Override + public BufferedReader getReader() throws IOException + { + return new BufferedReader(new InputStreamReader(getInputStream())); + } + + @Override + public ServletInputStream getInputStream() throws IOException + { + final ByteArrayInputStream bais = new ByteArrayInputStream(body); + return new ServletInputStream() + { + @Override + public int read() throws IOException + { + return bais.read(); + } + + @Override + public int available() throws IOException + { + return body.length; + } + + @Override + public boolean isFinished() + { + return false; + } + + @Override + public boolean isReady() + { + return false; + } + + @Override + public void setReadListener(ReadListener readListener) + { + + } + }; + } +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/config/filter/XssFilter.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/config/filter/XssFilter.java new file mode 100644 index 0000000..36143b1 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/config/filter/XssFilter.java @@ -0,0 +1,72 @@ +package com.ssdmn.config.filter; + +import cn.hutool.core.util.ObjectUtil; +import com.ssdmn.common.str.StringUtils; + +import javax.servlet.FilterConfig; +import javax.servlet.*; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +/** + * 防止XSS攻击的过滤器 + * + * @author 郭世旭 + */ +public class XssFilter implements Filter +{ + /** + * 排除链接 + */ + public List excludes = new ArrayList<>(); + + @Override + public void init(FilterConfig filterConfig) throws ServletException + { + String tempExcludes = filterConfig.getInitParameter("excludes"); + if (ObjectUtil.isNotEmpty(tempExcludes)) + { + String[] url = tempExcludes.split(","); + for (int i = 0; url != null && i < url.length; i++) + { + excludes.add(url[i]); + } + } + } + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) + throws IOException, ServletException + { + HttpServletRequest req = (HttpServletRequest) request; + HttpServletResponse resp = (HttpServletResponse) response; + if (handleExcludeURL(req, resp)) + { + chain.doFilter(request, response); + return; + } + XssHttpServletRequestWrapper xssRequest = new XssHttpServletRequestWrapper((HttpServletRequest) request); + chain.doFilter(xssRequest, response); + } + + private boolean handleExcludeURL(HttpServletRequest request, HttpServletResponse response) + { + String url = request.getServletPath(); + String method = request.getMethod(); + // GET DELETE 不过滤 + if (method == null || method.matches("GET") || method.matches("DELETE")) + { + return true; + } + return StringUtils.matches(url, excludes); + } + + @Override + public void destroy() + { + + } +} \ No newline at end of file diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/config/filter/XssHttpServletRequestWrapper.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/config/filter/XssHttpServletRequestWrapper.java new file mode 100644 index 0000000..7324c13 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/config/filter/XssHttpServletRequestWrapper.java @@ -0,0 +1,112 @@ +package com.ssdmn.config.filter; + +import cn.hutool.core.util.ObjectUtil; +import com.ssdmn.common.html.EscapeUtil; +import org.apache.commons.io.IOUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; + +import javax.servlet.ReadListener; +import javax.servlet.ServletInputStream; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletRequestWrapper; +import java.io.ByteArrayInputStream; +import java.io.IOException; + +/** + * XSS过滤处理 + * + * @author 郭世旭 + */ +public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper +{ + /** + * @param request + */ + public XssHttpServletRequestWrapper(HttpServletRequest request) + { + super(request); + } + + @Override + public String[] getParameterValues(String name) + { + String[] values = super.getParameterValues(name); + if (values != null) + { + int length = values.length; + String[] escapseValues = new String[length]; + for (int i = 0; i < length; i++) + { + // 防xss攻击和过滤前后空格 + escapseValues[i] = EscapeUtil.clean(values[i]).trim(); + } + return escapseValues; + } + return super.getParameterValues(name); + } + + @Override + public ServletInputStream getInputStream() throws IOException + { + // 非json类型,直接返回 + if (!isJsonRequest()) + { + return super.getInputStream(); + } + + // 为空,直接返回 + String json = IOUtils.toString(super.getInputStream(), "utf-8"); + if (ObjectUtil.isEmpty(json)) + { + return super.getInputStream(); + } + + // xss过滤 + json = EscapeUtil.clean(json).trim(); + byte[] jsonBytes = json.getBytes("utf-8"); + final ByteArrayInputStream bis = new ByteArrayInputStream(jsonBytes); + return new ServletInputStream() + { + @Override + public boolean isFinished() + { + return true; + } + + @Override + public boolean isReady() + { + return true; + } + + @Override + public int available() throws IOException + { + return jsonBytes.length; + } + + @Override + public void setReadListener(ReadListener readListener) + { + } + + @Override + public int read() throws IOException + { + return bis.read(); + } + }; + } + + /** + * 是否是Json请求 + * + */ + public boolean isJsonRequest() + { + String header = super.getHeader(HttpHeaders.CONTENT_TYPE); + return StringUtils.startsWithIgnoreCase(header, MediaType.APPLICATION_JSON_VALUE); + } +} \ No newline at end of file diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/framework/handler/HandlerUtil.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/framework/handler/HandlerUtil.java new file mode 100644 index 0000000..3236de7 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/framework/handler/HandlerUtil.java @@ -0,0 +1,42 @@ +package com.ssdmn.framework.handler; + +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.extra.spring.SpringUtil; +import com.ssdmn.system.bean.Message; +import com.ssdmn.system.handler.MessageHandler; +import com.ssdmn.system.manager.AsyncFactory; +import com.ssdmn.system.manager.AsyncManager; +import lombok.extern.slf4j.Slf4j; + +import java.io.Serializable; +import java.util.Map; + +/** + * 处理器工具类 + * + * @author 郭世旭 + */ +@Slf4j +public class HandlerUtil { + + public static void asyncHandle(Class> clazz, T param) { + Map> beans = SpringUtil.getBeansOfType(clazz); + beans.forEach((beanName, bean) -> { + log.info("发放通知,beanName->{}", beanName); + // 异步执行实现类的方法 + AsyncManager.me(). + execute(AsyncFactory.createHandleTimerTask(bean, ObjectUtil.clone(Message.create(param)))); + } + ); + } + + public static void handle(Class> clazz, T param) { + + Map> beans = SpringUtil.getBeansOfType(clazz); + beans.forEach((beanName, bean) -> { + // 同步执行实现类的方法 + bean.handle(ObjectUtil.clone(Message.create(param))); + } + ); + } +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/framework/handler/interceptor/DefaultHandlerInterceptor.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/framework/handler/interceptor/DefaultHandlerInterceptor.java new file mode 100644 index 0000000..48daa6b --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/framework/handler/interceptor/DefaultHandlerInterceptor.java @@ -0,0 +1,14 @@ +package com.ssdmn.framework.handler.interceptor; + +import lombok.Data; +import org.springframework.web.servlet.HandlerInterceptor; + +/** + * 默认参数拦截器 + * @author 郭世旭 + */ +@Data +public abstract class DefaultHandlerInterceptor implements HandlerInterceptor { + + public int order; +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/framework/handler/interfaces/Count.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/framework/handler/interfaces/Count.java new file mode 100644 index 0000000..2804fcd --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/framework/handler/interfaces/Count.java @@ -0,0 +1,6 @@ +package com.ssdmn.framework.handler.interfaces; + +public interface Count { + + V count(K k); +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/framework/handler/interfaces/Getter.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/framework/handler/interfaces/Getter.java new file mode 100644 index 0000000..a76700f --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/framework/handler/interfaces/Getter.java @@ -0,0 +1,16 @@ +package com.ssdmn.framework.handler.interfaces; + +/** + * 获取器 + * + * @author 郭世旭 + */ +public interface Getter { + + /** + * 获取方法 + * + * @return + */ + V get(K k); +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/framework/handler/interfaces/Handler.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/framework/handler/interfaces/Handler.java new file mode 100644 index 0000000..d2368f8 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/framework/handler/interfaces/Handler.java @@ -0,0 +1,14 @@ +package com.ssdmn.framework.handler.interfaces; + +/** + * 处理器 + * @author 郭世旭 + */ +public interface Handler { + + /** + * 处理 + * @param t 处理参数 + */ + void handle(T t); +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/framework/handler/interfaces/List.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/framework/handler/interfaces/List.java new file mode 100644 index 0000000..6c93d7b --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/framework/handler/interfaces/List.java @@ -0,0 +1,15 @@ +package com.ssdmn.framework.handler.interfaces; + +/** + * 获取器 + * @author 郭世旭 + */ +public interface List { + + /** + * 获取方法 + * @param k 入参 + * @return + */ + java.util.List list(K k); +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/framework/handler/interfaces/Page.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/framework/handler/interfaces/Page.java new file mode 100644 index 0000000..a6f6fc7 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/framework/handler/interfaces/Page.java @@ -0,0 +1,15 @@ +package com.ssdmn.framework.handler.interfaces; + +/** + * 获取器 + * @author 郭世旭 + */ +public interface Page { + + /** + * 获取方法 + * @param k 入参 + * @return + */ + com.baomidou.mybatisplus.extension.plugins.pagination.Page page(K k); +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/framework/handler/interfaces/Sum.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/framework/handler/interfaces/Sum.java new file mode 100644 index 0000000..7b0d818 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/framework/handler/interfaces/Sum.java @@ -0,0 +1,6 @@ +package com.ssdmn.framework.handler.interfaces; + +public interface Sum { + + V sum(K k); +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/framework/handler/interfaces/Valid.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/framework/handler/interfaces/Valid.java new file mode 100644 index 0000000..f96232c --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/framework/handler/interfaces/Valid.java @@ -0,0 +1,15 @@ +package com.ssdmn.framework.handler.interfaces; + +/** + * 验证信息 + * @author 郭世旭 + */ +public interface Valid { + + /** + * 验证方法 + * @param t 需要验证的参数 + * @return 验证结果--是否通过验证 + */ + boolean isValid(T t); +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/system/annatation/Violation.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/system/annatation/Violation.java new file mode 100644 index 0000000..a742cca --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/system/annatation/Violation.java @@ -0,0 +1,50 @@ +package com.ssdmn.system.annatation; + +import cn.hutool.extra.spring.SpringUtil; +import com.ssdmn.system.component.valid.BadWordValidComponent; +import com.ssdmn.framework.handler.interfaces.Valid; + +import javax.validation.Constraint; +import javax.validation.ConstraintValidator; +import javax.validation.ConstraintValidatorContext; +import javax.validation.Payload; +import java.lang.annotation.*; + +/** + * @author 郭世旭 + */ +@Target({ElementType.FIELD,ElementType.PARAMETER}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Constraint(validatedBy = {Violation.ViolationValidator.class}) +public @interface Violation { + String message() default "请勿输入违规内容"; + + /** + * 违规信息处理类 + * + * @return 违规信息处理类 + */ + Class> handler() default BadWordValidComponent.class; + + Class[] groups() default {}; + + Class[] payload() default {}; + + class ViolationValidator implements ConstraintValidator { + + Class> verification; + + @Override + public void initialize(Violation constraintAnnotation) { + verification = constraintAnnotation.handler(); + } + + @Override + public boolean isValid(String value, ConstraintValidatorContext constraintValidatorContext) { + + return SpringUtil.getBean(verification).isValid(value); + } + } + +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/system/aop/UserOperAspect.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/system/aop/UserOperAspect.java new file mode 100644 index 0000000..2092fe7 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/system/aop/UserOperAspect.java @@ -0,0 +1,83 @@ +package com.ssdmn.system.aop; + +import com.ssdmn.system.manager.AsyncFactory; +import com.ssdmn.system.manager.AsyncManager; +import com.ssdmn.common.exception.BizException; +import com.ssdmn.common.utils.ServletUtil; +import lombok.extern.slf4j.Slf4j; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Pointcut; +import org.springframework.stereotype.Component; + +import java.time.Instant; + +/** + * 用户操作记录 + * + * @author 郭世旭 + */ +@Slf4j +@Aspect +@Component +public class UserOperAspect { + + /** + * 通过切点表达式直接指定需要拦截的package,需要拦截的class 以及 method + * 切点表达式: execution(...) + */ + @Pointcut("execution(public * com.ssdmn.biz..*.controller..*.*(..))") + public void logPointCut() { + } + + /** + * 环绕通知 @Around , 当然也可以使用 @Before (前置通知) @After (后置通知) + * + * @param point + * @return + * @throws Throwable + */ + @Around("logPointCut()") + public Object around(ProceedingJoinPoint point) throws Throwable { + long beginTime = Instant.now().toEpochMilli(); + Object result = null; + int state = 0; + String errorMsg = ""; + try { + result = point.proceed(); + } catch (BizException e) { + state = 1; + errorMsg = createErrInfo(e); + throw e; + } catch (Throwable throwable) { + state = 99; + errorMsg = createErrInfo(throwable); + throw throwable; + } finally { + AsyncManager.me().execute(AsyncFactory.getTimerTask(point, ServletUtil.getRequest(), state, Instant.now().toEpochMilli() - beginTime, errorMsg, result)); + } + + return result; + } + + private String createErrInfo(Throwable throwable) { + int count = 1; + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.append("{\"") + .append(count) + .append("\":\"") + .append(throwable.toString()) + .append("\""); + for (StackTraceElement stackTraceElement : throwable.getStackTrace()) { + stringBuilder.append(",\"") + .append(++count) + .append("\":\"") + .append(stackTraceElement.toString()) + .append("\""); + } + stringBuilder.append("}"); + + return stringBuilder.toString(); + } +} \ No newline at end of file diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/system/bean/Message.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/system/bean/Message.java new file mode 100644 index 0000000..96aaa50 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/system/bean/Message.java @@ -0,0 +1,46 @@ +package com.ssdmn.system.bean; + +import com.ssdmn.system.enums.MessageType; +import lombok.Data; + +import java.io.Serializable; +import java.time.LocalDateTime; +import java.util.UUID; + +@Data +public class Message implements Serializable { + + /** + * 信息id + */ + private String messageId; + + /** + * 通知时间 + */ + private LocalDateTime messageTime; + + /** + * 通知对象 + */ + private T data; + + /** + * 消息类型--暂时没有使用 + */ + private MessageType type; + + private static final long serialVersionUID = 1L; + + private Message(){ + } + + public static Message create(T data){ + Message message = new Message<>(); + message.setMessageId(UUID.randomUUID().toString()); + message.setMessageTime(LocalDateTime.now()); + message.setData(data); + + return message; + } +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/system/component/captcha/CaptchaProperties.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/system/component/captcha/CaptchaProperties.java new file mode 100644 index 0000000..43808b8 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/system/component/captcha/CaptchaProperties.java @@ -0,0 +1,20 @@ +package com.ssdmn.system.component.captcha; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; + +@Data +@Configuration +@ConfigurationProperties(prefix = "captcha") +public class CaptchaProperties { + + private String captchaId; + + private String secretId; + + private String secretKey; + + private String captchaApi; + +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/system/component/captcha/NECaptchaVerifier.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/system/component/captcha/NECaptchaVerifier.java new file mode 100644 index 0000000..17f9281 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/system/component/captcha/NECaptchaVerifier.java @@ -0,0 +1,93 @@ +package com.ssdmn.system.component.captcha; + +import cn.hutool.crypto.SecureUtil; +import cn.hutool.http.HttpRequest; +import com.alibaba.fastjson.JSON; +import org.apache.commons.lang3.StringUtils; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ThreadLocalRandom; + +/** + * 行为验证服务器二次验证 + * + * @author zhang + * @createTime 2022-10-18 + */ +@Component +@ConditionalOnBean(CaptchaProperties.class) +public class NECaptchaVerifier { + + @Resource + private CaptchaProperties captchaProperties; + + /** + * 二次验证 + * + * @param validate 验证码组件提交上来的NECaptchaValidate值 + * @param user 用户 + * @return + */ + public boolean verify(String validate, String user) { + if (StringUtils.isEmpty(validate) || StringUtils.equals(validate, "null")) { + return false; + } + user = (user == null) ? "" : user; // bugfix:如果user为null会出现签名错误的问题 + Map params = new HashMap(); + params.put("captchaId", captchaProperties.getCaptchaId()); + params.put("validate", validate); + params.put("user", user); + // 公共参数 + params.put("secretId", captchaProperties.getSecretId()); + params.put("version", "v2"); + params.put("timestamp", String.valueOf(System.currentTimeMillis())); + params.put("nonce", String.valueOf(ThreadLocalRandom.current().nextInt())); + // 计算请求参数签名信息 + String signature = sign(captchaProperties.getSecretKey(), params); + params.put("signature", signature); + String resp = HttpRequest.post(captchaProperties.getCaptchaApi()).form(params).execute().body(); + + return verifyRet(resp); + } + + /** + * 生成签名信息 + * + * @param secretKey 验证码私钥 + * @param params 接口请求参数名和参数值map,不包括signature参数名 + * @return + */ + public static String sign(String secretKey, Map params) { + String[] keys = params.keySet().toArray(new String[0]); + Arrays.sort(keys); + StringBuilder sb = new StringBuilder(); + for (String key : keys) { + sb.append(key).append(params.get(key).toString()); + } + sb.append(secretKey); + return SecureUtil.md5(sb.toString()); +// return DigestUtils.md5Hex(sb.toString().getBytes("UTF-8")); + } + + /** + * 验证返回结果 + * + * @param resp 返回数据 + * @return 验证成功返回true + */ + private boolean verifyRet(String resp) { + if (StringUtils.isEmpty(resp)) { + return false; + } + try { + return JSON.parseObject(resp).getBoolean("result"); + } catch (Exception ex) { + return false; + } + } +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/system/component/sensitive/Sensitive.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/system/component/sensitive/Sensitive.java new file mode 100644 index 0000000..7ed0333 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/system/component/sensitive/Sensitive.java @@ -0,0 +1,34 @@ +package com.ssdmn.system.component.sensitive; + +import com.fasterxml.jackson.annotation.JacksonAnnotationsInside; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * 数据脱敏注解 + * + * @author 郭世旭 + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +@JacksonAnnotationsInside +@JsonSerialize(using = SensitiveJsonSerializer.class) +public @interface Sensitive { + +// /** +// * 脱敏策略 +// * @return 脱敏策略 SensitiveStrategy接口的具体实现 +// */ +// Class strategy(); + + /** + * 脱敏策略 + * 比之上面实现类的方式,枚举扩展时更方便,缺点是不支持调用者扩展 + * @return 脱敏策略-枚举 + */ + SensitiveStrategies strategy(); +} \ No newline at end of file diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/system/component/sensitive/SensitiveJsonSerializer.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/system/component/sensitive/SensitiveJsonSerializer.java new file mode 100644 index 0000000..be62e9a --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/system/component/sensitive/SensitiveJsonSerializer.java @@ -0,0 +1,41 @@ +package com.ssdmn.system.component.sensitive; + +import cn.hutool.core.util.ObjectUtil; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.BeanProperty; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.ser.ContextualSerializer; +import lombok.SneakyThrows; + +import java.io.IOException; + +/** + * 数据脱敏json序列化工具 + * + * @author 郭世旭 + */ +public class SensitiveJsonSerializer extends JsonSerializer implements ContextualSerializer { + +// private SensitiveStrategy strategy; + + private SensitiveStrategies strategy; + + @Override + public void serialize(String value, JsonGenerator gen, SerializerProvider serializers) throws IOException { + + gen.writeString(strategy.getDesensitize().apply(value)); + } + + @SneakyThrows + @Override + public JsonSerializer createContextual(SerializerProvider prov, BeanProperty property) { + Sensitive annotation = property.getAnnotation(Sensitive.class); + if (ObjectUtil.isNotEmpty(annotation) && ObjectUtil.equals(String.class, property.getType().getRawClass())) { + this.strategy = annotation.strategy(); +// this.strategy = annotation.strategy().newInstance(); + return this; + } + return prov.findValueSerializer(property.getType(), property); + } +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/system/component/sensitive/SensitiveStrategies.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/system/component/sensitive/SensitiveStrategies.java new file mode 100644 index 0000000..5beab01 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/system/component/sensitive/SensitiveStrategies.java @@ -0,0 +1,52 @@ +package com.ssdmn.system.component.sensitive; + +import cn.hutool.core.util.DesensitizedUtil; +import cn.hutool.core.util.NumberUtil; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.function.Function; + +/** + * 脱敏策略 + * + * @author 郭世旭 + */ +@AllArgsConstructor +public enum SensitiveStrategies{ + + /** + * 身份证脱敏 + */ + ID_CARD(s -> DesensitizedUtil.idCardNum(s, 3, 4)), + + /** + * 手机号脱敏 + */ + PHONE(DesensitizedUtil::mobilePhone), + + /** + * 地址脱敏 + */ + ADDRESS(s -> DesensitizedUtil.address(s, 8)), + + /** + * 邮箱脱敏 + */ + EMAIL(DesensitizedUtil::email), + + /** + * 银行卡 + */ + BANK_CARD(DesensitizedUtil::bankCard), + + /** + * 每三位以逗号进行分隔 如 12,123,123 + */ + PERMIL(s -> NumberUtil.decimalFormat(",###", Long.valueOf(s))) + + ; + + @Getter + private final Function desensitize; +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/system/component/sensitive/SensitiveStrategy.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/system/component/sensitive/SensitiveStrategy.java new file mode 100644 index 0000000..70bbc8a --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/system/component/sensitive/SensitiveStrategy.java @@ -0,0 +1,14 @@ +package com.ssdmn.system.component.sensitive; + +/** + * @author 郭世旭 + */ +public interface SensitiveStrategy { + + /** + * 脱敏策略 + * @param value 需要脱敏的值 + * @return 脱敏后的值 + */ + String strategy(String value); +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/system/component/sensitive/strategy/impl/AddressStrategy.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/system/component/sensitive/strategy/impl/AddressStrategy.java new file mode 100644 index 0000000..63711e1 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/system/component/sensitive/strategy/impl/AddressStrategy.java @@ -0,0 +1,16 @@ +package com.ssdmn.system.component.sensitive.strategy.impl; + +import cn.hutool.core.util.DesensitizedUtil; +import com.ssdmn.system.component.sensitive.SensitiveStrategy; + +/** + * 地址脱敏 + * @author 郭世旭 + */ +public class AddressStrategy implements SensitiveStrategy { + @Override + public String strategy(String value) { + + return DesensitizedUtil.address(value, 8); + } +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/system/component/sensitive/strategy/impl/BankCardStrategy.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/system/component/sensitive/strategy/impl/BankCardStrategy.java new file mode 100644 index 0000000..74add55 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/system/component/sensitive/strategy/impl/BankCardStrategy.java @@ -0,0 +1,16 @@ +package com.ssdmn.system.component.sensitive.strategy.impl; + +import cn.hutool.core.util.DesensitizedUtil; +import com.ssdmn.system.component.sensitive.SensitiveStrategy; + +/** + * 地址脱敏 + * @author 郭世旭 + */ +public class BankCardStrategy implements SensitiveStrategy { + @Override + public String strategy(String value) { + + return DesensitizedUtil.address(value, 8); + } +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/system/component/sensitive/strategy/impl/EmailStrategy.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/system/component/sensitive/strategy/impl/EmailStrategy.java new file mode 100644 index 0000000..cc31770 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/system/component/sensitive/strategy/impl/EmailStrategy.java @@ -0,0 +1,18 @@ +package com.ssdmn.system.component.sensitive.strategy.impl; + +import cn.hutool.core.util.DesensitizedUtil; +import com.ssdmn.system.component.sensitive.SensitiveStrategy; + +/** + * 邮箱脱敏 + * + * @author 郭世旭 + */ +public class EmailStrategy implements SensitiveStrategy { + + @Override + public String strategy(String value) { + + return DesensitizedUtil.email(value); + } +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/system/component/sensitive/strategy/impl/IdCardStrategy.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/system/component/sensitive/strategy/impl/IdCardStrategy.java new file mode 100644 index 0000000..a561d22 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/system/component/sensitive/strategy/impl/IdCardStrategy.java @@ -0,0 +1,18 @@ +package com.ssdmn.system.component.sensitive.strategy.impl; + +import cn.hutool.core.util.DesensitizedUtil; +import com.ssdmn.system.component.sensitive.SensitiveStrategy; + + +/** + * 身份证脱敏 + * @author 郭世旭 + */ +public class IdCardStrategy implements SensitiveStrategy { + + @Override + public String strategy(String value) { + + return DesensitizedUtil.idCardNum(value, 3, 4); + } +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/system/component/sensitive/strategy/impl/PhoneStrategy.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/system/component/sensitive/strategy/impl/PhoneStrategy.java new file mode 100644 index 0000000..f977df2 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/system/component/sensitive/strategy/impl/PhoneStrategy.java @@ -0,0 +1,17 @@ +package com.ssdmn.system.component.sensitive.strategy.impl; + +import cn.hutool.core.util.DesensitizedUtil; +import com.ssdmn.system.component.sensitive.SensitiveStrategy; + +/** + * 手机号脱敏 + * + * @author 郭世旭 + */ +public class PhoneStrategy implements SensitiveStrategy { + @Override + public String strategy(String value) { + + return DesensitizedUtil.mobilePhone(value); + } +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/system/component/valid/BadWordValidComponent.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/system/component/valid/BadWordValidComponent.java new file mode 100644 index 0000000..fe84476 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/system/component/valid/BadWordValidComponent.java @@ -0,0 +1,51 @@ +package com.ssdmn.system.component.valid; + +import cn.hutool.core.io.FileUtil; +import cn.hutool.core.io.resource.ResourceUtil; +import cn.hutool.core.util.CharsetUtil; +import cn.hutool.core.util.ObjectUtil; +import com.ssdmn.framework.handler.interfaces.Valid; +import org.springframework.stereotype.Component; + +import java.net.URL; +import java.util.ArrayList; +import java.util.List; + +/** + * 违规信息验证组件 + * + * @author 郭世旭 + */ +@Component +public class BadWordValidComponent implements Valid { + private List badWord; + + @Override + public boolean isValid(String s) { + List badWord = getBadWord(); + for (String s1 : badWord) { + if (s.contains(s1) && ObjectUtil.isNotEmpty(s1)) { + return false; + } + } + return !getBadWord().contains(s); + } + + /** + * 获取违规词库 + * + * @return 违规词库 + */ + private List getBadWord() { + if (ObjectUtil.isEmpty(badWord)) { + synchronized (this) { + if (ObjectUtil.isEmpty(badWord)) { + badWord = new ArrayList<>(); + URL resource = ResourceUtil.getResource("ck/1.txt"); + badWord.addAll(FileUtil.readLines(resource, CharsetUtil.CHARSET_UTF_8)); + } + } + } + return badWord; + } +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/system/contants/Constant.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/system/contants/Constant.java new file mode 100644 index 0000000..940c9f9 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/system/contants/Constant.java @@ -0,0 +1,12 @@ +package com.ssdmn.system.contants; + +/** + * 系统常量类 + * + * @author zhang + * @createTime 2022-10-18 + */ +public class Constant { + + public static final String TOKEN = "token"; +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/system/enums/MessageType.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/system/enums/MessageType.java new file mode 100644 index 0000000..f768f51 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/system/enums/MessageType.java @@ -0,0 +1,4 @@ +package com.ssdmn.system.enums; + +public enum MessageType { +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/system/handler/MessageHandler.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/system/handler/MessageHandler.java new file mode 100644 index 0000000..ca9eb80 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/system/handler/MessageHandler.java @@ -0,0 +1,7 @@ +package com.ssdmn.system.handler; + +import com.ssdmn.system.bean.Message; +import com.ssdmn.framework.handler.interfaces.Handler; + +public interface MessageHandler extends Handler> { +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/system/manager/AsyncFactory.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/system/manager/AsyncFactory.java new file mode 100644 index 0000000..4d43aa9 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/system/manager/AsyncFactory.java @@ -0,0 +1,142 @@ +package com.ssdmn.system.manager; + +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.ReflectUtil; +import cn.hutool.http.useragent.UserAgent; +import com.alibaba.fastjson.JSON; +import com.ssdmn.biz.user.pojo.domain.UserOperLog; +import com.ssdmn.biz.user.service.UserOperLogService; +import com.ssdmn.common.interceptor.UserCache; +import com.ssdmn.common.ip.IpUtils; +import com.ssdmn.common.jwt.JWTUtil; +import com.ssdmn.common.utils.SpringUtil; +import com.ssdmn.common.utils.UserAgentUtil; +import com.ssdmn.framework.handler.interfaces.Handler; +import com.ssdmn.system.bean.Message; +import com.ssdmn.system.handler.MessageHandler; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.reflect.MethodSignature; +import org.springframework.http.HttpMethod; +import org.springframework.validation.BindingResult; +import org.springframework.web.multipart.MultipartFile; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.util.Collection; +import java.util.Map; +import java.util.TimerTask; + +import static com.ssdmn.common.result.SysConstants.SSDMN_TOKEN_KEY; + +/** + * 异步工厂--生产给线程池的异步任务 + * @author 郭世旭 + */ +public class AsyncFactory { + + public static TimerTask getTimerTask(ProceedingJoinPoint point, HttpServletRequest request, int state, + long longTime, String errorMsg, Object result){ + String url = request.getServletPath(); + String ip = IpUtils.getIpAddr(); + String method = request.getMethod(); + String queryString = request.getQueryString(); + String ssdmnToken = request.getHeader(SSDMN_TOKEN_KEY); + String agent = request.getHeader("User-Agent"); + String channelType = request.getHeader("channel-type"); + return new TimerTask(){ + @Override + public void run() { + UserOperLog fordUserOperLog = new UserOperLog(); + MethodSignature signature = (MethodSignature) point.getSignature(); + UserAgent userAgent = UserAgentUtil.parse(agent); + fordUserOperLog.setRequestMethod(method); + if (HttpMethod.PUT.name().equals(method) || HttpMethod.POST.name().equals(method)) { + String params = argsArrayToString(point.getArgs()); + fordUserOperLog.setOperParam(params); + } else { + fordUserOperLog.setOperParam(queryString); + } + if (ObjectUtil.isNotEmpty(ssdmnToken)) { + UserCache userCache = JWTUtil.parseSsdmnToken(ssdmnToken); + fordUserOperLog.setUserId(userCache.getUserId()); +// fordUserOperLog.setPhone(userCache.getPhone()); + } + fordUserOperLog.setChannelType(channelType); +// try { +// fordUserOperLog.setChannelName(ChannelTypeEnum.valueOf(channelType).getChannelName()); +// } catch (Exception e) { +//// e.printStackTrace(); +// } + fordUserOperLog.setMethodName(signature.getName()); + fordUserOperLog.setUrl(url); + fordUserOperLog.setIp(ip); + fordUserOperLog.setBrower(userAgent.getBrowser().getName()); + fordUserOperLog.setOs(userAgent.getOs().getName()); + fordUserOperLog.setStatus(state); + fordUserOperLog.setLongTime(longTime); + fordUserOperLog.setErrorMsg(errorMsg); + fordUserOperLog.setJsonResult(JSON.toJSONString(result)); +// fordUserOperLog.setToken(fordToken); + fordUserOperLog.setSsdmnToken(ssdmnToken); + SpringUtil.getBean(UserOperLogService.class).save(fordUserOperLog); + } + }; + } + + + public static TimerTask createHandleTimerTask(MessageHandler handler, Message param){ + return new TimerTask() { + @Override + public void run() { + handler.handle(param); + } + }; + } + + /** + * 参数拼装 + */ + private static String argsArrayToString(Object[] paramsArray) { + StringBuilder params = new StringBuilder(); + if (paramsArray != null && paramsArray.length > 0) { + for (Object o : paramsArray) { + if (ObjectUtil.isNotNull(o) && !isFilterObject(o)) { + try { + String jsonObj = JSON.toJSONString(o); + params.append(jsonObj).append(" "); + } catch (Exception e) { + + } + } + } + } + return params.toString().trim(); + } + + /** + * 判断是否需要过滤的对象。 + * + * @param o 对象信息。 + * @return 如果是需要过滤的对象,则返回true;否则返回false。 + */ + @SuppressWarnings("rawtypes") + private static boolean isFilterObject(final Object o) { + Class clazz = o.getClass(); + if (clazz.isArray()) { + return clazz.getComponentType().isAssignableFrom(MultipartFile.class); + } else if (Collection.class.isAssignableFrom(clazz)) { + Collection collection = (Collection) o; + for (Object value : collection) { + return value instanceof MultipartFile; + } + } else if (Map.class.isAssignableFrom(clazz)) { + Map map = (Map) o; + for (Object value : map.entrySet()) { + Map.Entry entry = (Map.Entry) value; + return entry.getValue() instanceof MultipartFile; + } + } + return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse + || o instanceof BindingResult; + } +} \ No newline at end of file diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/system/manager/AsyncManager.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/system/manager/AsyncManager.java new file mode 100644 index 0000000..ea8b3b9 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/system/manager/AsyncManager.java @@ -0,0 +1,52 @@ +package com.ssdmn.system.manager; + +import com.ssdmn.common.utils.SpringUtil; + +import java.util.TimerTask; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +/** + * 异步任务管理器 + * + * @author 郭世旭 + */ +public class AsyncManager { + /** + * 操作延迟10毫秒 + */ + private static final int OPERATE_DELAY_TIME = 10; + + /** + * 异步操作任务调度线程池 + */ + private final ScheduledExecutorService executor = SpringUtil.getBean("scheduledExecutorService"); + + /** + * 单例模式 + */ + private AsyncManager() { + } + + private static final AsyncManager ME = new AsyncManager(); + + public static AsyncManager me() { + return ME; + } + + /** + * 执行任务 + * + * @param task 任务 + */ + public void execute(TimerTask task) { + executor.schedule(task, OPERATE_DELAY_TIME, TimeUnit.MILLISECONDS); + } + + /** + * 停止任务线程池 + */ + public void shutdown() { + Threads.shutdownAndAwaitTermination(executor); + } +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/system/manager/ShutdownManager.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/system/manager/ShutdownManager.java new file mode 100644 index 0000000..710efa8 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/system/manager/ShutdownManager.java @@ -0,0 +1,33 @@ +package com.ssdmn.system.manager; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import javax.annotation.PreDestroy; + +/** + * 确保应用退出时能关闭后台线程 + * + * @author 郭世旭 + */ +@Component +@Slf4j +public class ShutdownManager { + + @PreDestroy + public void destroy() { + shutdownAsyncManager(); + } + + /** + * 停止异步执行任务 + */ + private void shutdownAsyncManager() { + try { + log.info("====关闭后台任务任务线程池===="); + AsyncManager.me().shutdown(); + } catch (Exception e) { + log.error(e.getMessage(), e); + } + } +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/system/manager/ThreadPoolConfig.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/system/manager/ThreadPoolConfig.java new file mode 100644 index 0000000..fe76734 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/system/manager/ThreadPoolConfig.java @@ -0,0 +1,66 @@ +package com.ssdmn.system.manager; + +import org.apache.commons.lang3.concurrent.BasicThreadFactory; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; + +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledThreadPoolExecutor; +import java.util.concurrent.ThreadPoolExecutor; + +/** + * 线程池配置 + * + * @author 郭世旭 + **/ +@Configuration +public class ThreadPoolConfig { + /** + * 核心线程池大小 + */ + private final int corePoolSize = 50; + + /** + * 最大可创建的线程数 + */ + private final int maxPoolSize = 200; + + /** + * 队列最大长度 + */ + private final int queueCapacity = 1000; + + /** + * 线程池维护线程所允许的空闲时间 + */ + private final int keepAliveSeconds = 300; + + @Bean(name = "threadPoolTaskExecutor") + public ThreadPoolTaskExecutor threadPoolTaskExecutor() { + ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); + executor.setMaxPoolSize(maxPoolSize); + executor.setCorePoolSize(corePoolSize); + executor.setQueueCapacity(queueCapacity); + executor.setKeepAliveSeconds(keepAliveSeconds); + // 线程池对拒绝任务(无线程可用)的处理策略 + executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); + return executor; + } + + /** + * 执行周期性或定时任务 + */ + @Bean(name = "scheduledExecutorService") + protected ScheduledExecutorService scheduledExecutorService() { + return new ScheduledThreadPoolExecutor(corePoolSize, + new BasicThreadFactory.Builder().namingPattern("schedule-pool-%d").daemon(true).build(), + new ThreadPoolExecutor.CallerRunsPolicy()) { + @Override + protected void afterExecute(Runnable r, Throwable t) { + super.afterExecute(r, t); + Threads.printException(r, t); + } + }; + } +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/system/manager/Threads.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/system/manager/Threads.java new file mode 100644 index 0000000..ec72058 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/system/manager/Threads.java @@ -0,0 +1,71 @@ +package com.ssdmn.system.manager; + +import lombok.extern.slf4j.Slf4j; + +import java.util.concurrent.*; + +/** + * 线程相关工具类. + * + * @author 郭世旭 + */ +@Slf4j +public class Threads { + /** + * sleep等待,单位为毫秒 + */ + public static void sleep(long milliseconds) { + try { + Thread.sleep(milliseconds); + } catch (InterruptedException e) { + log.error(e.getMessage()); + } + } + + /** + * 停止线程池 + * 先使用shutdown, 停止接收新任务并尝试完成所有已存在任务. + * 如果超时, 则调用shutdownNow, 取消在workQueue中Pending的任务,并中断所有阻塞函数. + * 如果仍然超時,則強制退出. + * 另对在shutdown时线程本身被调用中断做了处理. + */ + public static void shutdownAndAwaitTermination(ExecutorService pool) { + if (pool != null && !pool.isShutdown()) { + pool.shutdown(); + try { + if (!pool.awaitTermination(120, TimeUnit.SECONDS)) { + pool.shutdownNow(); + if (!pool.awaitTermination(120, TimeUnit.SECONDS)) { + log.info("Pool did not terminate"); + } + } + } catch (InterruptedException ie) { + pool.shutdownNow(); + Thread.currentThread().interrupt(); + } + } + } + + /** + * 打印线程异常信息 + */ + public static void printException(Runnable r, Throwable t) { + if (t == null && r instanceof Future) { + try { + Future future = (Future) r; + if (future.isDone()) { + future.get(); + } + } catch (CancellationException ce) { + t = ce; + } catch (ExecutionException ee) { + t = ee.getCause(); + } catch (InterruptedException ie) { + Thread.currentThread().interrupt(); + } + } + if (t != null) { + log.error(t.getMessage(), t); + } + } +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/system/mapper/IgnoreBaseMapper.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/system/mapper/IgnoreBaseMapper.java new file mode 100644 index 0000000..823a771 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/system/mapper/IgnoreBaseMapper.java @@ -0,0 +1,83 @@ +package com.ssdmn.system.mapper; + +import com.baomidou.mybatisplus.annotation.InterceptorIgnore; +import com.baomidou.mybatisplus.core.conditions.Wrapper; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import org.apache.ibatis.annotations.Param; + +import java.io.Serializable; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +public interface IgnoreBaseMapper extends BaseMapper { + + @Override + @InterceptorIgnore(tenantLine = "true") + int insert(T entity); + + @Override + @InterceptorIgnore(tenantLine = "true") + int deleteById(Serializable id); + + @Override + @InterceptorIgnore(tenantLine = "true") + int deleteByMap(@Param("cm") Map columnMap); + + @Override + @InterceptorIgnore(tenantLine = "true") + int delete(@Param("ew") Wrapper queryWrapper); + + @Override + @InterceptorIgnore(tenantLine = "true") + int deleteBatchIds(@Param("coll") Collection idList); + + @Override + @InterceptorIgnore(tenantLine = "true") + int updateById(@Param("et") T entity); + + @Override + @InterceptorIgnore(tenantLine = "true") + int update(@Param("et") T entity, @Param("ew") Wrapper updateWrapper); + + @Override + @InterceptorIgnore(tenantLine = "true") + T selectById(Serializable id); + + @Override + @InterceptorIgnore(tenantLine = "true") + List selectBatchIds(@Param("coll") Collection idList); + + @Override + @InterceptorIgnore(tenantLine = "true") + List selectByMap(@Param("cm") Map columnMap); + + @Override + @InterceptorIgnore(tenantLine = "true") + T selectOne(@Param("ew") Wrapper queryWrapper); + + @Override + @InterceptorIgnore(tenantLine = "true") + Integer selectCount(@Param("ew") Wrapper queryWrapper); + + @Override + @InterceptorIgnore(tenantLine = "true") + List selectList(@Param("ew") Wrapper queryWrapper); + + @Override + @InterceptorIgnore(tenantLine = "true") + List> selectMaps(@Param("ew") Wrapper queryWrapper); + + @Override + @InterceptorIgnore(tenantLine = "true") + List selectObjs(@Param("ew") Wrapper queryWrapper); + + @Override + @InterceptorIgnore(tenantLine = "true") + > E selectPage(E page, @Param("ew") Wrapper queryWrapper); + + @Override + @InterceptorIgnore(tenantLine = "true") + >> E selectMapsPage(E page, @Param("ew") Wrapper queryWrapper); +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/system/restrict/pojo/dto/AddRestrictInfoRequest.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/system/restrict/pojo/dto/AddRestrictInfoRequest.java new file mode 100644 index 0000000..431827e --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/system/restrict/pojo/dto/AddRestrictInfoRequest.java @@ -0,0 +1,37 @@ +package com.ssdmn.system.restrict.pojo.dto; + +import com.ssdmn.common.restrict.enums.BlacklistOrWhiteListEnum; +import com.ssdmn.common.restrict.enums.RestrictTypeEnum; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; + +@Data +public class AddRestrictInfoRequest { + + /** + * 数据 + */ + @ApiModelProperty("数据:IP/手机号") + @NotBlank + private String data; + + /** + * 类型 + */ + @ApiModelProperty("类型") + @NotNull + private RestrictTypeEnum type; + + /** + * 黑白名单 + */ + @ApiModelProperty("黑白名单") + @NotNull + private BlacklistOrWhiteListEnum blacklistOrWhitelist; + + @ApiModelProperty("备注") + private String remark; +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/system/restrict/strategy/impl/CaptchaRestrictStrategy.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/system/restrict/strategy/impl/CaptchaRestrictStrategy.java new file mode 100644 index 0000000..6a601a5 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/system/restrict/strategy/impl/CaptchaRestrictStrategy.java @@ -0,0 +1,42 @@ +package com.ssdmn.system.restrict.strategy.impl; + +import cn.hutool.core.util.ObjectUtil; +import com.ssdmn.system.component.captcha.NECaptchaVerifier; +import com.ssdmn.common.exception.BizException; +import com.ssdmn.common.restrict.strategy.RestrictStrategy; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; +import javax.servlet.http.HttpServletRequest; + +import static com.ssdmn.system.contants.Constant.TOKEN; + +@Component +@Slf4j +public class CaptchaRestrictStrategy implements RestrictStrategy { + + @Resource + private NECaptchaVerifier captchaVerifier; + + @Override + public boolean isValid(HttpServletRequest request) { + String captchaValidateParams = request.getHeader("captchaValidate"); + String token = request.getHeader(TOKEN); + if (!ObjectUtil.isAllNotEmpty(captchaValidateParams, token)) { + log.error("captchaValidate和token不能为空"); + return false; + } + if (!captchaVerifier.verify(captchaValidateParams, token)) { + log.error("二次验证失败"); + return false; + } + + return true; + } + + @Override + public String getMessage() { + return "行为验证失败"; + } +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/system/restrict/strategy/impl/IpRestrictStrategy.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/system/restrict/strategy/impl/IpRestrictStrategy.java new file mode 100644 index 0000000..90c3fd1 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/system/restrict/strategy/impl/IpRestrictStrategy.java @@ -0,0 +1,24 @@ +package com.ssdmn.system.restrict.strategy.impl; + +import com.ssdmn.common.restrict.strategy.RestrictStrategy; +import com.ssdmn.common.ip.IpUtils; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; +import javax.servlet.http.HttpServletRequest; + +@Component +public class IpRestrictStrategy implements RestrictStrategy { + + + @Override + public boolean isValid(HttpServletRequest request) { + + return true; + } + + @Override + public String getMessage() { + return "ip not on the white list"; + } +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/system/restrict/strategy/impl/PhoneRestrictStrategy.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/system/restrict/strategy/impl/PhoneRestrictStrategy.java new file mode 100644 index 0000000..897bd79 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/system/restrict/strategy/impl/PhoneRestrictStrategy.java @@ -0,0 +1,20 @@ +package com.ssdmn.system.restrict.strategy.impl; + +import com.ssdmn.common.restrict.strategy.RestrictStrategy; +import org.springframework.stereotype.Component; + +import javax.servlet.http.HttpServletRequest; + +@Component +public class PhoneRestrictStrategy implements RestrictStrategy { + + @Override + public boolean isValid(HttpServletRequest request) { + return true; + } + + @Override + public String getMessage() { + return "黑名单用户"; + } +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/system/restrict/strategy/impl/SignRestrictStrategy.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/system/restrict/strategy/impl/SignRestrictStrategy.java new file mode 100644 index 0000000..c1b53a7 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/system/restrict/strategy/impl/SignRestrictStrategy.java @@ -0,0 +1,70 @@ +package com.ssdmn.system.restrict.strategy.impl; + +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.crypto.SecureUtil; +import com.ssdmn.common.exception.BizException; +import com.ssdmn.common.http.HttpHelper; +import com.ssdmn.common.redis.service.RedisService; +import com.ssdmn.common.restrict.strategy.RestrictStrategy; +import com.ssdmn.config.filter.RepeatedlyRequestWrapper; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; +import javax.servlet.http.HttpServletRequest; + +@Component +@Slf4j +public class SignRestrictStrategy implements RestrictStrategy { + + private static final String APP_KEY = "ssdmn-chat-gpt-api-yz"; + private static final Long EFFECTIVE_TIME_OF_SECONDS = 1800L; + + @Resource + private RedisService redisCache; + + @Override + public boolean isValid(HttpServletRequest request) { + if (request instanceof RepeatedlyRequestWrapper) { + RepeatedlyRequestWrapper repeatedlyRequest = (RepeatedlyRequestWrapper) request; + String bodyString = HttpHelper.getBodyString(repeatedlyRequest); + String sign = request.getHeader("sign"); + String timestampString = request.getHeader("timestamp"); + if (ObjectUtil.isEmpty(sign) || ObjectUtil.isEmpty(timestampString)) { + log.error("sign和timestamp不能为空"); + return false; + } + long timestamp = Long.parseLong(timestampString); + long currentTimeMillis = System.currentTimeMillis(); + // 当前时间大于请求时间或者超过60秒即为验证失败 + if (Math.abs(currentTimeMillis - timestamp) > EFFECTIVE_TIME_OF_SECONDS * 1000) { + log.error("timestamp验证失败"); + return false; + } + String md5 = SecureUtil.md5(StrUtil.format("{}{}{}{}", APP_KEY, bodyString, timestamp, APP_KEY)).toLowerCase(); + log.info("sign=>{} md5=>{}", sign, md5); + if (ObjectUtil.notEqual(md5, sign)) { + log.error("签名不一致,验证失败"); + return false; + } + // redis中以sign为key取值,不为空时即为验证失败 + String signKey = redisCache.getStr(sign); + if (ObjectUtil.isNotEmpty(signKey)) { + log.error("sign已使用,请勿二次提交"); + return false; + } + // redis中以sign为key存值,60秒后失效 + redisCache.set(sign, "ok", EFFECTIVE_TIME_OF_SECONDS); + + return true; + } + + return true; + } + + @Override + public String getMessage() { + return "签名验证失败"; + } +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/web/pojo/dto/login/LoginDTO.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/web/pojo/dto/login/LoginDTO.java new file mode 100644 index 0000000..a5c6b5a --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/web/pojo/dto/login/LoginDTO.java @@ -0,0 +1,28 @@ +package com.ssdmn.web.pojo.dto.login; + +import com.ssdmn.biz.login.pojo.dto.UserLoginDTO; +import lombok.Data; + + +/** + * @description: 登录DTO,包括用户信息和合伙人信息 + * @author: 郭世旭 + **/ +@Data +public class LoginDTO extends UserLoginDTO { + /** + * 创建渠道 + */ + private Integer createWay; + + + /** + * 上级id + */ + private Long superiorId; + + /** + * 创建说明 + */ + private String remark; +} diff --git a/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/web/pojo/dto/login/WxLoginRedisDTO.java b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/web/pojo/dto/login/WxLoginRedisDTO.java new file mode 100644 index 0000000..d828437 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/java/com/ssdmn/web/pojo/dto/login/WxLoginRedisDTO.java @@ -0,0 +1,16 @@ +package com.ssdmn.web.pojo.dto.login; + +import lombok.Data; + + +/** + * @description: 登录DTO,包括用户信息和合伙人信息 + * @author: 郭世旭 + **/ +@Data +public class WxLoginRedisDTO extends LoginDTO { + /** + * 用户登录渠道 + */ + Long userLoginId; +} diff --git a/ssdmn-chat-gpt-api/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/ssdmn-chat-gpt-api/src/main/resources/META-INF/additional-spring-configuration-metadata.json new file mode 100644 index 0000000..b670d59 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -0,0 +1,8 @@ +{ + "properties": [ + { + "name": "ribbon.ConnectTimeout", + "type": "java.lang.String", + "description": "Description for ribbon.ConnectTimeout." + } +] } \ No newline at end of file diff --git a/ssdmn-chat-gpt-api/src/main/resources/application-dev.yml b/ssdmn-chat-gpt-api/src/main/resources/application-dev.yml new file mode 100644 index 0000000..2af3e19 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/resources/application-dev.yml @@ -0,0 +1,111 @@ +spring: + datasource: + url: jdbc:mysql://124.223.98.178:3306/gupiao?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowMultiQueries=true&autoReconnect=true&serverTimezone=Asia/Shanghai +# url: jdbc:mysql://localhost:3306/gupiao?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowMultiQueries=true&autoReconnect=true&serverTimezone=Asia/Shanghai + username: root +# password: admin + password: 1qazse42W3 + driver-class-name: com.mysql.cj.jdbc.Driver + type: com.zaxxer.hikari.HikariDataSource + hikari: + minimum-idle: 3 + auto-commit: true + idle-timeout: 10000 + pool-name: DatebookHikariCP + max-lifetime: 1800000 + connection-timeout: 60000 + redis: + host: 118.31.244.48 + port: 6379 + password: dalu1688 + database: 0 + timeout: 10000 + jedis: + pool: + max-active: 50 + max-idle: 20 + min-idle: 10 + max-wait: 10000 + time-between-eviction-runs: 10000 + +server: + port: 8001 + +#定义文件存放的位置 +file: + path: D:/temporary-hotPot-fileUpload/ + qr: + path: ${file.path}/qr + file-name: ${file.qr.path}/qr.jpg + +#阿里云配置 +alibaba: + cloud: + region-id: cn-hangzhou + access-key: LTAIHdZiWxQEsFvc + secret-key: qSnrkjCbQZnsQacGOEJTtJdAwQ9u2Q + #短信配置 + sms: + #短信签名 + sign-name: 重庆幺零八零科技有限公司 + #短信模板 + template-code: SMS_152880348 + #验证码长度 + length: 6 + #验证码有效时长 单位:秒 + valid-time: 300 + up-queue-name: + report-queue-name: + + + +#支付配置配置 +pay: + #微信 + wx: + base: + #商户号 + mchId: 1640317532 + #商户号秘钥 有些地方写作mchKey + partnerKey: 357fa0ea410eaae86c4552a664b1e29f + #证书地址 + certPath: + #域名 + domain: http://guoshixu.frpgz1.idcfengye.com/ssdmn-chat-gpt-api + #支付成功回调地址 + payNotify: /wxPay/payNotify + #退款回调地址 + refundNotify: /wxPay/refundNotify + app: + appId: + secret: + h5: + appId: wx43d260d2d8f8ac92 + mini: + appId: wxd61b85a1a1452045 + secret: bdc05ff53f44383c8e86c42a9340a943 + publishNumber: + appId: wx7b51af75ccf64e88 + secret: 1bd666c4fc5f78047c372bac24a720b5 + token: + aesKey: +# 微信公众平台和开放平台配置 +wx: + mp: + useRedis: true + appId: wx7b51af75ccf64e88 # 第一个公众号的appid + secret: 1bd666c4fc5f78047c372bac24a720b5 # 公众号的appsecret + openAppId: wx43bda2bbf1de376f #微信开放平台的appid + # openSecret: a172f958a77241d9b1d0a5023bb81c7b #微信开放平台的secret + openSecret: 0d56caf0679c57e3e1c66d30abda3ca7 #微信开放平台的secret + maAppId: wx5a0321621b214585 #微信小程序的appid + maSecret: 416579fa9ef56d9b1bdeb1dad033ec3d #微信小程序的secret + token: # 接口配置里的Token值 + aesKey: # 接口配置里的EncodingAESKey值 +payTrueTotal: true +transferTrueTotal: false +#spring boot集成mybatis的方式打印sql +mybatis-plus: + configuration: + log-impl: org.apache.ibatis.logging.stdout.StdOutImpl #开启sql日志 + diff --git a/ssdmn-chat-gpt-api/src/main/resources/application-prod.yml b/ssdmn-chat-gpt-api/src/main/resources/application-prod.yml new file mode 100644 index 0000000..6ef78b1 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/resources/application-prod.yml @@ -0,0 +1,104 @@ +spring: + datasource: + url: jdbc:mysql://127.0.0.1:3306/gupiao?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowMultiQueries=true&autoReconnect=true&serverTimezone=Asia/Shanghai + username: root + password: 1qazse42W3 + driver-class-name: com.mysql.cj.jdbc.Driver + type: com.zaxxer.hikari.HikariDataSource + hikari: + minimum-idle: 3 + auto-commit: true + idle-timeout: 10000 + pool-name: DatebookHikariCP + max-lifetime: 1800000 + connection-timeout: 60000 + redis: + host: 127.0.0.1 + port: 6379 + password: + database: 10 + timeout: 10000 + jedis: + pool: + max-active: 50 + max-idle: 20 + min-idle: 10 + max-wait: 10000 + time-between-eviction-runs: 10000 + +server: + port: 8000 + +#定义文件存放的位置 +file: + path: /opt/ssdmn/temporary/fileUpload/ + qr: + path: ${file.path}/qr + file-name: ${file.qr.path}/qr.jpg + +#阿里云配置 +alibaba: + cloud: + region-id: cn-hangzhou + access-key: LTAIHdZiWxQEsFvc + secret-key: qSnrkjCbQZnsQacGOEJTtJdAwQ9u2Q + #短信配置 + sms: + #短信签名 + sign-name: + #短信模板 + template-code: + #验证码长度 + length: 6 + #验证码有效时长 单位:秒 + valid-time: 300 + up-queue-name: + report-queue-name: + + + +#支付配置配置 +pay: + #微信 + wx: + base: + #商户号 + mchId: + #商户号秘钥 有些地方写作mchKey + partnerKey: + #证书地址 + certPath: + #域名 + domain: http://43.163.241.233:8000/ssdmn-chat-gpt-api + #支付成功回调地址 + payNotify: /wxPay/payNotify + #退款回调地址 + refundNotify: /wxPay/refundNotify + app: + appId: + secret: + h5: + appId: + mini: + appId: + secret: + publishNumber: + appId: + secret: + token: + aesKey: +# 微信公众平台和开放平台配置 +wx: + mp: + useRedis: true + appId: # 第一个公众号的appid + secret: # 公众号的appsecret + openAppId: #微信开放平台的appid + # openSecret: #微信开放平台的secret + openSecret: #微信开放平台的secret + maAppId: #微信小程序的appid + maSecret: #微信小程序的secret + token: # 接口配置里的Token值 + aesKey: # 接口配置里的EncodingAESKey值 +payTrueTotal: true +transferTrueTotal: false \ No newline at end of file diff --git a/ssdmn-chat-gpt-api/src/main/resources/application-test.yml b/ssdmn-chat-gpt-api/src/main/resources/application-test.yml new file mode 100644 index 0000000..6e9ecfe --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/resources/application-test.yml @@ -0,0 +1,104 @@ +spring: + datasource: + url: jdbc:mysql://127.0.0.1:3306/gupiao?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowMultiQueries=true&autoReconnect=true&serverTimezone=Asia/Shanghai + username: root + password: 1qazse42W3 + driver-class-name: com.mysql.cj.jdbc.Driver + type: com.zaxxer.hikari.HikariDataSource + hikari: + minimum-idle: 3 + auto-commit: true + idle-timeout: 10000 + pool-name: DatebookHikariCP + max-lifetime: 1800000 + connection-timeout: 60000 + redis: + host: 127.0.0.1 + port: 6379 + password: dalu1688 + database: 0 + timeout: 10000 + jedis: + pool: + max-active: 50 + max-idle: 20 + min-idle: 10 + max-wait: 10000 + time-between-eviction-runs: 10000 + +#定义文件存放的位置 +file: + path: /opt/ssdmn/temporary/fileUpload/ + qr: + path: ${file.path}/qr + file-name: ${file.qr.path}/qr.jpg + +#阿里云配置 +alibaba: + cloud: + region-id: cn-hangzhou + access-key: LTAIHdZiWxQEsFvc + secret-key: qSnrkjCbQZnsQacGOEJTtJdAwQ9u2Q + #短信配置 + sms: + #短信签名 + sign-name: + #短信模板 + template-code: + #验证码长度 + length: 6 + #验证码有效时长 单位:秒 + valid-time: 300 + up-queue-name: + report-queue-name: + + + +#支付配置配置 +pay: + #微信 + wx: + base: + #商户号 + mchId: 1609323802 + #商户号秘钥 有些地方写作mchKey + partnerKey: 3YIR9oI6TNHTEYTewnz8FPG4sXgyMlB1 + #证书地址 + certPath: static/cert/apiclient_cert.p12 + #域名 + domain: http://guoshixu.vipgz1.91tunnel.com + #支付成功回调地址 + payNotify: /wxPay/payNotify + #退款回调地址 + refundNotify: /wxPay/refundNotify + app: + appId: wx43bda2bbf1de376f + secret: 0d56caf0679c57e3e1c66d30abda3ca7 + h5: + appId: wx43d260d2d8f8ac92 + mini: + appId: wx5a0321621b214585 + secret: 416579fa9ef56d9b1bdeb1dad033ec3d + publishNumber: + appId: wxbdbb73d6836a76e1 + secret: a4017afcba6b6f10db0c43b37f76fccc + token: + aesKey: +# 微信公众平台和开放平台配置 +wx: + mp: + useRedis: true + appId: wxbdbb73d6836a76e1 # 第一个公众号的appid + secret: a4017afcba6b6f10db0c43b37f76fccc # 公众号的appsecret + openAppId: wx43bda2bbf1de376f #微信开放平台的appid + # openSecret: a172f958a77241d9b1d0a5023bb81c7b #微信开放平台的secret + openSecret: 0d56caf0679c57e3e1c66d30abda3ca7 #微信开放平台的secret + maAppId: wx5a0321621b214585 #微信小程序的appid + maSecret: 416579fa9ef56d9b1bdeb1dad033ec3d #微信小程序的secret + token: # 接口配置里的Token值 + aesKey: # 接口配置里的EncodingAESKey值 +payTrueTotal: false +transferTrueTotal: false + +server: + port: 8101 diff --git a/ssdmn-chat-gpt-api/src/main/resources/application.yml b/ssdmn-chat-gpt-api/src/main/resources/application.yml new file mode 100644 index 0000000..44dff01 --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/resources/application.yml @@ -0,0 +1,98 @@ +spring: + main: + allow-circular-references: true + profiles: + # active: prod + active: dev + servlet: + multipart: + max-file-size: 120MB + max-request-size: 120MB + mvc: + pathmatch: + matching-strategy: ant_path_matcher + throw-exception-if-no-handler-found: true + servlet: + load-on-startup: 1 + jackson: + date-format: yyyy-MM-dd HH:mm:ss + time-zone: GMT+8 + generator: + #write-numbers-as-strings: true + write-bigdecimal-as-plain: true + # default-property-inclusion: non_null + application: + name: ssdmn-gupiao-api + web: + resources: + add-mappings: false + +server: + port: 8000 + servlet: + context-path: /${spring.application.name} + +ribbon: + ConnectTimeout: 10000 # ribbon链接超时时长 + ReadTimeout: 10000 # ribbon读取超时时间长 + eager-load: + enabled: true + clients: + +#Mybatis-plus 配置 +mybatis-plus: + mapper-locations: classpath*:com/ford/**/sqlmap/*.xml + executor-type: simple +# configuration: +# log-impl: org.apache.ibatis.logging.stdout.StdOutImpl + +# 防止XSS攻击 +xss: + # 过滤开关 + enabled: true + # 排除链接(多个用逗号分隔) + excludes: + # 匹配链接 + urlPatterns: /controller/* + +#阿里云配置 +alibaba: + cloud: + region-id: cn-hangzhou + access-key: LTAI5tMzTFSAEyFY6xVfCjaQ0 + secret-key: I9DyPPhYoFDjQ1bAzmJGqLtEg3SKKB0 + oss: + #如不需要OSS 则保留此项配置 + # enabled: false + #上传外网地址 线上替换上成内网地址 + # endpoint: https://oss-cn-hangzhou.aliyuncs.com + endpoint: https://fileupload.cqdlcy.com1 + bucket-name: dl-hot-pot1 + #基础路径 + base-dir: files + #永久文件路径 + everlast-dir: ${alibaba.cloud.oss.base-dir}/everlast + #临时文件路径 + temp-dir: ${alibaba.cloud.oss.base-dir}/tmp + #文件访问地址 + #domain: https://youruan-video-test.oss-cn-hangzhou.aliyuncs.com + #自定义域名 + domain: https://fileupload.cqdlcy.com1 + #回调地址 + call-back: + #超时时间(s) + expire-time: 30 + config: + supportCname: true + #短信配置 + sms: + #短信签名 + sign-name: cheers + #短信模板 + template-code: SMS_2553901420 + #验证码长度 + length: 6 + #验证码有效时长 单位:秒 + valid-time: 300 + up-queue-name: + report-queue-name: \ No newline at end of file diff --git a/ssdmn-chat-gpt-api/src/main/resources/ck/1.txt b/ssdmn-chat-gpt-api/src/main/resources/ck/1.txt new file mode 100644 index 0000000..e69de29 diff --git a/ssdmn-chat-gpt-api/src/main/resources/logback-spring.xml b/ssdmn-chat-gpt-api/src/main/resources/logback-spring.xml new file mode 100644 index 0000000..698c57f --- /dev/null +++ b/ssdmn-chat-gpt-api/src/main/resources/logback-spring.xml @@ -0,0 +1,84 @@ + + + + + + + + + + + + + + ${CONSOLE_LOG_PATTERN} + + + + + + + ERROR + DENY + ACCEPT + + + + ${log.path}/api-info.%d{yyyy-MM-dd}-%i.log + + 15 + + + 64MB + + + + + + UTF-8 + + %d{yyyy-MM-dd HH:mm:ss.SSS} [%X{traceId}] %-5level %logger{50} - %msg%n + + + + + + + ERROR + + + [%d{yyyy-MM-dd' 'HH:mm:ss.sss}] [%C] [%t] [%L] [%-5p] %m%n + + + + + ${log.path}/api-error.%d{yyyy-MM-dd}-%i.log + + 0 + + + 250MB + + + + + UTF-8 + + %d{yyyy-MM-dd HH:mm:ss.SSS} [%X{traceId}] %-5level %logger{50} - %msg%n + + + + + + + + + + + + + + +